From e3a034c3e77cea5f15a507b6f2e9425e5729fd3c Mon Sep 17 00:00:00 2001 From: prompt.ac/@jeffrey Date: Tue, 18 Aug 2026 20:03:40 +0000 Subject: [PATCH] slab: the menu bar had stopped following the appearance macOS derives the translucent menu bar's own light/dark treatment by sampling the desktop picture, and that derivation wedges. Measured today: system in Dark, wallpaper a near-black green, every window on screen dark — and the empty menu bar still sampling RGB 115,137,151, light gray with black text. Pushing a fresh wallpaper does not dislodge it; slab had already set several dark tints across the day and the bar stayed light. Re-applying the identical URL and options does nothing either. Restarting the Dock is the only thing that makes WindowServer re-derive it — the same sample then read 30,52,66, dark with white text. So nudge the Dock, but only on a real dark↔light flip: the tint color itself changes on every task state change, and restarting the Dock that often would be intolerable. Both tint paths now share one push, which is where the flip is noticed. --- slab/menubar-swift/Sources/SlabMenubar/AppDelegate.swift | 39 +++++++++++++++++++++++++++++---------- slab/menubar-swift/Sources/SlabMenubar/DesktopTint.swift | 10 ++++++++++ 2 file(s) changed, 39 insertion(s)(+), 10 deletion(s)(-) diff --git a/slab/menubar-swift/Sources/SlabMenubar/AppDelegate.swift b/slab/menubar-swift/Sources/SlabMenubar/AppDelegate.swift --- a/slab/menubar-swift/Sources/SlabMenubar/AppDelegate.swift +++ b/slab/menubar-swift/Sources/SlabMenubar/AppDelegate.swift @@ -66,6 +66,10 @@ /// Last desktop-picture path slab pushed via System Events (the tint PNG, /// or "" meaning "restored to the user's original"), so the per-tick /// refresh only re-sets the wallpaper when the aggregate status changed. private var lastDesktopTint: String? + /// Appearance the last wallpaper Slab actually pushed was rendered for. + /// Starts nil so the first push after launch is never mistaken for a + /// dark↔light flip. See `pushDesktopPicture`. + private var lastAppliedDark: Bool? /// Base font size from the most recent `tileNow()` pass. `applyTerminalDecor` /// scales typography off this — `.awaiting` ("orange") tiles get bumped /// up so focus reads typographically while the cell geometry stays put. @@ -2868,6 +2872,29 @@ let memoKey = "\(name)|\(color.0),\(color.1),\(color.2)" if lastDesktopTint == memoKey { return } lastDesktopTint = memoKey DesktopTint.publish(color: color, dark: dark) + pushDesktopPicture(name: name, color: color, dark: dark) + } + + /// Push the resolved flat tint to every screen's desktop picture, then + /// repair the menu bar if the appearance just flipped. + /// + /// macOS derives the translucent menu bar's own light/dark treatment by + /// sampling the desktop picture, and that derivation wedges: after an + /// appearance flip the bar keeps the OLD appearance's treatment. Measured + /// 2026-08-18 — system in Dark, wallpaper a near-black green, every window + /// on screen dark, yet the empty menu bar sampled RGB 115,137,151 (light + /// gray, black text). Pushing a fresh wallpaper does NOT dislodge it: + /// slab had already set several dark tints across that day and the bar + /// stayed light. Re-applying the identical URL+options does nothing + /// either. Restarting the Dock is the only thing that makes WindowServer + /// re-derive it — the same sample then read 30,52,66, dark with white text. + /// + /// So nudge the Dock, but ONLY on a genuine dark↔light flip. The tint + /// color itself changes on every task state change (it re-averages the + /// live palettes); restarting the Dock that often would be intolerable. + private func pushDesktopPicture(name: String, color: (Int, Int, Int), dark: Bool) { + let flipped = lastAppliedDark != nil && lastAppliedDark != dark + lastAppliedDark = dark let screens = NSScreen.screens let options = DesktopTint.workspaceOptions(color: color) DispatchQueue.global(qos: .utility).async { @@ -2877,6 +2904,7 @@ let url = URL(fileURLWithPath: path) for s in screens { try? NSWorkspace.shared.setDesktopImageURL(url, for: s, options: options) } + if flipped { DesktopTint.resyncMenuBar() } } } @@ -2912,16 +2940,7 @@ let memoKey = "idle|\(name)|\(color.0),\(color.1),\(color.2)" if lastDesktopTint == memoKey { return } lastDesktopTint = memoKey DesktopTint.publish(color: color, dark: dark) - let screens = NSScreen.screens - let options = DesktopTint.workspaceOptions(color: color) - DispatchQueue.global(qos: .utility).async { - guard let path = DesktopTint.ensure(name: name, color: color) - else { return } - let url = URL(fileURLWithPath: path) - for s in screens { - try? NSWorkspace.shared.setDesktopImageURL(url, for: s, options: options) - } - } + pushDesktopPicture(name: name, color: color, dark: dark) } /// Which terminal app to spawn restored sessions into. Honor whatever diff --git a/slab/menubar-swift/Sources/SlabMenubar/DesktopTint.swift b/slab/menubar-swift/Sources/SlabMenubar/DesktopTint.swift --- a/slab/menubar-swift/Sources/SlabMenubar/DesktopTint.swift +++ b/slab/menubar-swift/Sources/SlabMenubar/DesktopTint.swift @@ -33,6 +33,16 @@ .fillColor: fill, ] } + /// Force macOS to re-derive the translucent menu bar's light/dark + /// treatment from the current desktop picture. Nothing gentler works — + /// see `AppDelegate.pushDesktopPicture` for the measurements. Restarting + /// the Dock leaves windows, spaces and the wallpaper itself untouched; + /// it costs a brief flicker of the Dock, so callers must gate this on a + /// real appearance flip rather than every tint change. + static func resyncMenuBar() { + _ = ShellRunner.run("/usr/bin/killall", args: ["Dock"], timeout: 5) + } + /// Share Slab's resolved aggregate prompt colour with companion desktop /// surfaces. Persistence gives late starters the current tone; the /// distributed notification updates live renderers immediately. -- tangled.sh