aboutsummaryrefslogtreecommitdiff
path: root/menubar/main.swift
diff options
context:
space:
mode:
Diffstat (limited to 'menubar/main.swift')
-rw-r--r--menubar/main.swift62
1 files changed, 41 insertions, 21 deletions
diff --git a/menubar/main.swift b/menubar/main.swift
index b5a3059..6c6c9a3 100644
--- a/menubar/main.swift
+++ b/menubar/main.swift
@@ -1,10 +1,9 @@
// dingger menu bar: live agent states from tmux pane user options
// (@agent / @agent_state), written by the dingger daemon.
//
-// Design (per design panel): one question answered — "how many agents are
-// waiting on me?" The status item is a bell plus the ready-queue depth;
-// shape carries state (filled = needs you, outline = seen/calm, dotted =
-// working); color is reserved for the tmux segment's blocked accent.
+// Visual language is herdr's agent panel: green = ready (finished, waiting
+// for you), yellow = working, red = blocked; filled glyph = in the ready
+// queue (unseen), outline = already seen. Priority order = queue order.
import AppKit
@@ -15,18 +14,37 @@ struct PaneRow {
let state: String
}
-func symbol(_ name: String) -> NSImage? {
- let image = NSImage(systemSymbolName: name, accessibilityDescription: nil)
- return image?.withSymbolConfiguration(.init(pointSize: 13, weight: .regular))
+let red = NSColor.systemRed
+let yellow = NSColor.systemYellow
+let green = NSColor.systemGreen
+let dim = NSColor.systemGray
+
+/// Tint a symbol image (non-template, so it keeps its color in menus).
+func tinted(_ image: NSImage?, _ color: NSColor) -> NSImage? {
+ guard let image = image else { return nil }
+ let img = NSImage(size: image.size, flipped: false) { rect in
+ image.draw(in: rect)
+ color.set()
+ rect.fill(using: .sourceAtop)
+ return true
+ }
+ img.isTemplate = false
+ return img
+}
+
+func symbol(_ name: String, _ color: NSColor? = nil) -> NSImage? {
+ let image = NSImage(systemSymbolName: name, accessibilityDescription: nil)?
+ .withSymbolConfiguration(.init(pointSize: 13, weight: .regular))
+ return color.map { tinted(image, $0) } ?? image
}
-func stateSymbol(_ state: String, queued: Bool) -> NSImage? {
+func rowSymbol(_ state: String, queued: Bool) -> NSImage? {
switch (state, queued) {
- case ("blocked", true): return symbol("exclamationmark.circle.fill")
- case ("idle", true): return symbol("circle.fill")
- case ("working", _): return symbol("circle.dotted") ?? symbol("hourglass")
- case ("blocked", _): return symbol("exclamationmark.circle")
- default: return symbol("circle")
+ case ("blocked", true): return symbol("exclamationmark.circle.fill", red)
+ case ("idle", true): return symbol("checkmark.circle.fill", green)
+ case ("working", _): return symbol("circle.dotted", yellow)
+ case ("blocked", _): return symbol("exclamationmark.circle", dim)
+ default: return symbol("checkmark.circle", dim)
}
}
@@ -38,7 +56,7 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate {
func applicationDidFinishLaunching(_: Notification) {
NSApp.setActivationPolicy(.accessory) // no Dock icon
- item.button?.image = symbol("bell")
+ item.button?.image = symbol("bell", dim)
item.button?.font = NSFont.monospacedDigitSystemFont(ofSize: 13, weight: .regular)
item.menu = menu
menu.delegate = self
@@ -102,21 +120,23 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate {
private func refreshTitle() {
panes = fetchPanes()
queue = fetchQueue()
+ FileHandle.standardError.write(Data(
+ "\(Date()) menubar: panes=\(panes.count) queue=\(queue.count)\n".utf8))
let queued = panes.filter { row in queue.contains(row.paneId) }
let blocked = queued.filter { $0.state == "blocked" }.count
let working = panes.filter { $0.state == "working" }.count
- let (name, count): (String, String)
+ let (name, color, count): (String, NSColor, String)
if blocked > 0 {
- (name, count) = ("bell.fill", String(queued.count))
+ (name, color, count) = ("bell.fill", red, String(queued.count))
} else if !queued.isEmpty {
- (name, count) = ("bell", String(queued.count))
+ (name, color, count) = ("bell.fill", green, String(queued.count))
} else if working > 0 {
- (name, count) = ("bell", "")
+ (name, color, count) = ("bell", yellow, "")
} else {
- (name, count) = ("bell", "")
+ (name, color, count) = ("bell", dim, "")
}
- item.button?.image = symbol(name)
+ item.button?.image = symbol(name, color)
item.button?.title = count
var tip: [String] = []
@@ -169,7 +189,7 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate {
let row = NSMenuItem(title: "\(pane.agent)\t\(pane.target)",
action: #selector(paneClicked(_:)), keyEquivalent: "")
row.target = self
- row.image = stateSymbol(pane.state, queued: queued)
+ row.image = rowSymbol(pane.state, queued: queued)
row.attributedTitle = attributedRow(agent: pane.agent, target: pane.target)
row.toolTip = "\(pane.state) — \(pane.target)"
row.representedObject = pane.paneId