Removes SmartModeToggle, SmartSearchStatus, InterpretationChipRow,
DisambiguationPicker, chip-types utilities, and theme-chip-removal
utilities as part of NLP feature removal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Paraglide 2.5 runtime exports getLocale(), not languageTag(). The
8bed0cc6 commit introduced the wrong import when threading lang through
the NL search path.
Also updates two test assertions that still expected the old 'KI' button
label after 0b31a51e renamed it to 'Smart-Suche'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- NlSearchRequest gains @NotBlank @Pattern(regexp="de|en|es") lang field
- NlSearchController passes request.lang() to service
- NlQueryParserService.search signature: (String, String, Pageable); renames ollamaClient→nlpClient; removes redundant length guard (Bean Validation is enforcement point)
- application.yaml: replaces app.ollama.* with app.nlp.base-url; application-dev.yaml: points to localhost:8001
- frontend/documents/+page.svelte: sends lang: languageTag() in POST body
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drop unused MAX_CANDIDATES constant (not referenced in service)
- Keep detached-entity safety comment in resolveTags()
- Add 3 new partial-name match tests (23a/b/c) from #763
- Use resolveByName() API in test 28 (replaces findByDisplayNameContaining)
- Add NameMatches glossary entry from #763
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The trigger hardcoded the multiple-people label for every count, so a
single did-you-mean picker announced "Mehrere Personen gefunden" to
screen readers while sighted users saw one name and a "Meintest du …?"
heading. Derive the trigger's accessible name from persons.length: a
single suggestion reuses the heading prop, two or more keep the
multiple-people label. Visible truncated name span unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A 1-item picker now reads "Meintest du …?" (a single direct match auto-selects
and never reaches the picker), while ≥2 keeps the "Person auswählen" framing.
The prompt lives in a visible, non-truncated panel heading (the trigger span
clips at 320px), and the "(auswählen…)" cue is dropped for the 1-item case.
DisambiguationPicker takes heading + showCue props; the page derives both from
ambiguousPersons.length. New search_disambiguation_did_you_mean key in de/en/es.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When tagsApplied is true, each resolvedTag renders as a 'Thema: Name'
chip with optional inline color style from the tag's resolved color.
Clicking × calls onRemoveChip('theme', tag.name).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Manual update since Docker compose backend runs old build; regenerate with
npm run generate:api once new backend is deployed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pre-implementation step for #743: ChipType union extracted from
InterpretationChipRow and +page.svelte into shared chip-types.ts;
resolvedTags/tagsApplied neutral defaults added to test fixtures.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses @Sara review: browser tests in this spec fail silently when
the project path contains '+' (common in git worktrees). The comment
tells developers to copy the frontend directory to a clean path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When tagsApplied is true, each resolvedTag renders as a 'Thema: Name'
chip with optional inline color style from the tag's resolved color.
Clicking × calls onRemoveChip('theme', tag.name).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Manual update since Docker compose backend runs old build; regenerate with
npm run generate:api once new backend is deployed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pre-implementation step for #743: ChipType union extracted from
InterpretationChipRow and +page.svelte into shared chip-types.ts;
resolvedTags/tagsApplied neutral defaults added to test fixtures.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Leonie (UX): the toggle pill (text-[7.5px]) and loading subtitle
(text-[9px]) were below the 12px floor for the 60+ audience. Bump both
to text-xs and the toggle icon to h-3.5/w-3.5. Overrides the visual
spec's tokens, which conflicted with the issue's own legibility mandate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mock POST /api/search/nl (delayed fixture: 2-name directional + applied
keyword), assert loading announcement → chips render → axe-clean in light
and dark → removing the keyword chip re-runs a keyword GET with the
remaining sender+receiver params. Adds a data-testid wrapper on the NL
results region for axe scoping.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the smart-search sub-component directory to the frontend Project
Structure tree (merge blocker per #739).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SearchFilterBar drives chip-clearing via onModeToggle (mode switch) and
onSmartSearch (new query); pin that callback contract.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lift smartMode to documents/+page.svelte and drive the full smart-search
lifecycle: POST /api/search/nl via csrfFetch, loading/error panels, chip
row, single-select disambiguation, and a transparent empty state. Chip
removal and disambiguation selection map the interpretation to keyword
params and re-run via GET (Option A in-page fallback). Mode toggle and
new queries reset prior interpretation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add smartMode $bindable plus onSmartSearch/onModeToggle callbacks. The
toggle pill sits in the input's right slot (decorative icon moved to the
left); smart mode disables the live oninput keyword search, adds
maxlength=500, and submits the NL query on Enter. 4 integration specs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Accessible disclosure: aria-expanded/aria-controls trigger, focus moves
into the option list on open, Escape and click-outside close and return
focus to the trigger, selecting a candidate emits onSelect. Single-select
(GET re-run) per the resolved#738 open decision — backend has no
multi-sender OR param. 5 vitest-browser-svelte specs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Renders type-prefixed chips (Absender/Zeitraum/Stichwort), a single
directional chip for 2-name queries, gates keyword chips on
keywordsApplied, and emits onRemoveChip(type, value?). Truncating name
spans keep the 44px × button visible; chip wrappers show a focus ring.
9 vitest-browser-svelte specs (red/green).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Toggle pill with aria-pressed, active/resting styles matching the
AND/OR operator button pattern, and mobile-expanded KI/Text labels.
4 vitest-browser-svelte specs (red/green).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Toggle labels, loading panel, error panels (503/429), empty-state
retry, chip type-prefixes + remove label, and disambiguation strings
for the smart search UI (#739). Formal Sie form per project standard.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- error_smart_search_unavailable/rate_limited now use "Sie" (formal) to
match the tone of all existing German error messages
- Replace inline FQNs in DocumentService.buildPersonSpec with proper
JoinType + Predicate imports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strengthen one renderTranscriptionBody case into the AC-6 contract: a
@DisplayName with an empty mentionedPersons array (the deleted-person case
V71 produces) must render as plain readable text with no <a>, person-mention
class, data-person-id, or href. Guards against a future renderer refactor
silently reintroducing the dead-link-on-deleted-person degradation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The #718 keyboard-tab-order test hardcoded the visual order
['Eugenie','Walter','Clara','Hans'] on the assumption that buildLayout
sorts each generation alphabetically. #724 replaced that with the
tidy-tree layout, which orders a couple's run by structural ownership
(earliest birth year, then a deterministic id tie-break) — so Walter
(id …a1) now owns the run and Eugenie renders to his right.
Both PRs were green independently; the stale assertion only surfaced
once #718 and #724 landed together on main. Correct the expected reading
order to ['Walter','Eugenie','Clara','Hans'] and refresh the now-wrong
'alphabetical' comment. The companion self-validating test (DOM order ==
sorted by y,x) already guarded the real property, so only the hardcoded
assertion needed updating.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review follow-up (Sara, fast-follow): the t no-active-region guard and the
draw-cue arm/disarm rule lived inline in the page with no direct coverage.
Extracted to pure resolveTrainingMark() (no-op when no region; recognition
enrolled flip) and canArmDraw()/shouldDisarmDraw(), each with unit tests
(10 cases total). The page now arms the draw cue only via canArmDraw and
disarms via shouldDisarmDraw, and routes t through resolveTrainingMark.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review follow-up (Leonie, Felix, Markus): bump cheatsheet key caps to text-sm
for the 60+ audience, add a focus-visible ring to the close button, simplify
the draw-hint guard to {#if drawArmed} (the $effect already clears it outside
edit mode), and document why the transcribeShortcuts action ignores its node
and binds to window.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review follow-up (Sara): the prior single-owner evidence was two separate
unit facts against an inert DOM stub. This renders a real AnnotationShape,
attaches the live transcribeShortcuts action, focuses the region, and presses
Delete once — asserting deleteCurrentRegion fires exactly once. A genuine
integration guard against re-introducing a double-bind.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review follow-up (Sara): j/k wrap-around and fresh-entry had no direct
coverage — the logic lived inline in the page where the action spec only
mocks the callbacks. Extracted to a pure stepRegion() with 9 unit tests
(empty list, forward/back, both wraps, fresh-entry null + unknown id,
length-1). Also replaces the inline nested ternary Felix flagged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review follow-up (Requirements Engineer, Leonie) — closes the unmet
acceptance row. The coach card's "press ?" tip rendered unconditionally, so
a touch-only tablet transcriber (no hardware keyboard) was told to press a
key they don't have. The hint is now gated behind a fine-pointer media
query ([@media(pointer:coarse)]:hidden); the cheatsheet itself only opens
via the "?" key, so it already never surfaces without a keyboard. Also bumps
the key cap from 11px to text-xs for the 60+ audience.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review follow-up (Leonie, Requirements Engineer): the Delete key cap was a
hardcoded German "Entf" shown to EN/ES users — now driven by key_cap_delete
(Entf/Del/Supr). The annotation read-only aria-label was a hardcoded German
"Block anzeigen" in all locales — now annotation_view_label. Renamed the Esc
row label from "Bereich schließen" to "Panel schließen" so it no longer
collides with "Bereich" (= region) used elsewhere in the cheatsheet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Seeds a two-block document via API (annotations.spec pattern) and drives the
keyboard: ? opens the cheatsheet, Esc closes it then a second Esc closes the
panel (Esc ladder), e toggles read/edit, and j/k walk the regions forward and
back. Adds an axe-core pass over the open dialog asserting no critical
violations and aria-modal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a secondary keyboard hint to the existing coach footer row pointing
transcribers at the "?" cheatsheet, with a semantic <kbd>. Cross-references
the shortcuts introduced for the empty-state coach (#320).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Attaches the transcribeShortcuts action to the document page and wires every
command to existing context setters: j/k walk the sortOrder-sorted regions
and set activeAnnotationId, e toggles read/edit, n arms a draw cue (edit
only), Delete routes to the existing confirm path, ? opens the cheatsheet,
and Esc is now owned solely by the action — the inline onMount Esc listener
is removed (decision B1). Renders ShortcutCheatsheet and a draw-armed hint.
"t" toggles the document-level KURRENT_RECOGNITION training enrollment (the
only training surface that exists; there is no per-region flag yet — see
#321) and no-ops unless a region is active. Also reconciles annotation
Delete: the shape no longer self-handles the key, with onfocus syncing the
active region so the action deletes exactly once.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Native <dialog aria-modal> cheatsheet: showModal()/close() bridge, close
button focused on open, eight grouped <kbd> rows (nav/edit/utility), an
autosave footer line, and a reduced-motion-guarded fade. Closes on Esc,
backdrop click, and the close button; "?" while open is a no-op. Adds the
shortcut_close_panel i18n key. 8 component tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Single-owner window keydown action for the Transcribe panel: j/k region
nav, e mode toggle, n draw (edit only), t training mark, Delete, ? cheat-
sheet, and the Esc precedence ladder (cheatsheet → editable no-op → close
panel). Pure input-to-callback translator with a focus guard that exempts
only "?"; removes its listener on destroy. 20 unit tests cover every key,
the panel/focus guards, the Esc matrix, and teardown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds de/en/es Paraglide keys for the keyboard-shortcut cheatsheet,
coach hint, draw-armed hint, and the discoverable annotation Delete
aria-label.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bumps the title helper from text-xs (12px) to text-sm (14px) for the 60+ audience (FR-005
prefers a larger size than the field hints) and tightens the component test to assert the
actual localized string and the 14px class — addresses Leonie's and Sara's review notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the FR-TITLE-005 helper line under the title input in DescriptionSection, shown only
on the single-document edit form via a new showTitleHelp prop (off for the new-document and
bulk-edit forms). It is wired to the input with aria-describedby and uses text-ink-3 (WCAG AA
on bg-surface). New Paraglide key form_helper_title_autogenerated in de/en/es. Adds a
component test for the helper + aria wiring and an end-to-end pass: create an auto-titled doc,
edit its date, and see the title follow on the detail page.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review follow-ups:
- Leonie/UX: 0.55 navy on the sand canvas was ~2.6:1, under the WCAG 1.4.11 3:1
non-text floor for senior readers; 0.7 clears it.
- Sara/QA: add a browser test that actually renders a cross-level link and
asserts the distinct 2 6 dash, and that a non-cross-link parent edge stays
solid — the cadence was previously only validated via the structural
crossLinks array, never where it renders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>