Skip to content

refactor(app): carve the keyboard-shortcuts subsystem into static/js/shortcuts.js (R3d)#922

Merged
byrongamatos merged 1 commit into
mainfrom
r3d/shortcuts
Jul 12, 2026
Merged

refactor(app): carve the keyboard-shortcuts subsystem into static/js/shortcuts.js (R3d)#922
byrongamatos merged 1 commit into
mainfrom
r3d/shortcuts

Conversation

@byrongamatos

@byrongamatos byrongamatos commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

19 declarations + 23 top-level statements. 922 lines. app.js 3,243 → 2,325 (−28%).

The panel registry, both global keydown dispatchers, the library arrow-nav, and the whole plugin-facing shortcut API.

Most of this subsystem was not declarations

A declaration-seeded dependency closure reports this cluster as 10 names, 246 lines.

It is 42 statements and 922.

window.registerShortcut, createShortcutPanel, getAllShortcuts, unregisterShortcut, clearWindowShortcuts, the panel registry, and both global keydown dispatchers are bare top-level statements. A call-graph scan sees none of them.

That blind spot has now cost three times:

The extractor takes them by construction now — any top-level statement that touches a moved binding comes along — and the seed is closed to a fixed point, because those statements have their own dependencies (_modifiersMatch, _isShortcutActive, _handleLibArrowNav, _gridColumns…) that the declaration closure never walked.

seed → pull in the statements → the statements need more names → re-seed. Iterate until it stops growing.

syncLibrarySong goes across the seam, not through an import

The library arrow-nav calls it on Enter. It cannot be imported: syncLibrarySong reaches showScreen/playSong, and a module importing app.js closes a cycle.

It's the one name here that had to stay behind, so it comes across the host seam — which is exactly what the seam is for. host.js throws loudly if the wiring is ever dropped, and tests/js/host_contract.test.js fails in CI if the hook drifts.

Verification

A/B against origin/main in two browsers — identical, zero page errors — and driven for real, not merely present:

the plugin API (register / unregister / getAll / panels)
the global keydown dispatcher actually firing a registered shortcut
that same shortcut correctly suppressed while typing in a text input
? opening the help modal

node 1045 · pytest 2425 · ESLint 0 (no-cycle clean) · host contract 2/2 · Codex 0.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a keyboard shortcuts help panel, opened with ? and dismissible with Escape or the backdrop.
    • Added context-aware keyboard shortcut support for search, playback, library navigation, settings, and extensions.
    • Improved library navigation with arrow keys, including song, artist, and album activation.
  • Improvements

    • Keyboard shortcuts now avoid interfering while typing or interacting with controls.
    • Escape consistently closes dialogs or clears the active search field.

…shortcuts.js (R3d)

19 declarations + 23 TOP-LEVEL STATEMENTS. 922 lines. app.js 3,243 -> 2,325 (-28%).

The panel registry, both global keydown dispatchers, the library arrow-nav, and the whole
plugin-facing shortcut API.

━━━ MOST OF THIS SUBSYSTEM WAS NOT DECLARATIONS ━━━

A declaration-seeded dependency closure reports this cluster as 10 names, 246 lines.
It is 42 statements and 922.

window.registerShortcut, createShortcutPanel, getAllShortcuts, unregisterShortcut,
clearWindowShortcuts, the panel registry, and BOTH global keydown dispatchers are bare TOP-LEVEL
STATEMENTS at app.js's top level. A call-graph scan sees NONE of them.

That blind spot has now cost three times:
  * it nearly shipped a dead library A-Z rail (#896) — 43 of library.js's exports were
    referenced only from app.js's window contract;
  * it threw "Assignment to constant variable" in the session carve (#921), where the autoplay
    gate's top-level statements wrote state that had just become a read-only import;
  * and here it under-reported the slice by 3x.

The extractor takes them by construction now — any top-level statement that TOUCHES a moved
binding comes along — and the SEED is closed to a FIXED POINT, because those statements have
their own dependencies (_modifiersMatch, _isShortcutActive, _handleLibArrowNav, _gridColumns…)
that the declaration closure never walked. Seed -> pull the statements -> the statements need
more names -> re-seed. Iterate until it stops growing.

━━━ syncLibrarySong GOES ACROSS THE SEAM, NOT THROUGH AN IMPORT ━━━

The library arrow-nav calls it on Enter. It cannot be imported: syncLibrarySong reaches
showScreen/playSong, and a module importing app.js closes a cycle. It is the ONE name here that
had to stay behind, so it comes across the host seam — which is exactly what the seam is for.
host.js throws loudly if the wiring is ever dropped, and tests/js/host_contract.test.js fails in
CI if the hook drifts.

VERIFIED. A/B against origin/main in two browsers, IDENTICAL, zero page errors — and driven for
real, not merely present: the plugin API (register / unregister / getAll / panels), THE GLOBAL
KEYDOWN DISPATCHER actually firing a registered shortcut, that same shortcut correctly SUPPRESSED
while typing in a text input, and `?` opening the help modal.

node 1045, pytest 2425, ESLint 0 (no-cycle clean), host contract 2/2, Codex 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 927673b9-7753-4475-aa5d-cfc2ef1a1b56

📥 Commits

Reviewing files that changed from the base of the PR and between 545e569 and 0f6499b.

📒 Files selected for processing (2)
  • static/app.js
  • static/js/shortcuts.js

📝 Walkthrough

Walkthrough

The PR moves keyboard shortcut handling from static/app.js into static/js/shortcuts.js, including library navigation, shortcut panels, scoped registration, dispatching, modal help, and debugging APIs. syncLibrarySong is also exposed through the host configuration seam.

Changes

Keyboard shortcut subsystem

Layer / File(s) Summary
Host seam extraction
static/app.js, static/js/shortcuts.js
app.js imports shortcut functionality, removes its local implementations, and exposes syncLibrarySong through configureHost.
Library keyboard navigation
static/js/shortcuts.js
Input suppression, library selection, activation, playback synchronization, and list/grid arrow navigation are centralized in the new module.
Shortcut registry and context
static/js/shortcuts.js
ShortcutPanel, scoped registration APIs, panel management, context matching, debugging helpers, and window cleanup are implemented.
Help modal and dispatch flow
static/js/shortcuts.js
Shortcut help, search focus, library actions, Escape handling, built-in registrations, and guarded shortcut dispatch are wired through document keydown handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant shortcutsjs
  participant ShortcutPanel
  Browser->>shortcutsjs: document keydown
  shortcutsjs->>shortcutsjs: Check dispatch blocking
  shortcutsjs->>ShortcutPanel: Match key, modifiers, and scope
  ShortcutPanel-->>shortcutsjs: Return registered handler
  shortcutsjs->>Browser: Prevent default and execute handler
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: extracting the keyboard-shortcuts subsystem from app.js into static/js/shortcuts.js.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch r3d/shortcuts

Comment @coderabbitai help to get the list of available commands.

@byrongamatos
byrongamatos merged commit 57e7db5 into main Jul 12, 2026
5 checks passed
@byrongamatos
byrongamatos deleted the r3d/shortcuts branch July 12, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant