diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-23 19:11:23 +0300 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-23 19:11:23 +0300 |
| commit | e4d3bc242bd3d18aa83a76f25dcbe5cb7a2ced78 (patch) | |
| tree | 70b7631a0558829fb53814f3fb5a1ad32977149f /crates/dinggerd/src/tmux.rs | |
| parent | b4f45632debe6b8dc2f44ca6cc1b003d9093ad33 (diff) | |
raise the terminal window that holds the tmux client
a jump (any click, a shortcut, search) activated the terminal app, which
raised whichever window was used last. now:
- the client: the named one (a status-line click), else one already on a
session holding the agent's window (several terminal windows can show
one session; it shows the agent without switching, and keeps its own
session in a session group), else any; the focused one first, then the
one used last (its last input or focus).
- Ghostty (1.3+, scriptable by terminal id, not by tty): dingger learns
which terminal holds each client when it gains focus. tmux's
client-focus-in hook runs `dingger terminal focused`, which asks Ghostty
for its focused terminal and records it in @dingger_terminal_<tty>, but
only if the client is still focused once Ghostty answered (a fast switch
must not pair the wrong terminal); a terminal holds one client, so an
older pairing with it is dropped. `focus` raises the exact terminal
(window, tab, split) through `tmux run-shell`: Apple Events from the tmux
server count as Ghostty's own, so no permission prompt. a terminal that
is gone is forgotten and the app activated, as without a pairing.
- in a hook, #{client_name} can be another client (the previous event's);
the snippet's hook uses #{hook_client}.
e2e step 20: two nested clients on one session, focused by typing focus
reports into their outer panes, a fake osascript as Ghostty, a fake open
(tests never activate a real app); it loads the snippet's own hook line.
each rule has a negative control that fails.
Diffstat (limited to 'crates/dinggerd/src/tmux.rs')
| -rw-r--r-- | crates/dinggerd/src/tmux.rs | 40 |
1 files changed, 20 insertions, 20 deletions
diff --git a/crates/dinggerd/src/tmux.rs b/crates/dinggerd/src/tmux.rs index 6610161..54da39d 100644 --- a/crates/dinggerd/src/tmux.rs +++ b/crates/dinggerd/src/tmux.rs @@ -280,31 +280,31 @@ pub fn client_format(client: &str, format: &str) -> Option<String> { .filter(|value| !value.is_empty()) } -/// Attached clients as (name, pid), most recently active first. -pub fn clients() -> Result<Vec<(String, u32)>, String> { - let out = run_tmux(&[ - "list-clients", - "-F", - "#{client_activity}\t#{client_name}\t#{client_pid}", - ])?; - let mut clients: Vec<(u64, String, u32)> = out +/// The sessions holding a pane's window (several: a session group, a linked +/// window). +pub fn sessions_with_pane(pane_id: &str) -> Vec<String> { + run_tmux(&["list-panes", "-a", "-F", "#{session_id}\t#{pane_id}"]) + .unwrap_or_default() .lines() - .filter_map(|line| { - let mut fields = line.split('\t'); - let activity = fields.next()?.parse().ok()?; - let name = fields.next()?.to_string(); - let pid = fields.next()?.parse().ok()?; - Some((activity, name, pid)) - }) - .collect(); - clients.sort_by_key(|client| std::cmp::Reverse(client.0)); - Ok(clients.into_iter().map(|(_, name, pid)| (name, pid)).collect()) + .filter_map(|line| line.split_once('\t').filter(|(_, pane)| *pane == pane_id).map(|(s, _)| s.to_string())) + .collect() } /// Show a pane in a client: switch the client to the pane's window and make /// the pane the active one. -pub fn show_pane(client: &str, pane_id: &str) -> Result<(), String> { - run_tmux(&["switch-client", "-c", client, "-t", pane_id])?; +/// +/// `session`: the client's own session, when it holds the pane's window; +/// the client stays on it (a pane target alone lets tmux pick a session, +/// which in a session group may be another one). +pub fn show_pane(client: &str, pane_id: &str, session: Option<&str>) -> Result<(), String> { + let target = match session { + Some(session) => { + let window = pane_format(pane_id, "#{window_id}").ok_or_else(|| format!("no pane {pane_id}"))?; + format!("{session}:{window}") + } + None => pane_id.to_string(), + }; + run_tmux(&["switch-client", "-c", client, "-t", &target])?; run_tmux(&["select-pane", "-t", pane_id]).map(|_| ()) } |
