From 0f06b0b15d6b064703fa1166fb8e7ceda209aaa8 Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Fri, 23 Oct 2026 13:01:52 +0300 Subject: restore, step 1: layout snapshots and dingger restore the daemon saves the tmux layout when it changes: sessions, windows (index, name, automatic-rename, layout, size, zoom, active), and panes (directory, active, agent). a new history file for layout changes, a rewrite for details, the last 50 kept, nothing saved while tmux is down. when a different server runs (or none), the old server's last snapshot is pinned as the restore candidate before the new server's snapshots start. dingger restore rebuilds missing sessions by tmux id: panes split from the newest so they keep their order, the saved layout applied at the saved size, then the size released so windows follow clients again. a bare session of the same name (a terminal's `new-session -As 0`) is replaced, its clients switched over first; used sessions are kept. --dry-run, --list, --from, --session. e2e step 16 kills a server with a client attached like ghostty's, restarts it, restores, and compares every window and pane; splitting the wrong pane, not replacing bare sessions, not pinning, or keeping the saved size each fail it. e2e keeps its state in its temp dir. --- crates/dinggerd/src/main.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'crates/dinggerd/src/main.rs') diff --git a/crates/dinggerd/src/main.rs b/crates/dinggerd/src/main.rs index 3707d51..2129bd2 100644 --- a/crates/dinggerd/src/main.rs +++ b/crates/dinggerd/src/main.rs @@ -5,6 +5,8 @@ mod integrations; mod manifest_update; mod names; mod notify; +mod restore; +mod snapshot; mod tmux; mod watcher; @@ -52,6 +54,24 @@ enum Command { }, /// Wake the watcher so focus changes show up immediately (tmux hooks). Poke, + /// Restore tmux sessions saved before tmux last stopped (a crash, + /// shutdown, or kill-server): windows, panes, layouts, directories. + /// Only adds what is missing; a bare session of the same name (what a + /// terminal creates on startup) is replaced, its clients moved over. + Restore { + /// Show what would happen without changing anything. + #[arg(long)] + dry_run: bool, + /// List the restore candidate and saved snapshots. + #[arg(long)] + list: bool, + /// Restore from this snapshot file instead of the candidate. + #[arg(long)] + from: Option, + /// Restore only these sessions. + #[arg(long = "session")] + sessions: Vec, + }, /// Install agent integrations (hook extensions that report state). Install { /// Agent to install the integration for. @@ -112,6 +132,13 @@ fn main() { // Best effort: a stopped watcher just means nothing to wake. let _ = hooks::poke(); } + Command::Restore { dry_run, list, from, sessions } => { + let result = if list { restore::list() } else { restore::run(from.as_deref(), &sessions, dry_run) }; + if let Err(err) = result { + eprintln!("dingger: {err}"); + std::process::exit(1); + } + } Command::Install { agent } => { if let Err(err) = integrations::install(&agent) { eprintln!("dingger: {err}"); -- cgit v1.2.3