feat: auto-open transcription panel when navigating from mission-control cards #376

Closed
opened 2026-04-29 21:12:27 +02:00 by marcel · 9 comments
Owner

Problem

First-time users click document links from the "Text markieren" or "Text transkribieren" mission-control columns and then get lost on the document page. The transcription/segmentation panel is not visible by default — the PDF preview dominates, and the panel trigger button is overlooked.

Observed failure mode: user clicks card → document page loads → user cannot find where to act → drops off.

Goal

When a user arrives at a document page from a mission-control card, the transcription panel should already be open. No additional interaction required.

Requirements

REQ-ONBOARD-001 — Deep-link into transcription panel from mission control

When a user clicks a document link from the "Text markieren" or "Text transkribieren" mission-control column, the system shall open the document page with the transcription/segmentation panel already expanded.

Acceptance Criteria

  1. Given a user clicks a card link from the "Text markieren" column,
    when the document page loads,
    then the transcription panel is open without any additional user interaction.

  2. Given a user clicks a card link from the "Text transkribieren" column,
    when the document page loads,
    then the transcription panel is open without any additional user interaction.

  3. Given a user navigates to a document URL without the flag (e.g. from search, person detail, or direct URL),
    when the page loads,
    then the panel remains closed by default — existing behaviour is unchanged.

Implementation Hint

Both columns use the same panel. A single query parameter (e.g. ?task=transcribe) on the card href is sufficient. The document page reads the param on mount and opens the panel if present.

Both SegmentationColumn.svelte and TranscriptionColumn.svelte generate links as /documents/{id} — these need the flag appended.

Out of Scope

Column heading copy improvements (label clarity) are tracked separately as REQ-ONBOARD-002 and are not part of this issue.

## Problem First-time users click document links from the "Text markieren" or "Text transkribieren" mission-control columns and then get lost on the document page. The transcription/segmentation panel is not visible by default — the PDF preview dominates, and the panel trigger button is overlooked. Observed failure mode: user clicks card → document page loads → user cannot find where to act → drops off. ## Goal When a user arrives at a document page *from* a mission-control card, the transcription panel should already be open. No additional interaction required. ## Requirements **REQ-ONBOARD-001 — Deep-link into transcription panel from mission control** > When a user clicks a document link from the "Text markieren" or "Text transkribieren" mission-control column, the system shall open the document page with the transcription/segmentation panel already expanded. ## Acceptance Criteria 1. **Given** a user clicks a card link from the "Text markieren" column, **when** the document page loads, **then** the transcription panel is open without any additional user interaction. 2. **Given** a user clicks a card link from the "Text transkribieren" column, **when** the document page loads, **then** the transcription panel is open without any additional user interaction. 3. **Given** a user navigates to a document URL without the flag (e.g. from search, person detail, or direct URL), **when** the page loads, **then** the panel remains closed by default — existing behaviour is unchanged. ## Implementation Hint Both columns use the same panel. A single query parameter (e.g. `?task=transcribe`) on the card `href` is sufficient. The document page reads the param on mount and opens the panel if present. Both `SegmentationColumn.svelte` and `TranscriptionColumn.svelte` generate links as `/documents/{id}` — these need the flag appended. ## Out of Scope Column heading copy improvements (label clarity) are tracked separately as REQ-ONBOARD-002 and are not part of this issue.
marcel added the P1-highfeatureui labels 2026-04-29 21:12:31 +02:00
Author
Owner

👨‍💻 Felix Brandt — Senior Fullstack Developer

Observations

  • The change is minimal and well-scoped: two href updates (one in SegmentationColumn.svelte:39, one in TranscriptionColumn.svelte:44) plus a task param check in onMount inside +page.svelte.
  • transcribeMode is already a $state(false) on line 40 of +page.svelte. Setting it to true from onMount when the param is present requires no structural change to the component.
  • The existing scrollToCommentFromQuery utility in deepLinkScroll.ts handles commentId/annotationId deep links. The task param is a different concern — don't extend that utility. Handle it as a separate, sequential check in onMount before the scrollToCommentFromQuery call. Keep the responsibilities separated.
  • The SegmentationColumn.svelte.spec.ts test on line 65 explicitly asserts href="/documents/abc-123" and will break when the href changes to include ?task=transcribe. That test must be updated as part of this PR.
  • No TranscriptionColumn.svelte.spec.ts exists — a parallel spec file should be created with the same coverage plus the new link assertion.

Recommendations

  • TDD order: (1) update the failing SegmentationColumn link test to assert ?task=transcribe, watch it fail; (2) update the href; (3) add a TranscriptionColumn spec with the same assertion; (4) write a test for the onMount behavior — when the URL contains ?task=transcribe, transcribeMode should be true after mount.
  • Strip the query param after consuming it (follow the onStripUrl pattern already used in scrollToCommentFromQuery at line 381 of +page.svelte) so a browser refresh doesn't re-trigger the panel open. Use replaceState(page.url.pathname, ...).
  • Name the constant: const TASK_PARAM = 'task' and const TASK_TRANSCRIBE = 'transcribe' if this pattern is likely to grow. If it's genuinely one-off, inline is fine — KISS wins.

Open Decisions

  • Strip param after consume or leave it? Stripping prevents surprise-on-refresh and mirrors the existing commentId pattern. Leaving it means bookmarked mission-control links always auto-open the panel (which could be useful for re-visits). What's the intended behavior?
## 👨‍💻 Felix Brandt — Senior Fullstack Developer ### Observations - The change is minimal and well-scoped: two href updates (one in `SegmentationColumn.svelte:39`, one in `TranscriptionColumn.svelte:44`) plus a `task` param check in `onMount` inside `+page.svelte`. - `transcribeMode` is already a `$state(false)` on line 40 of `+page.svelte`. Setting it to `true` from `onMount` when the param is present requires no structural change to the component. - The existing `scrollToCommentFromQuery` utility in `deepLinkScroll.ts` handles `commentId`/`annotationId` deep links. The `task` param is a different concern — don't extend that utility. Handle it as a separate, sequential check in `onMount` before the `scrollToCommentFromQuery` call. Keep the responsibilities separated. - **The `SegmentationColumn.svelte.spec.ts` test on line 65 explicitly asserts `href="/documents/abc-123"` and will break** when the href changes to include `?task=transcribe`. That test must be updated as part of this PR. - No `TranscriptionColumn.svelte.spec.ts` exists — a parallel spec file should be created with the same coverage plus the new link assertion. ### Recommendations - TDD order: (1) update the failing `SegmentationColumn` link test to assert `?task=transcribe`, watch it fail; (2) update the href; (3) add a `TranscriptionColumn` spec with the same assertion; (4) write a test for the `onMount` behavior — when the URL contains `?task=transcribe`, `transcribeMode` should be `true` after mount. - Strip the query param after consuming it (follow the `onStripUrl` pattern already used in `scrollToCommentFromQuery` at line 381 of `+page.svelte`) so a browser refresh doesn't re-trigger the panel open. Use `replaceState(page.url.pathname, ...)`. - Name the constant: `const TASK_PARAM = 'task'` and `const TASK_TRANSCRIBE = 'transcribe'` if this pattern is likely to grow. If it's genuinely one-off, inline is fine — KISS wins. ### Open Decisions - **Strip param after consume or leave it?** Stripping prevents surprise-on-refresh and mirrors the existing `commentId` pattern. Leaving it means bookmarked mission-control links always auto-open the panel (which could be useful for re-visits). What's the intended behavior?
Author
Owner

🏛️ Markus Keller — Application Architect

Observations

  • This is entirely a frontend UI-state concern. No backend changes, no API changes, no Flyway migration, no new endpoints. The feature correctly stays at the layer where the problem lives.
  • The task query param is ephemeral client state — it signals intent at page load, not persisted data. The right home is onMount in +page.svelte, read via page.url.searchParams, not server-side in +page.server.ts. SvelteKit's load function should not be involved.
  • The scrollToCommentFromQuery utility has a single, clear responsibility: navigate to a comment annotation from a notification click. Adding task=transcribe handling there would give it two responsibilities and pollute a utility that other flows may reuse. The parallel onMount check is the correct structural decision.
  • The issue correctly identifies that segmentation and transcription use the same panel. One param value covers both columns. If a second panel ever existed, you'd extend the param value, not add a second param — good forward-compatible design.

