;
+ onTriggerOcr?: (scriptType: string) => void;
};
let {
@@ -23,9 +28,13 @@ let {
canComment,
currentUserId,
activeAnnotationId = null,
+ storedScriptType = '',
+ canRunOcr = false,
onBlockFocus,
onSaveBlock,
- onDeleteBlock
+ onDeleteBlock,
+ onReviewToggle,
+ onTriggerOcr
}: Props = $props();
let activeBlockId: string | null = $state(null);
@@ -282,6 +291,7 @@ $effect(() => {
text={block.text}
label={block.label}
active={activeBlockId === block.id}
+ reviewed={block.reviewed ?? false}
saveState={getSaveState(block.id)}
canComment={canComment}
currentUserId={currentUserId}
@@ -289,6 +299,7 @@ $effect(() => {
onFocus={() => handleFocus(block.id)}
onDeleteClick={() => handleDelete(block.id)}
onRetry={() => handleRetry(block.id)}
+ onReviewToggle={() => onReviewToggle(block.id)}
onMoveUp={() => handleMoveUp(block.id)}
onMoveDown={() => handleMoveDown(block.id)}
isFirst={i === 0}
@@ -323,9 +334,26 @@ $effect(() => {
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
/>
-
- {m.transcription_empty_cta()}
-
+
+ {#if canRunOcr && onTriggerOcr}
+
+ {m.transcription_empty_title()}
+
+
+
+
+
+ {m.transcription_empty_desc()}
+
+ {:else}
+
+ {m.transcription_empty_cta()}
+
+ {/if}
{/if}
diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts
index adb2aec3..24dbb848 100644
--- a/frontend/src/lib/types.ts
+++ b/frontend/src/lib/types.ts
@@ -35,6 +35,8 @@ export type TranscriptionBlockData = {
label: string | null;
sortOrder: number;
version: number;
+ source: 'MANUAL' | 'OCR';
+ reviewed: boolean;
updatedAt?: string | null;
};
diff --git a/frontend/src/routes/documents/[id]/+page.svelte b/frontend/src/routes/documents/[id]/+page.svelte
index 0717353c..498e8601 100644
--- a/frontend/src/routes/documents/[id]/+page.svelte
+++ b/frontend/src/routes/documents/[id]/+page.svelte
@@ -118,6 +118,31 @@ async function deleteBlock(blockId: string) {
annotationReloadKey++;
}
+async function reviewToggle(blockId: string) {
+ const res = await fetch(`/api/documents/${doc.id}/transcription-blocks/${blockId}/review`, {
+ method: 'PUT'
+ });
+ if (!res.ok) return;
+ const updated = await res.json();
+ transcriptionBlocks = transcriptionBlocks.map((b) => (b.id === blockId ? updated : b));
+}
+
+async function triggerOcr(scriptType: string) {
+ try {
+ const res = await fetch(`/api/documents/${doc.id}/ocr`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ scriptType })
+ });
+ if (res.ok) {
+ await loadTranscriptionBlocks();
+ annotationReloadKey++;
+ }
+ } catch (e) {
+ console.error('Failed to trigger OCR:', e);
+ }
+}
+
async function createBlockFromDraw(rect: {
x: number;
y: number;
@@ -316,9 +341,13 @@ onMount(() => {
canComment={canWrite}
currentUserId={currentUserId}
activeAnnotationId={activeAnnotationId}
+ storedScriptType={doc.scriptType ?? ''}
+ canRunOcr={canWrite && !!doc.filePath}
onBlockFocus={handleBlockFocus}
onSaveBlock={saveBlock}
onDeleteBlock={deleteBlock}
+ onReviewToggle={reviewToggle}
+ onTriggerOcr={triggerOcr}
/>
{/if}