Empty list early return, heading + per-doc row rendering, title link
href, date visibility tied to updatedAt, stats footnote presence
toggled by stats.totalDocuments.
7 tests covering ~16 of the dashboard section's branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Empty list early return, populated section, write-action link gated on
canWrite, visible-cap of 3, footer show-all link visibility based on
overflow, author name vs email fallback.
9 tests covering ~25 of GeschichtenCard's branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hidden when totalPages <= 1, prev/next disabled state matrix at
boundaries, link form when in range, aria-current for active page,
mobile page label, left ellipsis / right ellipsis branches based on
window position, custom ariaLabel.
11 tests covering ~30 of Pagination's branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Avatar with initials vs question-mark fallback, for-you marker
visibility, data-variant matrix (simple/for-you/rollup/comment),
count badge for rollup, comment preview rendering with fallback,
document title link, default vs comment-deep-link href, time-range
label for rollup with happenedAtUntil.
11 tests covering ~40 of ChronikRow's high-uncovered branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Title rendering with originalFilename fallback, sender vs unknown
placeholder, tag buttons per document tag, bulk-select checkbox gated
on canWrite, archive chips visibility, snippet/summary visibility,
em-dash for missing date.
11 tests covering ~30 of the row's branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Toggle button, form open on click, all relationship type options,
year-error alert when toYear < fromYear, no-error path when equal,
cancel button closes form, onSubmit prop wiring.
7 tests covering ~20 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prev/next nav buttons, chip count per file, aria-current matrix for
active id, error-state data attribute, onSelect callback, onRemove
callback, sr-only announcer for active title.
7 tests covering ~25 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Listbox label, empty-state placeholder, create-new escape hatch with
noopener target, populated list, default aria-selected on first item,
life-date range visibility, position fallback when clientRect is null,
positioning from clientRect.
8 tests covering ~25 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
One bar per filled bucket, singular vs plural aria-label, aria-pressed
matrix, drag-window visibility tied to isDragging, onbarclick callback,
minimum-height handling for zero-count buckets.
8 tests covering ~25 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dialog with bell label, empty state vs populated list, mark-all-read
visibility branch, REPLY vs MENTION text, unread-dot rendering, all
three callback wirings (onMarkRead, onMarkAllRead, onClose).
10 tests covering the notification dropdown surface.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TimelineControls: empty render when neither flag is set, reset button
gated on isZoomed, clear button gated on hasSelection, both-on, both
callback wirings.
TimelineXAxis: empty filled → no ticks, populated → ticks render,
omit-year branch when all buckets share a year, show-year branch
across multiple years, length-4 bucket-string fallback.
11 tests across two timeline primitives.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
UserPasswordSection: input rendering, type=password attribute,
required-prop propagation in both directions.
CorrespondenzFilterControls: dual date label rendering, both DateInput
ids, value hydration from fromDate/toDate, change-event smoke check.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BackButton: visible vs aria-only label branches, custom class
application, history.back() click handler.
OverflowPillButton: +N pill render, aria-expanded matrix
(closed default → open after click), per-person link rendering with
correct href, Escape closes the dropdown.
Both are reused widely; their coverage closes the line and function gap
left after the DocumentTopBar split inflated the denominator.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sixteen tests covering the four-column drawer: details column always
renders, persons column branches (no-persons placeholder vs sender
vs receivers), receiver overflow + show-all toggle, tags column
branches (placeholder vs anchor list with /?tag href encoding),
geschichten column visibility (hidden by default, shown for
canBlogWrite, attach link gated on canBlogWrite + documentId, list
rendering, show-all overflow), inferred-relationship pill on the
single-receiver branch.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PersonEditForm: PERSON vs INSTITUTION/GROUP visibility matrix (firstName,
title, alias, birth/deathYear toggle), lastName label switch, prop
hydration of all populated fields, fallback to PERSON for unknown type,
empty-string handling for null fields. 10 tests, ~30 branches.
SegmentationTrainingCard: trainingInfo null vs populated, block count
display, button disabled-state matrix (training × tooFewBlocks ×
serviceDown), too-few-blocks and service-down hints, success message
after a mocked fetch, training history heading. 10 tests, ~25 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Third Phase 5 split. The desktop action buttons — transcribe,
transcribe-stop, edit link, download link — become their own component
with a focused props interface (documentId, canWrite, isPdf,
transcribeMode bindable, filePath, originalFilename, fileUrl).
TDD: 8 tests covering empty render, transcribe button gating
(canWrite × isPdf × transcribeMode), stop-transcribe rendering, edit
link with documentId href, download link with filePath gating, all
hidden when in transcribe mode. After the test was red the component
was created.
DocumentTopBar dropped from 303 lines to 166. The orchestrator now
just composes BackButton, DocumentTopBarTitle, PersonChipRow,
OverflowPillButton, the details toggle, DocumentTopBarActions,
DocumentMobileMenu, and DocumentMetadataDrawer — each visual region
named in one or two words.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Second step of the Phase 5 split. The kebab dropdown — including
clickOutside handling and its own mobileMenuOpen state — becomes its
own component named after its visual region. The mobile snippet
duplication inside DocumentTopBar is removed; the component owns its
mobile-specific markup.
TDD: DocumentMobileMenu.svelte.test.ts (7 tests) was red first. The
component then made it green (kebab trigger, dropdown open/close on
click, transcribe button gated on canWrite × isPdf × !transcribeMode,
download link gated on filePath). DocumentTopBar wraps the new
component in a md:hidden div so responsive behaviour is unchanged.
Existing 18-test DocumentTopBar suite still passes.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
First step of the Phase 5 split plan from issue #496. The 14-line title
+ date block becomes its own component named after the visual region.
TDD red/green: DocumentTopBarTitle.svelte.test.ts written first
(7 tests covering title, originalFilename fallback, empty-string
fallback, short-date rendering, no-date branch, title attribute
sourcing). After the test was red the component was created.
DocumentTopBar.svelte updated to use it; the existing 18-test suite
still passes.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eighteen tests covering the user-observable matrix without yet splitting
the component (Phase 5 of the plan): title vs originalFilename fallback,
short-date rendering and absence, transcribe-button gating
(canWrite × isPdf × transcribeMode), edit-link gating, download-link
gating on filePath, kebab-menu visibility on (canWrite & isPdf) || filePath,
details drawer toggle, mobile menu open/close.
The 83 raw branches in the source map mostly to combinations of the
above flags — each test isolates one branch. Per Sara's guidance the
test names read as sentences and verify what the user sees, not internal
state.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DocumentViewer: loading / error / no-scan / image rendering branches.
filePath conditionally drives the direct-download link in the error
state; fileUrl + non-PDF contentType drives the <img> render.
PersonalInfoForm: default render, prop hydration including the German
date conversion path, success/error banner branches, form action wiring.
profile/+page: notification-checkbox enabled/disabled depending on
hasEmail, no-email hint visibility, prefsSuccess/prefsError banners,
fallback when notificationPrefs is null.
20 tests across three files.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PersonTypeBadge: one test per switch arm (INSTITUTION, GROUP, UNKNOWN)
plus the two no-render branches (unrecognised type, empty type).
ExpandableText: clamp detection, toggle visibility logic, expand →
collapse round-trip, default maxLines fallback.
PersonChipRow: sender-only, sender+arrow, abbreviated naming, max-two
visible receivers, +N overflow pill presence/absence, receivers-only
case (no sender → no arrow).
19 tests across three files. Each file uses afterEach(cleanup) and
queries via getByRole/getByText so tests stay decoupled from CSS.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers the abbreviated/full name branches, the firstName-null fallback
path, link href derivation from person id, initials rendering, and the
deterministic avatar palette colour. Six tests, six branches hit.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds DocumentStatusChip.svelte.test.ts asserting one branch per
DocumentStatus value (PLACEHOLDER, UPLOADED, TRANSCRIBED, REVIEWED,
ARCHIVED) plus the title/aria-label exposure. Each test queries the
element via getByTitle so the component's accessibility surface is
verified at the same time as its branch logic.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
UploadZone is the canonical browser-test template referenced from issue #496
implementation guidance. Adding afterEach(cleanup) makes it match the
TranscriptionPanelHeader pattern and prevents cross-test DOM leakage as more
tests are added in this branch.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per Felix's review on issue #496, tests should query observable behaviour via
ARIA roles, not test-only data-testid attributes. Replaces every
'document.querySelector([data-testid=...])' with 'page.getByRole(...)'.
The disabled-button click test uses force: true so Playwright bypasses its
enabled-check — the behaviour under test is precisely that the click is
ignored.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add min-h-[44px] min-w-[44px] to all five PDF viewer buttons (prev,
next, zoom in, zoom out, annotation toggle) and widen icon-only
padding from p-1 to p-2. Adds aria-pressed to the annotation toggle
for correct toggle semantics (WCAG 2.2 §2.5.8 + ARIA 1.2).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three root causes prevented filters from reflecting the URL after SvelteKit
client-side navigation:
1. +page.server.ts now resolves sender/receiver display names in parallel with
the document search (UUID validation + silent 404 drop), so initialSenderName
/ initialReceiverName land in server data ready for the UI to use.
2. +page.svelte passes initialSenderName, initialReceiverName, and navKey
(incremented via untrack on every navigation) down to SearchFilterBar.
The untrack() prevents the effect from re-running due to its own navKey write.
3. SearchFilterBar forwards navKey as resetKey to each PersonTypeahead, which
already had a void resetKey guard added in the previous commit.
Together these ensure that after navigating to /documents?senderId=<uuid> the
typeahead shows the person's display name, and clicking × reset clears it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the user types in the sender/receiver typeahead without selecting a
person and then clicks ×-reset (navigating back to /documents), the
manually-typed term was not cleared because initialName stayed '' between
navigations — the existing $effect tracking initialName never fired.
Adding `resetKey` (incremented by the page on every navigation) forces
the effect to re-run via `void resetKey`, clearing searchTerm=initialName
even when initialName is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`display` was initialised once and never updated, so the text box would
show a stale German date after the parent reset `value` (e.g. × reset
button or timeline drag). A guarded `$effect` re-derives `display` from
`value` whenever the two are out of sync while preserving mid-typing
partial dates (germanToIso returns '' for incomplete input, which matches
value='' during typing → no spurious re-derive).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
text-ink uses --c-ink which is #012851 in light and #f0efe9 in dark, responding
to both @media and [data-theme='dark'] via CSS variable — no extra token needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bg-white is hardcoded #fff and only flips via the Tailwind dark: media-query variant.
bg-surface uses a CSS variable (--c-surface) that responds to both the media query
and the [data-theme='dark'] attribute, matching how all other cards on the page work.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>