diff --git a/frontend/src/lib/document/DocumentViewer.svelte b/frontend/src/lib/document/DocumentViewer.svelte index 789a68d7..b0ce8af6 100644 --- a/frontend/src/lib/document/DocumentViewer.svelte +++ b/frontend/src/lib/document/DocumentViewer.svelte @@ -17,6 +17,7 @@ type Props = { isLoading: boolean; error: string; transcribeMode?: boolean; + canAnnotate?: boolean; blockNumbers?: Record; annotationReloadKey?: number; activeAnnotationId: string | null; @@ -33,6 +34,7 @@ let { isLoading, error, transcribeMode = false, + canAnnotate = false, blockNumbers = {}, annotationReloadKey = 0, activeAnnotationId = $bindable(), @@ -93,6 +95,7 @@ let { url={fileUrl} documentId={doc.id} transcribeMode={transcribeMode} + canAnnotate={canAnnotate} blockNumbers={blockNumbers} annotationReloadKey={annotationReloadKey} bind:activeAnnotationId={activeAnnotationId} diff --git a/frontend/src/lib/document/viewer/PdfViewer.svelte b/frontend/src/lib/document/viewer/PdfViewer.svelte index 5b6d09c3..e9169c5d 100644 --- a/frontend/src/lib/document/viewer/PdfViewer.svelte +++ b/frontend/src/lib/document/viewer/PdfViewer.svelte @@ -14,6 +14,7 @@ let { url, documentId = '', transcribeMode = false, + canAnnotate = false, blockNumbers = {}, annotationReloadKey = 0, activeAnnotationId = $bindable(null), @@ -28,6 +29,7 @@ let { url: string; documentId?: string; transcribeMode?: boolean; + canAnnotate?: boolean; blockNumbers?: Record; annotationReloadKey?: number; activeAnnotationId?: string | null; @@ -262,7 +264,7 @@ function handleAnnotationClick(id: string) { annotations={visibleAnnotations.filter( (a) => a.pageNumber === renderer.currentPage )} - canDraw={transcribeMode} + canDraw={transcribeMode && canAnnotate} color={TRANSCRIPTION_COLOR} blockNumbers={blockNumbers} activeAnnotationId={activeAnnotationId} diff --git a/frontend/src/lib/document/viewer/PdfViewer.svelte.test.ts b/frontend/src/lib/document/viewer/PdfViewer.svelte.test.ts index 17d1827c..e528b443 100644 --- a/frontend/src/lib/document/viewer/PdfViewer.svelte.test.ts +++ b/frontend/src/lib/document/viewer/PdfViewer.svelte.test.ts @@ -86,6 +86,38 @@ describe('PdfViewer — loaded state', () => { } }); + it('makes the annotation surface drawable (crosshair) when transcribeMode and canAnnotate', async () => { + render(PdfViewer, { + url: '/api/documents/test/file', + documentId: 'test', + transcribeMode: true, + canAnnotate: true, + libLoader: makeFakeLibLoader() + }); + + await vi.waitFor(() => { + const surface = document.querySelector('[role="presentation"]'); + expect(surface).not.toBeNull(); + expect(surface?.getAttribute('style') ?? '').toContain('crosshair'); + }); + }); + + it('does not make the annotation surface drawable when canAnnotate is false (read-only user)', async () => { + render(PdfViewer, { + url: '/api/documents/test/file', + documentId: 'test', + transcribeMode: true, + canAnnotate: false, + libLoader: makeFakeLibLoader() + }); + + await vi.waitFor(() => { + expect(document.querySelector('.bg-pdf-bg')).not.toBeNull(); + }); + const surface = document.querySelector('[role="presentation"]'); + expect(surface?.getAttribute('style') ?? '').not.toContain('crosshair'); + }); + it('renders the canvas region when documentFileHash is provided', async () => { render(PdfViewer, { url: '/api/documents/test/file', diff --git a/frontend/src/routes/documents/[id]/+page.svelte b/frontend/src/routes/documents/[id]/+page.svelte index ef03a55f..32cd894e 100644 --- a/frontend/src/routes/documents/[id]/+page.svelte +++ b/frontend/src/routes/documents/[id]/+page.svelte @@ -71,7 +71,7 @@ const ocrJob = createOcrJob({ onJobFinished: async () => { await transcription.load(); transcription.bumpAnnotationReloadKey(); - panelMode = transcription.hasBlocks ? 'read' : 'edit'; + panelMode = canWrite && !transcription.hasBlocks ? 'edit' : 'read'; } }); @@ -148,10 +148,10 @@ $effect(() => { if (skipInitialPanelMode) { skipInitialPanelMode = false; } else { - panelMode = transcription.hasBlocks ? 'read' : 'edit'; + panelMode = canWrite && !transcription.hasBlocks ? 'edit' : 'read'; } }); - ocrJob.checkStatus(); + if (canWrite) ocrJob.checkStatus(); } }); @@ -252,6 +252,7 @@ onMount(() => { isLoading={fileLoader.isLoading} error={fileLoader.fileError} transcribeMode={transcribeMode && !ocrJob.running} + canAnnotate={canWrite} blockNumbers={transcription.blockNumbers} annotationReloadKey={transcription.annotationReloadKey} annotationsDimmed={transcribeMode && panelMode === 'read'} @@ -293,7 +294,10 @@ onMount(() => { hasBlocks={transcription.hasBlocks} blockCount={transcription.blocks.length} lastEditedAt={transcription.lastEditedAt} - onModeChange={(newMode) => (panelMode = newMode)} + canEdit={canWrite} + onModeChange={(newMode) => { + if (canWrite) panelMode = newMode; + }} onClose={() => (transcribeMode = false)} />