mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-07-10 01:29:41 +00:00
feat(menubar): polish the status-item menu and About tab (#509)
Right-click menu: - Add a dimmed "Today · $X · N calls" usage row at the top. - Give Settings an explicit gearshape icon (instead of relying on the macOS Tahoe auto-decoration). - Add an "About CodeBurn" item that opens Settings on the About tab. - Drop the per-item keyboard-shortcut labels and the section separators for a flat, compact list. - Open the menu a few px below the status item so it clears the menu bar (anchoring flush clipped the top edge and triggered menu scrolling on hover). Settings: - Add a settingsTab selection to AppStore and bind SettingsView's TabView to it (tagged tabs) so "About CodeBurn" can jump straight to the About tab. - About tab: add a "Star on GitHub" button and a "Sponsor" link.
This commit is contained in:
parent
dbe4e94322
commit
96d74a04b3
3 changed files with 48 additions and 8 deletions
|
|
@ -52,6 +52,9 @@ final class AppStore {
|
|||
}
|
||||
var showingAccentPicker: Bool = false
|
||||
var currency: String = "USD"
|
||||
/// Which Settings tab to show; lets the menu's "About CodeBurn" item jump
|
||||
/// straight to the About tab even when the Settings window is reused.
|
||||
var settingsTab: String = "general"
|
||||
var displayMetric: DisplayMetric = DisplayMetric(rawValue: UserDefaults.standard.string(forKey: "CodeBurnDisplayMetric") ?? "") ?? .cost {
|
||||
didSet { UserDefaults.standard.set(displayMetric.rawValue, forKey: "CodeBurnDisplayMetric") }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -857,31 +857,56 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
|
||||
let menu = NSMenu()
|
||||
|
||||
let settingsItem = NSMenuItem(title: "Settings…", action: #selector(openSettings), keyEquivalent: ",")
|
||||
// Glanceable "today" usage at the top as a single non-interactive (dimmed)
|
||||
// row. A real .sectionHeader adds section padding (and pulls the actions
|
||||
// into its group without a separator), so use a plain disabled item.
|
||||
let usageItem = NSMenuItem(title: contextMenuUsageSummary(), action: nil, keyEquivalent: "")
|
||||
usageItem.isEnabled = false
|
||||
menu.addItem(usageItem)
|
||||
|
||||
let settingsItem = NSMenuItem(title: "Settings…", action: #selector(openSettings), keyEquivalent: "")
|
||||
settingsItem.target = self
|
||||
settingsItem.image = NSImage(systemSymbolName: "gearshape", accessibilityDescription: "Settings")
|
||||
menu.addItem(settingsItem)
|
||||
|
||||
let refreshNow = NSMenuItem(title: "Refresh Now", action: #selector(refreshNowAction), keyEquivalent: "r")
|
||||
let refreshNow = NSMenuItem(title: "Refresh Now", action: #selector(refreshNowAction), keyEquivalent: "")
|
||||
refreshNow.target = self
|
||||
menu.addItem(refreshNow)
|
||||
|
||||
menu.addItem(.separator())
|
||||
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")
|
||||
|
||||
let aboutItem = NSMenuItem(title: "About CodeBurn", action: #selector(openAbout), keyEquivalent: "")
|
||||
aboutItem.target = self
|
||||
menu.addItem(aboutItem)
|
||||
|
||||
let quitItem = NSMenuItem(title: "Quit CodeBurn", action: #selector(quitApp), keyEquivalent: "")
|
||||
quitItem.target = self
|
||||
menu.addItem(quitItem)
|
||||
|
||||
// Present directly. The previous `statusItem.menu = menu; button.performClick`
|
||||
// trick relies on the click -> action path that macOS 27 changed; popUp is
|
||||
// version-stable.
|
||||
menu.popUp(positioning: nil, at: NSPoint(x: 0, y: button.bounds.height + 4), in: button)
|
||||
// version-stable. Open a few px below the status item so the menu clears the
|
||||
// menu bar: anchoring flush clips the top edge and makes macOS engage menu
|
||||
// scrolling (a scroll chevron appears and the first row slides up on hover).
|
||||
menu.popUp(positioning: nil, at: NSPoint(x: 0, y: button.bounds.height + 6), in: button)
|
||||
}
|
||||
|
||||
/// One-line "today" summary for the context menu's usage row.
|
||||
private func contextMenuUsageSummary() -> String {
|
||||
guard let current = store.todayPayload?.current else { return "Today · no usage yet" }
|
||||
let calls = current.calls == 1 ? "1 call" : "\(current.calls) calls"
|
||||
return "Today · \(current.cost.asCurrency()) · \(calls)"
|
||||
}
|
||||
|
||||
private var settingsWindowController: NSWindowController?
|
||||
|
||||
@objc private func openAbout() {
|
||||
store.settingsTab = "about"
|
||||
openSettings()
|
||||
}
|
||||
|
||||
@objc private func openSettings() {
|
||||
// Accessory-policy apps (no Dock icon, no main menu) don't get the
|
||||
// SwiftUI Settings scene wired into the responder chain reliably, so
|
||||
|
|
|
|||
|
|
@ -8,21 +8,26 @@ struct SettingsView: View {
|
|||
@Environment(AppStore.self) private var store
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
TabView(selection: Binding(get: { store.settingsTab }, set: { store.settingsTab = $0 })) {
|
||||
GeneralSettingsTab()
|
||||
.tabItem { Label("General", systemImage: "gearshape") }
|
||||
.tag("general")
|
||||
|
||||
ClaudeSettingsTab()
|
||||
.tabItem { Label("Claude", systemImage: "brain") }
|
||||
.tag("claude")
|
||||
|
||||
CodexSettingsTab()
|
||||
.tabItem { Label("Codex", systemImage: "chevron.left.forwardslash.chevron.right") }
|
||||
.tag("codex")
|
||||
|
||||
DevinSettingsTab()
|
||||
.tabItem { Label("Devin", systemImage: "flame.fill") }
|
||||
.tag("devin")
|
||||
|
||||
AboutSettingsTab()
|
||||
.tabItem { Label("About", systemImage: "info.circle") }
|
||||
.tag("about")
|
||||
}
|
||||
.frame(width: 520, height: 430)
|
||||
}
|
||||
|
|
@ -660,9 +665,16 @@ private struct AboutSettingsTab: View {
|
|||
Text("Version \(appVersion) (\(buildVersion))")
|
||||
.font(.codeMono(size: 11))
|
||||
.foregroundStyle(.secondary)
|
||||
Link(destination: URL(string: "https://github.com/getagentseal/codeburn")!) {
|
||||
Label("Star on GitHub", systemImage: "star.fill")
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(Theme.brandAccent)
|
||||
HStack(spacing: 10) {
|
||||
Link("GitHub", destination: URL(string: "https://github.com/getagentseal/codeburn")!)
|
||||
Link("Issues", destination: URL(string: "https://github.com/getagentseal/codeburn/issues")!)
|
||||
Link("Sponsor", destination: URL(string: "https://github.com/sponsors/iamtoruk")!)
|
||||
}
|
||||
.font(.system(size: 12))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue