feat(transcribe): wire keyboard shortcuts into the document panel (#327)

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>
This commit is contained in:
Marcel
2026-06-04 16:56:36 +02:00
committed by marcel
parent 6aaf8ddb9e
commit 61256942e1
6 changed files with 142 additions and 35 deletions

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import type { Annotation } from '$lib/shared/types';
import { m } from '$lib/paraglide/messages.js';
import AnnotationEditOverlay from './AnnotationEditOverlay.svelte';
let {
@@ -12,8 +13,8 @@ let {
isFlashing = false,
isResizable = false,
showDelete = false,
onDeleteRequest,
onclick,
onfocus,
onpointerenter,
onpointerleave
}: {
@@ -26,12 +27,17 @@ let {
isFlashing?: boolean;
isResizable?: boolean;
showDelete?: boolean;
onDeleteRequest?: () => void;
onclick: () => void;
onfocus?: () => void;
onpointerenter: () => void;
onpointerleave: () => void;
} = $props();
// When deletion is available (transcribe mode), announce the otherwise-hidden
// Delete affordance to assistive tech (issue #327). The transcribeShortcuts
// action is the single owner of the key itself.
const ariaLabel = $derived(showDelete ? m.annotation_label_with_delete() : 'Block anzeigen');
function hexToRgba(hex: string, alpha: number): string {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
@@ -83,11 +89,12 @@ let shapeStyle = $derived(
class:annotation-flash={isFlashing}
role="button"
tabindex="0"
aria-label="Block anzeigen"
aria-label={ariaLabel}
aria-keyshortcuts={showDelete ? 'Delete' : undefined}
onclick={onclick}
onfocus={onfocus}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') onclick();
if (e.key === 'Delete' && showDelete) onDeleteRequest?.();
}}
onpointerenter={onpointerenter}
onpointerleave={onpointerleave}