You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Keybinds 2/4: migrate the editing keys (Delete, Backspace, G, F, K) to registerShortcut #38
Register Delete, Backspace, G, F and K through window.registerShortcut() with the scope and condition decided in 1/4 (expected: plugin-editor), so they appear in the global ? help panel.
Keep the existing typing-target guard (_editorIsTypingTarget) so the keys do not fire while a text field is focused.
Remove the corresponding branches from onKeyDown.
Clean up with window.unregisterShortcut(key, scope) using the same scope on teardown.
Acceptance criteria
The five keys behave as before in the editor and no longer work outside its screen.
They are listed in the ? help panel.
Tests cover registration and the typing-target guard.
Plan — Keybinds 2/4: migrate Delete, Backspace, G, F, K to registerShortcut (#38)
Context
Host API (confirmed):window.registerShortcut({ key, handler, description, scope, condition? }); scopes are global | player | library | settings | plugin-<id>. It returns an unregister handle, and the guide says to "keep the returned handle … and unregister when your screen tears down" (feedBack plugin spec, best-practices rule 43). The issue's window.unregisterShortcut(key, scope) is the alternate form to verify against the installed host.
What actually hardcodes these keys today (src/input.js):
G/F/K — the drum-edit articulation toggles (ghost / flam / choke), one guard block at input.js:2023-2043.
Delete/Backspace — a first-match ladder at input.js:2138-2222 (tempo barline → anchor → handshape → tone → drum hits → notes) plus the Tracks-overview region delete at input.js:2011-2016.
Registered as one document keydown listener at main.js:2387; the editor's own ? panel (EDITOR_SHORTCUT_COMMANDS) is a separate, in-plugin surface — the host ? panel is the new target.
No registerShortcut call exists in the repo yet (grep returns only a comment), so this issue is the first consumer and must feature-detect the host API.
Host dispatch order vs. onKeyDown. The read-only Tab-preview/User-Guide lenses swallow every key today by returning early in onKeyDown (input.js:1802-1831); a host-dispatched shortcut may run before/independently of that listener. The migrated handlers must re-check the lens/recording/mode guards themselves, or lens keyboard ownership must move to a capture-phase swallow.
condition semantics and the exact unregister signature — verify against the installed host; fall back to guarding inside the handler and calling the returned handle on teardown.
Milestones
M1 — Registration plumbing (no behavior change).
Add a small module (e.g. src/shortcut-registry.js, or a section of src/shortcuts.js) exporting registerEditorShortcuts() / unregisterEditorShortcuts():
Feature-detect window.registerShortcut (typeof === 'function'), no-op under node/host-absent, matching the repo's degrade-under-node rule.
Store returned handles in a module-level slot; unregister all in teardown.
Call register from init() (main.js:2364) and unregister from _teardownScreenSoft() (main.js:919, the complexity-budgeted helper, not the published closure).
M2 — Extract the handlers so onKeyDown and the registry share one body.
_editorDeleteSelection(e) — the ordered input.js:2138-2222 ladder, returning a handled boolean; include the parts-view region rung (host.partsViewRegionDelete()).
_editorDrumArticulation(dk) — the G/F/K block, returning handled.
Each registered handler: run the context guard (typing target + lens/mode/recording), then e.preventDefault() when it consumed the key — preserving today's fall-through.
M3 — Register the five keys, remove the onKeyDown branches.
Delete + Backspace (one handler, two registrations; description "delete the current selection"), scope plugin-editor.
M4 — Tests (tests/keybind_registration.test.mjs, plus adjustments):
Stub window.registerShortcut to capture calls; assert 5 registrations with correct key/scope/description.
Typing-target guard: invoking a handler with a focused text field must not act; a canvas target must.
Delete ladder parity: one representative per rung (notes, tempo, parts-view region) through the registered handler.
Teardown calls the unregister handle(s); no registration when the host API is absent.
Re-run tests/user_guide_gate.test.mjs (fires Delete through onKeyDown) and the drum suites; update any that depended on the removed branches.
M5 — Docs. Add a CHANGELOG.md[Unreleased] ▸ Added entry (the five keys now surface in the host ? panel); note the #37 decision-table scope correction if a comment edit is warranted.
Acceptance-criteria mapping
Five keys behave as before in-editor, off-screen no longer → plugin-editor scope + preserved guards (M2/M3).
Listed in the ? help panel → registerShortcut with description (M3/M5).
Tests for registration + typing-target guard → M4.
Out of scope
Space and Escape (#39); removing the document listener / final ? verification (#40).
The editing keys (Delete/Backspace, drum G/F/K) now register with window.registerShortcut under the plugin-editor scope, with the shared delete ladder and drum toggles extracted so the host handler and the onKeyDown fallback use one body. Teardown unregisters; hosts without the API degrade to the old path.
Pre-commit review caught a real Shift+Delete (EOF Cut) regression from the modifier-blind early return — fixed with modifier guards on the early-return and both handlers, plus atomic registration rollback and the restored tempo-map hint. node --test → 384 pass / 0 fail (15 new); lint clean.
Activity
pullfrog commentedon Oct 2, 2026
Plan — Keybinds 2/4: migrate Delete, Backspace, G, F, K to
registerShortcut(#38)Context
window.registerShortcut({ key, handler, description, scope, condition? }); scopes areglobal | player | library | settings | plugin-<id>. It returns an unregister handle, and the guide says to "keep the returned handle … and unregister when your screen tears down" (feedBack plugin spec, best-practices rule 43). The issue'swindow.unregisterShortcut(key, scope)is the alternate form to verify against the installed host.src/input.js):G/F/K— the drum-edit articulation toggles (ghost / flam / choke), one guard block atinput.js:2023-2043.Delete/Backspace— a first-match ladder atinput.js:2138-2222(tempo barline → anchor → handshape → tone → drum hits → notes) plus the Tracks-overview region delete atinput.js:2011-2016.documentkeydown listener atmain.js:2387; the editor's own?panel (EDITOR_SHORTCUT_COMMANDS) is a separate, in-plugin surface — the host?panel is the new target.registerShortcutcall exists in the repo yet (grepreturns only a comment), so this issue is the first consumer and must feature-detect the host API.Decisions to confirm before coding
Delete/Backspace:plugin-editor, notplayer. The Keybinds 1/4: audit the hardcoded keys in onKeyDown and decide each key's scope #37 audit recommendedplayer, but it assumed a three-scope vocabulary; the real contract makesplayera distinct screen, and Keybinds 2/4: migrate the editing keys (Delete, Backspace, G, F, K) to registerShortcut #38's acceptance criterion is "behave as before in the editor and no longer work outside its screen." All seven ladder rungs are editor-chrome/editor-data operations, soplugin-editoris the narrowest correct scope.G/F/Kare unambiguouslyplugin-editor.onKeyDown. The read-only Tab-preview/User-Guide lenses swallow every key today by returning early inonKeyDown(input.js:1802-1831); a host-dispatched shortcut may run before/independently of that listener. The migrated handlers must re-check the lens/recording/mode guards themselves, or lens keyboard ownership must move to a capture-phase swallow.conditionsemantics and the exact unregister signature — verify against the installed host; fall back to guarding inside the handler and calling the returned handle on teardown.Milestones
M1 — Registration plumbing (no behavior change).
Add a small module (e.g.
src/shortcut-registry.js, or a section ofsrc/shortcuts.js) exportingregisterEditorShortcuts()/unregisterEditorShortcuts():window.registerShortcut(typeof === 'function'), no-op under node/host-absent, matching the repo's degrade-under-node rule.init()(main.js:2364) and unregister from_teardownScreenSoft()(main.js:919, the complexity-budgeted helper, not the published closure).M2 — Extract the handlers so
onKeyDownand the registry share one body._editorDeleteSelection(e)— the orderedinput.js:2138-2222ladder, returning a handled boolean; include the parts-view region rung (host.partsViewRegionDelete())._editorDrumArticulation(dk)— theG/F/Kblock, returning handled.e.preventDefault()when it consumed the key — preserving today's fall-through.M3 — Register the five keys, remove the
onKeyDownbranches.Delete+Backspace(one handler, two registrations; description "delete the current selection"), scopeplugin-editor.G,F,K(drum articulation) scopeplugin-editor.onKeyDown; leave the Space/Escape/Ctrl+A/profile-dispatch branches for Keybinds 3/4: migrate Space and Escape without clobbering the host's built-ins #39/Keybinds 4/4: remove the document-level key listener and verify the help panel #40 (themain.js:2387listener stays until Keybinds 4/4: remove the document-level key listener and verify the help panel #40).M4 — Tests (
tests/keybind_registration.test.mjs, plus adjustments):window.registerShortcutto capture calls; assert 5 registrations with correct key/scope/description.tests/user_guide_gate.test.mjs(firesDeletethroughonKeyDown) and the drum suites; update any that depended on the removed branches.M5 — Docs. Add a
CHANGELOG.md[Unreleased] ▸ Addedentry (the five keys now surface in the host?panel); note the #37 decision-table scope correction if a comment edit is warranted.Acceptance-criteria mapping
plugin-editorscope + preserved guards (M2/M3).?help panel →registerShortcutwithdescription(M3/M5).Out of scope
Space and Escape (#39); removing the document listener / final
?verification (#40).Task list (3/3 completed)
big-pickle(free) | 𝕏feat(editor): per-track offset nudge, numeric prompt + coalescing undo (
pullfrog commentedon Oct 9, 2026
Implemented #38 and opened PR #65.
The editing keys (
Delete/Backspace, drumG/F/K) now register withwindow.registerShortcutunder theplugin-editorscope, with the shared delete ladder and drum toggles extracted so the host handler and theonKeyDownfallback use one body. Teardown unregisters; hosts without the API degrade to the old path.Pre-commit review caught a real
Shift+Delete(EOF Cut) regression from the modifier-blind early return — fixed with modifier guards on the early-return and both handlers, plus atomic registration rollback and the restored tempo-map hint.node --test→ 384 pass / 0 fail (15 new); lint clean.big-pickle(free) | 𝕏feat(editor): register editing keys with the host shortcut service (#38)