aboutsummaryrefslogtreecommitdiff
path: root/src/client/shell/agent_sidebar.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-06 02:27:06 +0300
committerGitHub <noreply@github.com>2026-09-06 02:27:06 +0300
commit9e9bc8a144667a6b7debfd4f0c7a31ff0e11493a (patch)
tree03f1259f91fd9edeacfd4b904f2e84d4acf25232 /src/client/shell/agent_sidebar.rs
parent35b0dff96579f099e0436290085024424ffb289c (diff)
feat: manage multiple ssh machines from one client (#3670)
Diffstat (limited to 'src/client/shell/agent_sidebar.rs')
-rw-r--r--src/client/shell/agent_sidebar.rs106
1 files changed, 80 insertions, 26 deletions
diff --git a/src/client/shell/agent_sidebar.rs b/src/client/shell/agent_sidebar.rs
index 8f87b6ad..d6fe3352 100644
--- a/src/client/shell/agent_sidebar.rs
+++ b/src/client/shell/agent_sidebar.rs
@@ -10,11 +10,11 @@ use ratatui::{
use super::*;
-struct AgentRow {
- pane_id: String,
- status: crate::api::schema::AgentStatus,
- focused: bool,
- rows: Vec<Vec<crate::ui::ResolvedToken>>,
+pub(super) struct AgentRow {
+ pub(super) pane_id: String,
+ pub(super) status: crate::api::schema::AgentStatus,
+ pub(super) focused: bool,
+ pub(super) rows: Vec<Vec<crate::ui::ResolvedToken>>,
}
pub(super) fn ordered_agent_pane_ids(
@@ -57,9 +57,46 @@ pub(super) fn render_agent_panel(
agent_scroll: &mut usize,
hits: &mut ShellHitMap,
) {
- if area.height == 0 {
+ if !render_agent_panel_header(
+ buffer,
+ area,
+ snapshot.agent_view_label.as_deref(),
+ config,
+ hits,
+ ) {
return;
}
+
+ let rows = agent_rows(snapshot, config, None);
+ render_agent_list(
+ buffer,
+ area,
+ &rows,
+ snapshot
+ .agent_view_label
+ .as_ref()
+ .map(|_| " no matching agents"),
+ config,
+ agent_scroll,
+ hits,
+ |row| row.rows.len(),
+ |buffer, rect, row, hits| {
+ hits.agents.push((rect, row.pane_id.clone()));
+ render_agent_row(buffer, rect, row, config);
+ },
+ );
+}
+
+pub(super) fn render_agent_panel_header(
+ buffer: &mut Buffer,
+ area: Rect,
+ agent_view_label: Option<&str>,
+ config: &ClientShellConfig,
+ hits: &mut ShellHitMap,
+) -> bool {
+ if area.height == 0 {
+ return false;
+ }
put_text(
buffer,
area.x,
@@ -69,7 +106,7 @@ pub(super) fn render_agent_panel(
Style::default().fg(config.palette.surface_dim),
);
if area.height < 2 {
- return;
+ return false;
}
put_text(
buffer,
@@ -81,14 +118,10 @@ pub(super) fn render_agent_panel(
.fg(config.palette.overlay0)
.add_modifier(Modifier::BOLD),
);
- let sort_label =
- snapshot
- .agent_view_label
- .as_deref()
- .unwrap_or(match config.agent_panel_sort {
- crate::config::AgentPanelSortConfig::Spaces => "grouped",
- crate::config::AgentPanelSortConfig::Priority => "priority",
- });
+ let sort_label = agent_view_label.unwrap_or(match config.agent_panel_sort {
+ crate::config::AgentPanelSortConfig::Spaces => "grouped",
+ crate::config::AgentPanelSortConfig::Priority => "priority",
+ });
let sort_width = display_width(sort_label).min(area.width as usize) as u16;
let sort_rect = Rect::new(
area.right().saturating_sub(sort_width),
@@ -96,7 +129,7 @@ pub(super) fn render_agent_panel(
sort_width,
1,
);
- hits.agent_sort_toggle = if config.mouse_capture && snapshot.agent_view_label.is_none() {
+ hits.agent_sort_toggle = if config.mouse_capture && agent_view_label.is_none() {
sort_rect
} else {
Rect::default()
@@ -108,15 +141,27 @@ pub(super) fn render_agent_panel(
sort_rect.width,
sort_label,
Style::default()
- .fg(if snapshot.agent_view_label.is_some() {
+ .fg(if agent_view_label.is_some() {
config.palette.accent
} else {
config.palette.overlay0
})
.add_modifier(Modifier::BOLD),
);
+ true
+}
- let rows = agent_rows(snapshot, config);
+pub(super) fn render_agent_list<T>(
+ buffer: &mut Buffer,
+ area: Rect,
+ rows: &[T],
+ empty_message: Option<&str>,
+ config: &ClientShellConfig,
+ agent_scroll: &mut usize,
+ hits: &mut ShellHitMap,
+ row_lines: impl Fn(&T) -> usize,
+ mut render_row: impl FnMut(&mut Buffer, Rect, &T, &mut ShellHitMap),
+) {
let body = Rect::new(
area.x,
area.y.saturating_add(3),
@@ -126,13 +171,13 @@ pub(super) fn render_agent_panel(
hits.agent_body = body;
if body.is_empty() || rows.is_empty() {
*agent_scroll = 0;
- if !body.is_empty() && snapshot.agent_view_label.is_some() {
+ if let Some(message) = empty_message.filter(|_| !body.is_empty()) {
put_text(
buffer,
body.x,
body.y,
body.width,
- " no matching agents",
+ message,
Style::default()
.fg(config.palette.overlay0)
.add_modifier(Modifier::DIM),
@@ -143,7 +188,7 @@ pub(super) fn render_agent_panel(
let row_heights = rows
.iter()
- .map(|row| row.rows.len().max(1).min(u16::MAX as usize) as u16)
+ .map(|row| row_lines(row).max(1).min(u16::MAX as usize) as u16)
.collect::<Vec<_>>();
let gaps = rows
.iter()
@@ -167,13 +212,12 @@ pub(super) fn render_agent_panel(
let content_width = body.width.saturating_sub(u16::from(show_scrollbar));
let mut y = body.y;
for (index, row) in rows.iter().enumerate().skip(*agent_scroll) {
- let height = (row.rows.len().max(1).min(u16::MAX as usize) as u16).min(body.height);
+ let height = row_heights[index].min(body.height);
if y.saturating_add(height) > body.bottom() {
break;
}
let rect = Rect::new(body.x, y, content_width, height);
- hits.agents.push((rect, row.pane_id.clone()));
- render_agent_row(buffer, rect, row, config);
+ render_row(buffer, rect, row, hits);
y = y
.saturating_add(height)
.saturating_add(if index + 1 < rows.len() {
@@ -190,7 +234,11 @@ pub(super) fn render_agent_panel(
}
}
-fn agent_rows(snapshot: &ClientShellSnapshot, config: &ClientShellConfig) -> Vec<AgentRow> {
+pub(super) fn agent_rows(
+ snapshot: &ClientShellSnapshot,
+ config: &ClientShellConfig,
+ machine: Option<&str>,
+) -> Vec<AgentRow> {
ordered_agent_pane_ids(snapshot, config.agent_panel_sort)
.into_iter()
.filter_map(|pane_id| {
@@ -238,6 +286,7 @@ fn agent_rows(snapshot: &ClientShellSnapshot, config: &ClientShellConfig) -> Vec
let rows = crate::ui::sidebar_agent_rows(
&config.agents,
crate::ui::AgentTokenContext {
+ machine,
workspace: &workspace.label,
tab: tab_label,
pane: agent
@@ -262,7 +311,12 @@ fn agent_rows(snapshot: &ClientShellSnapshot, config: &ClientShellConfig) -> Vec
.collect()
}
-fn render_agent_row(buffer: &mut Buffer, rect: Rect, row: &AgentRow, config: &ClientShellConfig) {
+pub(super) fn render_agent_row(
+ buffer: &mut Buffer,
+ rect: Rect,
+ row: &AgentRow,
+ config: &ClientShellConfig,
+) {
let palette = &config.palette;
let row_style = if row.focused {
Style::default().bg(palette.active_row_bg)