Fix menubar refresh stuck after first load (#179)

forceRefresh() was missing force:true, so the cache TTL guard
silently skipped every LaunchAgent and wake-triggered refresh.
Also adds right-click context menu and version label in footer.
This commit is contained in:
iamtoruk 2026-04-30 09:22:26 -07:00
parent 78aa6fc534
commit f35400f199
3 changed files with 71 additions and 3 deletions

View file

@ -26,9 +26,14 @@
- **Instant cached data display.** Shows cached data immediately instead of blocking on CLI refresh.
### Fixed (macOS menubar)
- **Menubar stops updating after first load.** Background refresh was silently skipped by the cache TTL guard. Data loaded once, then froze. Fixes #179.
- **Menubar not dimming on inactive screens.**
- **Performance improvements.** Reduced unnecessary redraws and CLI invocations.
### Added (macOS menubar)
- **Right-click context menu.** Right-click the status bar icon for "Check for Updates" and "Quit CodeBurn".
- **Version label in footer.**
### Changed
- README restructured with honeycomb provider hero image, 2x2 screenshot grid, and complete inline reference.
- `bunx codeburn` added as alternative install option.

View file

@ -174,7 +174,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
lastRefreshTime = now
Task {
await store.refresh(includeOptimize: true)
await store.refresh(includeOptimize: true, force: true)
refreshStatusButton()
}
}
@ -311,7 +311,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
}
@objc private func handleButtonClick(_ sender: AnyObject?) {
guard let button = statusItem.button else { return }
guard let button = statusItem.button,
let event = NSApp.currentEvent else { return }
if event.type == .rightMouseUp {
showContextMenu(from: button)
return
}
if popover.isShown {
popover.performClose(sender)
} else {
@ -321,6 +328,58 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
}
}
private func showContextMenu(from button: NSStatusBarButton) {
let menu = NSMenu()
let updateItem = NSMenuItem(title: "Check for Updates", action: #selector(checkForUpdates), keyEquivalent: "")
updateItem.target = self
menu.addItem(updateItem)
menu.addItem(.separator())
let quitItem = NSMenuItem(title: "Quit CodeBurn", action: #selector(quitApp), keyEquivalent: "q")
quitItem.target = self
menu.addItem(quitItem)
statusItem.menu = menu
button.performClick(nil)
statusItem.menu = nil
}
private func codeburnAlertIcon() -> NSImage? {
let config = NSImage.SymbolConfiguration(pointSize: 32, weight: .medium)
guard let symbol = NSImage(systemSymbolName: "flame.fill", accessibilityDescription: "CodeBurn")?
.withSymbolConfiguration(config) else { return nil }
let size = NSSize(width: 64, height: 64)
let img = NSImage(size: size, flipped: false) { rect in
let symbolSize = symbol.size
let x = (rect.width - symbolSize.width) / 2
let y = (rect.height - symbolSize.height) / 2
symbol.draw(in: NSRect(x: x, y: y, width: symbolSize.width, height: symbolSize.height))
return true
}
img.isTemplate = false
return img
}
@objc private func checkForUpdates() {
Task {
await updateChecker.check()
let alert = NSAlert()
alert.icon = codeburnAlertIcon()
if updateChecker.updateAvailable, let latest = updateChecker.latestVersion {
alert.messageText = "Update Available"
alert.informativeText = "v\(latest) is available (you have v\(updateChecker.currentVersion)). Run:\n\ncodeburn menubar --force"
} else {
alert.messageText = "Up to Date"
alert.informativeText = "You're on the latest version (v\(updateChecker.currentVersion))."
}
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
@objc private func quitApp() {
NSApp.terminate(nil)
}
// MARK: - NSPopoverDelegate
func popoverShouldDetach(_ popover: NSPopover) -> Bool {

View file

@ -422,8 +422,12 @@ struct FooterBar: View {
Spacer()
Text("v\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "?")")
.font(.system(size: 10, weight: .regular, design: .monospaced))
.foregroundStyle(.tertiary)
Button { openReport() } label: {
Label("Open Full Report", systemImage: "terminal")
Label("Full Report", systemImage: "terminal")
.font(.system(size: 11, weight: .semibold))
.labelStyle(.titleAndIcon)
}