refactor: remove legacy annotate mode — transcription replaces it
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Backend Unit Tests (pull_request) Failing after 4m26s
CI / Unit & Component Tests (pull_request) Failing after 14m40s
CI / E2E Tests (pull_request) Failing after 1h26m51s

The yellow annotation+comment system is now redundant. Transcription
blocks handle the same use case (mark region → discuss) but better,
because they also produce a transcription.

Removed:
- annotateMode state and all wiring through page/topbar/viewer/pdfviewer
- Annotate/Stop annotate buttons from DocumentTopBar
- AnnotateHintStrip import and rendering
- AnnotationSidePanel from document detail page
- canAnnotate prop from DocumentTopBar
- Color picker from PdfViewer
- Comment count badges and loadCommentCounts from PdfViewer
- Delete button from AnnotationLayer (blocks own annotation lifecycle)
- dimColor prop from AnnotationLayer

Simplified:
- AnnotationLayer: only canDraw + color + onDraw + onAnnotationClick
- PdfViewer: only draws in transcribeMode with turquoise
- Clicking annotation in transcribe mode scrolls to corresponding block
- canComment derived from canWrite (no longer needs canAnnotate)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-05 21:17:27 +02:00
parent 8c26876345
commit f3c29ffe58
6 changed files with 76 additions and 378 deletions

View File

@@ -1,6 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { SvelteMap } from 'svelte/reactivity';
import type { PDFDocumentProxy, PDFPageProxy, RenderTask } from 'pdfjs-dist';
import AnnotationLayer from './AnnotationLayer.svelte';
import type { Annotation } from '$lib/types';
@@ -11,20 +10,16 @@ type DrawRect = { x: number; y: number; width: number; height: number; pageNumbe
let {
url,
documentId = '',
annotateMode = $bindable(false),
transcribeMode = false,
activeAnnotationId = $bindable<string | null>(null),
activeAnnotationPage = $bindable<number | null>(null),
onAnnotationClick,
onTranscriptionDraw,
documentFileHash
}: {
url: string;
documentId?: string;
annotateMode?: boolean;
transcribeMode?: boolean;
activeAnnotationId?: string | null;
activeAnnotationPage?: number | null;
onAnnotationClick?: (id: string) => void;
onTranscriptionDraw?: (rect: DrawRect) => void;
documentFileHash?: string | null;
@@ -51,13 +46,9 @@ let pdfjsLib: typeof import('pdfjs-dist') | null = null;
let pdfjsReady = $state(false);
let annotations = $state<Annotation[]>([]);
let annotateColor = $state('#ffff00');
let commentCounts = new SvelteMap<string, number>();
let showAnnotations = $state(true);
const TRANSCRIPTION_COLOR = '#00C7B1';
const drawingEnabled = $derived(annotateMode || transcribeMode);
const drawColor = $derived(transcribeMode ? TRANSCRIPTION_COLOR : annotateColor);
const visibleAnnotations = $derived(
annotations.filter((a) => !a.fileHash || !documentFileHash || a.fileHash === documentFileHash)
@@ -174,30 +165,12 @@ async function prerender(doc: PDFDocumentProxy, pageNum: number) {
}
}
async function loadCommentCounts(docId: string, anns: Annotation[]) {
await Promise.all(
anns.map(async (a) => {
try {
const res = await fetch(`/api/documents/${docId}/annotations/${a.id}/comments`);
if (res.ok) {
const threads = (await res.json()) as Array<{ replies: unknown[] }>;
const total = threads.reduce((sum, t) => sum + 1 + t.replies.length, 0);
commentCounts.set(a.id, total);
}
} catch {
// ignore
}
})
);
}
async function loadAnnotations(docId: string) {
if (!docId) return;
try {
const res = await fetch(`/api/documents/${docId}/annotations`);
if (res.ok) {
annotations = await res.json();
await loadCommentCounts(docId, annotations);
}
} catch {
// ignore
@@ -205,57 +178,13 @@ async function loadAnnotations(docId: string) {
}
async function handleDraw(rect: { x: number; y: number; width: number; height: number }) {
if (!documentId) return;
if (transcribeMode) {
await onTranscriptionDraw?.({ ...rect, pageNumber: currentPage });
await loadAnnotations(documentId);
return;
}
try {
const res = await fetch(`/api/documents/${documentId}/annotations`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
pageNumber: currentPage,
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
color: annotateColor
})
});
if (res.ok) {
const created: Annotation = await res.json();
annotations = [...annotations, created];
activeAnnotationId = created.id;
activeAnnotationPage = created.pageNumber;
onAnnotationClick?.(created.id);
}
} catch {
// ignore
}
}
async function handleAnnotationDelete(annotationId: string) {
if (!documentId) return;
try {
const res = await fetch(`/api/documents/${documentId}/annotations/${annotationId}`, {
method: 'DELETE'
});
if (res.ok) {
annotations = annotations.filter((a) => a.id !== annotationId);
}
} catch {
// ignore
}
if (!documentId || !transcribeMode) return;
await onTranscriptionDraw?.({ ...rect, pageNumber: currentPage });
await loadAnnotations(documentId);
}
function handleAnnotationClick(id: string) {
activeAnnotationId = id;
const ann = annotations.find((a) => a.id === id);
activeAnnotationPage = ann?.pageNumber ?? null;
onAnnotationClick?.(id);
}
@@ -283,7 +212,7 @@ $effect(() => {
});
$effect(() => {
if (annotateMode) showAnnotations = true;
if (transcribeMode) showAnnotations = true;
});
function prevPage() {
@@ -429,16 +358,6 @@ function zoomOut() {
</button>
</div>
<!-- Color picker (shown in annotate mode) -->
{#if annotateMode}
<input
type="color"
bind:value={annotateColor}
aria-label="Farbe wählen"
class="h-6 w-6 cursor-pointer rounded border-0 bg-transparent p-0"
title="Farbe wählen"
/>
{/if}
<!-- Annotation visibility toggle (shown when annotations exist) -->
{#if annotations.length > 0}
<button
@@ -503,12 +422,9 @@ function zoomOut() {
{#if showAnnotations}
<AnnotationLayer
annotations={visibleAnnotations.filter((a) => a.pageNumber === currentPage)}
canAnnotate={drawingEnabled}
color={drawColor}
dimColor={transcribeMode ? '#ffff00' : annotateMode ? TRANSCRIPTION_COLOR : undefined}
canDraw={transcribeMode}
color={TRANSCRIPTION_COLOR}
onDraw={handleDraw}
onDelete={handleAnnotationDelete}
commentCounts={Object.fromEntries(commentCounts)}
onAnnotationClick={handleAnnotationClick}
/>
{/if}