refactor(editor): move the loop region and scroll viewport to src/loop.js (R2, step 28) - #180
Conversation
…p.js (R2, step 28) src/main.js 7,714 -> 7,183. Twenty-ninth module; the graph stays acyclic. 66% below the 21,176 lines this refactor started from. 528 lines: the A/B loop strip and its drag / nudge / keyboard handling, bar-range selection, the scroll-bounds math, and snapTime — the one place a raw time becomes a snapped one (grid, or the nearest audio onset). Every module that places something on the timeline snaps through host.snapTime, which resolves here. Three main.js symbols arrive as host hooks: the seek, the snap-step query, and the Loop-in-3D button refresh. The loop-region + scroll functions and snapTime were already host hooks pointing at main.js; they resolve to the loop.js exports now. loop.js imports the A/B functions from audio.js; audio.js reaches loop's _selectedLoopRegion / _setLoopRegionEnabled through host, not import, so no cycle. Caught while wiring: updateLoopIn3DBtn was ALREADY a host hook from a prior step. My scripted add made a second one — removed the duplicate in both host.js and the setHostHooks call. (The 3-new-hook wiring itself landed correctly this time; verified by Codex before trusting it, given the same edit silently no-op'd in #176 and #179.) Also drops a dead _guideTimerSync import left in main.js when #179's _abDisarm consolidation removed its last caller. Tests: 9 loop/onset/group suites retarget their source read to src/loop.js and strip the `export` keyword before eval; loop_ab / loop_nudge_live gained host stubs for the new hooks; loop_undo_mode reads both loop.js and main.js (its @pure:pending-view block stayed behind). Several CJS -> .mjs. verify_loop.py drives editorSetLoopSnapMode end to end (persists the pref + reports through setStatus) and asserts the window.* re-attach; snapTime is covered by verify_drum. Comment out the re-attach and it throws where 90 unit tests pass. node --test 90/90, pytest 248/248, npm run lint 0 errors (6 warnings), Codex clean, all 21 headless harnesses pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughLoop-region geometry, snapping, strip interaction, playback controls, and viewport helpers move into ChangesLoop editor behavior
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Editor
participant LoopStrip
participant LoopModule
participant Host
Editor->>LoopStrip: interact with loop strip
LoopStrip->>LoopModule: create or adjust loop region
LoopModule->>Host: read snap step or seek editor
Host-->>LoopModule: provide hook result
LoopModule-->>LoopStrip: render updated region and controls
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Refactors the editor by extracting the loop-region / bar selection / loop strip UI handling, scroll viewport bounds math, and snapTime into a new src/loop.js module, reducing src/main.js size and keeping the module graph acyclic via host hooks.
Changes:
- Adds
src/loop.jsand rewiressrc/main.jsto import/export loop-region + viewport functionality from it (includingsnapTime), exposing required wiring throughhosthooks. - Extends
src/host.jswith new default hooks (editorSeekToTime,editorSnapStepSeconds) used byloop.js. - Retargets loop/onset-related tests to slice pures from
src/loop.jsand updates eval-stripping to handleexport, plus updates the changelog entry.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/onset_snap.test.js | Retargets snapTime extraction source from main.js to loop.js. |
| tests/loop_undo_mode.test.mjs | Splits sourcing between loop.js (loop helpers) and main.js (@pure:pending-view). |
| tests/loop_snap_modes.test.mjs | Retargets @pure:loop-region slice to loop.js and strips export before eval. |
| tests/loop_region.test.mjs | Retargets @pure:loop-region slice to loop.js and strips export before eval. |
| tests/loop_nudge.test.mjs | Retargets loop nudge-related slices to loop.js and strips export before eval. |
| tests/loop_nudge_live.test.mjs | Updates the injected wiring to match new host-based dependencies and export-tolerant extraction. |
| tests/loop_beats.test.mjs | Retargets loop-beats helper extraction from main.js to loop.js. |
| tests/loop_ab.test.js | Updates _setLoopRegionEnabled sourcing and extraction to come from loop.js. |
| tests/group_move_snap.test.js | Retargets _groupTimeDeltaPure slice from main.js to loop.js and strips export. |
| src/main.js | Removes inlined loop/viewport/snapTime code and wires new host hooks + window handlers to loop.js exports. |
| src/loop.js | New module containing loop-region UI logic, scroll bounds helpers, and snapTime (including onset snapping). |
| src/host.js | Adds default no-op host hooks for seek + snap-step queried by loop.js. |
| CHANGELOG.md | Documents the refactor/module move and the main.js size reduction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/loop.js (1)
168-174: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winViewport-duration formula duplicated instead of reusing
_editorViewportDuration().
((canvas.width / DPR) - LABEL_W) / S.zoomis recomputed inline in_loopStripTimeFromClientX(Line 172) and again in_renderLoopStrip(Line 222), duplicating the same math that_editorViewportDuration()(Lines 35-38) already encapsulates. If the geometry formula ever changes, these two call sites will silently drift out of sync with the canonical implementation.♻️ Proposed consolidation
function _loopStripTimeFromClientX(clientX) { const b = _loopStripTrackBounds(); if (!b || !canvas) return 0; const ratio = Math.max(0, Math.min(1, (clientX - b.left) / b.width)); - const viewDur = Math.max(0, ((canvas.width / DPR) - LABEL_W) / S.zoom); + const viewDur = Math.max(0, _editorViewportDuration()); return S.scrollX + ratio * viewDur; }- const viewDur = Math.max(0, ((canvas.width / DPR) - LABEL_W) / S.zoom); + const viewDur = Math.max(0, _editorViewportDuration()); const left = ((S.barSel.startTime - S.scrollX) / Math.max(0.0001, viewDur)) * 100;Also applies to: 196-238
🤖 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/loop.js` around lines 168 - 174, Replace the duplicated viewport-duration calculations in _loopStripTimeFromClientX and _renderLoopStrip with calls to the canonical _editorViewportDuration() helper, preserving the existing clamping and behavior while ensuring both loop-strip paths use the shared geometry formula.
🤖 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 `@tests/loop_region.test.mjs`:
- Line 34: Update the failure diagnostic near the `@pure`:loop-region block check
to reference src/loop.js instead of the stale src/main.js path, matching the
source file read by the test harness.
In `@tests/loop_snap_modes.test.mjs`:
- Line 47: The extraction failure diagnostic still references the outdated
src/main.js path; update the console.error message in the test harness to report
src/loop.js, matching the module actually read by the test.
---
Nitpick comments:
In `@src/loop.js`:
- Around line 168-174: Replace the duplicated viewport-duration calculations in
_loopStripTimeFromClientX and _renderLoopStrip with calls to the canonical
_editorViewportDuration() helper, preserving the existing clamping and behavior
while ensuring both loop-strip paths use the shared geometry formula.
🪄 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: 50fdb966-2b60-4098-8e4d-695bbd7ab80a
📒 Files selected for processing (13)
CHANGELOG.mdsrc/host.jssrc/loop.jssrc/main.jstests/group_move_snap.test.jstests/loop_ab.test.jstests/loop_beats.test.mjstests/loop_nudge.test.mjstests/loop_nudge_live.test.mjstests/loop_region.test.mjstests/loop_snap_modes.test.mjstests/loop_undo_mode.test.mjstests/onset_snap.test.js
Copilot + CodeRabbit, on #180. Six stale references, all the same class: after retargeting the loop suites' source reads from main.js to loop.js (and audio.js for the @pure:loop-ab / onset-snap blocks), the comments and block-missing failure messages still named main.js. Corrected each to the actual source so a CI failure points at the right file. Verified each against where the sliced symbol now lives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
All six fixed — same class: after retargeting the source reads to |
Twenty-ninth module.
src/main.js7,714 → 7,183 — 66% below where it started.528 lines: the A/B loop strip and its drag / nudge / keyboard handling, bar-range selection, the scroll-bounds math, and
snapTime— the one place a raw time becomes a snapped one (grid, or the nearest audio onset). Every module that places something on the timeline snaps throughhost.snapTime, which resolves here.Wiring
Three
main.jssymbols arrive as host hooks: the seek, the snap-step query, and the Loop-in-3D button refresh. The loop-region + scroll functions andsnapTimewere already host hooks pointing atmain.js; they resolve to theloop.jsexports now.loop.jsimports the A/B functions fromaudio.js;audio.jsreaches loop’s_selectedLoopRegion/_setLoopRegionEnabledthrough host, not import — so no cycle. Verified: 29 modules,NO_CYCLES.The recurring hook trap, watched for
updateLoopIn3DBtnwas already a host hook from a prior step. My scripted add made a second one — caught it, removed the duplicate in bothhost.jsand thesetHostHookscall.The three genuinely-new hooks landed correctly this time — but I had Codex confirm all three are present and point at real functions before trusting it, because the same scripted-edit-anchor-miss silently no-op’d in #176 and #179. It has bitten twice; it now gets an explicit check every time.
Also drops a dead
_guideTimerSyncimport left inmain.jswhen #179’s_abDisarmconsolidation removed its last caller.Tests
9 loop/onset/group suites retarget their source read to
src/loop.jsand strip theexportkeyword before eval.loop_ab/loop_nudge_livegained host stubs for the new hooks;loop_undo_modereads bothloop.jsandmain.js(its@pure:pending-viewblock stayed behind). Several CJS →.mjs.Harness
verify_loop.pydriveseditorSetLoopSnapModeend to end (persists the pref + reports throughsetStatus) and asserts thewindow.*re-attach. Comment out the re-attach and it throws where 90 unit tests pass.snapTimeis covered byverify_drum(a drum hit snaps through the same hook).Verification
node --test90/90 ·pytest248/248 ·npm run lint0 errors (6 warnings) · Codex clean · all 21 headless harnesses pass.🤖 Generated with Claude Code
Summary by CodeRabbit