diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-06 02:27:06 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-06 02:27:06 +0300 |
| commit | 9e9bc8a144667a6b7debfd4f0c7a31ff0e11493a (patch) | |
| tree | 03f1259f91fd9edeacfd4b904f2e84d4acf25232 /src/client/shell/agent_sidebar.rs | |
| parent | 35b0dff96579f099e0436290085024424ffb289c (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.rs | 106 |
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) |
