diff --git a/CHANGELOG.md b/CHANGELOG.md index 65f6ccc..4820b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/mac/Sources/CodeBurnMenubar/CodeBurnApp.swift b/mac/Sources/CodeBurnMenubar/CodeBurnApp.swift index c1d002f..1c87221 100644 --- a/mac/Sources/CodeBurnMenubar/CodeBurnApp.swift +++ b/mac/Sources/CodeBurnMenubar/CodeBurnApp.swift @@ -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 { diff --git a/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift b/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift index f067aa0..44879d8 100644 --- a/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift +++ b/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift @@ -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) }