diff --git a/apps/client/src/widgets/type_widgets/options/images/images.ts b/apps/client/src/widgets/type_widgets/options/images/images.ts index 904cd4eb6..50e14cf66 100644 --- a/apps/client/src/widgets/type_widgets/options/images/images.ts +++ b/apps/client/src/widgets/type_widgets/options/images/images.ts @@ -17,6 +17,37 @@ const TPL = /*html*/` .batch-ocr-button { margin-top: 10px; } + .ocr-language-checkboxes { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 8px; + margin-bottom: 10px; + max-height: 200px; + overflow-y: auto; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 10px; + } + .ocr-language-display { + background-color: #f8f9fa; + min-height: 38px; + padding: 8px 12px; + border: 1px solid #dee2e6; + border-radius: 4px; + font-family: monospace; + font-size: 0.9em; + } + .ocr-language-display .placeholder-text { + color: #6c757d; + font-style: italic; + } + .ocr-language-display .language-code { + background-color: #e9ecef; + padding: 2px 6px; + border-radius: 3px; + margin-right: 4px; + font-weight: 500; + }

${t("images.images_section_title")}

@@ -72,23 +103,76 @@ const TPL = /*html*/`
- +

${t("images.ocr_multi_language_description")}

+
+ + + + + + + + + + + + + + + + +
+
+ ${t("images.ocr_no_languages_selected")} +
@@ -130,7 +214,8 @@ export default class ImageOptions extends OptionsWidget { // OCR elements private $ocrEnabled!: JQuery; private $ocrAutoProcess!: JQuery; - private $ocrLanguage!: JQuery; + private $ocrLanguageCheckboxes!: JQuery; + private $ocrLanguageDisplay!: JQuery; private $ocrMinConfidence!: JQuery; private $ocrSettingsWrapper!: JQuery; private $batchOcrButton!: JQuery; @@ -164,7 +249,8 @@ export default class ImageOptions extends OptionsWidget { // OCR settings this.$ocrEnabled = this.$widget.find(".ocr-enabled"); this.$ocrAutoProcess = this.$widget.find(".ocr-auto-process"); - this.$ocrLanguage = this.$widget.find(".ocr-language"); + this.$ocrLanguageCheckboxes = this.$widget.find(".ocr-language-checkboxes"); + this.$ocrLanguageDisplay = this.$widget.find(".ocr-language-display"); this.$ocrMinConfidence = this.$widget.find(".ocr-min-confidence"); this.$ocrSettingsWrapper = this.$widget.find(".ocr-settings-wrapper"); this.$batchOcrButton = this.$widget.find(".batch-ocr-button"); @@ -179,7 +265,7 @@ export default class ImageOptions extends OptionsWidget { this.$ocrAutoProcess.on("change", () => this.updateCheckboxOption("ocrAutoProcessImages", this.$ocrAutoProcess)); - this.$ocrLanguage.on("change", () => this.updateOption("ocrLanguage", this.$ocrLanguage.val())); + this.$ocrLanguageCheckboxes.on("change", "input[type='checkbox']", () => this.updateOcrLanguages()); this.$ocrMinConfidence.on("change", () => this.updateOption("ocrMinConfidence", String(this.$ocrMinConfidence.val()).trim() || "0.6")); @@ -197,7 +283,7 @@ export default class ImageOptions extends OptionsWidget { // OCR settings this.setCheckboxState(this.$ocrEnabled, options.ocrEnabled); this.setCheckboxState(this.$ocrAutoProcess, options.ocrAutoProcessImages); - this.$ocrLanguage.val(options.ocrLanguage || "eng"); + this.setOcrLanguages(options.ocrLanguage || "eng"); this.$ocrMinConfidence.val(options.ocrMinConfidence || "0.6"); this.setImageCompression(); @@ -220,6 +306,59 @@ export default class ImageOptions extends OptionsWidget { } } + setOcrLanguages(languageString: string) { + // Clear all checkboxes first + this.$ocrLanguageCheckboxes.find('input[type="checkbox"]').prop('checked', false); + + if (languageString) { + // Split by '+' to handle multi-language format like "ron+eng" + const languages = languageString.split('+'); + + languages.forEach(lang => { + const checkbox = this.$ocrLanguageCheckboxes.find(`input[data-language="${lang.trim()}"]`); + if (checkbox.length > 0) { + checkbox.prop('checked', true); + } + }); + } + + this.updateOcrLanguageDisplay(); + } + + updateOcrLanguages() { + const selectedLanguages: string[] = []; + + this.$ocrLanguageCheckboxes.find('input[type="checkbox"]:checked').each(function() { + selectedLanguages.push($(this).val() as string); + }); + + // Join with '+' for Tesseract multi-language format + const languageString = selectedLanguages.join('+'); + + this.updateOption("ocrLanguage", languageString || "eng"); + this.updateOcrLanguageDisplay(); + } + + updateOcrLanguageDisplay() { + const selectedLanguages: string[] = []; + + this.$ocrLanguageCheckboxes.find('input[type="checkbox"]:checked').each(function() { + selectedLanguages.push($(this).val() as string); + }); + + const displayContent = this.$ocrLanguageDisplay.find('.placeholder-text, .language-code'); + displayContent.remove(); + + if (selectedLanguages.length === 0) { + this.$ocrLanguageDisplay.html(`${t("images.ocr_no_languages_selected")}`); + } else { + const languageTags = selectedLanguages.map(lang => + `${lang}` + ).join(''); + this.$ocrLanguageDisplay.html(languageTags); + } + } + async startBatchOcr() { this.$batchOcrButton.prop("disabled", true); this.$batchOcrProgress.show();