Skip to content

refactor(editor): move Tab preview to src/tab-preview.js (R2, step 35) - #187

Merged
byrongamatos merged 1 commit into
mainfrom
refactor/r2-step35-tab-preview
Jul 10, 2026
Merged

refactor(editor): move Tab preview to src/tab-preview.js (R2, step 35)#187
byrongamatos merged 1 commit into
mainfrom
refactor/r2-step35-tab-preview

Conversation

@byrongamatos

@byrongamatos byrongamatos commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

What

Step 35 of the R2 module-extraction refactor: move the read-only Tab preview out of src/main.js into a new native ES module src/tab-preview.js.

Convert the saved pack of the current fretted part to GP5 and engrave it with a CDN-loaded alphaTab (render-only, no synth) — a modal proofreading lens whose key policy blocks every editor shortcut behind it.

Why now (prerequisite for the keyboard layer)

This is a self-contained leaf (0 deps into main.js, imports only S), and it's the prerequisite for a clean keyboard-command-layer cut next: the keyboard shortcut dispatcher calls _editorShowTabPreview and onKeyDown calls _tabPreviewKeyPolicyPure. Landing tab-preview as its own module first turns those into plain module→module imports instead of awkward host hooks (one of them is a pure function). Same pattern as landing host.js before the command-class steps.

How the seam is drawn

  • 3 window.editor*TabPreview handlers (re-attached by main.js) + the 2 symbols the keyboard layer still needs exported back (_editorShowTabPreview, _tabPreviewKeyPolicyPure).
  • The @pure:tab-preview block is kept export-free (a trailing export { … } statement) so the JS suite's new Function slice of that block stays valid.
  • tests/tab_preview.test.js + tab_preview_race.test.js retargeted to slice from src/tab-preview.js.

main.js drops ~178 lines. 36 modules.

Verification

  • 90/90 JS suites pass; ESLint gate clean (0 errors); strict no-undef clean on tab-preview.js.
  • Codex preflight: clean.
  • New headless harness (verify_tab_preview.py): 3 handlers bound, editorShowTabPreview opens the modal + sets the title + runs the render path (status populated — the engraving itself needs the alphaTab CDN + Tab View plugin, out of headless scope), editorHideTabPreview closes it, zero page errors. Negative-checked by dropping a re-attach.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a read-only Tab Preview modal for supported fretted parts.
    • Preview rendering loads on demand and refreshes safely during rapid updates.
    • Added clear status and error messages for unsupported parts, loading issues, and rendering failures.
    • Preview playback is disabled for a focused viewing experience.
  • Bug Fixes

    • Prevented outdated preview renders from replacing newer results.
    • Improved cleanup when closing the preview modal.

Extract the read-only Tab preview (convert the saved pack of the current fretted
part to GP5 and engrave it with a CDN-loaded alphaTab, render-only) out of the
src/main.js monolith into a new native ES module.

This is a self-contained leaf (0 deps into main.js, imports only S) AND the
prerequisite for a clean keyboard-layer cut next: the keyboard command dispatcher
calls _editorShowTabPreview and onKeyDown calls _tabPreviewKeyPolicyPure, so
landing tab-preview as its own module turns those into plain module imports
instead of awkward host hooks for a pure function.

- src/tab-preview.js: 3 window handlers (re-attached by main.js) + the 2 symbols
  the keyboard layer still needs exported back. The @pure:tab-preview block is
  kept export-free (a trailing export statement) so the JS suite's new-Function
  slice stays valid.
- tests/tab_preview.test.js + tab_preview_race.test.js retargeted to slice from
  src/tab-preview.js.

main.js drops ~178 lines. 36 modules.

Verified: 90/90 JS suites pass, ESLint gate clean (0 errors), strict no-undef
clean on tab-preview.js, Codex preflight clean. New headless harness: 3 handlers
bound, editorShowTabPreview opens the modal + sets the title + runs the render
path (status populated), editorHideTabPreview closes it, zero page errors.
Negative-checked by dropping a re-attach.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 10, 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: 8cd87972-53a8-4df6-9c61-f10aad6b72a4

📥 Commits

Reviewing files that changed from the base of the PR and between 661c0cd and 2d1fce1.

📒 Files selected for processing (4)
  • src/main.js
  • src/tab-preview.js
  • tests/tab_preview.test.js
  • tests/tab_preview_race.test.js

