Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

slab/menuband: skinnier popover, brand→About, sonic-browse hover

Width:
- All popover-content widths now reference InstrumentListView.preferred
Width (224 px) instead of the previous fixed 248 — the input
segmented, update banner, and waveform align to the same 224 px slab
as the instrument grid. Floor on preferredContentSize removed; the
popover sizes to its actual fitting size, ~248 wide vs the prior 280.

Brand → About:
- Title row up top no longer carries "Menu Band" + the subtitle. Top
row is just the octave control + MIDI switch hugging the right.
- About column gains the Menu Band heading + subtitle as its first two
lines, then the about body, then the link row. One section instead
of two.
- "ac" link button restored to verbose "aesthetic.computer".

Sonic browse:
- Re-enable instrument hover-preview. New
MenuBandController.setInstrumentPreview(_:) holds a preview note
while the cursor sits on a cell; switching cells stops the old note
and starts a new one in the new program. Hover-out releases and
restores the committed program. Silent in MIDI mode (DAW handles
audio there); the program change still applies so the menubar piano
is correct when the user toggles MIDI off.
- InstrumentListView.onHover callback wired in the popover.

Site:
- Bump download cache-buster query string to ?v=0d61fdb.

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

+58 -24
+6
slab/menuband/Sources/MenuBand/InstrumentMapView.swift
··· 21 21 var selectedProgram: UInt8 = 0 { didSet { needsDisplay = true } } 22 22 private(set) var hoveredProgram: UInt8? 23 23 var onCommit: ((Int) -> Void)? 24 + /// Fires whenever the hovered cell changes (including transitions to 25 + /// "no hover" → nil). Drives the controller's hover-preview note for 26 + /// sonic browsing. 27 + var onHover: ((Int?) -> Void)? 24 28 25 29 private var trackingArea: NSTrackingArea? 26 30 ··· 134 138 if let prev = hoveredProgram { 135 139 hoveredProgram = nil 136 140 setNeedsDisplay(cellRect(program: Int(prev))) 141 + onHover?(nil) 137 142 } 138 143 } 139 144 ··· 144 149 hoveredProgram = p 145 150 if let prev = prev { setNeedsDisplay(cellRect(program: Int(prev))) } 146 151 if let p = p { setNeedsDisplay(cellRect(program: Int(p))) } 152 + onHover?(p.map { Int($0) }) 147 153 } 148 154 } 149 155
+28
slab/menuband/Sources/MenuBand/MenuBandController.swift
··· 43 43 synth.snapshotWaveform(into: &dest) 44 44 } 45 45 46 + // Held preview note for sonic-browse hover over the instrument map. 47 + // Continuous tone — switching cells stops the old note + starts a new 48 + // one in the new program. Hover-out releases. Silent in MIDI mode 49 + // (DAW is the audio path then; we still apply the program change so 50 + // it's correct when the user toggles MIDI off). 51 + private var previewNote: UInt8? 52 + 53 + /// Hover-preview a program in the instrument map. Pass nil when the 54 + /// hover ends to release the held note and restore the committed 55 + /// program. 56 + func setInstrumentPreview(_ program: UInt8?) { 57 + if let prev = previewNote { 58 + synth.noteOff(prev, channel: 0) 59 + previewNote = nil 60 + } 61 + guard let prog = program else { 62 + // Hover ended — flip back to the committed program so the 63 + // menubar piano still plays whatever the user actually picked. 64 + synth.setMelodicProgram(melodicProgram) 65 + return 66 + } 67 + synth.setMelodicProgram(prog) 68 + guard !midiMode else { return } 69 + let note: UInt8 = 60 70 + synth.noteOn(note, velocity: 75, channel: 0) 71 + previewNote = note 72 + } 73 + 46 74 func auditionCurrentProgram() { 47 75 let note: UInt8 = 60 48 76 debugLog("audition: synth.noteOn 60 (program \(melodicProgram))")
+23 -23
slab/menuband/Sources/MenuBand/MenuBandPopover.swift
··· 93 93 stack.translatesAutoresizingMaskIntoConstraints = false 94 94 root.addSubview(stack) 95 95 96 - // Title row: app name on the left, octave control hugging the right. 97 - // Octave gets its own row in the popover via this top-anchored 98 - // arrangement so we don't need a dedicated "Octave" panel below. 96 + // Top control row: octave + MIDI hugging the right. Brand title 97 + // moved into the About section below — fewer wasted rows up top. 99 98 let titleRow = NSStackView() 100 99 titleRow.orientation = .horizontal 101 100 titleRow.alignment = .centerY 102 101 titleRow.distribution = .fill 103 102 titleRow.spacing = 8 104 - 105 - let titleStack = NSStackView() 106 - titleStack.orientation = .vertical 107 - titleStack.alignment = .leading 108 - titleStack.spacing = 0 109 - let title = NSTextField(labelWithString: "Menu Band") 110 - title.font = NSFont.systemFont(ofSize: 13, weight: .bold) 111 - title.textColor = .labelColor 112 - let subtitle = NSTextField(labelWithString: "Built-in macOS instruments, in the menu bar.") 113 - subtitle.font = NSFont.systemFont(ofSize: 10.5) 114 - subtitle.textColor = .secondaryLabelColor 115 - titleStack.addArrangedSubview(title) 116 - titleStack.addArrangedSubview(subtitle) 117 - titleRow.addArrangedSubview(titleStack) 118 103 119 104 let titleSpacer = NSView() 120 105 titleSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal) ··· 227 212 updateLink.bottomAnchor.constraint(equalTo: updateBanner.bottomAnchor, constant: -7), 228 213 ]) 229 214 stack.addArrangedSubview(updateBanner) 230 - updateBanner.widthAnchor.constraint(equalToConstant: 248).isActive = true 215 + updateBanner.widthAnchor.constraint(equalToConstant: InstrumentListView.preferredWidth).isActive = true 231 216 updateBanner.isHidden = true 232 217 233 218 stack.addArrangedSubview(makeSeparator()) ··· 253 238 inputSegmented.translatesAutoresizingMaskIntoConstraints = false 254 239 // No hover preview — clicks commit the mode directly. 255 240 stack.addArrangedSubview(inputSegmented) 256 - inputSegmented.widthAnchor.constraint(equalToConstant: 248).isActive = true 241 + inputSegmented.widthAnchor.constraint(equalToConstant: InstrumentListView.preferredWidth).isActive = true 257 242 258 243 let inputHint = NSTextField(labelWithString: 259 244 "⌃⌥⌘P toggles last keystrokes mode") ··· 298 283 instrumentList.onCommit = { [weak self] prog in 299 284 self?.handleInstrumentCommit(prog) 300 285 } 286 + instrumentList.onHover = { [weak self] prog in 287 + // Hover plays a continuous preview note in the hovered program; 288 + // moving to another cell stops + restarts in the new program. 289 + self?.menuBand?.setInstrumentPreview(prog.map { UInt8($0) }) 290 + } 301 291 stack.addArrangedSubview(instrumentList) 302 292 instrumentList.widthAnchor.constraint(equalToConstant: InstrumentListView.preferredWidth).isActive = true 303 293 instrumentList.heightAnchor.constraint(equalToConstant: InstrumentListView.preferredHeight).isActive = true ··· 327 317 aboutCol.orientation = .vertical 328 318 aboutCol.alignment = .leading 329 319 aboutCol.spacing = 4 330 - let aboutTitle = NSTextField(labelWithString: "About") 331 - aboutTitle.font = NSFont.systemFont(ofSize: 11, weight: .semibold) 320 + // Brand identity now lives here instead of at the top of the 321 + // popover — Menu Band heading + subtitle + about body + links all 322 + // collapse into one section. 323 + let aboutTitle = NSTextField(labelWithString: "Menu Band") 324 + aboutTitle.font = NSFont.systemFont(ofSize: 13, weight: .bold) 332 325 aboutTitle.textColor = .labelColor 326 + let aboutSubtitle = NSTextField(labelWithString: 327 + "Built-in macOS instruments, in the menu bar.") 328 + aboutSubtitle.font = NSFont.systemFont(ofSize: 10.5) 329 + aboutSubtitle.textColor = .secondaryLabelColor 333 330 let aboutBody = NSTextField(wrappingLabelWithString: 334 331 "A political project to bring the built-in macOS instruments — " + 335 332 "the ones GarageBand uses — into the menu bar. Free + open source.") ··· 344 341 linksRow.orientation = .horizontal 345 342 linksRow.alignment = .centerY 346 343 linksRow.spacing = 6 347 - let acLink = NSButton(title: "ac", 344 + let acLink = NSButton(title: "aesthetic.computer", 348 345 target: self, action: #selector(openAesthetic)) 349 346 acLink.bezelStyle = .recessed 350 347 acLink.controlSize = .small ··· 421 418 // the instrument map is added/removed/resized. 422 419 root.layoutSubtreeIfNeeded() 423 420 let fitting = stack.fittingSize 424 - preferredContentSize = NSSize(width: max(280, fitting.width), 421 + // Width tracks the actual fitting size — the instrument map's 422 + // 224 px sets the floor, so the popover is as skinny as the 423 + // contents allow. 424 + preferredContentSize = NSSize(width: fitting.width, 425 425 height: fitting.height) 426 426 } 427 427
+1 -1
system/public/menuband/index.html
··· 879 879 <p class="tagline">Built-in macOS instruments, in the menu bar.</p> 880 880 881 881 <div class="button-row"> 882 - <a class="aqua" href="https://assets.aesthetic.computer/menuband/Menu-Band-0.1.dmg?v=237f18c" download> 882 + <a class="aqua" href="https://assets.aesthetic.computer/menuband/Menu-Band-0.1.dmg?v=0d61fdb" download> 883 883 Download 884 884 <small>0.1 · Apple Silicon · 1.1 MB</small> 885 885 </a>