slab/menuband: voice palette polish + qwerty-pan + physics ghost
Voice palette
- Static GM family palette (16 hand-picked CSS-named hues, one per
family) replaces the hue gradient. Each program inside a family
gets its own shade via brightness + saturation walk so all 128
voices read distinctly while staying cohesive within their family.
- Cells now look like keycap chiclets — rounded inset outlines tinted
in the family color, edge-to-edge so dragging stays seamless.
- Voice readout (Voice:) shows just the program name in a chip
whose backdrop tracks the family color. Hover-drag previews retint
the chip + visualizer in real time; the visualizer's bars now use
the chosen voice's hue (top of the bar still brightens toward
white for a peaking cue).
- Auto-disable MIDI on instrument commit + audition the picked voice
at lastPlayedNote (last pitch the user touched, not always C4).
Arrow-key navigation on the voice grid
- ← → ↑ ↓ walk the 8×16 grid. Auto-repeat ignored so each press is
exactly one move; preview note still sustains while held, releases
on keyUp + commits the cell.
- Non-arrow keys forwarded through handleLocalKey so notepat /
Ableton letter keys still play notes while the popover holds key
focus on the grid.
- ArrowKeysIndicator: custom MacBook-style four-keycap cluster in
the bottom-right corner of the palette. Each key lights to accent
while pressed. Tooltip-only hint glyph.
Physics-based letter ghost
- Replaced the phase / smoothstep curve animation with per-cell
alphas smoothed toward targets every tick. Asymmetric rates
(fast attack, slow decay) feel reactive without snapping when a
fresh keypress lands during the fade-out.
- Wave attack / release stays distance-driven (cells far from the
pivot light up later, far cells fade first), but a cell that's
already been "reached" stays reached so pivot changes mid-wave
don't dim already-bright cells back to 0.
QWERTY-position panning
- Ported notepat native's getPanForQwertyKey to MenuBandLayout —
physical keyboard column → MIDI CC10 pan. Left-hand keys play left,
right-hand keys play right, kept inside ±0.9 so nothing is hard-
panned. Sent to the local synth (MIDISynth backend) and outbound
MIDI port so DAWs hear the same stereo placement.
Misc
- LocalKeyCapture: don't disarm when key focus passes to another
window inside our own app (popover takes key without killing
arming).
- White-key letter labels nudged down a hair (rect.minY+1.8) — j
descender no longer kisses the menubar edge but glyphs feel
anchored in the bottom of the key.
- Visualizer: RMS-per-bar (was peak), asymmetric temporal smoothing
to kill rolling, segmented LED-meter look in a dark bezel housing
with per-segment glow.
- Settings chip in the menubar piano: bigger glyph (13 pt) and a
systray-style hover pill, with the popover anchor pointing at the
visible icon rect.
- Set a stable kMIDIPropertyUniqueID + manufacturer + model on the
virtual MIDI source so DAW routing survives reinstalls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>