feat(editor): loop snap modes — bar / grid / free + no-grid fallback - #92
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughAdds three loop snap modes to the loop strip, with mode-aware loop math, persisted mode state, Shift-drag overrides, tempo-edit relocking, pending-view preservation, and test coverage. ChangesLoop Snap Modes
Estimated code review effort: 4 (Complex) | ~45 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.44.1)screen.jsast-grep timed out on this file Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
screen.html (1)
133-135: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider
aria-pressedfor the active mode button.All three buttons share identical base classes with no built-in indicator of which mode is currently selected; this relies entirely on JS toggling classes at runtime (not shown in this batch). Using
aria-pressedalongside the visual class toggle would make the active mode discoverable to assistive technology, independent of how the styling is implemented.🤖 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 `@screen.html` around lines 133 - 135, Add an accessible active-state indicator to the loop snap mode controls in the button group that uses editorSetLoopSnapMode; the current selection is only conveyed visually via runtime class changes. Update the Bar, Grid, and Free buttons to expose their selected state with aria-pressed, and ensure the value is kept in sync with the active mode wherever the UI updates the classes.
🤖 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.
Nitpick comments:
In `@screen.html`:
- Around line 133-135: Add an accessible active-state indicator to the loop snap
mode controls in the button group that uses editorSetLoopSnapMode; the current
selection is only conveyed visually via runtime class changes. Update the Bar,
Grid, and Free buttons to expose their selected state with aria-pressed, and
ensure the value is kept in sync with the active mode wherever the UI updates
the classes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 0ecce53f-eadb-491c-bb72-689530971c1c
📒 Files selected for processing (4)
CHANGELOG.mdscreen.htmlscreen.jstests/loop_snap_modes.test.js
The loop region was bar-snapped only (_barSpanForTimes on every drag), and looping was disabled entirely on a chart with no downbeats — the exact starting state of a song that wasn't recorded to a click. - Three-way mode control in the loop strip: Bar (whole-downbeat spans, unchanged default), Grid (edges snap via snapTime at the current subdivision, honoring the snap on/off toggle), Free (no snapping). - Shift-drag = temporary Free in any mode, resolved live mid-gesture. - No-grid fallback: the strip stays enabled with zero downbeats and forces Free, so drifting-tempo songs can loop while their tempo map is being authored. - Regions carry their mode; TempoMapCmd/TempoGridCmd exec+rollback now relock bar/grid loops onto the new grid (end-edge probe avoids growing a bar span whose end already sits on a downbeat) while freely drawn loops never move. Loop-in-3D is seconds-based and unaffected. - Grid/free regions show a plain time range, not a "Bars X-Y" label. - Mode preference persists as an editor pref (localStorage). Tests: tests/loop_snap_modes.test.js (10 cases). node --check clean; all 26 JS test files pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu
…gion.mode through 3D handoff TempoMapCmd.rollback and TempoGridCmd.rollback previously re-ran _loopRelockAfterGridChange(), which re-derives S.barSel from the (already-relocked) grid. Relock is lossy / not self-inverse, so exec->rollback was non-idempotent: a bar loop [2,4] resurfaced as [1,6] after edit+undo. Both commands now snapshot S.barSel (deep copy incl. mode) on first exec BEFORE mutating, and rollback restores that exact snapshot. Forward relock stays on exec so a live tempo edit still re-snaps the loop onto the new grid. The editor<->3D-highway round-trip dropped region.mode: _effectiveLoopRegion, _resolvePendingViewStatePure and editorLoopIn3D rebuilt the region without mode, so a freely drawn loop returned as mode===undefined (treated as 'bar' -> moved on the next tempo edit and mislabeled). All three now carry mode through. Tests: TempoMapCmd exec/rollback loop-symmetry (undo restores the exact original barSel; redo re-applies) and mode preservation through the pending-view handoff resolver. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8b95704 to
70a4291
Compare
- Shift+click after a marquee anchors on the nearest multi-selected downbeat instead of falling through to the plain path (which cleared the selection and started a drag) — _tempoShiftAnchorPure. - The context menu offers 'Delete N barlines' only when the clicked pole is itself in the selection; otherwise the bulk action would delete the OTHER barlines and leave the one under the cursor — _tempoPoleBulkCountPure. - Undoing the bulk delete restores the multi-selection and the focus (#92: snapshot, restore selection state) — TempoGridCmd grows an optional oldMulti snapshot, restored on rollback only; exec (redo) still drops the set, and commands without a snapshot keep the drop-never-remap contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
The loop region was bar-snapped only — every drag ran through
_barSpanForTimes— and looping was disabled entirely when the chart has no downbeats, which is the exact starting state of a song that wasn't recorded to a click. Transcribers need to loop a phrase before the tempo map exists, loop in subdivisions, and draw completely free loops.snapTimeat the current snap subdivision, honoring the snap on/off toggle (snap off ⇒ behaves as Free — one coherent grid setting). Free = unsnapped.TempoMapCmd/TempoGridCmdexec+rollback now relock bar/grid loops onto the changed grid (with an end-edge probe so a bar span whose end sits exactly on a new downbeat doesn't grow by a bar), while freely drawn loops are absolute seconds and never move.Verification
node --check screen.jscleantests/loop_snap_modes.test.js— 10 cases via the@pure:loop-regionblock: bar-span legacy behavior + mode stamping, bar-degrades-to-free with no downbeats, grid snap-fn routing, free normalization/clamping, zero-width create, edge adjust per mode (no edge-crossing), mid-drag mode switch restampsloop_region.test.jsuntouched and green)🤖 Generated with Claude Code
https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu
Summary by CodeRabbit
New Features
Bug Fixes
Tests