From ea1dda2f056fdfd56bffe8b978c0b0786b82aa7d Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Mon, 19 Oct 2026 16:55:30 +0300 Subject: ready-queue fifo with click-to-focus; redesign presentation per design panel daemon tracks when each agent became ready (idle/blocked, unacked) and publishes fifo queue + numeric counts as server-global options; unknown to idle transitions stay out of the queue (restart spam). new dingger focus subcommand jumps to the longest-waiting pane and acks it, bound to status-right clicks in tmux. menu bar redesigned per two-model design panel: monochrome sf-symbol bell + queue depth, queue-ordered rows with state glyphs and right-aligned targets, row clicks ack like focus does. tmux segment renders glyph+count with a single red accent for blocked; fixes unescaped comma in the conditional format that truncated the segment entirely. --- crates/dinggerd/src/main.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'crates/dinggerd/src/main.rs') diff --git a/crates/dinggerd/src/main.rs b/crates/dinggerd/src/main.rs index f046732..4c8521c 100644 --- a/crates/dinggerd/src/main.rs +++ b/crates/dinggerd/src/main.rs @@ -33,6 +33,12 @@ enum Command { #[command(subcommand)] command: ManifestCommand, }, + /// Switch to the agent that has been waiting the longest (FIFO). Bound to + /// a mouse click on the tmux status line by the packaged config. + Focus { + /// Name of the tmux client that clicked, passed as #{client_name}. + client: Option, + }, } #[derive(Subcommand)] @@ -73,7 +79,43 @@ fn main() { } } }, + Command::Focus { client } => { + if let Err(err) = focus(client) { + eprintln!("dingger: {err}"); + std::process::exit(1); + } + } + } +} + +/// Jump to the head of the ready queue (oldest waiting agent) and ack it, so +/// the next focus moves on to the following one. +fn focus(client: Option) -> Result<(), String> { + let queue = tmux::show_global("@dingger_queue") + .unwrap_or_default() + .split(',') + .filter(|entry| !entry.is_empty()) + .map(|entry| entry.split(':').next().unwrap_or("").to_string()) + .collect::>(); + let Some(pane_id) = queue.first() else { + return Ok(()); // nothing waiting + }; + let target = tmux::show_pane_format(pane_id, "#{session_name}:#{window_index}.#{pane_index}") + .unwrap_or_default(); + if target.is_empty() { + return Err("ready pane no longer exists".to_string()); + } + tmux::ack(pane_id)?; + let mut args = vec!["switch-client".to_string()]; + if let Some(client) = client { + args.push("-c".to_string()); + args.push(client); } + args.push("-t".to_string()); + args.push(target); + tmux::run_tmux_client( + &args.iter().map(String::as_str).collect::>(), + ) } fn status() -> Result<(), String> { -- cgit v1.2.3