Skip to content

feat(editor): tuning-aware Strings modal — either-end add, direct per-string tuning entry - #112

Merged
byrongamatos merged 4 commits into
mainfrom
feat/editor-strings-modal
Jul 8, 2026
Merged

feat(editor): tuning-aware Strings modal — either-end add, direct per-string tuning entry#112
byrongamatos merged 4 commits into
mainfrom
feat/editor-strings-modal

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Roadmap 4.18 (Phase 4 — instruments). The command layer (AddStringCmd / RemoveStringCmd) always supported both ends; the modal was the bottleneck — it hardcoded the position policy, offered no tuning editing, and its guitar floor of 6 made 5-lane banjo charts unconstructible.

What

  • Either-end add/remove as the user''s choice: + Add low / + Add high and − Remove low / − Remove high buttons replace the policy-picked pair. High-side adds were previously unreachable except the one hardcoded 5→6 bass case. Removal still refuses an end string that carries notes — now checked per end independently, so notes on one end never block removing the other.
  • Direct per-string tuning entry: each string row gains a numeric offset input (semitones from the lane''s standard pitch, clamped ±36) through the new undoable SetStringTuningCmd. It captures its target arrangement index so undo lands on the right arrangement after a switch, pads short tuning arrays instead of writing holes, and skips no-op edits. Drop tunings, open tunings, and re-entrant setups — a banjo''s high drone 5th — are now typable instead of unconstructible.
  • Guitar-family range widens to 4–8 strings (bass stays 4–6): the floor of 6 blocked 5-lane banjo-family charts outright.
  • Deferred with rationale: the E9 pedal-steel preset rides on the MAX_LANES=8 vs 10-string display decision (roadmap 4.19) — no point shipping a preset the canvas can''t show.

Tests

tests/strings_modal.test.js (8 cases) drives the real AddStringCmd + SetStringTuningCmd + range policy: the 4-floor, high-side adds (note lanes untouched), low-add note renumbering, the banjo construction sequence end-to-end with exact undo, arrangement-switch undo targeting, array padding, and boundary clamps. The floor/high-add/tuning-entry assertions fail on main. Full JS suite: 42 files green; node --check clean.

