feat(ocr): show translated progress messages during OCR processing
Some checks failed
CI / Unit & Component Tests (push) Failing after 2s
CI / Backend Unit Tests (push) Failing after 1s
CI / Unit & Component Tests (pull_request) Failing after 2s
CI / Backend Unit Tests (pull_request) Failing after 1s

Backend sends progress codes (PREPARING, LOADING, ANALYZING,
CREATING_BLOCKS:N, DONE:N, ERROR) via OcrJob.progressMessage.
Frontend translates them via Paraglide (de/en/es) and displays
below the spinner.

- V27 migration: adds progress_message column to ocr_jobs
- OcrAsyncRunner updates progress at each phase
- Poll interval reduced to 2s for snappier updates

Refs #226

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-12 23:31:23 +02:00
parent 0b0d4a7d5e
commit 971527a50e
7 changed files with 66 additions and 3 deletions

View File

@@ -128,8 +128,30 @@ async function reviewToggle(blockId: string) {
}
let ocrRunning = $state(false);
let ocrProgressMessage = $state('');
let ocrPollTimer = $state<ReturnType<typeof setInterval> | null>(null);
function translateOcrProgress(code: string): string {
if (!code) return m.ocr_progress_heading();
const [key, param] = code.split(':');
switch (key) {
case 'PREPARING':
return m.ocr_status_preparing();
case 'LOADING':
return m.ocr_status_loading();
case 'ANALYZING':
return m.ocr_status_analyzing();
case 'CREATING_BLOCKS':
return m.ocr_status_creating_blocks({ count: param ?? '0' });
case 'DONE':
return m.ocr_status_done_blocks({ count: param ?? '0' });
case 'ERROR':
return m.ocr_status_error();
default:
return code;
}
}
async function triggerOcr(scriptType: string) {
ocrRunning = true;
try {
@@ -157,10 +179,12 @@ function pollOcrJob(jobId: string) {
const res = await fetch(`/api/ocr/jobs/${jobId}`);
if (!res.ok) return;
const job = await res.json();
ocrProgressMessage = job.progressMessage ?? '';
if (job.status === 'DONE' || job.status === 'FAILED') {
if (ocrPollTimer) clearInterval(ocrPollTimer);
ocrPollTimer = null;
ocrRunning = false;
ocrProgressMessage = '';
await loadTranscriptionBlocks();
annotationReloadKey++;
panelMode = transcriptionBlocks.length > 0 ? 'read' : 'edit';
@@ -168,7 +192,7 @@ function pollOcrJob(jobId: string) {
} catch {
// polling is best-effort
}
}, 3000);
}, 2000);
}
async function createBlockFromDraw(rect: {
@@ -399,6 +423,9 @@ onMount(() => {
<p class="text-xs font-bold tracking-widest text-gray-400 uppercase">
{m.ocr_progress_heading()}
</p>
<p class="mt-2 text-sm text-ink-2">
{translateOcrProgress(ocrProgressMessage)}
</p>
</div>
{:else if panelMode === 'read'}
<TranscriptionReadView