feat(ocr): add self-hosted OCR engine support (Tesseract, EasyOCR) with multi-provider cross-checking

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-23 20:59:43 +00:00
parent 9bba1f025f
commit 1f843fe460
7 changed files with 892 additions and 9 deletions
+43
View File
@@ -139,6 +139,27 @@
Enable {{ setting.key.replace('_', ' ').title() }}
</label>
</div>
{% elif setting.metadata.type == 'multiselect' and setting.metadata.options %}
<!-- Multi-select checkboxes: stored as comma-separated string -->
<div class="flex flex-wrap gap-2 mt-1">
{% for opt in setting.metadata.options %}
<label class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full border cursor-pointer select-none
transition-colors
bg-white border-gray-300 hover:border-blue-400"
:class="isSelected('{{ setting.key }}', '{{ opt }}')
? 'bg-blue-100 border-blue-500 text-blue-800 font-medium'
: 'text-gray-700'">
<input type="checkbox"
class="sr-only"
:checked="isSelected('{{ setting.key }}', '{{ opt }}')"
@change="toggleMultiselect('{{ setting.key }}', '{{ opt }}', $event.target.checked)"
/>
<i :class="isSelected('{{ setting.key }}', '{{ opt }}') ? 'fas fa-check-circle text-blue-600' : 'far fa-circle text-gray-400'"></i>
<span class="text-sm">{{ opt }}</span>
</label>
{% endfor %}
</div>
<p class="mt-1 text-xs text-gray-400">Effective value: <code x-text="formData['{{ setting.key }}'] || '(none)'"></code></p>
{% elif setting.metadata.options %}
<!-- Dropdown Select for fields with a fixed list of values -->
<select
@@ -280,6 +301,28 @@ function settingsApp() {
{% endfor %}
},
/** Parse a comma-separated multiselect value into a trimmed array. */
parseMultiselect(key) {
return (this.formData[key] || '').split(',').map(s => s.trim()).filter(Boolean);
},
/** Return true when *option* is present in the comma-separated *key* value. */
isSelected(key, option) {
return this.parseMultiselect(key).includes(option);
},
/** Add or remove *option* from the comma-separated *key* value. */
toggleMultiselect(key, option, checked) {
const current = this.parseMultiselect(key);
if (checked) {
if (!current.includes(option)) current.push(option);
} else {
const idx = current.indexOf(option);
if (idx !== -1) current.splice(idx, 1);
}
this.formData[key] = current.join(',');
},
togglePassword(key) {
this.showPassword[key] = !this.showPassword[key];
},