Fresh region vs the open queue (#101#108) — Strings modal + its command block only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu

Summary by CodeRabbit

  • New Features

    • Updated the Strings (extended range) modal to be tuning-aware, with per-string semitone offset inputs (±36, clamped) and working undo/redo.
    • Split add/remove into low/high end actions; buttons are enabled based on whether the instrument supports the corresponding extended-range operation.
    • Refreshed guidance for supported string counts (guitar-family 6–8, bass 4–6) and retuning via per-string offsets.
  • Bug Fixes

    • End-string removal now prevents removal only when notes exist on the specific target end.
  • Tests

    • Expanded the Strings modal regression coverage for end eligibility, clamping, and undo/redo behavior.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bf508238-3fb4-481a-8c32-7785193c5a51

📥 Commits

Reviewing files that changed from the base of the PR and between bc77535 and 3c5e5df.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • screen.html
  • screen.js

📝 Walkthrough

Walkthrough

The Strings modal now supports per-string semitone tuning inputs, explicit low/high add/remove controls, updated end-eligibility logic, and expanded regression coverage for the new tuning and boundary behavior.

Changes

Strings modal tuning and directional controls

Layer / File(s) Summary
Core tuning helpers and undo command
screen.js
Adds pure range and clamp helpers, defines SetStringTuningCmd for per-string tuning edits, and updates _stringsRangeForActive() to use the new range logic.
Modal rendering and control wiring
screen.js
Reworks the strings modal to render numeric tuning inputs, updates add/remove enablement and warnings for low/high ends, and adds the window.editorSetStringTuning handler alongside the explicit-position add/remove handlers.
Directional buttons and help text
screen.html, CHANGELOG.md
Replaces the modal’s single add/remove controls with low/high-specific buttons, updates their labels and tooltips, rewrites the help text for bidirectional extended-range support, and adds the changelog note.
Test suite for strings modal
tests/strings_modal.test.js
Adds runtime-extracted regression tests for range policy, tuning clamping, SetStringTuningCmd undo behavior, AddStringCmd low/high cases, and handler-level end-operation validation.

Estimated code review effort: 3 (Moderate) | ~25 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 accurately summarizes the main change: a tuning-aware Strings modal with end-specific add controls and direct per-string tuning entry.
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 feat/editor-strings-modal

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.js

ast-grep timed out on this file


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

@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.

🧹 Nitpick comments (3)
tests/strings_modal.test.js (2)

33-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Brace-counting class extraction is fragile to future syntax.

extractClass counts {/} naively, so any future string, regex, or template literal inside AddStringCmd containing an unbalanced brace would silently mis-extract the class body. Not an issue with current code, just a latent trap for future edits to AddStringCmd.

🤖 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/strings_modal.test.js` around lines 33 - 43, The class extraction
helper is using naive brace counting and can break on future `AddStringCmd`
syntax changes. Update `extractClass` to use a more robust parsing approach that
ignores braces inside strings, regexes, and template literals, or otherwise
constrain it to extract the `AddStringCmd` class body safely without relying on
raw character counts. Keep the helper and its use in `strings_modal.test.js`
aligned with the `extractClass` and `AddStringCmd` symbols.

108-122: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test doesn't exercise _historyEnsureArr's arrangement-switch path.

makeEnv doesn't provide _historyEnsureArr/window.editorSelectArrangement, so doUndo's typeof _historyEnsureArr === 'function' guard is always false here (per EditHistory.doUndo in the provided context snippet). This test verifies that SetStringTuningCmd targets its constructor-captured arrIdx regardless of S.currentArr — which is real coverage — but it doesn't verify the UI-switch-on-undo behavior the PR description calls out ("undo works after arrangement switches"), since that mechanism is bypassed entirely in this harness.

Not a correctness bug in this file, just worth being aware the arrangement-switch UI flow (_historyEnsureArr) remains untested at this layer.

🤖 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/strings_modal.test.js` around lines 108 - 122, The undo test currently
only covers the captured-arrangement behavior of SetStringTuningCmd and never
exercises the arrangement-switch path in EditHistory.doUndo because makeEnv
lacks _historyEnsureArr/window.editorSelectArrangement. Update the test harness
so doUndo can take the _historyEnsureArr branch, or add a separate test that
stubs _historyEnsureArr and verifies the undo action switches to the stored
arrangement before applying the command; use EditHistory.doUndo, makeEnv, and
SetStringTuningCmd to locate the relevant setup.
screen.js (1)

11124-11140: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Give each tuning input an accessible name.

The <input> is wrapped by a <label> whose only text is the st unit, and the identifying String ${i} (${lbl}) text lives in a separate, unlinked <span>. Screen-reader users hear the field announced only as “st spinbutton,” with no indication of which string it edits. Add an aria-label (or wire the left span via for/id).

♿ Proposed tweak
             input.value = String(off);
             input.className = 'w-14 bg-dark-700 border border-gray-700 rounded px-1 py-0.5 text-xs text-gray-300 outline-none text-center';
+            input.setAttribute('aria-label', `String ${i} (${lbl}) tuning offset in semitones`);
             input.title = 'Semitones from this lane’s standard pitch (e.g. -2 = whole-step down; a re-entrant drone can sit far above)';
🤖 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 `@screen.js` around lines 11124 - 11140, The tuning input in the string editor
lacks an accessible name because the `String ${i} (${lbl})` text is not
associated with the `input` created in this block. Update the tuning control in
the `screen.js` string-tuning UI by giving the `input` an `aria-label` that
includes the string identifier, or by wiring the existing left text span to the
input with matching `id`/`for` semantics via the surrounding label markup, so
screen readers announce which string the field edits.
🤖 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.

Nitpick comments:
In `@screen.js`:
- Around line 11124-11140: The tuning input in the string editor lacks an
accessible name because the `String ${i} (${lbl})` text is not associated with
the `input` created in this block. Update the tuning control in the `screen.js`
string-tuning UI by giving the `input` an `aria-label` that includes the string
identifier, or by wiring the existing left text span to the input with matching
`id`/`for` semantics via the surrounding label markup, so screen readers
announce which string the field edits.

In `@tests/strings_modal.test.js`:
- Around line 33-43: The class extraction helper is using naive brace counting
and can break on future `AddStringCmd` syntax changes. Update `extractClass` to
use a more robust parsing approach that ignores braces inside strings, regexes,
and template literals, or otherwise constrain it to extract the `AddStringCmd`
class body safely without relying on raw character counts. Keep the helper and
its use in `strings_modal.test.js` aligned with the `extractClass` and
`AddStringCmd` symbols.
- Around line 108-122: The undo test currently only covers the
captured-arrangement behavior of SetStringTuningCmd and never exercises the
arrangement-switch path in EditHistory.doUndo because makeEnv lacks
_historyEnsureArr/window.editorSelectArrangement. Update the test harness so
doUndo can take the _historyEnsureArr branch, or add a separate test that stubs
_historyEnsureArr and verifies the undo action switches to the stored
arrangement before applying the command; use EditHistory.doUndo, makeEnv, and
SetStringTuningCmd to locate the relevant setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a16ffdaa-6d99-4c6d-a44f-2e1adb6fb835

📥 Commits

Reviewing files that changed from the base of the PR and between 2222c59 and 49ccf18.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • screen.html
  • screen.js
  • tests/strings_modal.test.js

@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-strings-modal branch from 49ccf18 to a3c09a7 Compare July 7, 2026 21:32
byrongamatos added a commit that referenced this pull request Jul 7, 2026
Gate add/remove string ops to the end the pitch/label model can
represent. The either-end/guitar-floor-4 design silently re-snapped the
string count and re-interpreted note indices (guitar high-add, guitar
remove-below-6, high-remove on a low-extended shape, bass high-add at 4).
Add/remove now go through _addPositionPure/_removePositionPure; guitar
floor restored to 6. Direct per-string tuning entry (SetStringTuningCmd)
is unchanged and covers exotic tunings without changing the count.
Handler-level regression tests drive the real _stringCountFor.

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

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@byrongamatos

Copy link
Copy Markdown
Collaborator

Addressed the a11y nitpick: each per-string tuning input now carries an aria-label ("String i (label) tuning offset in semitones"). Skipping the two Trivial/Low-value test nitpicks by design: (1) extractClass brace-counting is fine for the current plain-syntax command classes and a full tokenizer would over-engineer a test helper; (2) the undo harness is deliberately DOM-free — the data-integrity guarantee that matters (SetStringTuningCmd targets its constructor-captured arrIdx) is covered, and the _historyEnsureArr UI-switch path belongs to the shared history layer's own tests. Also fixed a more serious issue this pass: add/remove were gated to the pitch/label model's supported end to prevent silent note-index corruption.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

@byrongamatos Thanks for the update — the a11y fix (per-string aria-labels) looks good, and I appreciate the reasoning on the two skipped nitpicks. Glad you caught the add/remove gating issue on the pitch/label model's supported end; that's a good catch to prevent note-index corruption. Kicking off another review now.

(=^・ω・^=)


✏️ Learnings added
Learnt from: byrongamatos
URL: https://github.com/got-feedBack/feedBack-plugin-editor/pull/112

Timestamp: 2026-07-07T23:18:48.505Z
Learning: In `screen.js` of the got-feedBack/feedBack-plugin-editor repo, the test helper `extractClass` in `tests/strings_modal.test.js` uses simple brace-counting to extract command classes (e.g., `SetStringTuningCmd`, `AddStringCmd`) from `screen.js` for testing. This is an intentional design choice suitable for the current plain-syntax command classes; a full tokenizer is considered over-engineering for this test helper.

Learnt from: byrongamatos
URL: https://github.com/got-feedBack/feedBack-plugin-editor/pull/112

Timestamp: 2026-07-07T23:18:48.505Z
Learning: In `tests/strings_modal.test.js` of the got-feedBack/feedBack-plugin-editor repo, the undo test harness for `SetStringTuningCmd` is intentionally DOM-free. It only verifies the data-integrity guarantee that `SetStringTuningCmd` targets its constructor-captured `arrIdx` on undo (not whatever arrangement is active at undo time). The `_historyEnsureArr` UI-switch path is considered out of scope and is expected to be covered by the shared history layer's own tests.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

ChrisBeWithYou and others added 3 commits July 7, 2026 21:02
…-string entry

Roadmap 4.18. The command layer (AddStringCmd/RemoveStringCmd) always
supported both ends, but the modal hardcoded the position policy (high-side
adds unreachable except the one 5->6 bass case), offered no way to edit a
string tuning, and the guitar floor of 6 made 5-lane banjo charts
unconstructible.

- Add low / Add high + Remove low / Remove high buttons — the END is the
  user choice; removal refuses an end string carrying notes, per end
  independently (the other end stays removable).
- Direct per-string offset entry (semitones from the lane standard pitch,
  clamped +/-36) through the new undoable SetStringTuningCmd, which
  captures its target arrangement index so undo survives an arrangement
  switch and pads short tuning arrays instead of writing holes. Re-entrant
  setups (banjo high drone 5th) are now typable.
- Guitar-family range widens to 4-8 strings (bass stays 4-6) so 5-lane
  banjo-family charts are legal.
- E9 pedal-steel preset deferred with the MAX_LANES=8 vs 10-string display
  decision (roadmap 4.19).

Tests: tests/strings_modal.test.js (8 cases) drives the real AddStringCmd +
SetStringTuningCmd + range policy — the floor, high-side add, and tuning
entry assertions fail on main. Full JS suite 42 files green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu
Gate add/remove string ops to the end the pitch/label model can
represent. The either-end/guitar-floor-4 design silently re-snapped the
string count and re-interpreted note indices (guitar high-add, guitar
remove-below-6, high-remove on a low-extended shape, bass high-add at 4).
Add/remove now go through _addPositionPure/_removePositionPure; guitar
floor restored to 6. Direct per-string tuning entry (SetStringTuningCmd)
is unchanged and covers exotic tunings without changing the count.
Handler-level regression tests drive the real _stringCountFor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The wrapping label's only text is the "st" unit, so a screen reader
announced the field as "st spinbutton" with no string identity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-strings-modal branch from 55d5191 to bc77535 Compare July 8, 2026 02:03

@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.

🧹 Nitpick comments (1)
tests/strings_modal.test.js (1)

209-219: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a test for the "ignore no-op edits" behavior mentioned in the PR summary.

The PR summary states the tuning-edit flow "ignores no-op edits," but that guard lives in editorSetStringTuning (a different cohort layer, not extracted here), so this suite doesn't cover it. Worth a follow-up test once that handler is in scope, to confirm identical old/new offsets don't push a no-op command onto the undo stack.

Also applies to: 221-229

🤖 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/strings_modal.test.js` around lines 209 - 219, Add a follow-up test for
the “ignore no-op edits” path in the tuning-edit flow: this suite currently
covers padding in SetStringTuningCmd, but not the guard in
editorSetStringTuning. When that handler is available in scope, verify that
editing a string to the same old/new offset does not enqueue a command or change
the undo history, using the existing tuning-edit test setup in
strings_modal.test.js as the reference point.
🤖 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.

Nitpick comments:
In `@tests/strings_modal.test.js`:
- Around line 209-219: Add a follow-up test for the “ignore no-op edits” path in
the tuning-edit flow: this suite currently covers padding in SetStringTuningCmd,
but not the guard in editorSetStringTuning. When that handler is available in
scope, verify that editing a string to the same old/new offset does not enqueue
a command or change the undo history, using the existing tuning-edit test setup
in strings_modal.test.js as the reference point.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dfc82d0e-6e4e-44fd-ad59-1d7e29c800a2

📥 Commits

Reviewing files that changed from the base of the PR and between a3c09a7 and bc77535.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • screen.html
  • screen.js
  • tests/strings_modal.test.js
💤 Files with no reviewable changes (1)
  • screen.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • screen.html

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.

2 participants