feat(timetable): keyboard a11y composable + page entry — Session 4 step 11 + ship
useTimetableKeyboard (RFC v0.2 D20):
- Arrow ←/→ nudges by SNAP_MIN; Shift+Arrow = ±60min
- Arrow ↑/↓ shifts lane; Shift+Arrow ↑/↓ = ±1 stage
- [/] cycles stages preserving time + lane
- Space starts a "keyboard drag" (announced via aria-live), arrows
accumulate the offset, Enter commits, Esc cancels
- Enter on a focused block opens the popover; Delete confirms+removes
- Pure orchestration — the actual mutation goes through useTimetableMutations
so keyboard moves inherit optimistic update + 409 rollback
pages/events/[id]/timetable/index.vue:
- definePage with organizer context + navActiveLink=events
- ?day query param ↔ store.activeDayId in both directions
- Composes EventTabsNav, TimeAxis, GridBg, StageHeaderCell, StageRow,
Wachtrij, PerformancePopover, AddPerformanceDialog, StageEditor,
LineupMatrix, EmptyDayState
- Conflict pill in toolbar (header total) per prototype audit §4.8
- Status filter chips applied to canvas blocks via store.isStatusVisible
- usePointerDrag + useDragOrClick wires drag to a single move() call;
on success flashes pulseSet on cascaded[] for 1.5s (D18 + D21 keyframe)
- aria-live region echoes keyboard-drag announcements
Tweaks for boundary/lint cleanliness:
- Dialog props switched from Ref<T> to T + toRef inside (Vue templates
auto-unwrap refs; Ref-typed props clashed with template usage)
- Wachtrij counts shadow + sonarjs cleanup
- no-void watcher
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>