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

@@ -18,7 +18,7 @@ type Annotation = {
createdAt: string;
};
function makeAnnotation(id = 'ann-1'): Annotation {
function makeAnnotation(id = 'ann-1', color = '#00C7B1'): Annotation {
return {
id,
documentId: 'doc-1',
@@ -27,7 +27,7 @@ function makeAnnotation(id = 'ann-1'): Annotation {
y: 0.1,
width: 0.3,
height: 0.2,
color: '#ff0000',
color,
createdAt: new Date().toISOString()
};
}
@@ -36,87 +36,48 @@ describe('AnnotationLayer', () => {
it('renders a colored element for each annotation', async () => {
render(AnnotationLayer, {
annotations: [makeAnnotation('ann-1'), makeAnnotation('ann-2')],
canAnnotate: false,
color: '#ff0000',
onDraw: () => {},
onDelete: () => {}
canDraw: false,
color: '#00C7B1',
onDraw: () => {}
});
await expect.element(page.getByTestId('annotation-ann-1')).toBeInTheDocument();
await expect.element(page.getByTestId('annotation-ann-2')).toBeInTheDocument();
});
it('shows a delete button for each annotation when canAnnotate is true', async () => {
render(AnnotationLayer, {
annotations: [makeAnnotation('ann-1')],
canAnnotate: true,
color: '#ff0000',
onDraw: () => {},
onDelete: () => {}
});
await expect
.element(page.getByRole('button', { name: /annotation löschen/i }))
.toBeInTheDocument();
});
it('does not show delete buttons when canAnnotate is false', async () => {
render(AnnotationLayer, {
annotations: [makeAnnotation('ann-1')],
canAnnotate: false,
color: '#ff0000',
onDraw: () => {},
onDelete: () => {}
});
expect(page.getByRole('button', { name: /annotation löschen/i }).query()).toBeNull();
});
it('dims annotations matching dimColor', async () => {
render(AnnotationLayer, {
annotations: [makeAnnotation('ann-1')],
canAnnotate: false,
color: '#00C7B1',
dimColor: '#ff0000',
onDraw: () => {},
onDelete: () => {}
});
const el = page.getByTestId('annotation-ann-1');
await expect.element(el).toBeInTheDocument();
const style = el.element().style;
expect(style.opacity).toBe('0.3');
expect(style.pointerEvents).toBe('none');
});
it('does not dim annotations that do not match dimColor', async () => {
const ann = makeAnnotation('ann-1');
ann.color = '#00C7B1';
render(AnnotationLayer, {
annotations: [ann],
canAnnotate: false,
color: '#00C7B1',
dimColor: '#ff0000',
onDraw: () => {},
onDelete: () => {}
});
const el = page.getByTestId('annotation-ann-1');
await expect.element(el).toBeInTheDocument();
const style = el.element().style;
expect(style.opacity).toBe('1');
});
it('has crosshair cursor when canAnnotate is true', async () => {
it('has crosshair cursor when canDraw is true', async () => {
render(AnnotationLayer, {
annotations: [],
canAnnotate: true,
canDraw: true,
color: '#00C7B1',
onDraw: () => {},
onDelete: () => {}
onDraw: () => {}
});
const container = document.querySelector('[role="presentation"]')!;
expect(container.getAttribute('style')).toContain('cursor: crosshair');
});
it('does not have crosshair cursor when canDraw is false', async () => {
render(AnnotationLayer, {
annotations: [],
canDraw: false,
color: '#00C7B1',
onDraw: () => {}
});
const container = document.querySelector('[role="presentation"]')!;
expect(container.getAttribute('style')).not.toContain('cursor: crosshair');
});
it('does not show delete buttons (annotations owned by blocks)', async () => {
render(AnnotationLayer, {
annotations: [makeAnnotation('ann-1')],
canDraw: true,
color: '#00C7B1',
onDraw: () => {}
});
await expect.element(page.getByTestId('annotation-ann-1')).toBeInTheDocument();
expect(page.getByRole('button', { name: /löschen/i }).query()).toBeNull();
});
});