feat(ocr): integrate progress bar and streaming progress into document page

Replace inline translateOcrProgress with the extracted module. Add
OcrProgressBar below the spinner during OCR. Parse page numbers from
ANALYZING_PAGE progress codes and feed them to the bar. On Done, fill
bar to 100% briefly before clearing the overlay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-13 10:15:55 +02:00
parent 035f9768bd
commit bac67706b9

View File

@@ -8,6 +8,8 @@ import TranscriptionReadView from '$lib/components/TranscriptionReadView.svelte'
import TranscriptionPanelHeader from '$lib/components/TranscriptionPanelHeader.svelte'; import TranscriptionPanelHeader from '$lib/components/TranscriptionPanelHeader.svelte';
import type { TranscriptionBlockData } from '$lib/types'; import type { TranscriptionBlockData } from '$lib/types';
import { getErrorMessage } from '$lib/errors'; import { getErrorMessage } from '$lib/errors';
import { translateOcrProgress } from '$lib/ocr/translateOcrProgress';
import OcrProgressBar from '$lib/components/OcrProgressBar.svelte';
let { data } = $props(); let { data } = $props();
@@ -132,27 +134,9 @@ let ocrRunning = $state(false);
let ocrProgressMessage = $state(''); let ocrProgressMessage = $state('');
let ocrErrorMessage = $state(''); let ocrErrorMessage = $state('');
let ocrPollTimer = $state<ReturnType<typeof setInterval> | null>(null); let ocrPollTimer = $state<ReturnType<typeof setInterval> | null>(null);
let ocrCurrentPage = $state(0);
function translateOcrProgress(code: string): string { let ocrTotalPages = $state(0);
if (!code) return m.ocr_progress_heading(); let ocrSkippedPages = $state(0);
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) { async function triggerOcr(scriptType: string) {
ocrRunning = true; ocrRunning = true;
@@ -186,12 +170,23 @@ function pollOcrJob(jobId: string) {
const res = await fetch(`/api/ocr/jobs/${jobId}`); const res = await fetch(`/api/ocr/jobs/${jobId}`);
if (!res.ok) return; if (!res.ok) return;
const job = await res.json(); const job = await res.json();
ocrProgressMessage = job.progressMessage ?? ''; const rawCode = job.progressMessage ?? '';
const progress = translateOcrProgress(rawCode);
ocrProgressMessage = progress.message;
if (progress.currentPage !== undefined) ocrCurrentPage = progress.currentPage;
if (progress.totalPages !== undefined) ocrTotalPages = progress.totalPages;
if (progress.skippedPages !== undefined) ocrSkippedPages = progress.skippedPages;
if (job.status === 'DONE' || job.status === 'FAILED') { if (job.status === 'DONE' || job.status === 'FAILED') {
ocrCurrentPage = ocrTotalPages;
if (ocrPollTimer) clearInterval(ocrPollTimer); if (ocrPollTimer) clearInterval(ocrPollTimer);
ocrPollTimer = null; ocrPollTimer = null;
ocrRunning = false; setTimeout(() => {
ocrProgressMessage = ''; ocrRunning = false;
ocrProgressMessage = '';
ocrCurrentPage = 0;
ocrTotalPages = 0;
ocrSkippedPages = 0;
}, 1000);
if (job.status === 'FAILED') { if (job.status === 'FAILED') {
ocrErrorMessage = m.ocr_status_error(); ocrErrorMessage = m.ocr_status_error();
} }
@@ -439,8 +434,13 @@ onMount(() => {
{m.ocr_progress_heading()} {m.ocr_progress_heading()}
</p> </p>
<p class="mt-2 text-sm text-ink-2"> <p class="mt-2 text-sm text-ink-2">
{translateOcrProgress(ocrProgressMessage)} {ocrProgressMessage}
</p> </p>
<OcrProgressBar
currentPage={ocrCurrentPage}
totalPages={ocrTotalPages}
skippedPages={ocrSkippedPages}
/>
</div> </div>
{:else if panelMode === 'read'} {:else if panelMode === 'read'}
<TranscriptionReadView <TranscriptionReadView