aboutsummaryrefslogtreecommitdiff
path: root/menubar/panel.swift
diff options
context:
space:
mode:
Diffstat (limited to 'menubar/panel.swift')
-rw-r--r--menubar/panel.swift63
1 files changed, 51 insertions, 12 deletions
diff --git a/menubar/panel.swift b/menubar/panel.swift
index 85cc81e..5362d32 100644
--- a/menubar/panel.swift
+++ b/menubar/panel.swift
@@ -5,7 +5,8 @@
// - On or off (the global shortcut and the menu toggle it; off stays off),
// and while on: shown always, or only when agents are waiting.
// - Layout: compact (waiting agents in full, working and idle as one summary
-// line); when shown always, a toggle switches to the full list.
+// line, with a count of forgotten drafts that jumps to the first); when
+// shown always, a toggle switches to the full list.
// - Always dark: herdr's mocha palette with fixed colors on a dark tint, so
// text reads the same over any desktop.
@@ -85,6 +86,8 @@ struct PanelContent {
let full: Bool
/// Whether the panel offers the compact/full toggle (shown always).
let canToggle: Bool
+ /// Agents with a forgotten draft not shown as rows, in list order.
+ let drafts: [Row]
/// Sessions to restore, shown first.
let offer: RestoreOffer?
@@ -107,6 +110,8 @@ struct PanelContent {
}
working = all.filter { $0.status == .working }.count
idle = all.filter { $0.status == .idle }.count
+ let shown = Set(rows.map(\.paneId))
+ drafts = all.filter { $0.forgottenDraft && !shown.contains($0.paneId) }
self.full = full
self.canToggle = canToggle
}
@@ -137,6 +142,11 @@ struct PanelContent {
var toggleLabel: String { full ? "Show less ▴" : "Show all ▾" }
+ /// "✎ 2 drafts", when some agents not shown have one.
+ var draftsLabel: String? {
+ drafts.isEmpty ? nil : "✎ \(drafts.count) \(drafts.count == 1 ? "draft" : "drafts")"
+ }
+
/// The offer row's title: what restore will do, or that it runs.
var offerTitle: String? {
guard let offer else { return nil }
@@ -157,7 +167,8 @@ struct PanelContent {
}
if overflow > 0 { lines.append("+\(overflow) more waiting") }
if showsSummary {
- lines.append(summary.string + (canToggle ? "\t[\(toggleLabel)]" : ""))
+ lines.append(summary.string + (draftsLabel.map { "\t[\($0)]" } ?? "")
+ + (canToggle ? "\t[\(toggleLabel)]" : ""))
}
return lines.joined(separator: "\n")
}
@@ -369,7 +380,7 @@ private final class OfferView: ClickView {
}
}
-/// "Show all ▾" / "Show less ▴".
+/// Clickable caption text: "Show all ▾" / "Show less ▴", "Not now", "✎ 2 drafts".
private final class ToggleView: ClickView {
private let label: NSTextField
@@ -550,17 +561,29 @@ final class PanelController {
return box
}
- /// Summary on the left, the compact/full toggle on the right.
+ /// Summary on the left; on the right, the forgotten drafts (a click
+ /// jumps to the first) and the compact/full toggle.
private func summaryLine(_ content: PanelContent) -> NSView {
let box = caption(content.summary)
- guard content.canToggle else { return box }
- let toggle = ToggleView(text: content.toggleLabel, onClick: onToggleFull)
- toggle.translatesAutoresizingMaskIntoConstraints = false
- box.addSubview(toggle)
- NSLayoutConstraint.activate([
- toggle.trailingAnchor.constraint(equalTo: box.trailingAnchor, constant: -8),
- toggle.centerYAnchor.constraint(equalTo: box.centerYAnchor, constant: 1),
- ])
+ var trailing = box.trailingAnchor
+ var gap: CGFloat = -8
+ var constraints: [NSLayoutConstraint] = []
+ let onClick = self.onClick
+ let draftsView = zip(content.draftsLabel.map { [$0] } ?? [], content.drafts.prefix(1)).map { label, first in
+ ToggleView(text: label, onClick: { onClick(first.paneId) })
+ }
+ let toggleView = content.canToggle ? [ToggleView(text: content.toggleLabel, onClick: onToggleFull)] : []
+ for view in (toggleView + draftsView) {
+ view.translatesAutoresizingMaskIntoConstraints = false
+ box.addSubview(view)
+ constraints += [
+ view.trailingAnchor.constraint(equalTo: trailing, constant: gap),
+ view.centerYAnchor.constraint(equalTo: box.centerYAnchor, constant: 1),
+ ]
+ trailing = view.leadingAnchor
+ gap = -14
+ }
+ NSLayoutConstraint.activate(constraints)
return box
}
@@ -695,6 +718,22 @@ func panelSelftest() -> [String] {
check(full.plainText.hasSuffix("[Show less ▴]") && !compact.plainText.contains("Show"),
"toggle offered only when allowed, labelled by layout")
+ // Forgotten drafts: a draft the user is not looking at.
+ var drafty = [row("d", .done), row("w", .working), row("i1", .idle), row("i2", .idle), row("i3", .idle)]
+ drafty[0].draft = "x"; drafty[1].draft = "x"; drafty[3].draft = "x"; drafty[4].draft = "x"
+ drafty[4].attended = true
+ check(drafty.map(\.forgottenDraft) == [true, true, false, true, false], "draft: forgotten unless looked at")
+ check(drafty.sorted(by: listedBefore).map(\.paneId) == ["d", "w", "i2", "i1", "i3"],
+ "draft: an idle agent with one first among idle agents only")
+ check(rowTitlePrefix(drafty[3]) == "○ ✎ " && rowTitle(drafty[3]).string == "○ ✎ i2"
+ && rowTitle(drafty[2]).string == "○ i1", "draft: ✎ before the name")
+ let draftPanel = PanelContent(rows: drafty.sorted(by: listedBefore), canToggle: true)
+ check(draftPanel.drafts.map(\.paneId) == ["w", "i2"] && draftPanel.draftsLabel == "✎ 2 drafts",
+ "draft: compact counts those not shown, in list order")
+ check(draftPanel.plainText.hasSuffix("○ 3 idle\t[✎ 2 drafts]\t[Show all ▾]"), "draft: the count before the toggle")
+ check(PanelContent(rows: drafty, full: true).draftsLabel == nil, "draft: none counted when every row shows")
+ check(PanelContent(rows: [drafty[3]]).draftsLabel == "✎ 1 draft", "draft: one")
+
let offer = RestoreOffer(saved_at: 0, sessions: [
.init(name: "a", windows: 2, panes: 3, agents: 2), .init(name: "b", windows: 1, panes: 1, agents: 0),
], agents: 2, interrupted: 1)