Skip to content

feat(editor): view dropdown — Tab, Notation, and drum-track parity - #316

Merged
ChrisBeWithYou merged 4 commits into
mainfrom
feat/view-switch-tab-notation
Jul 19, 2026
Merged

feat(editor): view dropdown — Tab, Notation, and drum-track parity#316
ChrisBeWithYou merged 4 commits into
mainfrom
feat/view-switch-tab-notation

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Merge order: #317 first, then this PR. This branch has #317 merged into it so the drum dropdown can expose the GM roll. Once #317 lands, this PR's diff-vs-main collapses to its own work.

Why

Christian (07-18): "I need to also add the tab/notation view", then on review "it should be a dropdown, not a pill toggle" and "make sure drums have parity — drums should have notation available as well."

The live engraved score already shipped (tab-view-live.js) but was effectively invisible: reachable only via the view-cycle shortcut or View ▸ Score staff, while the top-right switcher showed just String | Piano roll — and kept String lit even while the engraving owned the timeline. Drums had no engraved view at all, and (with #317) their piano roll was reachable only from the Rows button.

What

A per-track view dropdown, replacing the two-button switcher:

Context Options
Fretted track String view · Piano roll · Tab · Notation · Notation + Tab
Drum editor Drum grid · Piano roll · Notation
Keys track (piano-locked, as before)

Every staff profile is an explicit option, so the selected value is always a truthful readback of state — the lens included. No pref-preservation special-casing needed.

Drum parity, both halves:

  • Notation — the lens engraves the drum tab on a real percussion staff. Clicking an engraved beat selects those hits in the drum grid (S.drumSel) and seeks. The drum-editor mode stays on underneath the lens, so switching back restores the grid with all its state.
  • Piano roll — selects feat(editor): GM roll — a piano-roll layout for the drum editor #317's GM-roll density, so the drum piano roll is discoverable where users look for views. Full/Compact stay on the Rows button (densities of the kit-ordered grid; GM roll is the pitch axis), returning to Drum grid restores the density you came from rather than resetting to Full, and both controls route through one setter so they can't drift.

_alphaTexFromDrumHitsPure is the percussion flavor of the generator; the bar walker (bucketing, gap durations, rest fill, beatMap) is factored into a shared _alphaTexAssemblePure, so drums and fretted parts keep one quantization contract. Pieces with no notation symbol (stack) are skipped and counted, never silently dropped.

Three things only the browser caught

Each was a real defect found by driving the actual host; each is now pinned by a test:

  1. A bare 36.4 lexes as the float 36.4, not articulation 36 with a quarter duration — fretted notes dodge this because 3.6.4 has two dots. Hard alphaTex parse error: the engraving came up blank. Every drum beat is now parenthesized ((36).4).
  2. \clef neutral is bar metadata and load-bearing — without it alphaTab engraves the kit under a treble clef.
  3. The percussion staff needs StaveProfile.Default — forcing Score (what fretted parts use) engraves it as a pitched staff.

Tests

  • view_switch_lens.test.mjs (new): value derivation incl. both and the drum triple, lens entry/exit per option, drum-notation not leaving the drum editor, Piano roll ↔ GM density with both controls agreeing, the Compact→roll→Compact restore, the Rows cycle through the shared setter, keys/drums refusal.
  • alphatex.test.mjs (+5): percussion header/clef/articulation ids, chord grouping with same-piece dedupe, unmapped counting, clef on bar 1 only, and the fretted generator never emitting a clef.

All fail on main. npm test 210/210 · lint 0 errors.

Runtime verification (real host + Playwright)

Fretted: dropdown renders the right option set, Tab and Notation + Tab render and persist the staff pref, String exits the lens. Drums: all three options listed; Rows→Compact then dropdown→Piano roll flips the Rows button to GM roll; returning to Drum grid restores Compact, not Full; Notation engraves with a percussion clef (kick bottom space, snare middle line, X noteheads for hats) without disturbing density. Cross-checked against alphaTab's parsed model: isPercussion: true, channel 9, articulations resolving to MIDI 36/38/42.

🤖 Generated with Claude Code

https://claude.ai/code/session_01929LgKdJMyPGLf8N1WpEVW

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ChrisBeWithYou, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a3bd0f73-2a5f-4709-9912-d11fc86b6f6d

📥 Commits

Reviewing files that changed from the base of the PR and between f4dbf2c and 431298f.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • docs/USER-GUIDE.md
  • screen.html
  • src/alphatex.js
  • src/drum.js
  • src/key-view.js
  • src/tab-view-live.js
  • tests/alphatex.test.mjs
  • tests/view_switch_lens.test.mjs
📝 Walkthrough

Walkthrough

The PR replaces the per-track String/Piano controls with a view dropdown, adds shared drum alphaTex generation, and extends the live Tab lens to render and select drum notation while preserving drum-editor state.

Changes

Track view lens and drum notation

Layer / File(s) Summary
Shared alphaTex and drum engraving
src/alphatex.js
The alphaTex assembler now accepts generic timed items, and drum hits generate percussion notation with mapped articulations, neutral clef metadata, grouping, and unmapped-hit counts.
Live chart and drum rendering
src/tab-view-live.js
The live lens switches between chart and drum engraving sources, configures the matching staff, supports drum-hit selection and seeking, and validates source availability.
Dropdown view routing
src/key-view.js, screen.html
The top-bar control becomes a dropdown supporting String, Piano roll, Tab, Notation, Notation + Tab, Drum grid, and Drum notation states.
Behavior validation and documentation
tests/alphatex.test.mjs, tests/view_switch_lens.test.mjs, docs/USER-GUIDE.md, CHANGELOG.md
Tests cover drum engraving and lens transitions, while documentation describes the expanded track-view controls.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ViewDropdown
  participant ViewModeRouter
  participant TabLens
  participant AlphaTex
  participant Renderer
  User->>ViewDropdown: Choose a track view
  ViewDropdown->>ViewModeRouter: Pass selected mode
  ViewModeRouter->>TabLens: Enable or disable chart/drum lens
  TabLens->>AlphaTex: Generate notes or drum-hit engraving
  AlphaTex->>Renderer: Provide alphaTex and staff configuration
  Renderer-->>User: Display live notation or Tab
Loading

Possibly related PRs

Suggested reviewers: byrongamatos

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: an editor view dropdown adding Tab, Notation, and drum-track parity.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/view-switch-tab-notation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@ChrisBeWithYou

Copy link
Copy Markdown
Contributor Author

Reworked per Christian's design call: the switcher is now a dropdown, not a pill toggle — String view · Piano roll · Tab · Notation · Notation + Tab (every staff profile an explicit option, so the selected value always reads back truthfully and no pref-preservation special-casing is needed). Re-verified in the host with Playwright: five options, Tab renders the engraving, Notation + Tab persists the 'both' pref, String exits the lens.

@ChrisBeWithYou ChrisBeWithYou changed the title feat(editor): Tab and Notation join the view switcher feat(editor): view dropdown — Tab, Notation, and drum-track parity Jul 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/view_switch_lens.test.mjs (1)

58-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover selection preservation when changing staff profiles.

Seed S.sel before switching from Tab to Notation/Both and assert it remains unchanged. The current assertions miss the redundant lens re-entry regression.

🤖 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 `@tests/view_switch_lens.test.mjs` around lines 58 - 67, Update the test
"'notation' and 'both' set the matching staff profile" to seed S.sel while in
Tab mode before switching profiles, then assert the selection is unchanged after
switching to both Notation and Both. Preserve the existing tabViewMode,
tabViewStaff, and dropdown-value assertions while covering redundant lens
re-entry.
🤖 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 `@CHANGELOG.md`:
- Line 42: Restore the changelog bullet boundary before the “In Tempo Map mode”
text so it becomes a separate entry rather than continuing the drum-notation
entry. Update the affected changelog text only, preserving both entries’
wording.

In `@src/key-view.js`:
- Around line 148-151: Remove the redundant editorToggleTabView(true) call from
the mode-switch branch in src/key-view.js lines 148-151, relying on
editorSetTabViewStaff() to enter the lens without clearing S.sel. Update
tests/view_switch_lens.test.mjs lines 58-67 to seed S.sel and assert that
switching from Tab to Notation and Both preserves the selection.

---

Nitpick comments:
In `@tests/view_switch_lens.test.mjs`:
- Around line 58-67: Update the test "'notation' and 'both' set the matching
staff profile" to seed S.sel while in Tab mode before switching profiles, then
assert the selection is unchanged after switching to both Notation and Both.
Preserve the existing tabViewMode, tabViewStaff, and dropdown-value assertions
while covering redundant lens re-entry.
🪄 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: 7f66cafb-aa2a-46f6-adfc-98ba384604e5

📥 Commits

Reviewing files that changed from the base of the PR and between 6243cf8 and f4dbf2c.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • docs/USER-GUIDE.md
  • screen.html
  • src/alphatex.js
  • src/key-view.js
  • src/tab-view-live.js
  • tests/alphatex.test.mjs
  • tests/view_switch_lens.test.mjs

Comment thread CHANGELOG.md Outdated
with no notation symbol are skipped and counted, never silently
dropped). Clicking an engraved beat selects its hits in the drum grid
and seeks. The drum-editor mode stays on underneath the lens, so
switching back to Drum grid restores it with all its state. In Tempo Map

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore the Tempo Map bullet boundary.

