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

@@ -10,29 +10,18 @@ type DrawRect = {
let {
annotations = [],
canAnnotate,
canDraw,
color,
dimColor,
onDraw,
onDelete,
commentCounts,
onAnnotationClick
}: {
annotations: Annotation[];
canAnnotate: boolean;
canDraw: boolean;
color: string;
dimColor?: string;
onDraw: (rect: { x: number; y: number; width: number; height: number }) => void;
onDelete: (id: string) => void;
commentCounts?: Record<string, number>;
onDraw: (rect: DrawRect) => void;
onAnnotationClick?: (id: string) => void;
} = $props();
function isDimmed(annotation: Annotation): boolean {
if (!dimColor) return false;
return annotation.color.toLowerCase() === dimColor.toLowerCase();
}
let drawStart = $state<{ x: number; y: number } | null>(null);
let drawRect = $state<DrawRect | null>(null);
@@ -52,7 +41,7 @@ function getNormalizedCoords(event: PointerEvent, element: HTMLElement): { x: nu
}
function handlePointerDown(event: PointerEvent) {
if (!canAnnotate) return;
if (!canDraw) return;
if ((event.target as HTMLElement).closest('[data-annotation]')) return;
@@ -65,7 +54,7 @@ function handlePointerDown(event: PointerEvent) {
}
function handlePointerMove(event: PointerEvent) {
if (!canAnnotate || !drawStart) return;
if (!canDraw || !drawStart) return;
const container = event.currentTarget as HTMLElement;
const coords = getNormalizedCoords(event, container);
@@ -79,7 +68,7 @@ function handlePointerMove(event: PointerEvent) {
}
function handlePointerUp(event: PointerEvent) {
if (!canAnnotate || !drawStart || !drawRect) return;
if (!canDraw || !drawStart || !drawRect) return;
const container = event.currentTarget as HTMLElement;
const coords = getNormalizedCoords(event, container);
@@ -100,7 +89,7 @@ function handlePointerUp(event: PointerEvent) {
let hoveredId = $state<string | null>(null);
const containerStyle = $derived(
`position: absolute; top: 0; left: 0; width: 100%; height: 100%;${canAnnotate ? ' cursor: crosshair; touch-action: none;' : ''}`
`position: absolute; top: 0; left: 0; width: 100%; height: 100%;${canDraw ? ' cursor: crosshair; touch-action: none;' : ''}`
);
</script>
@@ -117,9 +106,11 @@ const containerStyle = $derived(
data-annotation
role="button"
tabindex="0"
aria-label="Kommentare anzeigen"
aria-label="Block anzeigen"
onclick={() => onAnnotationClick?.(annotation.id)}
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') onAnnotationClick?.(annotation.id); }}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') onAnnotationClick?.(annotation.id);
}}
onpointerenter={() => (hoveredId = annotation.id)}
onpointerleave={() => (hoveredId = null)}
style="
@@ -130,73 +121,11 @@ const containerStyle = $derived(
height: {annotation.height * 100}%;
background-color: {hexToRgba(annotation.color, hoveredId === annotation.id ? 0.5 : 0.3)};
box-shadow: {hoveredId === annotation.id ? `inset 0 0 0 2px ${hexToRgba(annotation.color, 0.8)}` : 'none'};
opacity: {isDimmed(annotation) ? 0.3 : 1};
pointer-events: {isDimmed(annotation) ? 'none' : 'auto'};
transition: background-color 0.15s ease, box-shadow 0.15s ease, opacity 0.3s ease;
{onAnnotationClick && !canAnnotate ? 'cursor: pointer;' : ''}
pointer-events: auto;
cursor: pointer;
transition: background-color 0.15s ease, box-shadow 0.15s ease;
"
>
{#if canAnnotate}
<button
aria-label="Annotation löschen"
onclick={(e) => {
e.stopPropagation();
const count = commentCounts?.[annotation.id] ?? 0;
if (count > 0) {
const msg =
count === 1
? 'Diese Annotation hat 1 Kommentar. Beim Löschen wird er ebenfalls entfernt. Fortfahren?'
: `Diese Annotation hat ${count} Kommentare. Beim Löschen werden sie ebenfalls entfernt. Fortfahren?`;
if (!window.confirm(msg)) return;
}
onDelete(annotation.id);
}}
style="
position: absolute;
top: -8px;
right: -8px;
width: 16px;
height: 16px;
background-color: #ef4444;
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 1;
padding: 0;
pointer-events: auto;
">×</button
>
{/if}
{#if (commentCounts?.[annotation.id] ?? 0) > 0}
<div
style="
position: absolute;
bottom: -10px;
right: -10px;
background-color: #002850;
color: white;
font-size: 11px;
font-family: sans-serif;
font-weight: 600;
padding: 2px 6px;
border-radius: 999px;
min-width: 20px;
text-align: center;
white-space: nowrap;
pointer-events: none;
line-height: 18px;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
"
>
{commentCounts?.[annotation.id]}
</div>
{/if}
</div>
></div>
{/each}
{#if drawRect && drawRect.width > 0}