mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-19 07:43:09 +00:00
Merge pull request #133 from getagentseal/feat/menubar-compact
Some checks are pending
CI / semgrep (push) Waiting to run
Some checks are pending
CI / semgrep (push) Waiting to run
feat(menubar): opt-in compact mode
This commit is contained in:
commit
66606c81f8
3 changed files with 28 additions and 8 deletions
|
|
@ -211,6 +211,14 @@ npx codeburn menubar
|
|||
|
||||
One command: downloads the latest `.app`, installs into `~/Applications`, and launches it. Re-run with `--force` to reinstall. Native Swift + SwiftUI app lives in `mac/` (see `mac/README.md` for build details). Shows today's cost with a flame icon, opens a popover with agent tabs, period switcher (Today / 7 Days / 30 Days / Month / All), Trend / Forecast / Pulse / Stats / Plan insights, activity and model breakdowns, optimize findings, and CSV/JSON export. Refreshes live via FSEvents plus a 15-second poll.
|
||||
|
||||
**Compact mode** shrinks the menubar item to fit the text, dropping decimals (e.g. `$110` instead of `$110.20`). Opt in with:
|
||||
|
||||
```bash
|
||||
defaults write CodeBurnMenubar CodeBurnMenubarCompact -bool true
|
||||
```
|
||||
|
||||
Relaunch the app to apply. To revert: `defaults delete CodeBurnMenubar CodeBurnMenubarCompact`.
|
||||
|
||||
## What it tracks
|
||||
|
||||
**13 task categories** classified from tool usage patterns and user message keywords. No LLM calls, fully deterministic.
|
||||
|
|
|
|||
|
|
@ -301,6 +301,11 @@ extension Double {
|
|||
let state = CurrencyState.shared
|
||||
return String(format: "\(state.symbol)%.2f", self * state.rate)
|
||||
}
|
||||
|
||||
func asCompactCurrencyWhole() -> String {
|
||||
let state = CurrencyState.shared
|
||||
return "\(state.symbol)\(Int((self * state.rate).rounded()))"
|
||||
}
|
||||
}
|
||||
|
||||
extension Int {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ private let nanosPerSecond: UInt64 = 1_000_000_000
|
|||
private let refreshIntervalNanos: UInt64 = refreshIntervalSeconds * nanosPerSecond
|
||||
/// Fixed so the popover's anchor point doesn't shift each time today's cost changes.
|
||||
private let statusItemFixedWidth: CGFloat = 130
|
||||
private let statusItemCompactWidth: CGFloat = NSStatusItem.variableLength
|
||||
private let popoverWidth: CGFloat = 360
|
||||
private let popoverHeight: CGFloat = 660
|
||||
private let menubarTitleFontSize: CGFloat = 13
|
||||
|
|
@ -120,10 +121,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
|
||||
// MARK: - Status Item
|
||||
|
||||
private var isCompact: Bool {
|
||||
UserDefaults.standard.bool(forKey: "CodeBurnMenubarCompact")
|
||||
}
|
||||
|
||||
private func setupStatusItem() {
|
||||
// Fixed width so the popover anchor (and thus popover position) doesn't shift
|
||||
// every time today's cost or findings badge changes.
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: statusItemFixedWidth)
|
||||
let width = isCompact ? statusItemCompactWidth : statusItemFixedWidth
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: width)
|
||||
guard let button = statusItem.button else { return }
|
||||
button.target = self
|
||||
button.action = #selector(handleButtonClick(_:))
|
||||
|
|
@ -152,20 +156,23 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
let attachment = NSTextAttachment()
|
||||
attachment.image = flame
|
||||
if let size = flame?.size {
|
||||
// Nudge the image down ~2pt so its visual centre sits on the text baseline mid-line
|
||||
// rather than riding high. Exact value tuned against SF Pro Display 13pt.
|
||||
attachment.bounds = CGRect(x: 0, y: -2, width: size.width, height: size.height)
|
||||
attachment.bounds = CGRect(x: 0, y: -3, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
let hasPayload = store.todayPayload != nil
|
||||
let valueText = " " + (store.todayPayload?.current.cost.asCompactCurrency() ?? "$—")
|
||||
let compact = isCompact
|
||||
let fallback = compact ? "$-" : "$—"
|
||||
let formatted = store.todayPayload?.current.cost
|
||||
let valueText = compact
|
||||
? (formatted?.asCompactCurrencyWhole() ?? fallback)
|
||||
: " " + (formatted?.asCompactCurrency() ?? fallback)
|
||||
let color: NSColor = hasPayload ? .labelColor : .secondaryLabelColor
|
||||
|
||||
let composed = NSMutableAttributedString()
|
||||
composed.append(NSAttributedString(attachment: attachment))
|
||||
composed.append(NSAttributedString(
|
||||
string: valueText,
|
||||
attributes: [.font: font, .foregroundColor: color]
|
||||
attributes: [.font: font, .foregroundColor: color, .baselineOffset: -1.0]
|
||||
))
|
||||
button.attributedTitle = composed
|
||||
// Force immediate redraw. NSStatusItem sometimes defers the status bar paint for an
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue