aboutsummaryrefslogtreecommitdiff
path: root/menubar/model.swift
diff options
context:
space:
mode:
Diffstat (limited to 'menubar/model.swift')
-rw-r--r--menubar/model.swift52
1 files changed, 52 insertions, 0 deletions
diff --git a/menubar/model.swift b/menubar/model.swift
index e891697..da5ad15 100644
--- a/menubar/model.swift
+++ b/menubar/model.swift
@@ -165,6 +165,58 @@ func tmux(_ args: [String]) -> String? {
let dinggerBinary = environment["DINGGER_BIN"] ?? (NSHomeDirectory() + "/.local/bin/dingger")
+// MARK: restore offer
+
+/// tmux sessions the daemon offers to restore after tmux stopped
+/// (`offer.json`, written by `dingger watch`).
+struct RestoreOffer: Decodable, Equatable {
+ struct Session: Decodable, Equatable {
+ let name: String
+ let windows: Int
+ let panes: Int
+ let agents: Int
+ }
+ let saved_at: Int
+ let sessions: [Session]
+ let agents: Int
+ let interrupted: Int
+
+ /// "Restore 9 sessions · 22 agents"
+ var title: String {
+ var text = "Restore \(sessions.count) \(sessions.count == 1 ? "session" : "sessions")"
+ if agents > 0 { text += " · \(agents) \(agents == 1 ? "agent" : "agents")" }
+ return text
+ }
+
+ /// "saved 13:01 · 1 was mid-turn"
+ var detail: String {
+ let formatter = DateFormatter()
+ let saved = Date(timeIntervalSince1970: TimeInterval(saved_at) / 1000)
+ formatter.dateFormat = Calendar.current.isDateInToday(saved) ? "HH:mm" : "MMM d, HH:mm"
+ var parts = ["tmux stopped · saved \(formatter.string(from: saved))"]
+ if interrupted > 0 { parts.append("\(interrupted) \(interrupted == 1 ? "was" : "were") mid-turn") }
+ return parts.joined(separator: " · ")
+ }
+}
+
+let stateDirectory = environment["DINGGER_STATE_DIR"] ?? (NSHomeDirectory() + "/.local/state/dingger")
+
+func fetchRestoreOffer() -> RestoreOffer? {
+ guard let data = FileManager.default.contents(atPath: stateDirectory + "/offer.json") else { return nil }
+ return try? JSONDecoder().decode(RestoreOffer.self, from: data)
+}
+
+/// Run `dingger <args>` through the user's login shell, the way a terminal
+/// starts tmux: restored panes inherit this environment (PATH, LANG), and
+/// launchd's is bare. DINGGER_RESTORE_DIRECT runs it directly (tests).
+func runDinggerInLoginShell(_ args: [String]) -> (ok: Bool, output: String) {
+ if environment["DINGGER_RESTORE_DIRECT"] != nil {
+ return runReporting(dinggerBinary, args)
+ }
+ let shell = getpwuid(getuid()).flatMap { String(validatingUTF8: $0.pointee.pw_shell) } ?? "/bin/zsh"
+ return runReporting(shell, ["--login", "-i", "-c", "exec \"$0\" \"$@\"", dinggerBinary] + args)
+}
+
private let home = NSHomeDirectory()
/// Every agent pane, in herdr's priority order, oldest waiting first.