diff options
Diffstat (limited to 'menubar/model.swift')
| -rw-r--r-- | menubar/model.swift | 53 |
1 files changed, 44 insertions, 9 deletions
diff --git a/menubar/model.swift b/menubar/model.swift index 8bed858..760ff31 100644 --- a/menubar/model.swift +++ b/menubar/model.swift @@ -65,6 +65,20 @@ enum Status: String { } } +/// The agent's importance (`@dingger_importance`, unset for normal): it +/// orders agents within a status, and a low agent makes no sound. +enum Importance: Int { + case low = 0, normal = 1, high = 2 + + init(option: String) { + switch option { + case "high": self = .high + case "low": self = .low + default: self = .normal + } + } +} + struct Row: Equatable { let paneId: String let agent: String @@ -76,6 +90,7 @@ struct Row: Equatable { let since: Int /// The pane's working directory, home as "~". var path: String = "" + var importance: Importance = .normal var location: String { "\(session) · \(window)" } var waiting: Bool { status == .blocked || status == .done } @@ -86,9 +101,14 @@ struct Row: Equatable { return name.count > 80 ? String(name.prefix(79)) + "…" : name } - /// Dim second line: where it runs, which harness, and how long it waits. + /// The title's text as plain text: `! ` marks a high agent. + var markedLabel: String { (importance == .high ? "! " : "") + label } + + /// Dim second line: where it runs, which harness, whether it is low, + /// and how long it waits. var subtitle: String { var parts = [location, agent] + if importance == .low { parts.append("low") } if waiting, since != Int.max { parts.append("\(status.rawValue) \(elapsed(sinceMs: since))") } @@ -219,13 +239,15 @@ func runDinggerInLoginShell(_ args: [String]) -> (ok: Bool, output: String) { private let home = NSHomeDirectory() -/// Every agent pane: herdr's status priority; within a status, done and -/// blocked oldest first (the queue), working and idle most recent first -/// (`@dingger_since`: when the pane entered its status); then by place. +/// Every agent pane: herdr's status priority; within a status, the more +/// important first, then done and blocked oldest first (the queue), +/// working and idle most recent first (`@dingger_since`: when the pane +/// entered its status); then by place. func fetchRows() -> [Row] { let format = [ "#{pane_id}", "#{@agent}", "#{@agent_state}", "#{@dingger_since}", "#{@agent_name}", "#{session_name}", "#{window_name}", "#{window_panes}", "#{pane_index}", "#{pane_current_path}", + "#{@dingger_importance}", ].joined(separator: "\t") guard let out = tmux(["list-panes", "-a", "-F", format]) else { return [] } // A pane in several sessions (a session group, a linked window) is @@ -233,13 +255,14 @@ func fetchRows() -> [Row] { var listed = Set<String>() let rows: [Row] = out.split(separator: "\n").compactMap { line in let f = line.split(separator: "\t", omittingEmptySubsequences: false).map(String.init) - guard f.count >= 10, !f[1].isEmpty, listed.insert(f[0]).inserted else { return nil } + guard f.count >= 11, !f[1].isEmpty, listed.insert(f[0]).inserted else { return nil } let status = Status(rawValue: f[2]) ?? .unknown guard status != .unknown else { return nil } let window = (Int(f[7]) ?? 1) > 1 ? "\(f[6]).\(f[8])" : f[6] return Row(paneId: f[0], agent: f[1], name: f[4], status: status, session: f[5], window: window, since: Int(f[3]) ?? Int.max, - path: f[9].hasPrefix(home) ? "~" + f[9].dropFirst(home.count) : f[9]) + path: f[9].hasPrefix(home) ? "~" + f[9].dropFirst(home.count) : f[9], + importance: Importance(option: f[10])) } return rows.sorted(by: listedBefore) } @@ -247,6 +270,7 @@ func fetchRows() -> [Row] { /// The list order (see `fetchRows`). func listedBefore(_ a: Row, _ b: Row) -> Bool { if a.status.priority != b.status.priority { return a.status.priority > b.status.priority } + if a.importance != b.importance { return a.importance.rawValue > b.importance.rawValue } if a.since != b.since { // Int.max: never stamped; last either way. if a.since == Int.max || b.since == Int.max { return a.since < b.since } @@ -291,8 +315,15 @@ func plainTitle(for rows: [Row]) -> String { title(for: rows).string } -/// Row title: the colored status symbol, then the agent's name. Unnamed -/// agents show their harness, dimmed, so it never competes with real names. +/// The title's characters before the name: the status symbol, and `!` for a +/// high agent. +func rowTitlePrefix(_ row: Row) -> String { + "\(row.status.symbol) " + (row.importance == .high ? "! " : "") +} + +/// Row title: the colored status symbol, a bold `!` for a high agent, then +/// the agent's name. Unnamed agents show their harness, dimmed, so it never +/// competes with real names; a low agent's name is lightly dimmed. func rowTitle(_ row: Row, primary: NSColor = .labelColor, secondary: NSColor = .secondaryLabelColor) -> NSAttributedString { let font = NSFont.menuFont(ofSize: 0) @@ -300,7 +331,11 @@ func rowTitle(_ row: Row, primary: NSColor = .labelColor, let text = NSMutableAttributedString() text.append(NSAttributedString( string: "\(row.status.symbol) ", attributes: [.font: mono, .foregroundColor: row.status.color])) - let labelColor = row.name.isEmpty ? secondary : primary + if row.importance == .high { + text.append(NSAttributedString( + string: "! ", attributes: [.font: NSFont.boldSystemFont(ofSize: font.pointSize), .foregroundColor: primary])) + } + let labelColor = row.name.isEmpty ? secondary : row.importance == .low ? primary.withAlphaComponent(0.7) : primary text.append(NSAttributedString( string: row.label, attributes: [.font: font, .foregroundColor: labelColor])) return text |
