refactor(editor): collapse the per-module hooks into src/host.js (R2, step 17) - #168
Conversation
… step 17) history.js, drum.js and annotation-lanes.js each grew a setXHooks() for the same reason: they need a few main.js symbols that cannot be imported back without closing a cycle. By the fourth module the SAME four callbacks — draw, hideContextMenu, snapTime, editorPromptText — were being threaded through three separate hook objects, and the next extraction (the note command classes) needs nine. That is the moment to stop duplicating. One `host` object, wired once. A new module imports `host` and calls `host.draw()`; no new plumbing, no fourth setter. No behaviour change: every former _hooks.X() call site resolves to the same callback (verified by Codex, one-for-one, including the two renamed keys _editorPromptText -> editorPromptText and ensureArr). The draw thunk stays, and host.js's header now carries the warning where the next person will actually read it: `draw` is reassigned near the bottom of main.js to a button-refreshing wrapper, so passing the bare identifier captures the original function and the refreshes silently stop. That shipped in #165/#166. The header says: pass a thunk, and check `grep -n '^\s*<name> = '` before wiring anything. The inert defaults are type-honest rather than uniformly no-op — snapTime is the identity, editorPromptText resolves to null (a cancelled prompt) — so a module imported under node with no host wired degrades instead of crashing. That is exactly how the unit tests exercise them, which is also why the unit tests cannot see the wiring: comment out setHostHooks() and all 88 still pass, while verify_history.py, verify_drum.py and verify_lanes.py all fail. node --test 88/88, pytest 248/248, npm run lint 0 errors, Codex clean, all 12 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 (3)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughA new shared ChangesShared Host Object Consolidation
Estimated code review effort: 3 (Moderate) | ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR consolidates the per-module “cycle-break” hook setters (setHistoryHooks, setDrumHooks, setLaneHooks) into a single shared host object (src/host.js) that is wired once by src/main.js, allowing extracted modules to call host.* without introducing new plumbing (and avoiding import cycles).
Changes:
- Introduces
src/host.jswith a sharedhostcallback object andsetHostHooks()wiring function. - Refactors
src/history.js,src/drum.js, andsrc/annotation-lanes.jsto read callbacks fromhostinstead of per-module hook objects/setters. - Updates
src/main.jsand affected unit tests to wire callbacks viasetHostHooks, and documents the change inCHANGELOG.md.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/drum_velocity.test.mjs | Switches test wiring from per-module drum hooks to setHostHooks. |
| tests/drum_undo.test.mjs | Switches test wiring to setHostHooks (one stale comment remains). |
| tests/cross_arr_undo.test.mjs | Switches history wiring to setHostHooks. |
| tests/_history_env.mjs | Updates hook-tracking helper to install callbacks via setHostHooks. |
| src/main.js | Replaces multiple per-module hook wiring calls with a single setHostHooks({ ... }). |
| src/host.js | Adds shared host callback object and setHostHooks() setter, with rationale docs. |
| src/history.js | Replaces _hooks.* usage with host.* and removes setHistoryHooks. |
| src/drum.js | Replaces _hooks.* usage with host.* and removes setDrumHooks. |
| src/annotation-lanes.js | Replaces _hooks.* usage with host.* and removes setLaneHooks. |
| CHANGELOG.md | Adds an Unreleased entry describing the consolidation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… references Copilot, on #168. - host.js suggested `grep -n '^\s*<name> = '`. GNU grep accepts `\s` as an extension so it works here, but BSD/macOS grep treats it as a literal 's' and returns a silent zero match — the worst possible answer for a check whose whole job is to catch a reassignment. Switched to a POSIX class and said why. - drum_undo.test.mjs comment still named setDrumHooks. - Three CHANGELOG entries in the same Unreleased block still described the per-module setters that this PR removes, so the release notes contradicted the code they ship with. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
All three fixed. On the Also swept the CHANGELOG: three other entries in the same Unreleased block still described the per-module setters this PR removes, so the release notes contradicted the code shipping with them. |
A small consolidation that pays for the next lift.
Why
history.js,drum.jsandannotation-lanes.jseach grew asetXHooks()for the same reason: they need a fewmain.jssymbols that cannot be imported back without closing a cycle. By the fourth module the same four callbacks —draw,hideContextMenu,snapTime,editorPromptText— were being threaded through three separate hook objects. The next extraction (the note command classes) needs nine.So: one shared
hostobject, wired once bymain.js. A new module importshostand callshost.draw(). No new plumbing, no fourth setter.No behaviour change. Every former
_hooks.X()call site resolves to the same callback — Codex verified the mapping one-for-one, including the two renamed keys (_editorPromptText→editorPromptText, andensureArr).The trap, documented where it will be read
The
drawthunk stays, andhost.js’s header now carries the reason.drawis reassigned near the bottom ofmain.jsto a wrapper that refreshes seven toolbar buttons; passing the bare identifier captures the original function and the refreshes silently stop. That is what shipped in #165/#166 and took a Codex review to find. The header says plainly: pass a thunk, and checkgrep -n ^\s*<name> =before wiring anything.The inert defaults are type-honest rather than uniformly no-op —
snapTimeis the identity,editorPromptTextresolves tonull(a cancelled prompt) — so a module imported under node with no host wired degrades instead of crashing. That is precisely how the unit tests exercise them, and precisely why the unit tests cannot see the wiring.Verification
Comment out the single
setHostHooks(...)call: all 88 unit tests still pass, andverify_history.py,verify_drum.pyandverify_lanes.pyall fail. One call site now guards all three modules.node --test88/88 ·pytest248/248 ·npm run lint0 errors (9 warnings) · Codex clean · all 12 headless harnesses pass.🤖 Generated with Claude Code
Summary by CodeRabbit
Refactor
Bug Fixes
Tests