Recommendations

  • Keep the param read in onMount, not in a derived or effect. The param is consumed once on mount; it is not reactive state that needs tracking.
  • The check should be before the scrollToCommentFromQuery call so that if both params are present (unlikely but possible via a manually constructed URL), transcribeMode is already true when scrollToCommentFromQuery runs and tries to set it.

Open Decisions

None. The architectural approach is straightforward and fits the existing patterns.

## 🏛️ Markus Keller — Application Architect ### Observations - This is entirely a frontend UI-state concern. No backend changes, no API changes, no Flyway migration, no new endpoints. The feature correctly stays at the layer where the problem lives. - The `task` query param is ephemeral client state — it signals intent at page load, not persisted data. The right home is `onMount` in `+page.svelte`, read via `page.url.searchParams`, not server-side in `+page.server.ts`. SvelteKit's `load` function should not be involved. - The `scrollToCommentFromQuery` utility has a single, clear responsibility: navigate to a comment annotation from a notification click. Adding `task=transcribe` handling there would give it two responsibilities and pollute a utility that other flows may reuse. The parallel `onMount` check is the correct structural decision. - The issue correctly identifies that segmentation and transcription use the same panel. One param value covers both columns. If a second panel ever existed, you'd extend the param value, not add a second param — good forward-compatible design. ### Recommendations - Keep the param read in `onMount`, not in a derived or effect. The param is consumed once on mount; it is not reactive state that needs tracking. - The check should be **before** the `scrollToCommentFromQuery` call so that if both params are present (unlikely but possible via a manually constructed URL), `transcribeMode` is already `true` when `scrollToCommentFromQuery` runs and tries to set it. ### Open Decisions None. The architectural approach is straightforward and fits the existing patterns.
Author
Owner

🔒 Nora "NullX" Steiner — Security Engineer

Observations

  • No server-side attack surface is introduced. The task param is read client-side in onMount only — it does not reach the backend, does not affect authorization, does not change what data is fetched.
  • The param controls only which UI panel is visible after mount. An attacker who crafts ?task=transcribe on a document they can already access gets… the transcription panel open. This is identical to clicking the panel button themselves. Zero privilege escalation.
  • The document's authentication gate is already enforced by +page.server.ts and Spring Security before any client-side code runs. The param does not affect that gate.
  • No XSS surface: the param value is compared against a string literal (=== 'transcribe'), never rendered into the DOM.

Recommendations

  • Do not reflect the raw param value into any DOM attribute (e.g., aria-label="task: {taskParam}"). The comparison should be param === 'transcribe' and nothing else. This is the natural implementation but worth stating explicitly.
  • Strip the param from the URL after consuming it (replaceState). This is primarily a UX concern (no accidental bookmark of an auto-open URL) but also avoids the param lingering in referrer headers on any subsequent navigation.

Open Decisions

None — this is a clean, low-risk UI-state change with no security implications.

## 🔒 Nora "NullX" Steiner — Security Engineer ### Observations - No server-side attack surface is introduced. The `task` param is read client-side in `onMount` only — it does not reach the backend, does not affect authorization, does not change what data is fetched. - The param controls only which UI panel is visible after mount. An attacker who crafts `?task=transcribe` on a document they can already access gets… the transcription panel open. This is identical to clicking the panel button themselves. Zero privilege escalation. - The document's authentication gate is already enforced by `+page.server.ts` and Spring Security before any client-side code runs. The param does not affect that gate. - No XSS surface: the param value is compared against a string literal (`=== 'transcribe'`), never rendered into the DOM. ### Recommendations - **Do not reflect the raw param value into any DOM attribute** (e.g., `aria-label="task: {taskParam}"`). The comparison should be `param === 'transcribe'` and nothing else. This is the natural implementation but worth stating explicitly. - Strip the param from the URL after consuming it (`replaceState`). This is primarily a UX concern (no accidental bookmark of an auto-open URL) but also avoids the param lingering in referrer headers on any subsequent navigation. ### Open Decisions None — this is a clean, low-risk UI-state change with no security implications.
Author
Owner

🧪 Sara Holt — QA Engineer

