aboutsummaryrefslogtreecommitdiff
path: root/crates/dinggerd/src/tmux.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/dinggerd/src/tmux.rs')
-rw-r--r--crates/dinggerd/src/tmux.rs67
1 files changed, 38 insertions, 29 deletions
diff --git a/crates/dinggerd/src/tmux.rs b/crates/dinggerd/src/tmux.rs
index cba1ca6..f6c2f17 100644
--- a/crates/dinggerd/src/tmux.rs
+++ b/crates/dinggerd/src/tmux.rs
@@ -2,15 +2,14 @@
use std::process::Command;
-use detect_core::manifest::agent_state_label;
use detect_core::{identify_agent, Agent, AgentState};
#[derive(Debug, Clone)]
pub struct Pane {
/// Unique tmux pane id, e.g. `%3`. Stable for the pane's lifetime.
pub pane_id: String,
- /// User-facing target, e.g. `main:1.0`.
- pub target: String,
+ /// Human-readable target, e.g. `main:work` (`.pane` suffix when split).
+ pub display_target: String,
/// Foreground command name in the pane, from `#{pane_current_command}`.
pub current_command: String,
/// Last OSC title the pane's application set, from `#{pane_title}`.
@@ -29,14 +28,21 @@ pub struct Pane {
/// The `@dingger_ack` user option: "1" when the user already jumped to
/// this pane in its current ready episode.
pub written_ack: String,
+ /// The pane is the active pane of the active window.
+ pub pane_active: bool,
}
impl Pane {
- /// Panes the user is actively looking at suppress notifications, mirroring
- /// herdr's active-tab suppression.
+ /// Panes the user is actively looking at suppress finished notifications,
+ /// mirroring herdr's active-tab suppression.
pub fn suppresses_notifications(&self) -> bool {
self.window_active && self.session_attached
}
+
+ /// The pane is fully focused: the user is looking at this exact pane.
+ pub fn attended(&self) -> bool {
+ self.pane_active && self.suppresses_notifications()
+ }
}
fn run_tmux(args: &[&str]) -> Result<String, String> {
@@ -64,7 +70,6 @@ fn run_tmux(args: &[&str]) -> Result<String, String> {
pub fn list_panes() -> Result<Vec<Pane>, String> {
let format = concat!(
"#{pane_id}\t",
- "#{session_name}:#{window_index}.#{pane_index}\t",
"#{pane_current_command}\t",
"#{pane_title}\t",
"#{pane_dead}\t",
@@ -74,18 +79,22 @@ pub fn list_panes() -> Result<Vec<Pane>, String> {
"#{pane_pid}\t",
"#{@agent_state}\t",
"#{@agent}\t",
- "#{@dingger_ack}"
+ "#{@dingger_ack}\t",
+ "#{pane_active}\t",
+ "#{session_name}:#{window_name}\t",
+ "#{window_panes}\t",
+ "#{pane_index}",
);
let out = run_tmux(&["list-panes", "-a", "-F", format])?;
let mut panes = Vec::new();
for line in out.lines() {
let fields: Vec<&str> = line.split('\t').collect();
- if fields.len() < 11 {
+ if fields.len() < 15 {
continue;
}
- let current_command = fields[2].to_string();
- let start_command = fields[7].to_string();
- let pane_pid: Option<u32> = fields[8].trim().parse().ok();
+ let current_command = fields[1].to_string();
+ let start_command = fields[6].to_string();
+ let pane_pid: Option<u32> = fields[7].trim().parse().ok();
// An agent may be started via a wrapper or still be initializing
// behind the shell; `pane_start_command` covers launch-command cases.
let mut agent =
@@ -97,18 +106,25 @@ pub fn list_panes() -> Result<Vec<Pane>, String> {
let tree = process_tree.get_or_insert_with(fetch_process_tree);
agent = resolve_agent_in_process_tree(pane_pid, tree);
}
+ // Display target: session:window-name, pane index only when split.
+ let mut display_target = fields[12].to_string();
+ if fields[13].parse::<u32>().map(|n| n > 1).unwrap_or(false) {
+ display_target.push('.');
+ display_target.push_str(fields[14]);
+ }
panes.push(Pane {
pane_id: fields[0].to_string(),
- target: fields[1].to_string(),
+ display_target,
current_command,
- pane_title: fields[3].to_string(),
- dead: fields[4] == "1",
- window_active: fields[5] == "1",
- session_attached: fields[6] != "0",
+ pane_title: fields[2].to_string(),
+ dead: fields[3] == "1",
+ window_active: fields[4] == "1",
+ session_attached: fields[5] != "0",
agent,
- written_state: fields[9].to_string(),
- written_agent: fields[10].to_string(),
- written_ack: fields.get(11).copied().unwrap_or("").to_string(),
+ written_state: fields[8].to_string(),
+ written_agent: fields[9].to_string(),
+ written_ack: fields[10].to_string(),
+ pane_active: fields[11] == "1",
});
}
Ok(panes)
@@ -190,16 +206,9 @@ pub fn capture_pane(pane_id: &str) -> Result<String, String> {
run_tmux(&["capture-pane", "-p", "-q", "-t", pane_id])
}
-pub fn set_agent_state(pane_id: &str, state: AgentState) -> Result<(), String> {
- run_tmux(&[
- "set-option",
- "-p",
- "-t",
- pane_id,
- "@agent_state",
- agent_state_label(state),
- ])
- .map(|_| ())
+/// Write the pane's display status (herdr's vocabulary: done/idle/...).
+pub fn set_agent_state(pane_id: &str, label: &str) -> Result<(), String> {
+ run_tmux(&["set-option", "-p", "-t", pane_id, "@agent_state", label]).map(|_| ())
}
/// Label the pane with the agent running in it, for tmux status-line and