diff --git a/.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-RESEARCH.md b/.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-RESEARCH.md new file mode 100644 index 0000000..bf995ef --- /dev/null +++ b/.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-RESEARCH.md @@ -0,0 +1,555 @@ +# Phase 9: Scan Cancellation & Keyboard Shortcuts - Research + +**Researched:** 2026-03-06 +**Domain:** Go context cancellation, frontend keyboard event management, Lit web component architecture +**Confidence:** HIGH + +## Summary + +This phase adds two independent feature sets to YellowJacket: scan control (cancel/pause/resume) on the Go backend with frontend buttons, and a full keyboard shortcut system on the Lit frontend with configurable bindings persisted via the existing TOML config. + +**Scan cancellation** requires threading a cancellable `context.Context` through the existing scan pipeline. The current `Scan()` method already checks `l.ctx.Done()` in several `select` blocks within the directory walker and worker pool. The implementation adds a dedicated `scanCancel context.CancelFunc` field on `Library`, Pause/Resume via a sync-based mechanism (channel or mutex), and new Wails-bound methods (`CancelScan`, `PauseScan`, `ResumeScan`). The cancel confirmation dialog ("Keep X tracks found so far, or discard?") is a frontend concern — the backend simply stops and reports partial results vs rolls back. + +**Keyboard shortcuts** are a pure frontend feature. No external libraries are needed — the browser's `KeyboardEvent` API is sufficient for a Wails desktop app. A central `KeyboardShortcutService` singleton listens on `document.keydown`, resolves the active scope (Global, Panel-specific, Text Input), looks up the action, and dispatches it. Bindings are stored in the Go config (new `Shortcuts` TOML section) and exposed via Wails bindings. The settings UI adds a "Keyboard Shortcuts" tab to the existing `config-page` component with record-style key capture. + +**Primary recommendation:** Implement scan cancellation via `context.WithCancel` + a pause channel on the backend, and keyboard shortcuts as a frontend-only `KeyboardShortcutService` with Go config persistence. Both are zero-dependency — no new libraries needed on either side. + + +## User Constraints (from CONTEXT.md) + +### Locked Decisions +- Hybrid style: Space/arrows for player controls (no modifier), Ctrl+key for app actions +- Up/Down arrows adjust volume, Left/Right seek within track +- Both `/` and `Ctrl+F` focus the search box +- `Q` toggles the queue panel +- `S` for shuffle, `R` for repeat (single-key player controls) +- `Ctrl+A` for select-all in any multi-select context (track lists, etc.) +- All bindings are configurable — the above are defaults +- Claude fills in remaining defaults (mute, etc.) using common media player conventions +- Record-style key capture: click a shortcut row, press the new key combo, it captures live +- Conflicts show a warning with the conflicting action — user chooses to overwrite (old becomes unbound) or cancel +- Shortcuts grouped by category (Player, Navigation, App) in the settings view +- "Reset to defaults" button resets all shortcuts; individual per-shortcut reset also available +- Lives as a "Keyboard Shortcuts" tab within the existing settings dialog +- Three scopes: Global (always active), Panel-specific (when a panel has focus), Text Input (shortcuts suppressed) +- Global scope: player controls (Space, arrows, S, R, Q, etc.) fire regardless of which panel is focused +- Panel-specific scope: track list gets Enter-to-play and Delete-to-remove when focused +- Text Input scope: only Escape works (blurs the text input) — all other shortcuts suppressed +- No visual scope indicator — relies on natural browser focus behavior; users learn through use +- Pause and Cancel buttons placed next to the existing status label, above the existing progress bar in the scanner UI +- On cancel: prompt the user — "Keep X tracks found so far, or discard?" — gives user control over partial results +- On resume after pause: skip already-processed files and continue with remaining — no duplicate work +- Scan control is buttons-only — no keyboard shortcuts for cancel/pause (scans are infrequent) + +### Claude's Discretion +- Remaining default key assignments not explicitly discussed (mute, volume step size, etc.) +- Scan progress detail level and error handling during scan +- Loading/disabled states for scan control buttons +- Visual design of the shortcut settings UI (spacing, grouping headers, etc.) +- How the cancel confirmation dialog looks and behaves + +### Deferred Ideas (OUT OF SCOPE) +None — discussion stayed within phase scope + + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|-----------------| +| SCAN-01 | User can cancel an in-progress library scan via a cancel button | Go context cancellation pattern; new `CancelScan()` Wails binding; frontend cancel button in config-page scan section | +| SCAN-02 | Cancelled scan stops gracefully without corrupting the database | Batch-transactional writes already atomic; cancel skips orphan cleanup (STATE.md warning); partial results either kept or discarded per user choice | +| SCAN-03 | User can pause a library scan and resume it without re-scanning processed files | Pause channel blocks worker pool goroutines; resume unblocks; existingPaths sync.Map already tracks processed files | +| KEY-01 | Default keybindings work out of box | Frontend `KeyboardShortcutService` with hardcoded default map; Go config stores overrides | +| KEY-02 | User can customize all keyboard shortcuts via a visual settings UI | "Keyboard Shortcuts" tab in config-page; record-style key capture component; Wails config bindings for persistence | +| KEY-03 | Shortcut conflicts are detected and warned about when rebinding | Frontend conflict detection during key capture — compare against all bindings in same scope | +| KEY-04 | Shortcuts are scoped — different bindings apply based on focused component | Three-scope system (Global, Panel, TextInput); scope resolved by checking `document.activeElement` shadow DOM chain | +| KEY-05 | Shortcuts are disabled when text input has focus (except Escape to blur) | TextInput scope check: if active element is ``, `