Observations

  • Existing test will break: SegmentationColumn.svelte.spec.ts line 65 asserts toHaveAttribute('href', '/documents/abc-123'). After the href changes to /documents/abc-123?task=transcribe, this test fails. This is a required update, not optional cleanup.
  • No TranscriptionColumn.svelte.spec.ts exists. The transcription column currently has zero component-level test coverage. This PR is the right time to create it.
  • The onMount param-reading behavior in +page.svelte is not currently tested at any layer. The component is complex enough that adding an isolated test for this specific behavior may require mocking page.url, which is $app/state — doable with vi.mock.
  • AC3 ("navigating without the flag leaves the panel closed") is the existing behavior and is already tested implicitly by the current spec. No additional test needed for it.

Test Plan

Unit layer (Vitest component tests):

  1. SegmentationColumn.svelte.spec.ts — update line 65: assert href ends with ?task=transcribe
  2. TranscriptionColumn.svelte.spec.ts — new file, matching structure, assert ?task=transcribe on links
  3. deepLinkScroll.spec.ts — no changes needed (task param is handled separately)

E2E layer (Playwright) — optional but recommended:
4. Navigate to /documents/{id}?task=transcribe, assert the transcription panel is visible without user interaction
5. Navigate to /documents/{id} (no param), assert panel is not visible

Open Decisions

  • Should an E2E test be added for the auto-open behavior, or is the component-level coverage sufficient for now? Given this is a critical onboarding flow, I'd recommend at least a smoke-level Playwright assertion.
## 🧪 Sara Holt — QA Engineer ### Observations - **Existing test will break**: `SegmentationColumn.svelte.spec.ts` line 65 asserts `toHaveAttribute('href', '/documents/abc-123')`. After the href changes to `/documents/abc-123?task=transcribe`, this test fails. This is a required update, not optional cleanup. - No `TranscriptionColumn.svelte.spec.ts` exists. The transcription column currently has zero component-level test coverage. This PR is the right time to create it. - The `onMount` param-reading behavior in `+page.svelte` is not currently tested at any layer. The component is complex enough that adding an isolated test for this specific behavior may require mocking `page.url`, which is `$app/state` — doable with `vi.mock`. - AC3 ("navigating without the flag leaves the panel closed") is the existing behavior and is already tested implicitly by the current spec. No additional test needed for it. ### Test Plan **Unit layer** (Vitest component tests): 1. `SegmentationColumn.svelte.spec.ts` — update line 65: assert `href` ends with `?task=transcribe` 2. `TranscriptionColumn.svelte.spec.ts` — new file, matching structure, assert `?task=transcribe` on links 3. `deepLinkScroll.spec.ts` — no changes needed (task param is handled separately) **E2E layer** (Playwright) — optional but recommended: 4. Navigate to `/documents/{id}?task=transcribe`, assert the transcription panel is visible without user interaction 5. Navigate to `/documents/{id}` (no param), assert panel is not visible ### Open Decisions - Should an E2E test be added for the auto-open behavior, or is the component-level coverage sufficient for now? Given this is a critical onboarding flow, I'd recommend at least a smoke-level Playwright assertion.
Author
Owner

🎨 Leonie Voss — UX Designer & Accessibility Strategist

Observations

  • The fix is the right call and the right scope. Passive enhancement: users navigating via normal search or person pages see no change; users arriving from a mission-control card get immediate orientation.
  • Screen reader announcement: when transcribeMode flips to true programmatically on mount, the panel opens but focus stays at the top of the page. A keyboard/screen reader user arrives at the document, hears the title, and does not know a panel has opened below. The panel opening should either (a) move focus into the panel header or first interactive element, or (b) be announced via an aria-live="polite" region.
  • I looked at how scrollToCommentFromQuery handles this: it calls el.scrollIntoView() and el.focus() after the tick. The task-param path should do the same — scroll the panel into view and move focus to the panel's first heading or close button.
  • URL stripping: after consuming the param, call replaceState(page.url.pathname, page.state ?? {}) to clean the URL. The existing deep-link pattern (line 381) already does this. A URL with ?task=transcribe in the address bar would confuse users who copy-paste links to share documents, since their recipients would also land with an open panel.

Recommendations

  1. After setting transcribeMode = true in onMount, call await tick() and then scroll the panel header into view and move focus there — matching the pattern used for comment deep-links.
  2. Strip the param after consuming it.
  3. (Nice-to-have, out of scope for this issue) The panel trigger button could also use aria-expanded to reflect open/closed state — this is already a gap independent of this change.

