feat(editor): loop edges as beat coordinates (Phase A4) - #137
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 (7)
📝 WalkthroughWalkthroughLoop edges for bar/grid selections now track beat coordinates, with seconds derived from tempo/grid changes. ChangesBeat-anchored loop edges
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
5fe575b to
4eb557f
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
…the grid (Phase A4) A bar/grid loop's edges are now anchored to beat coordinates (like note.beat); their seconds are a derived cache. So a loop follows the grid the same way a note does: on a tempo FLEX (TempoMapCmd) its beats are the truth and its seconds re-derive (_loopReprojectFromBeats), so it stays on the same bars; on a grid RE-INDEX (TempoGridCmd) its seconds stay put and its beats re-lift (_loopReliftBeats). Free loops stay absolute seconds (D17) and never move. Both operations are exact inverses, so the lossy _loopRelockAfterGridChange re-snap and the TempoMapCmd/TempoGridCmd beforeBarSel undo-snapshots (which only existed because relock was not self-inverse) are deleted. Undoing a tempo edit now restores a bar loop's position EXACTLY, and a flex keeps it on its bars instead of re-snapping it. - _setBarSel(): the single writer for a non-null S.barSel; syncs beat coordinates via _loopSyncBeats. Every loop-edit site (drag / nudge / bar-span / Loop-in-3D / pending-view) routes through it. Loop-in-3D still bakes to seconds (core setLoop unchanged). Requires A2 (beat-primary) — stacked on feat/editor-beat-primary; rebase onto main when A2 merges. Tests: tests/loop_beats.test.js (new, 10) + loop_undo_mode rewritten for the reproject mechanism; harness deps updated in beat_primary / loop_nudge_live / midi_tempo_import. Full JS suite 72/0. No routes.py / no wire change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013JgxKh99UAeQqmhzSc73tv
4eb557f to
d3a2e6e
Compare
Phase A4 — loop edges as beat coordinates
Fourth step of the workspace-shell time-model foundation (§1.6). Stacked on A2 (#135) — base is
feat/editor-beat-primary; it'll retarget tomainwhen A2 merges.What changes
A bar/grid loop's edges are now anchored to beat coordinates — like
note.beat— with their seconds as a derived cache. The loop follows the grid the same way a note does (A2):TempoMapCmd, times move, indexing fixed)_loopReprojectFromBeats— β fixed, seconds =timeOf(newGrid, β)→ stays on its barsTempoGridCmd, insert/delete sync-point, time-sig)_loopReliftBeats— seconds fixed, β =beatOf(newGrid, seconds)Free loops are unchanged — absolute seconds (D17); a grid edit never moves them.
Why this is better
[2,4]could resurface as[1,6]after edit+undo), which is the only reasonTempoMapCmd/TempoGridCmdsnapshottedbeforeBarSel.Because both operations are exact inverses, this deletes the lossy
_loopRelockAfterGridChangere-snap and bothbeforeBarSelundo-snapshots. Netscreen.jsis +71/−57 despite adding the whole beat-anchored model.How
_setBarSel(region)— the single writer for a non-nullS.barSel; it assigns and syncs β via_loopSyncBeats(bar/grid →beatOfof the edges; free → no β). Every loop-edit site (drag create/move, edge-adjust, arrow-key nudge, beat-bar span, Loop-in-3D pin, 3D-handoff restore) routes through it.setLooptakes seconds and the seconds cache is always current, so that boundary is unchanged.S.barSelis runtime-only editor state; β is never persisted.Tests
tests/loop_beats.test.js(new, 10) —_loopSyncBeats(integer/fractional β, free clears, degenerate grid = identity);_loopReprojectFromBeats(bar loop stays on its bars, free untouched, β-less safe-degrade);_loopReliftBeats(seconds fixed, β re-lifts);TempoGridCmdround-trips the loop with no snapshot.tests/loop_undo_mode.test.js— rewritten for the reproject mechanism:TempoMapCmdflex keeps β and undo restores exact seconds with nobeforeBarSelfield; free-loop-untouched; null round-trip; the 3D-handoff mode round-trip stays.beat_primary/loop_nudge_live/midi_tempo_import(the extracted commands/nudge now call the new helpers).Verification
node --check screen.jscleanroutes.pychange (no pytest); no wire/spec changeOrdering
Requires A2 (#135). A5 (beat-lock) also needs A2. Phase B waits on the feature-PR backlog draining. Design source:
SONG-EDITOR-WORKSPACE-SHELL-CHARRETTE.md§1.6.🤖 Generated with Claude Code
Summary by CodeRabbit