1use crate::protocol::SENTINEL;
4use crate::zellij::PaneInfo;
5
6pub struct PaneStatus {
8 pub pane_id: String,
9 pub title: String,
10 pub command: String,
11 pub running: bool,
12 pub exit_status: Option<i32>,
13 pub message_count: usize,
14}
15
16pub struct StatusSummary {
18 pub total: usize,
19 pub running: usize,
20 pub exited: usize,
21 pub panes: Vec<PaneStatus>,
22}
23
24fn count_messages(scrollback: &str) -> usize {
26 scrollback.lines().filter(|l| l.contains(SENTINEL)).count()
27}
28
29pub fn summarize(
35 panes: &[PaneInfo],
36 get_scrollback: impl Fn(&str) -> Option<String>,
37) -> StatusSummary {
38 let mut running = 0usize;
39 let mut exited = 0usize;
40 let mut statuses = Vec::with_capacity(panes.len());
41
42 for pane in panes {
43 if pane.exited {
44 exited += 1;
45 } else {
46 running += 1;
47 }
48
49 let pane_id = pane.pane_id();
50 let msg_count = get_scrollback(&pane_id)
51 .map(|s| count_messages(&s))
52 .unwrap_or(0);
53
54 let command = pane
55 .pane_command
56 .as_deref()
57 .map(|c| {
58 c.rsplit('/').next().unwrap_or(c)
60 })
61 .unwrap_or("-")
62 .to_string();
63
64 statuses.push(PaneStatus {
65 pane_id,
66 title: pane.title.clone(),
67 command,
68 running: !pane.exited,
69 exit_status: pane.exit_status,
70 message_count: msg_count,
71 });
72 }
73
74 StatusSummary {
75 total: panes.len(),
76 running,
77 exited,
78 panes: statuses,
79 }
80}
81
82pub fn format_summary(summary: &StatusSummary) -> String {
84 let mut out = format!(
85 "{} panes ({} running, {} exited)\n",
86 summary.total, summary.running, summary.exited,
87 );
88
89 for p in &summary.panes {
90 let state = if p.running {
91 "running".to_string()
92 } else {
93 match p.exit_status {
94 Some(code) => format!("exited ({})", code),
95 None => "exited".to_string(),
96 }
97 };
98 out.push_str(&format!(
99 " {} | {} | {} | {} | {} msgs\n",
100 p.pane_id, p.title, p.command, state, p.message_count,
101 ));
102 }
103
104 out
105}