Skip to content

feat(editor): handshape / arpeggio region authoring (E2, PR-B) - #10

Merged
byrongamatos merged 1 commit into
mainfrom
feat/editor-handshape-lane
Jun 20, 2026
Merged

byrongamatos merged 1 commit into
mainfrom
feat/editor-handshape-lane

Conversation

@byrongamatos

Copy link
Copy Markdown
Collaborator

PR-B of E2 — the span-lane authoring UI. Rebased onto main after PR-A (#8) merged (supersedes the auto-closed #9, which lost its base branch when #8 was merged with --delete-branch).

Lets the user draw, move, resize, retarget and delete handshape (chord-shape / arpeggio framing) regions in the Arrangement Editor. Modelled on the existing anchor lane, adapted from point markers to time spans.

What this PR adds

  • LayoutHS_LANE_H strip below the anchor lane (_handshapeLaneTopY); LANE_H math reserves it (keeps the max(30,…) floor so note lanes aren't starved).
  • DrawdrawHandshapeLane / _drawHandshapeBar: bars start_time..end_time, coloured by arp, labelled with the covered template; selection highlight + live create-preview.
  • Interactions — drag empty lane = create (arp defaults true); click = select; drag body = move; drag edges = resize; Del = remove; right-click = toggle arp / pick covered template. Commands Add/Remove/Move/Resize/ToggleArp/SetChord HandshapeCmd via S.history.
  • chord_id resolution — from the voicing under the span (_handshapeSpanFrets: same-time chord, else combined single notes = arpeggio). AddHandshapeCmd find-or-creates the template, tail-appended + LIFO-removed on rollback so live indices stay stable; reconstructChords() remaps on save.
  • State/routingS.handshapeSel + S.drag.type='handshape', routing + Delete mirroring the anchor lane; S.handshapeSel cleared wherever tone/anchor selection activates.

Robustness (Codex preflight, 3 rounds, converged — no P1s)

  • remapHandshapeChordIds mutates chord_id in place (identity preserved → undo/redo survives a save's reconstruct).
  • SetHandshapeChordCmd resolves templates by fret-pattern key (survives the save-time rebuild), index fallback.

Tests / verification

  • tests/handshape_authoring.test.js (new) drives a full authoring → reconstruct round-trip on the real extracted _handshapeSpanFrets + pure remap helpers. 14/14 pass; node --check clean.
  • Live browser E2E (native core + Playwright on /v2): loaded a real pack (289 existing handshapes round-tripped), drew an arpeggio span, saved, confirmed the authored arp=true handshape serialized validly — 0 JS errors.

Closes #5. Part of got-feedBack/feedBack#334.

🤖 Generated with Claude Code

PR-B of E2: the span-lane authoring UI, built on PR-A's load/serialize/
chord_id-remap foundation. Lets the user draw, move, resize, retarget and
delete handshape (chord-shape / arpeggio framing) regions directly in the
Arrangement Editor.

Modelled on the existing anchor lane, adapted from point markers to time
spans:
- Layout: HS_LANE_H strip below the anchor lane (_handshapeLaneTopY); the
  LANE_H math reserves it alongside the anchor lane (keeps the max(30,…)
  floor so note lanes aren't starved).
- Draw: drawHandshapeLane / _drawHandshapeBar — horizontal bars from
  start_time..end_time, coloured by arp (arpeggio vs held shape), labelled
  with the covered template's name; selection highlight.
- Interactions: drag empty lane = create span (arp defaults true); click =
  select; drag body = move; drag edges = resize; Del = remove; right-click =
  toggle arp / pick the covered template. Commands AddHandshapeCmd,
  RemoveHandshapeCmd, MoveHandshapeCmd, ResizeHandshapeCmd,
  ToggleHandshapeArpCmd, SetHandshapeChordCmd run through S.history.
- chord_id for a new span resolves from the voicing under it
  (_handshapeSpanFrets): a same-time chord's frets, else the span's single
  notes combined (arpeggio). AddHandshapeCmd find-or-creates the matching
  template, tail-appended + LIFO-removed on rollback so other handshapes'
  live chord_id indices stay stable. reconstructChords() remaps everything
  to the rebuilt indices on save (PR-A).
- State/routing: S.handshapeSel + S.drag.type='handshape', mouse/contextmenu
  routing + Delete wiring mirroring the anchor lane; S.handshapeSel cleared
  wherever tone/anchor selection activates (mutual exclusion).

Robustness (Codex preflight, 3 rounds):
- remapHandshapeChordIds mutates chord_id in place (preserves object
  identity) so undo/redo refs survive a save's reconstruct.
- SetHandshapeChordCmd resolves templates by fret-pattern key (survives the
  save-time template rebuild) with the index as fallback.

Tests: tests/handshape_authoring.test.js (new) extracts the real
_handshapeSpanFrets + the pure remap helpers and drives a full authoring →
reconstruct round-trip (held + arpeggio spans stay backend-valid; orphan
template appended for the arpeggio). chord_relink.test.js updated for the
in-place remap. Verified end-to-end in a real browser (native core +
Playwright on /v2): loaded a real pack, drew an arp span on the lane, saved,
and confirmed the authored arp handshape + the loaded set serialized validly
with 0 JS errors.

Closes #5 (with PR-A). Part of got-feedBack/feedBack#334.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos
byrongamatos merged commit 4fb4eee into main Jun 20, 2026
@byrongamatos
byrongamatos deleted the feat/editor-handshape-lane branch June 20, 2026 16:40
byrongamatos added a commit that referenced this pull request Jun 20, 2026
Post-merge Codex review of the E2 span-lane authoring (PR #10) surfaced a P1
plus four P2/P3 edge cases. Fix all:

- P1: drawing a span over empty bars created a handshape with chord_id=0 (an
  unrelated/invalid template). onHandshapeLaneMouseUp now requires
  _handshapeSpanFrets() to resolve a voicing before creating.
- P2: a tempo/drum mode toggle mid-drag dropped an in-flight handshape edit with
  no history command. _finalizeActiveDrag now commits it via onHandshapeLaneMouseUp.
- P2: deleting a selected handshape mid-drag left S.drag dangling -> clear it.
- P2: AddHandshapeCmd tracked its template by object identity, so undo/redo across
  a save's reconstruct could orphan/duplicate templates. exec() now resolves/appends
  by fret-key (no redo duplicate); rollback() removes by object identity (so a save,
  which replaces template objects, isn't wrongly undone -> won't splice a template
  now backing a real chord).
- P3: zero-/negative-length handshapes dropped on load (end_time > start_time).
- P3: reconstructChords clears S.handshapeSel if the remap drops the selected
  handshape from the current arrangement.

Codex re-reviewed twice (a first fix introduced a rollback regression, now
resolved); final pass clean. node --check + both JS suites pass (10/10, 4/4).

Part of got-feedBack/feedBack#334.

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.

[E2] Handshape / arpeggio (chord-shape) region authoring

1 participant