Open Decisions

None — concrete recommendations above cover the gaps.

## 🎨 Leonie Voss — UX Designer & Accessibility Strategist ### Observations - The fix is the right call and the right scope. Passive enhancement: users navigating via normal search or person pages see no change; users arriving from a mission-control card get immediate orientation. - **Screen reader announcement**: when `transcribeMode` flips to `true` programmatically on mount, the panel opens but focus stays at the top of the page. A keyboard/screen reader user arrives at the document, hears the title, and does not know a panel has opened below. The panel opening should either (a) move focus into the panel header or first interactive element, or (b) be announced via an `aria-live="polite"` region. - I looked at how `scrollToCommentFromQuery` handles this: it calls `el.scrollIntoView()` and `el.focus()` after the tick. The task-param path should do the same — scroll the panel into view and move focus to the panel's first heading or close button. - **URL stripping**: after consuming the param, call `replaceState(page.url.pathname, page.state ?? {})` to clean the URL. The existing deep-link pattern (line 381) already does this. A URL with `?task=transcribe` in the address bar would confuse users who copy-paste links to share documents, since their recipients would also land with an open panel. ### Recommendations 1. After setting `transcribeMode = true` in `onMount`, call `await tick()` and then scroll the panel header into view and move focus there — matching the pattern used for comment deep-links. 2. Strip the param after consuming it. 3. (Nice-to-have, out of scope for this issue) The panel trigger button could also use `aria-expanded` to reflect open/closed state — this is already a gap independent of this change. ### Open Decisions None — concrete recommendations above cover the gaps.
Author
Owner

🚀 Tobias Wendt — DevOps & Platform Engineer

Observations

  • Purely a frontend change. No Docker Compose modifications, no environment variables, no CI pipeline changes, no new services, no infrastructure touch.
  • The change is backward-compatible: existing URLs without the param continue to work exactly as before. No rollback concern.
  • Build pipeline: npm run check (svelte-check) and npm run test (Vitest) are the relevant gates. The existing SegmentationColumn test will fail until updated — this is expected and CI should catch it before merge.
  • No new npm dependencies introduced.

Recommendations

  • Nothing to do from my side. The change is clean and has no operational footprint.
  • One note: if ?task=transcribe URLs are ever generated server-side (e.g., in notification emails), they'll go through the same SvelteKit SSR path without issue — the param is read in onMount (client-side), so SSR renders the closed-panel state and hydration opens it. No hydration mismatch risk.

Open Decisions

None.

## 🚀 Tobias Wendt — DevOps & Platform Engineer ### Observations - Purely a frontend change. No Docker Compose modifications, no environment variables, no CI pipeline changes, no new services, no infrastructure touch. - The change is backward-compatible: existing URLs without the param continue to work exactly as before. No rollback concern. - Build pipeline: `npm run check` (svelte-check) and `npm run test` (Vitest) are the relevant gates. The existing `SegmentationColumn` test will fail until updated — this is expected and CI should catch it before merge. - No new npm dependencies introduced. ### Recommendations - Nothing to do from my side. The change is clean and has no operational footprint. - One note: if `?task=transcribe` URLs are ever generated server-side (e.g., in notification emails), they'll go through the same SvelteKit SSR path without issue — the param is read in `onMount` (client-side), so SSR renders the closed-panel state and hydration opens it. No hydration mismatch risk. ### Open Decisions None.
Author
Owner

🗳️ Decision Queue — 2 decisions needed before implementation

URL Behaviour

  • Strip ?task=transcribe from the URL after the panel opens, or keep it?
    • Strip (Felix, Nora, Leonie all recommend): mirrors the existing commentId deep-link pattern (onStripUrl at +page.svelte:381), prevents the param appearing in shared links or copy-pasted URLs, avoids surprising recipients who'd land with a panel already open.
    • Keep: a bookmarked mission-control card link would always re-open the panel on revisit — potentially useful if users bookmark their queue.
    • Recommendation from all three: strip. The mission-control strip already remembers the queue; users don't need a bookmark for it.

