slab/menuband: popover stays open on piano taps + visualizer + tighter layout
Popover behavior:
- popover.behavior = .applicationDefined (was .transient). The popover
no longer auto-dismisses on clicks "outside" — which counted clicks
on our own status-item button as outside, killing the popover every
time the user tapped a menubar piano key.
- Manual click-away monitor: a global NSEvent monitor on
leftMouseDown/rightMouseDown closes the popover when the user clicks
*another app*. In-app clicks (status item button, popover internals)
don't fire the global monitor, so piano taps + popover-internal
clicks keep the popover up. Esc local monitor still dismisses.
- closePopover() helper tears down both monitors so they don't leak.
Live audio visualizer (WaveformView):
- Layer-based: CAGradientLayer masked by a CAShapeLayer line. Each
frame the path is updated inside an actions-disabled CATransaction
so Core Animation snaps cleanly without tweening.
- Rainbow stroke whose hue rotates 0.0035/frame for shimmer; soft
cyan glow underneath via CAShapeLayer shadow + 4.5px stroke at 20%
white. Reads as "visualizer" rather than flat scope.
- 60 Hz refresh, 512 samples decimated to view width. Auto-gain so
quiet content still reads. Hidden in MIDI mode (local mixer is
silent there). 64px tall (was 36).
- New audio tap on engine.mainMixerNode → ring buffer (4096 frames)
→ snapshotWaveform(into:) read by the view each tick. Tap is
installed once at synth.start().
Layout reshuffle (popover slimmer):
- Octave moved out of its own row → top-right of the title row.
Compact stepper + monospaced "+0" label + tiny "octave" caption,
no Reset button (the stepper steps to 0 just as fast).
- About + Crash logs combined in a single side-by-side row, distri-
bution .fillEqually. Crash column hides itself when there are no
recent reports → About expands to the row width.
- Crash column copy tightened: "1 crash" / "N crashes" + "Send to
aesthetic.computer to help debug." + "Send 1" / "Send all (N)".
- AC link button shortened to "ac" (was "aesthetic.computer") to fit
the half-width column alongside notepat.com.
Click-instrument-plays fix:
- 70 ms delay between setMelodicProgram and audition note. AVAudio
UnitSampler's loadSoundBankInstrument is synchronous on the calling
thread but briefly suppresses scheduled notes on the audio render
thread while it swaps banks; without the delay the audition fell
into the gap and the user heard nothing. New auditionCurrent
Program() bypasses the midiMode gate so clicks always sound.
- debugLog stamps in mouseDown / handleInstrumentCommit /
auditionCurrentProgram for tracing.
Re-deployed:
- DMG submission 5031590b-2e87-44fe-88fa-2cfd5ccde71c, stapled.
- md5 8fbda61a540ea78eef45b9e44dcd6053. Page bumped to ?v=8fbda61.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>