PDF viewer collapses to 70px on mobile in read mode, expandable to
50vh. Toggle button with chevron. Paragraph tap auto-expands strip.
Mode toggle abbreviates to "Bearb." on small screens.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Paragraph click flashes the PDF annotation outline (1.5s fade).
Annotation click highlights the paragraph with a background flash.
Both directions scroll the target into view.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds TranscriptionPanelHeader and TranscriptionReadView to the
document detail page. Default mode is 'read' when blocks exist,
'edit' otherwise. Annotations dimmed in read mode.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aligns the top/bottom padding of the Briefwechsel results view with
the document search page wrapper (both py-8).
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swap button: stack right/left arrows vertically at h-3.5 for a
compact look. Timeline: replace → ← text with Long-Arrow icons on
each letter entry and the distribution bar summary.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove border-dashed and bg-canvas conditional styles so the
receiver input matches the sender input styling. The placeholder
"Alle Korrespondenten" already communicates the optional state.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Only navigate (applyFilters) when a person is actually selected, not
when the sender input is cleared. Combined with showHero checking
data.filters.senderId, the user stays in the search bar view after
clearing — no jump back to the hero.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove custom placeholder props so DateInput falls back to its default
format hint (TT.MM.JJJJ / DD.MM.YYYY / DD.MM.AAAA) instead of
repeating the label text above.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace native <input type="date"> on the document search page with
the custom DateInput component (German dd.mm.yyyy format with auto-dot
insertion). Align both pages' date input styling: add rounded-md,
border, bg-surface, px-3, text-ink, placeholder color, and focus ring
to match all other inputs in the card.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace ↑↓ text with Long-Arrow-Up/Down-MD.svg on the document search
SortDropdown and the Briefwechsel sort button. Rotate the swap button
SVG 90° so arrows point left/right matching the horizontal person
field layout.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace tiny ↑↓ text with Long-Arrow-Up/Down-MD.svg icons from the
brand icon set for better visibility and consistency.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chevrons indicate collapsible elements, not sort direction. Match
the document search SortDropdown pattern using ↑/↓ text arrows.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move sort button and filter toggle to the person row, matching the
document search page pattern (sort + filter + count inline). Date
range inputs are now a collapsible section behind the filter toggle,
using slide transition and the same grid layout as the document
search advanced filters. Fix date input padding (add px-3).
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap person bar + filter controls in a card matching the document
search page (rounded-sm border p-6 shadow-sm). Switch PersonTypeahead
to default mode with matching label/input overrides. Bump date inputs
and sort button to text-sm py-2.5. Filter row uses border-t separator
like the document search advanced section.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restores early return when id is empty, preventing a wasteful
navigation to /briefwechsel with no senderId param.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds conv_hero_divider to de/en/es messages and uses it in the
CorrespondenzHero divider. Fixes i18n blocker from review.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add px-3 to person bar, filter controls, and hint bar so inputs
don't sit flush against the container edge.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move strips inside the max-w-7xl container so person bar, filter
controls, and hint bar are no longer full-bleed. Remove duplicate
side padding from strip components — the parent container handles it.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Align person bar, filter controls, and hint bar side padding to
px-4 sm:px-6 lg:px-8, matching the standard layout of all other
overview pages. Override person bar inputs from compact h-9 to h-12
for better touch targets in the results state.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hero state (no senderId): centred CorrespondenzHero with discovery
headline, cross-link, large typeahead, recent persons. No person bar
or filter controls shown. Results state (senderId set): full-width
strips then content area with max-w-7xl responsive padding matching
other overview pages. Removes focus delegation hack.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New centred hero component for the Briefwechsel page: headline
"Wessen Briefe möchten Sie lesen?", cross-link to document search,
h-14 PersonTypeahead, and recent persons chips. Adds `large` prop
to PersonTypeahead and `conv_hero_crosslink` message key.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update all internal links (AppNav, CoCorrespondentsList, goto) to the
new URL. No redirect needed — no production URLs exist yet.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- isDashboard was ignoring tagQ so typing in tag filter showed dashboard
- addTag now calls onTextInput('') to clear tagQ when a chip is selected
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment text:
- Body and quote bumped from text-sm (14px) to text-base (16px)
to visually match the font-sans author name at text-sm
Annotation reload on delete:
- Add annotationReloadKey prop through DocumentViewer → PdfViewer
- Increment key after block delete in +page.svelte
- PdfViewer reloads annotations when key changes
- Annotation rectangle disappears immediately, not just after refresh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass activeAnnotationId to TranscriptionEditView. An $effect watches
it and sets activeBlockId to the block matching the annotation,
activating its turquoise focus border.
2 new tests (RED/GREEN):
- activates block matching activeAnnotationId (turquoise border)
- no block activated when activeAnnotationId is null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mobile layout (< 768px):
- Split view stacks vertically: PDF top (min 40vh), blocks below
- Blocks panel gets border-top instead of border-left
- PDF remains interactive for drawing in stacked mode
Scroll-sync (block → PDF):
- Clicking a block sets activeAnnotationId
- PdfViewer effect watches activeAnnotationId, navigates to the
annotation's page if different from current, then scrolls the
annotation element into view (double-rAF for async render timing)
- Works across pages: block on page 3 navigates PDF to page 3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Own comments:
- Click the text to open inline edit (textarea replaces text)
- Enter saves, Escape cancels
- Small trash icon always visible in bottom-right corner
- Hover on text shows cursor-text + subtle bg highlight
Other people's comments: read-only, no edit/delete affordances.
Re-add currentUserId prop chain for ownership check.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add blockNumbers prop through AnnotationLayer → PdfViewer → DocumentViewer.
Each turquoise annotation rectangle now shows a numbered badge (top-left,
matching the block card number in the right panel).
Block numbers are derived from sorted transcriptionBlocks, mapped by
annotationId, creating a visual link between PDF regions and block cards.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When clicking a turquoise annotation on the PDF:
- If not in transcribe mode, enters it and loads blocks
- Waits for DOM render, then scrolls to the corresponding block card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
TranscriptionBlock.svelte:
- "Kommentieren" button opens expandable comment thread per block
- Text selection in textarea captured as quoted text (> "...") prefix
- Quote hint "Text markieren für Zitat" shown when block is active/focused
- Comment thread uses existing CommentThread with blockId prop
CommentThread.svelte:
- Add blockId prop for block-level comments URL routing
- Add quotedText prop — pre-fills comment input with markdown blockquote
- commentsBase now supports 3 URL patterns: document, annotation, block
TranscriptionEditView.svelte:
- Pass canComment + currentUserId through to block components
3 new frontend tests:
- Kommentieren button present
- Quote hint shown when active
- Quote hint hidden when inactive
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AnnotationLayer: add dimColor prop — annotations matching dim color
render at 30% opacity with pointer-events disabled (300ms transition)
- PdfViewer: add transcribeMode prop, derived drawingEnabled/drawColor;
in transcribe mode draws with turquoise (#00C7B1), routes draw events
to onTranscriptionDraw callback instead of annotation endpoint
- DocumentViewer: pass through transcribeMode + onTranscriptionDraw
- Document detail page: createBlockFromDraw() POSTs to transcription
blocks API on draw completion, adds created block to list
- Mode-based dimming: yellow annotations dim in transcribe mode,
turquoise annotations dim in annotate mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace async executeSave in beforeunload handler with
navigator.sendBeacon — synchronous and reliable for page unload.
Sends pending text as JSON blob to the block update endpoint.
Fixes @Sara: "beforeunload handlers cannot reliably await async"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add turquoise/turquoise-fg semantic color tokens to layout.css
(light + dark mode), replacing all hardcoded #00C7B1 in components
- Bump Details toggle from text-xs to text-sm for visual hierarchy
- Block badge: navy → turquoise, overlapping top-left card border
with absolute positioning to visually link PDF annotation badges
- Saved indicator: smooth 300ms opacity fade before removal
(new 'fading' state in SaveState type)
- Transcribe buttons: use border-turquoise/bg-turquoise/text-turquoise-fg
Fixes @Leonie concerns: toggle visual weight, semantic tokens,
badge styling, saved fade animation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move duplicated type definition from TranscriptionEditView.svelte
and +page.svelte into $lib/types.ts for single source of truth.
Fixes @Felix: "Consider extracting the TranscriptionBlockData type"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes from PR #178 review:
Migration fixes:
- V18/V19: fix FK references from app_users to users (correct table name)
- V18: change annotation_id FK from ON DELETE CASCADE to ON DELETE RESTRICT
(block is aggregate root, cascade flows from block, not annotation)
Backend fixes:
- TranscriptionService.deleteBlock(): remove userId param, delete block first
then annotation directly via repository (no ownership check — block owns annotation)
- TranscriptionService.sanitizeText(): remove flawed regex HTML stripping,
textarea content is plain text by design — just enforce max length
- TranscriptionBlockController.requireUserId(): throw DomainException.unauthorized()
instead of silently returning null on auth failure
- CreateTranscriptionBlockDTO: add @Min/@Positive validation on coordinates
- Add @Slf4j logging to TranscriptionService for create/delete operations
Frontend fixes:
- Delete DocumentBottomPanel.svelte entirely (issue #175 requirement)
- Remove redundant mode exclusivity $effect (handled at toggle call sites)
- Remove dead handleCommentClick + onCommentClick prop (comments are future work)
- Remove quote hint UI (depends on comment feature)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DocumentMetadataDrawer: 3-column grid (≥1024px), single-column mobile
Shows document date, location, status, person cards, tag chips
Person names link to /persons/{id}, tags link to filtered search
Empty states for missing persons/tags, receiver cap with expand button
- DocumentTopBar: "Details" toggle button with animated SVG chevron
44×44px tap target, aria-expanded, Svelte slide transition
Semantic color tokens for dark mode compatibility
- Remove DocumentBottomPanel from document detail page
Bottom panel replaced by topbar drawer for metadata access
Simplify +page.server.ts (remove comments loading)
Update page.server.spec.ts for new load signature
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>