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;
+ }
`;
@@ -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();
}