Skip to content

Commit f35400f

Browse files
committed
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.
1 parent 78aa6fc commit f35400f

3 files changed

Lines changed: 71 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@
2626
- **Instant cached data display.** Shows cached data immediately instead of blocking on CLI refresh.
2727

2828
### Fixed (macOS menubar)
29+
- **Menubar stops updating after first load.** Background refresh was silently skipped by the cache TTL guard. Data loaded once, then froze. Fixes #179.
2930
- **Menubar not dimming on inactive screens.**
3031
- **Performance improvements.** Reduced unnecessary redraws and CLI invocations.
3132

33+
### Added (macOS menubar)
34+
- **Right-click context menu.** Right-click the status bar icon for "Check for Updates" and "Quit CodeBurn".
35+
- **Version label in footer.**
36+
3237
### Changed
3338
- README restructured with honeycomb provider hero image, 2x2 screenshot grid, and complete inline reference.
3439
- `bunx codeburn` added as alternative install option.

mac/Sources/CodeBurnMenubar/CodeBurnApp.swift

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
174174
lastRefreshTime = now
175175

176176
Task {
177-
await store.refresh(includeOptimize: true)
177+
await store.refresh(includeOptimize: true, force: true)
178178
refreshStatusButton()
179179
}
180180
}
@@ -311,7 +311,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
311311
}
312312

313313
@objc private func handleButtonClick(_ sender: AnyObject?) {
314-
guard let button = statusItem.button else { return }
314+
guard let button = statusItem.button,
315+
let event = NSApp.currentEvent else { return }
316+
317+
if event.type == .rightMouseUp {
318+
showContextMenu(from: button)
319+
return
320+
}
321+
315322
if popover.isShown {
316323
popover.performClose(sender)
317324
} else {
@@ -321,6 +328,58 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
321328
}
322329
}
323330

331+
private func showContextMenu(from button: NSStatusBarButton) {
332+
let menu = NSMenu()
333+
let updateItem = NSMenuItem(title: "Check for Updates", action: #selector(checkForUpdates), keyEquivalent: "")
334+
updateItem.target = self
335+
menu.addItem(updateItem)
336+
menu.addItem(.separator())
337+
let quitItem = NSMenuItem(title: "Quit CodeBurn", action: #selector(quitApp), keyEquivalent: "q")
338+
quitItem.target = self
339+
menu.addItem(quitItem)
340+
statusItem.menu = menu
341+
button.performClick(nil)
342+
statusItem.menu = nil
343+
}
344+
345+
private func codeburnAlertIcon() -> NSImage? {
346+
let config = NSImage.SymbolConfiguration(pointSize: 32, weight: .medium)
347+
guard let symbol = NSImage(systemSymbolName: "flame.fill", accessibilityDescription: "CodeBurn")?
348+
.withSymbolConfiguration(config) else { return nil }
349+
let size = NSSize(width: 64, height: 64)
350+
let img = NSImage(size: size, flipped: false) { rect in
351+
let symbolSize = symbol.size
352+
let x = (rect.width - symbolSize.width) / 2
353+
let y = (rect.height - symbolSize.height) / 2
354+
symbol.draw(in: NSRect(x: x, y: y, width: symbolSize.width, height: symbolSize.height))
355+
return true
356+
}
357+
img.isTemplate = false
358+
return img
359+
}
360+
361+
@objc private func checkForUpdates() {
362+
Task {
363+
await updateChecker.check()
364+
let alert = NSAlert()
365+
alert.icon = codeburnAlertIcon()
366+
if updateChecker.updateAvailable, let latest = updateChecker.latestVersion {
367+
alert.messageText = "Update Available"
368+
alert.informativeText = "v\(latest) is available (you have v\(updateChecker.currentVersion)). Run:\n\ncodeburn menubar --force"
369+
} else {
370+
alert.messageText = "Up to Date"
371+
alert.informativeText = "You're on the latest version (v\(updateChecker.currentVersion))."
372+
}
373+
alert.alertStyle = .informational
374+
alert.addButton(withTitle: "OK")
375+
alert.runModal()
376+
}
377+
}
378+
379+
@objc private func quitApp() {
380+
NSApp.terminate(nil)
381+
}
382+
324383
// MARK: - NSPopoverDelegate
325384

326385
func popoverShouldDetach(_ popover: NSPopover) -> Bool {

mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,12 @@ struct FooterBar: View {
422422

423423
Spacer()
424424

425+
Text("v\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "?")")
426+
.font(.system(size: 10, weight: .regular, design: .monospaced))
427+
.foregroundStyle(.tertiary)
428+
425429
Button { openReport() } label: {
426-
Label("Open Full Report", systemImage: "terminal")
430+
Label("Full Report", systemImage: "terminal")
427431
.font(.system(size: 11, weight: .semibold))
428432
.labelStyle(.titleAndIcon)
429433
}

0 commit comments

Comments
 (0)