Test Coverage

  • Add a Playwright E2E smoke test for the auto-open behavior, or keep coverage at the Vitest component level only?
    • Component only: faster CI, sufficient for verifying the href and param-read logic in isolation.
    • E2E smoke test too (Sara recommends): confirms the full path — card link → document page load → panel visible — which is the exact failure mode being fixed. Gives confidence that SSR + hydration + onMount all cooperate correctly.
    • This is a critical onboarding path; an E2E assertion is a low-cost safety net for regressions.

(Raised by: Felix, Nora, Leonie for URL behaviour; Sara for test coverage)

## 🗳️ Decision Queue — 2 decisions needed before implementation ### URL Behaviour - **Strip `?task=transcribe` from the URL after the panel opens, or keep it?** - **Strip** (Felix, Nora, Leonie all recommend): mirrors the existing `commentId` deep-link pattern (`onStripUrl` at `+page.svelte:381`), prevents the param appearing in shared links or copy-pasted URLs, avoids surprising recipients who'd land with a panel already open. - **Keep**: a bookmarked mission-control card link would always re-open the panel on revisit — potentially useful if users bookmark their queue. - _Recommendation from all three: strip. The mission-control strip already remembers the queue; users don't need a bookmark for it._ ### Test Coverage - **Add a Playwright E2E smoke test for the auto-open behavior, or keep coverage at the Vitest component level only?** - **Component only**: faster CI, sufficient for verifying the href and param-read logic in isolation. - **E2E smoke test too** (Sara recommends): confirms the full path — card link → document page load → panel visible — which is the exact failure mode being fixed. Gives confidence that SSR + hydration + `onMount` all cooperate correctly. - _This is a critical onboarding path; an E2E assertion is a low-cost safety net for regressions._ _(Raised by: Felix, Nora, Leonie for URL behaviour; Sara for test coverage)_
Author
Owner

🎯 Discussion Resolutions

After reviewing the persona feedback with the user, here are the agreed decisions:

Theme 1 — Implementation location

  • Decision: task param check lives in onMount of +page.svelte, separate from scrollToCommentFromQuery. Runs before the scrollToCommentFromQuery call.
  • Rationale: scrollToCommentFromQuery has a single responsibility (comment notification deep-linking). The task param is a different concern and must not be mixed in. +page.server.ts is not involved — this is client-only UI state that does not affect data fetching.

Theme 2 — href changes

  • Decision: Append ?task=transcribe to links in both column components.
    • SegmentationColumn.svelte:39href="/documents/{doc.id}?task=transcribe"
    • TranscriptionColumn.svelte:44href="/documents/{doc.id}?task=transcribe"
  • Rationale: Both columns open the same panel; one param value covers both. Inline string, no constants needed (KISS — genuinely one-off).

Theme 3 — URL stripping

  • Decision: Strip the param after consuming it via replaceState(page.url.pathname, page.state ?? {}).
  • Rationale: Mirrors the existing onStripUrl pattern used by scrollToCommentFromQuery. Prevents the param appearing in shared/copy-pasted URLs and avoids surprising recipients.
  • Sequence: set transcribeMode = trueawait tick() → scroll + focus → replaceState.

Theme 4 — Focus management

  • Decision: After await tick(), scroll the panel into view and move focus to the panel's close button.
  • Rationale: When transcribeMode flips programmatically, focus stays at the top of the page — keyboard and screen reader users get no signal that the panel opened. The close button is the safest focus target (always present, clearly labelled, immediate escape). Mirrors el.scrollIntoView() + el.focus() pattern from scrollToCommentFromQuery.

Theme 5 — Test coverage

  • Decision: Component-level Vitest tests only (no Playwright E2E for this feature).
    • Update SegmentationColumn.svelte.spec.ts line 65: href assertion must include ?task=transcribe
    • Create TranscriptionColumn.svelte.spec.ts: new file mirroring SegmentationColumn spec structure, including link assertion
    • deepLinkScroll.spec.ts: no changes (task param handled separately)
  • Rationale: E2E coverage deferred — component tests cover the href change and the param-reading logic sufficiently for this scope.

These resolutions are the authoritative design for implementation. The implement skill will read this comment alongside the original issue.

