aboutsummaryrefslogtreecommitdiff
path: root/menubar/model.swift
diff options
context:
space:
mode:
Diffstat (limited to 'menubar/model.swift')
-rw-r--r--menubar/model.swift45
1 files changed, 32 insertions, 13 deletions
diff --git a/menubar/model.swift b/menubar/model.swift
index e4d179c..3ed1a7e 100644
--- a/menubar/model.swift
+++ b/menubar/model.swift
@@ -93,8 +93,14 @@ struct Row: Equatable {
/// The pane's working directory, home as "~".
var path: String = ""
var importance: Importance = .normal
+ /// The draft in the agent's input box, as one line (`@dingger_draft`).
+ var draft: String = ""
+ /// The user looks at this pane: its window is active in an attached session.
+ var attended: Bool = false
var location: String { "\(session) · \(window)" }
+ /// A draft left behind: where the user looks, they may be typing it.
+ var forgottenDraft: Bool { !draft.isEmpty && !attended }
var waiting: Bool { status == .blocked || status == .done }
/// Only an agent you have seen can be marked unread (done again).
var canMarkUnread: Bool { status == .idle }
@@ -105,8 +111,9 @@ struct Row: Equatable {
return name.count > 80 ? String(name.prefix(79)) + "…" : name
}
- /// The title's text as plain text: `! ` marks a high agent.
- var markedLabel: String { (importance == .high ? "! " : "") + label }
+ /// The title's text as plain text: `! ` marks a high agent, `✎ ` a
+ /// forgotten draft.
+ var markedLabel: String { (importance == .high ? "! " : "") + (forgottenDraft ? "✎ " : "") + label }
/// Dim second line: where it runs, which harness, whether it is low,
/// and how long it waits.
@@ -274,30 +281,35 @@ func runDinggerInLoginShell(_ args: [String]) -> (ok: Bool, output: String) {
private let home = NSHomeDirectory()
-/// Every agent pane: herdr's status priority; within a status, the more
-/// important first, then done and blocked oldest first (the queue),
+/// Every agent pane: herdr's status priority; idle agents with a forgotten
+/// draft before the other idle ones; 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}",
+ "#{@dingger_importance}", "#{window_active}", "#{session_attached}", "#{@dingger_draft}",
].joined(separator: "\t")
guard let out = tmux(["list-panes", "-a", "-F", format]) else { return [] }
+ let lines = out.split(separator: "\n").map {
+ $0.split(separator: "\t", omittingEmptySubsequences: false).map(String.init)
+ }
+ // Looked at in any of its sessions, as the daemon counts it.
+ let attended = Set(lines.filter { $0.count >= 14 && $0[11] == "1" && $0[12] != "0" }.map { $0[0] })
// A pane in several sessions (a session group, a linked window) is
// listed once, under its first session, as the daemon counts it.
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 >= 11, !f[1].isEmpty, listed.insert(f[0]).inserted else { return nil }
+ let rows: [Row] = lines.compactMap { f in
+ guard f.count >= 14, !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],
- importance: Importance(option: f[10]))
+ importance: Importance(option: f[10]), draft: f[13], attended: attended.contains(f[0]))
}
return rows.sorted(by: listedBefore)
}
@@ -305,6 +317,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.status == .idle, a.forgottenDraft != b.forgottenDraft { return a.forgottenDraft }
if a.importance != b.importance { return a.importance.rawValue > b.importance.rawValue }
if a.since != b.since {
// Int.max: never stamped; last either way.
@@ -357,12 +370,15 @@ func plainTitle(for rows: [Row]) -> String {
title(for: rows).string
}
-/// The title's characters before the name: the status symbol and the
-/// importance column.
+/// The title's characters before the name: the status symbol, the
+/// importance column, and the draft mark.
func rowTitlePrefix(_ row: Row) -> String {
- "\(row.status.symbol) " + importanceMark(row) + " "
+ "\(row.status.symbol) " + importanceMark(row) + " " + draftMark(row)
}
+/// `✎ ` before the name of an agent with a forgotten draft.
+private func draftMark(_ row: Row) -> String { row.forgottenDraft ? "✎ " : "" }
+
/// The importance column: `!` for a high agent, a blank otherwise, so every
/// name starts in the same place.
private func importanceMark(_ row: Row) -> String { row.importance == .high ? "!" : " " }
@@ -371,7 +387,7 @@ private func importanceMark(_ row: Row) -> String { row.importance == .high ? "!
let lowImportanceAlpha: CGFloat = 0.5
/// Row title: the colored status symbol, the importance column (a peach `!`
-/// for a high agent), then the agent's name. Unnamed agents show their
+/// for a high agent), `✎` for a forgotten draft, then the agent's name. Unnamed agents show their
/// harness, dimmed, so it never competes with real names; a low agent's name
/// is dimmed.
func rowTitle(_ row: Row, primary: NSColor = .labelColor,
@@ -385,6 +401,9 @@ func rowTitle(_ row: Row, primary: NSColor = .labelColor,
string: importanceMark(row) + " ",
attributes: [.font: NSFont.monospacedSystemFont(ofSize: font.pointSize, weight: .heavy),
.foregroundColor: herdrPeach]))
+ if row.forgottenDraft {
+ text.append(NSAttributedString(string: draftMark(row), attributes: [.font: font, .foregroundColor: primary]))
+ }
let labelColor = row.name.isEmpty ? secondary
: row.importance == .low ? primary.withAlphaComponent(lowImportanceAlpha) : primary
text.append(NSAttributedString(