diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 8a5884297..a5d715e50 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2011,6 +2011,10 @@ "no_text_explanation": "This note has not been processed for OCR text extraction or no text was found.", "failed_to_load": "Failed to load OCR text", "extracted_on": "Extracted on: {{date}}", - "unknown_date": "Unknown" + "unknown_date": "Unknown", + "process_now": "Process OCR", + "processing": "Processing...", + "processing_started": "OCR processing has been started. Please wait a moment and refresh.", + "processing_failed": "Failed to start OCR processing" } } diff --git a/apps/client/src/widgets/type_widgets/read_only_ocr_text.ts b/apps/client/src/widgets/type_widgets/read_only_ocr_text.ts index bdb425fed..c94f896a5 100644 --- a/apps/client/src/widgets/type_widgets/read_only_ocr_text.ts +++ b/apps/client/src/widgets/type_widgets/read_only_ocr_text.ts @@ -64,6 +64,10 @@ const TPL = /*html*/` border-radius: 4px; margin-top: 10px; } + + .ocr-process-button { + margin-top: 15px; + }
@@ -72,6 +76,8 @@ const TPL = /*html*/`
+
+
`; @@ -86,7 +92,9 @@ interface OCRResponse { export default class ReadOnlyOCRTextWidget extends TypeWidget { private $content!: JQuery; + private $actions!: JQuery; private $meta!: JQuery; + private currentNote?: FNote; static getType() { return "readOnlyOCRText"; @@ -96,16 +104,20 @@ export default class ReadOnlyOCRTextWidget extends TypeWidget { this.$widget = $(TPL); this.contentSized(); this.$content = this.$widget.find(".ocr-text-content"); + this.$actions = this.$widget.find(".ocr-text-actions"); this.$meta = this.$widget.find(".ocr-text-meta"); super.doRender(); } async doRefresh(note: FNote) { + this.currentNote = note; + // Show loading state this.$content.html(`
${t("ocr.loading_text")}
`); + this.$actions.empty(); this.$meta.empty(); try { @@ -117,10 +129,7 @@ export default class ReadOnlyOCRTextWidget extends TypeWidget { } if (!response.hasOcr || !response.text) { - this.$content.html(`
- ${t("ocr.no_text_available")} -
`); - this.$meta.html(t("ocr.no_text_explanation")); + this.showNoOCRAvailable(); return; } @@ -137,10 +146,61 @@ export default class ReadOnlyOCRTextWidget extends TypeWidget { } } + private showNoOCRAvailable() { + const $processButton = $(``); + + $processButton.on("click", () => this.processOCR()); + + this.$content.html(`
+ ${t("ocr.no_text_available")} +
`); + + this.$actions.append($processButton); + this.$meta.html(t("ocr.no_text_explanation")); + } + + private async processOCR() { + if (!this.currentNote) { + return; + } + + const $button = this.$actions.find(".ocr-process-button"); + + // Disable button and show processing state + $button.prop("disabled", true); + $button.html(` ${t("ocr.processing")}`); + + try { + const response = await server.post(`ocr/process-note/${this.currentNote.noteId}`); + + if (response.success) { + toastService.showMessage(t("ocr.processing_started")); + // Refresh the view after a short delay to allow processing to begin + setTimeout(() => { + if (this.currentNote) { + this.doRefresh(this.currentNote); + } + }, 2000); + } else { + throw new Error(response.error || t("ocr.processing_failed")); + } + } catch (error: any) { + console.error("Error processing OCR:", error); + toastService.showError(error.message || t("ocr.processing_failed")); + + // Re-enable button + $button.prop("disabled", false); + $button.html(` ${t("ocr.process_now")}`); + } + } + private showError(message: string) { this.$content.html(`
${message}
`); + this.$actions.empty(); this.$meta.empty(); }