Skip to content

feat(editor): keyboard time-nudge — ←/→ moves selected notes in time - #240

Merged
byrongamatos merged 2 commits into
mainfrom
feat/editor-time-nudge
Jul 13, 2026
Merged

feat(editor): keyboard time-nudge — ←/→ moves selected notes in time#240
byrongamatos merged 2 commits into
mainfrom
feat/editor-time-nudge

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Gap-audit item #1. Moving a note in time used to require a mouse drag — the single most-repeated timing tweak.

What's new

  • ←/→ nudge the selected notes one snap step earlier/later, as a single grouped, undoable MoveNoteCmd (mirrors _editorResnapSelection's per-note-delta pattern). The group is clamped so the earliest note can't cross the start.
  • With nothing selected, ←/→ seek the playhead a step instead — which also positions the keyboard-entry caret (feat(editor): keyboard note entry — place notes without a mouse #239), completing the no-mouse editing loop.
  • Alt+←/→ still jump note-to-note; plain ←/→ were free (the loop-edge nudge is gated on Alt + a bar selection, and the code even reserves plain/Ctrl arrows for note-ops).

Implementation

New nudgeTimeLeft/nudgeTimeRight registry commands (both keymap profiles) bound to plain ←/→; _editorNudgeSelectionTime(dir) in input.js.

Tests / gates

tests/time_nudge.test.mjs (3: nudge + exact undo, clamp at 0 preserving spacing, no-selection playhead seek). 129 JS suites green (menu_model included), lint 0 errors (3 pre-existing warnings). routes.py untouched.

Verified live

On AC/DC — Back In Black: Ctrl+A then "Nudged 1328 notes later by one step", nudges them back. No page errors.

Follow-up (audit's Shift/Ctrl variants): Shift+←/→ for a fine (ms) nudge and Ctrl+←/→ for a coarse (beat) nudge could layer on the same verb.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Left/Right (←/→) shortcuts to nudge the selected note group by one snap step as a single undoable action, with clamping to prevent crossing the start boundary.
    • When no notes are selected, ←/→ now advances/recedes the playhead and keyboard-entry caret by one snap step.
    • Alt+Left/Right continues to jump note-to-note.
  • Tests
    • Added automated tests covering time nudging, clamping, grouped undo, and playhead caret movement.

Gap-audit item #1. Moving a note in time used to require a mouse drag — the most
repeated timing tweak. Now the Left/Right arrows nudge the selection one snap
step earlier/later as a single grouped, undoable MoveNoteCmd (mirroring
_editorResnapSelection's per-note-delta pattern), clamped so the earliest note
can't cross the start. With NOTHING selected, ←/→ seek the playhead a step
instead — which also positions the keyboard-entry caret (#239). Alt+←/→ still
jump note-to-note; plain ←/→ were free (the loop-edge nudge is gated on Alt).

New `nudgeTimeLeft`/`nudgeTimeRight` registry commands (both profiles) bound to
plain ←/→; `_editorNudgeSelectionTime(dir)` in input.js. `tests/time_nudge.test.mjs`
(3: nudge + undo, clamp at 0, no-selection playhead seek). 129 JS suites green
(menu_model incl.), lint 0-err (3 pre-existing warnings). routes.py untouched.

Verified live on AC/DC — Back In Black: Ctrl+A then → → "Nudged 1328 notes later
by one step", ← nudges them back. No page errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

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: 6582a44c-cbd1-41bb-8cfb-bc436131532a

📥 Commits

Reviewing files that changed from the base of the PR and between 559d84c and 1b415f8.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • src/input.js
  • src/shortcuts.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • src/shortcuts.js

📝 Walkthrough

Walkthrough

Changes

Keyboard time nudging

Layer / File(s) Summary
Shortcut command wiring
src/shortcuts.js, src/input.js
Registers left/right nudge commands, maps plain arrow keys in both profiles, and dispatches the commands.
Selection and playhead nudging
src/input.js
Moves selected notes by one snap step through one undoable grouped command, clamps movement at time zero, or steps the playhead and caret when no notes are selected.
Behavior validation and documentation
tests/time_nudge.test.mjs, CHANGELOG.md
Tests movement, undo, clamping, spacing, and playhead behavior, and documents the shortcuts.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Keyboard
  participant ShortcutMapping
  participant EditorDispatcher
  participant EditorState
  participant History
  Keyboard->>ShortcutMapping: Press plain ArrowLeft or ArrowRight
  ShortcutMapping->>EditorDispatcher: Resolve nudgeTimeLeft or nudgeTimeRight
  EditorDispatcher->>EditorState: Nudge selected notes or playhead by one snap step
  EditorState->>History: Record grouped note movement
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: keyboard arrow-key nudging of selected notes in time.
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 feat/editor-time-nudge

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/time_nudge.test.mjs (1)

36-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the new key-to-command mappings.

These tests call _editorRunEofCommand directly, so they do not verify that plain ArrowLeft/ArrowRight map correctly in either shortcut profile. Add assertions for both mapping functions, including Alt+ArrowLeft/Right remaining prevNote/nextNote.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/time_nudge.test.mjs` around lines 36 - 58, Extend the time-nudge tests
to cover both shortcut-profile mapping functions, asserting plain
ArrowLeft/ArrowRight resolve to the appropriate nudge commands and
Alt+ArrowLeft/ArrowRight remain mapped to prevNote/nextNote. Keep the existing
direct _editorRunEofCommand behavior tests unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/input.js`:
- Around line 420-430: Update the status message in the note-nudge flow around
_editorSnapStepSeconds and delta so clamped movement is reported accurately
instead of always saying “by one step.” Use the actual applied delta or
explicitly indicate when movement was clamped at time zero, while preserving the
existing movement behavior.

---

Nitpick comments:
In `@tests/time_nudge.test.mjs`:
- Around line 36-58: Extend the time-nudge tests to cover both shortcut-profile
mapping functions, asserting plain ArrowLeft/ArrowRight resolve to the
appropriate nudge commands and Alt+ArrowLeft/ArrowRight remain mapped to
prevNote/nextNote. Keep the existing direct _editorRunEofCommand behavior tests
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b7551657-5e8e-4498-9608-7c995cd1803c

📥 Commits

Reviewing files that changed from the base of the PR and between 33521a4 and 559d84c.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/input.js
  • src/shortcuts.js
  • tests/time_nudge.test.mjs

Comment thread src/input.js
Comment on lines +420 to +430
let delta = dir * _editorSnapStepSeconds();
const minT = Math.min(...idxs.map(i => nn[i].time));
if (minT + delta < 0) delta = -minT; // don't push before 0
if (Math.abs(delta) < 1e-9) return false;
const dtimes = idxs.map(() => delta);
const dstrings = idxs.map(() => 0);
S.history.exec(new MoveNoteCmd(idxs, dtimes, dstrings, null));
_editBlipAt();
host.draw();
host.updateStatus();
setStatus(`Nudged ${idxs.length} note${idxs.length === 1 ? '' : 's'} ${dir > 0 ? 'later' : 'earlier'} by one step`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Report clamped movement accurately.

When the earliest note is near zero, delta is reduced below one snap step, but the status still says “by one step.” The existing clamp test moves by only 0.1 seconds. Use the actual delta or report that the selection was clamped at the start.

🧰 Tools
🪛 ast-grep (0.44.1)

[error] 429-429: React's useState should not be directly called
Context: setStatus(Nudged ${idxs.length} note${idxs.length === 1 ? '' : 's'} ${dir > 0 ? 'later' : 'earlier'} by one step)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)

🪛 OpenGrep (1.23.0)

[ERROR] 426-426: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/input.js` around lines 420 - 430, Update the status message in the
note-nudge flow around _editorSnapStepSeconds and delta so clamped movement is
reported accurately instead of always saying “by one step.” Use the actual
applied delta or explicitly indicate when movement was clamped at time zero,
while preserving the existing movement behavior.

@byrongamatos
byrongamatos merged commit 4968f8b into main Jul 13, 2026
4 checks passed
@byrongamatos
byrongamatos deleted the feat/editor-time-nudge branch July 13, 2026 13:37
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.

2 participants