# 🎯 Discussion Resolutions After reviewing the persona feedback with the user, here are the agreed decisions: ## Theme 1 — Implementation location - **Decision**: `task` param check lives in `onMount` of `+page.svelte`, separate from `scrollToCommentFromQuery`. Runs **before** the `scrollToCommentFromQuery` call. - **Rationale**: `scrollToCommentFromQuery` has a single responsibility (comment notification deep-linking). The `task` param is a different concern and must not be mixed in. `+page.server.ts` is not involved — this is client-only UI state that does not affect data fetching. ## Theme 2 — href changes - **Decision**: Append `?task=transcribe` to links in both column components. - `SegmentationColumn.svelte:39` → `href="/documents/{doc.id}?task=transcribe"` - `TranscriptionColumn.svelte:44` → `href="/documents/{doc.id}?task=transcribe"` - **Rationale**: Both columns open the same panel; one param value covers both. Inline string, no constants needed (KISS — genuinely one-off). ## Theme 3 — URL stripping - **Decision**: Strip the param after consuming it via `replaceState(page.url.pathname, page.state ?? {})`. - **Rationale**: Mirrors the existing `onStripUrl` pattern used by `scrollToCommentFromQuery`. Prevents the param appearing in shared/copy-pasted URLs and avoids surprising recipients. - **Sequence**: set `transcribeMode = true` → `await tick()` → scroll + focus → `replaceState`. ## Theme 4 — Focus management - **Decision**: After `await tick()`, scroll the panel into view and move focus to the panel's close button. - **Rationale**: When `transcribeMode` flips programmatically, focus stays at the top of the page — keyboard and screen reader users get no signal that the panel opened. The close button is the safest focus target (always present, clearly labelled, immediate escape). Mirrors `el.scrollIntoView()` + `el.focus()` pattern from `scrollToCommentFromQuery`. ## Theme 5 — Test coverage - **Decision**: Component-level Vitest tests only (no Playwright E2E for this feature). - Update `SegmentationColumn.svelte.spec.ts` line 65: href assertion must include `?task=transcribe` - Create `TranscriptionColumn.svelte.spec.ts`: new file mirroring SegmentationColumn spec structure, including link assertion - `deepLinkScroll.spec.ts`: no changes (task param handled separately) - **Rationale**: E2E coverage deferred — component tests cover the href change and the param-reading logic sufficiently for this scope. --- These resolutions are the authoritative design for implementation. The `implement` skill will read this comment alongside the original issue.
Author
Owner

Implementation complete

Three commits on feat/issue-376-auto-open-transcription-panel:

  • Updated href from /documents/{id} to /documents/{id}?task=transcribe
  • Updated SegmentationColumn.svelte.spec.ts link assertion to expect the param
  • Updated href from /documents/{id} to /documents/{id}?task=transcribe
  • Updated TranscriptionColumn.svelte.spec.ts link assertion to expect the param

84ba728e — feat(document-page): auto-open transcription panel when ?task=transcribe is present

  • In onMount of +page.svelte, reads task param before scrollToCommentFromQuery
  • When task === 'transcribe': sets transcribeMode = true, waits a tick, scrolls the close button ([data-testid="panel-close"]) into view, moves focus to it, then strips the param via replaceState

All 10 component tests pass. Type check clean on affected files.

## ✅ Implementation complete Three commits on `feat/issue-376-auto-open-transcription-panel`: ### `690eb234` — feat(SegmentationColumn): deep-link to transcription panel via ?task=transcribe - Updated `href` from `/documents/{id}` to `/documents/{id}?task=transcribe` - Updated `SegmentationColumn.svelte.spec.ts` link assertion to expect the param ### `6f40b2c3` — feat(TranscriptionColumn): deep-link to transcription panel via ?task=transcribe - Updated `href` from `/documents/{id}` to `/documents/{id}?task=transcribe` - Updated `TranscriptionColumn.svelte.spec.ts` link assertion to expect the param ### `84ba728e` — feat(document-page): auto-open transcription panel when ?task=transcribe is present - In `onMount` of `+page.svelte`, reads `task` param before `scrollToCommentFromQuery` - When `task === 'transcribe'`: sets `transcribeMode = true`, waits a tick, scrolls the close button (`[data-testid="panel-close"]`) into view, moves focus to it, then strips the param via `replaceState` All 10 component tests pass. Type check clean on affected files.
Sign in to join this conversation.
No Label P1-high feature ui
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#376