* feat(player): drum-part picker for multiple drum charts (feedpak 1.17.0)
The last mile of the multiple-drum-parts feature: let a player CHOOSE which
drum chart plays. #1020 taught the loader + highway WS to carry several drum
parts (song_info.drum_parts + ?drum_part=<id> + a part_id echo on drum_tab);
this adds the host-chrome selector that drives it.
A "Drum part" <select> sits beside the arrangement switcher in the advanced
settings popover, shown only when a song has 2+ drum charts (drum_parts is
always present — empty for non-drum songs — so single-drum / no-drum songs
hide the row and nothing changes for them). Selecting a part re-streams that
part's tab over the highway WS, exactly like an arrangement switch.
- static/highway.js:
- reconnect() gains a third `drumPart` arg → sets `?drum_part=<id>` on the WS
URL (mirrors the existing `arrangement` param one line up). Empty/undefined
→ the primary part, i.e. byte-identical to today for any pack untouched.
- song_info handler populates #drum-part-select from msg.drum_parts and
shows/hides #v3-drum-part-row on `length > 1` (parallel to the #arr-select
block right above it).
- drum_tab handler carries msg.part_id onto hwState.drumTab (plugins can read
bundle.drumTab.part_id) and reflects it as the picker's selected value, so
the dropdown stays honest even when the server resolves an unknown/absent
selection to the primary.
- static/app.js:
- changeArrangement() gains an optional `drumPart`; at reconnect it forwards
the explicit part, else preserves the current picker selection — so an
ARRANGEMENT switch keeps the chosen drum part (parts are song-level).
- new changeDrumPart(id) delegates to changeArrangement with the current
arrangement held + the new part applied (a part switch is the same
re-stream, so it reuses all the transition ceremony). Exported on window.
- static/v3/index.html: the #drum-part-select row (hidden by default).
No plugin change: the drum renderers just draw whatever drum_tab streams.
RUNTIME-VERIFIED (Playwright, the core player, a 2-drum pack + a no-drum pack):
10/10 — the picker populates with both parts and shows for the multi-drum song;
song_info.drum_parts reaches getSongInfo(); the primary is pre-selected;
selecting the 2nd part drives highway.reconnect with the id and the WS URL
carries `?drum_part=drums-2`; the picker then reflects the server's part_id
echo; a no-drum song hides the row; no page errors. ESLint 0 errors (the two
max-lines warnings are pre-existing on these files). No pytest touched (JS-only).
Stacked on #1020.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xGPjDBF8NTwTK7VQvizix
Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>
* Update reconnect source contract test
---------
Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>