The unrelated In Tempo Map mode... text currently continues the drum-notation entry.

Proposed fix
-  switching back to Drum grid restores it with all its state. In Tempo Map
+  switching back to Drum grid restores it with all its state.
+- **Tempo Map issue navigation.** In Tempo Map
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
switching back to Drum grid restores it with all its state. In Tempo Map
switching back to Drum grid restores it with all its state.
- **Tempo Map issue navigation.** In Tempo Map
🤖 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 `@CHANGELOG.md` at line 42, Restore the changelog bullet boundary before the
“In Tempo Map mode” text so it becomes a separate entry rather than continuing
the drum-notation entry. Update the affected changelog text only, preserving
both entries’ wording.

Comment thread src/key-view.js
@ChrisBeWithYou

Copy link
Copy Markdown
Contributor Author

Planned follow-up (Christian's call, 07-18): unify the drum layouts under this dropdown.

This PR gives drum tracks Drum grid · Notation. #317 separately adds a GM roll row density (one row per piece on its General-MIDI note, pitch-descending — the DAW drum-roll layout). Right now the two don't know about each other, so the dropdown implies drums have no piano roll while the piano roll sits behind the Rows button.

Once both this and #317 are merged, a small follow-up commit lands on whichever merged second:

  • drum dropdown becomes Drum grid · Piano roll · Notation, where Piano roll selects GM-roll density;
  • Full / Compact stay on the Rows button — they're densities of the same kit-ordered grid, whereas GM roll is a different axis (pitch);
  • picking Drum grid from the dropdown restores the last non-GM density (default Full), so a Compact user isn't silently reset;
  • the Rows button and the dropdown stay in sync — both read _drumDensityMode(), so _viewSwitchValuePure takes the density as an input.

Net effect is drums on the same three-view vocabulary as fretted tracks: instrument geometry (String / Drum grid) → pitch-ordered (Piano roll / GM rows) → engraved (Tab-Notation / Notation).

Deliberately not stacked as a third PR — this repo has been bitten by stacked-PR merge order before (#302 auto-closing), so it waits for a clean base.

@ChrisBeWithYou
ChrisBeWithYou changed the base branch from main to feat/drum-gm-roll July 19, 2026 03:53
@ChrisBeWithYou
ChrisBeWithYou changed the base branch from feat/drum-gm-roll to main July 19, 2026 05:15
ChrisBeWithYou and others added 4 commits July 19, 2026 00:17
The live engraved score (tab-view-live.js) existed but was reachable
only through the view-cycle shortcut and the View menu — the top-right
switcher showed just String | Piano roll, and its active state ignored
the lens entirely (String stayed lit while the engraving was showing).

- The switcher becomes String · Piano roll · Tab · Notation. Tab opens
  the engraved tablature; Notation the standard-notation staff — both
  drive the existing lens (editorToggleTabView) with the matching staff
  profile (editorSetTabViewStaff).
- A user whose Score-staff preference is "notation + tab" keeps it:
  _tabStaffForClickPure preserves 'both' on either click, and
  _viewSwitchActivePure lights Tab AND Notation together for it (the
  view genuinely shows both).
- The active state is lens-aware; the roll-lock pill hides while the
  lens owns the timeline; Tab/Notation hide on drum tracks (which have
  no tab — same refusal the lens itself makes); keys stay piano-locked.
- Returning to String/Piano roll drops the lens before writing the
  per-part view pref (mirrors the cycle's ordering).
- User Guide: the view list documents all four + the switcher.

Tests: tests/view_switch_lens.test.mjs — active-set derivation
(including 'both' lighting two buttons and junk-staff degradation),
'both'-preserving staff-for-click, lens entry via
editorSetViewMode('tab'/'notation'), String exit dropping the lens, and
keys/drums refusal. All fail on main. Existing tab_view_cycle suite
unchanged and green; full suite 210/210, lint 0 errors.
Runtime-verified through the host with Playwright: 4 buttons, Tab
renders the engraving, Notation switches staff + persists the pref,
String hides the mount.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01929LgKdJMyPGLf8N1WpEVW
Signed-off-by: ChrisBeWithYou <chris@rifflarr.local>
The pill toggle from the previous commit becomes a per-track view
dropdown: String view · Piano roll · Tab · Notation · Notation + Tab.
With a dropdown every staff profile is an explicit option, so the
selected value is always a truthful readback of state (lens included)
and the 'preserve the both pref on click' dance disappears —
_tabStaffForClickPure is gone, _viewSwitchValuePure derives the single
value. Drum tracks lose the engraved options (disabled + hidden)
instead of buttons that refuse. Tests reworked to the value model;
'both' now pinned as an explicit choice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01929LgKdJMyPGLf8N1WpEVW
Signed-off-by: ChrisBeWithYou <chris@rifflarr.local>
…down

Christian's parity call: drums should get Notation too. In the drum
editor the same dropdown now offers Drum grid / Notation, and the live
lens engraves the drum tab on a real percussion staff.

- src/alphatex.js: _alphaTexFromDrumHitsPure, the percussion flavor of
  the generator. The bar walker (bucketing, gap durations, rest fill,
  beatMap) is factored into a shared _alphaTexAssemblePure so drums and
  fretted parts keep ONE quantization contract. DRUM_TEX_ARTICULATIONS
  maps each piece to its alphaTab articulation id; `stack` has none, so
  its hits are skipped and COUNTED, never silently dropped.
- Three things the browser taught us, each now pinned by a test:
  * every drum beat is parenthesized — a bare `36.4` lexes as the FLOAT
    36.4, not articulation 36 with a quarter duration (fretted notes
    dodge this because `3.6.4` has two dots). This was a hard parse
    error: the engraving came up blank.
  * `\clef neutral` is BAR metadata and load-bearing — without it
    alphaTab engraves the kit under a TREBLE clef.
  * the percussion staff renders under StaveProfile.Default; forcing
    Score (as fretted parts do) engraves it as a pitched staff.
- src/tab-view-live.js: a `_texSource` ('chart' | 'drums') selects the
  generator, keys the render + api-rebuild, and routes click-to-select
  into S.drumSel instead of S.sel. Entering the lens from the drum
  editor keeps S.drumEditMode ON underneath (the lens paints over it),
  so leaving Notation restores the grid with its state intact; the draw
  pass drops the lens if the drum tab or mode goes away.
- src/key-view.js + screen.html: the dropdown shows the drum pair in
  drum mode and the five standard views otherwise.

Tests: alphatex suite +5 (percussion header/clef/articulations, chord
grouping with same-piece dedupe, unmapped counting, clef only on bar 1,
and the fretted generator NEVER emitting a clef); view-switch suite +2
(drum value derivation, and drum-notation not leaving the drum editor).
All fail on main. Full suite 210/210, lint 0 errors.

Runtime-verified in the host: the dropdown offers Drum grid / Notation,
the engraving renders with a PERCUSSION clef, kick on the bottom space,
snare on the middle line, X noteheads for hats; switching back restores
the grid. Verified via alphaTab's parsed model too: isPercussion=true,
channel 9, articulations resolving to MIDI 36/38/42.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01929LgKdJMyPGLf8N1WpEVW
Signed-off-by: ChrisBeWithYou <chris@rifflarr.local>
…tion

With the GM roll (#317, merged into this branch) and the view dropdown
both present, the drum piano roll was reachable only from the Rows
button while fretted tracks advertised "Piano roll" in the dropdown — so
a user checking the dropdown for "what layouts does this track have?"
concluded drums had none. Christian's call: fold the unification in
rather than defer it, since he controls the merge order.

- The drum dropdown becomes Drum grid · Piano roll · Notation, where
  Piano roll selects GM-roll density. Full/Compact stay on the Rows
  button: they are densities of the same KIT-ordered grid, whereas the
  GM roll is a different axis (pitch).
- Returning to Drum grid restores the density you came from
  (_drumKitDensityBack), so a Compact user isn't silently reset to Full
  by a trip through the roll.
- Both controls route through ONE setter (_editorSetDrumDensity, which
  _editorToggleDrumDensity now delegates to), so the Rows button and the
  dropdown cannot drift apart — the dropdown reads _drumDensityMode().
- No import cycle: key-view.js -> drum.js is one-directional.

Drums now match fretted tracks' vocabulary: instrument geometry (String
view / Drum grid) -> pitch-ordered (Piano roll / GM rows) -> engraved
(Tab-Notation / Notation).

Tests: +4 in view_switch_lens (density-driven value derivation, Piano
roll setting GM density with both controls agreeing, the Compact/Full
round-trip restore, and the Rows cycle still wrapping through the same
setter). Full suite 210/210, lint 0 errors.

Runtime-verified in the host: the dropdown lists all three; Rows ->
Compact then dropdown -> Piano roll flips the Rows button to GM roll;
returning to Drum grid restores COMPACT (not Full); Notation engraves
without disturbing the density; leaving it returns to the grid.

Merge order: #317 first, then this PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01929LgKdJMyPGLf8N1WpEVW
Signed-off-by: ChrisBeWithYou <chris@rifflarr.local>
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/view-switch-tab-notation branch from 8d8e90e to 431298f Compare July 19, 2026 05:21
@ChrisBeWithYou
ChrisBeWithYou merged commit 73bc53f into main Jul 19, 2026
4 checks passed
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