📝 Walkthrough

Walkthrough

Tab Preview functionality is extracted from src/main.js into src/tab-preview.js, including rendering, modal lifecycle, and keyboard handling. main.js rewires imports and globals, while tests update their source extraction paths.

Changes

Tab Preview module extraction

Layer / File(s) Summary
Preview contracts and loader
src/tab-preview.js
Defines preview eligibility, keyboard handling, conversion URL and error helpers, plus lazy loading for the pinned alphaTab renderer.
Preview rendering lifecycle
src/tab-preview.js
Implements asynchronous conversion and rendering with stale-render protection, status updates, modal opening, refreshing, and cleanup.
Editor wiring and source-based tests
src/main.js, tests/tab_preview.test.js, tests/tab_preview_race.test.js
Imports the extracted handlers, reattaches the editor globals, removes the former inline implementation, and updates tests to read from src/tab-preview.js.

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

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main refactor: moving Tab preview into src/tab-preview.js.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 refactor/r2-step35-tab-preview

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

@byrongamatos
byrongamatos merged commit 51be9a0 into main Jul 10, 2026
4 checks passed
@byrongamatos
byrongamatos deleted the refactor/r2-step35-tab-preview branch July 10, 2026 08:38
byrongamatos added a commit that referenced this pull request Jul 10, 2026
…188)

* refactor(editor): move the input layer to src/input.js (R2, step 36)

Extract the entire remaining "Mouse interactions" banner — the keyboard shortcut
dispatch (onKeyDown), all the _editorX command implementations, the
bookmarks/duplicate/section-cmds command clusters, the shortcut panel, and the
canvas context menu (onContextMenu/showSectionMenu) — out of the src/main.js
monolith into a new native ES module. The banner is now empty and gone.

Enabled by landing tab-preview first (#187): the two tab-preview couplings
(_editorShowTabPreview, _tabPreviewKeyPolicyPure) are now plain module imports.

- src/input.js (~1240 lines): imports across ~20 modules; 7 symbols exported back
  (onKeyDown + onContextMenu, wired onto the canvas by main.js init();
  _editorCurrentNoteIndices/_editorSeekToTime/_editorSnapStepSeconds host hooks;
  _bookmarks + the editorWaveformVisible live binding, read by the draw path).
- 5 new host hooks for the composition-root symbols the commands call:
  editorCycleViewMode, editorMovePart, editorToggleKeyHighlight,
  editorTogglePartsView, tempoResolvedMeasureIdx.
- The 4 @pure blocks (shortcut-panel-hint, bookmarks, duplicate, section-cmds)
  and the command classes inside them stay export-free (internal), so the JS
  suite's new-Function slices stay valid.
- Removed 72 now-dead main.js imports (used only by the moved layer) — strict
  no-undef confirmed no over-removal.
- 4 tests retargeted to slice from input.js (bookmarks, duplicate_selection,
  section_undo, roll_position_cycle).

main.js drops ~1243 lines. 37 modules.

Verified: 90/90 JS suites pass, ESLint gate clean (0 errors), strict no-undef
clean on input.js, Codex preflight 0 regressions (init() attachment +
editorWaveformVisible live binding confirmed). New headless harness drives REAL
input: right-click opens the context menu (onContextMenu), the shortcut panel
toggles, editorRunShortcutCommand routes 'toggleKeyHighlight'/'togglePartsView'
into their new host hooks (distinct status each), and a real Shift+A keypress
routes through onKeyDown to the parts-view command — zero page errors. The two
new hooks + onKeyDown negative-checked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(input): CodeRabbit review on #188 — inert measure default, section-menu XSS, tempo-mode delete guard

- host.js: tempoResolvedMeasureIdx default 0 -> -1 so an unwired hook is inert
  (callers apply the time-signature edit only on >= 0). This one is a regression
  from this PR's new hook.
- input.js showSectionMenu: build the buttons as DOM nodes with textContent
  instead of interpolating the user-authored section name into innerHTML —
  removes a stored-XSS sink. (Pre-existing, moved verbatim; hardened here.)
- input.js Delete key: add the !S.tempoMapMode guard (matching duplicate/copy/
  paste) so a stale selection can't delete hidden notes while Tempo Map mode is
  active. (Pre-existing, moved verbatim.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant