mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-10 11:23:32 +08:00
feat: scroll ecc2 metrics across full teams
This commit is contained in:
@@ -83,6 +83,8 @@ pub struct Dashboard {
|
||||
output_follow: bool,
|
||||
output_scroll_offset: usize,
|
||||
last_output_height: usize,
|
||||
metrics_scroll_offset: usize,
|
||||
last_metrics_height: usize,
|
||||
pane_size_percent: u16,
|
||||
search_input: Option<String>,
|
||||
spawn_input: Option<String>,
|
||||
@@ -267,6 +269,8 @@ impl Dashboard {
|
||||
output_follow: true,
|
||||
output_scroll_offset: 0,
|
||||
last_output_height: 0,
|
||||
metrics_scroll_offset: 0,
|
||||
last_metrics_height: 0,
|
||||
pane_size_percent,
|
||||
search_input: None,
|
||||
spawn_input: None,
|
||||
@@ -631,7 +635,7 @@ impl Dashboard {
|
||||
)
|
||||
}
|
||||
|
||||
fn render_metrics(&self, frame: &mut Frame, area: Rect) {
|
||||
fn render_metrics(&mut self, frame: &mut Frame, area: Rect) {
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(" Metrics ")
|
||||
@@ -670,9 +674,12 @@ impl Dashboard {
|
||||
chunks[1],
|
||||
);
|
||||
frame.render_widget(
|
||||
Paragraph::new(self.selected_session_metrics_text()).wrap(Wrap { trim: true }),
|
||||
Paragraph::new(self.selected_session_metrics_text())
|
||||
.scroll((self.metrics_scroll_offset as u16, 0))
|
||||
.wrap(Wrap { trim: true }),
|
||||
chunks[2],
|
||||
);
|
||||
self.sync_metrics_scroll(chunks[2].height as usize);
|
||||
}
|
||||
|
||||
fn render_log(&self, frame: &mut Frame, area: Rect) {
|
||||
@@ -1060,6 +1067,7 @@ impl Dashboard {
|
||||
self.selected_session = (self.selected_session + 1).min(self.sessions.len() - 1);
|
||||
self.sync_selection();
|
||||
self.reset_output_view();
|
||||
self.reset_metrics_view();
|
||||
self.sync_selected_output();
|
||||
self.sync_selected_diff();
|
||||
self.sync_selected_messages();
|
||||
@@ -1079,7 +1087,11 @@ impl Dashboard {
|
||||
self.output_scroll_offset = self.output_scroll_offset.saturating_add(1);
|
||||
}
|
||||
}
|
||||
Pane::Metrics => {}
|
||||
Pane::Metrics => {
|
||||
let max_scroll = self.max_metrics_scroll();
|
||||
self.metrics_scroll_offset =
|
||||
self.metrics_scroll_offset.saturating_add(1).min(max_scroll);
|
||||
}
|
||||
Pane::Log => {
|
||||
self.output_follow = false;
|
||||
self.output_scroll_offset = self.output_scroll_offset.saturating_add(1);
|
||||
@@ -1094,6 +1106,7 @@ impl Dashboard {
|
||||
self.selected_session = self.selected_session.saturating_sub(1);
|
||||
self.sync_selection();
|
||||
self.reset_output_view();
|
||||
self.reset_metrics_view();
|
||||
self.sync_selected_output();
|
||||
self.sync_selected_diff();
|
||||
self.sync_selected_messages();
|
||||
@@ -1108,7 +1121,9 @@ impl Dashboard {
|
||||
|
||||
self.output_scroll_offset = self.output_scroll_offset.saturating_sub(1);
|
||||
}
|
||||
Pane::Metrics => {}
|
||||
Pane::Metrics => {
|
||||
self.metrics_scroll_offset = self.metrics_scroll_offset.saturating_sub(1);
|
||||
}
|
||||
Pane::Log => {
|
||||
self.output_follow = false;
|
||||
self.output_scroll_offset = self.output_scroll_offset.saturating_sub(1);
|
||||
@@ -2584,7 +2599,6 @@ impl Dashboard {
|
||||
delegate.session_id.clone(),
|
||||
)
|
||||
});
|
||||
delegated.truncate(3);
|
||||
delegated
|
||||
}
|
||||
Err(error) => {
|
||||
@@ -2830,6 +2844,19 @@ impl Dashboard {
|
||||
.saturating_sub(self.last_output_height.max(1))
|
||||
}
|
||||
|
||||
fn sync_metrics_scroll(&mut self, viewport_height: usize) {
|
||||
self.last_metrics_height = viewport_height.max(1);
|
||||
let max_scroll = self.max_metrics_scroll();
|
||||
self.metrics_scroll_offset = self.metrics_scroll_offset.min(max_scroll);
|
||||
}
|
||||
|
||||
fn max_metrics_scroll(&self) -> usize {
|
||||
self.selected_session_metrics_text()
|
||||
.lines()
|
||||
.count()
|
||||
.saturating_sub(self.last_metrics_height.max(1))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn visible_output_text(&self) -> String {
|
||||
self.visible_output_lines()
|
||||
@@ -2844,6 +2871,10 @@ impl Dashboard {
|
||||
self.output_scroll_offset = 0;
|
||||
}
|
||||
|
||||
fn reset_metrics_view(&mut self) {
|
||||
self.metrics_scroll_offset = 0;
|
||||
}
|
||||
|
||||
fn refresh_logs(&mut self) {
|
||||
let Some(session_id) = self.selected_session_id().map(ToOwned::to_owned) else {
|
||||
self.logs.clear();
|
||||
@@ -3242,6 +3273,7 @@ impl Dashboard {
|
||||
self.refresh();
|
||||
self.sync_selection_by_id(select_session_id);
|
||||
self.reset_output_view();
|
||||
self.reset_metrics_view();
|
||||
self.sync_selected_output();
|
||||
self.sync_selected_diff();
|
||||
self.sync_selected_messages();
|
||||
@@ -5275,6 +5307,50 @@ diff --git a/src/next.rs b/src/next.rs
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_selected_lineage_keeps_all_delegate_rows() {
|
||||
let lead = sample_session(
|
||||
"lead-12345678",
|
||||
"planner",
|
||||
SessionState::Running,
|
||||
Some("ecc/lead"),
|
||||
512,
|
||||
42,
|
||||
);
|
||||
|
||||
let mut sessions = vec![lead.clone()];
|
||||
let mut dashboard = test_dashboard(vec![lead.clone()], 0);
|
||||
dashboard.db.insert_session(&lead).unwrap();
|
||||
|
||||
for index in 0..5 {
|
||||
let child_id = format!("worker-{index}");
|
||||
let child = sample_session(
|
||||
&child_id,
|
||||
"planner",
|
||||
SessionState::Running,
|
||||
Some(&format!("ecc/{child_id}")),
|
||||
64,
|
||||
6,
|
||||
);
|
||||
sessions.push(child.clone());
|
||||
dashboard.db.insert_session(&child).unwrap();
|
||||
dashboard
|
||||
.db
|
||||
.send_message(
|
||||
"lead-12345678",
|
||||
&child_id,
|
||||
"{\"task\":\"Delegated work\",\"context\":\"Delegated from lead\"}",
|
||||
"task_handoff",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
dashboard.sessions = sessions;
|
||||
dashboard.sync_selected_lineage();
|
||||
|
||||
assert_eq!(dashboard.selected_child_sessions.len(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aggregate_cost_summary_mentions_total_cost() {
|
||||
let db = StateStore::open(Path::new(":memory:")).unwrap();
|
||||
@@ -5454,7 +5530,7 @@ diff --git a/src/next.rs b/src/next.rs
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metrics_scroll_does_not_mutate_output_scroll() -> Result<()> {
|
||||
fn metrics_scroll_uses_independent_offset() -> Result<()> {
|
||||
let db_path = std::env::temp_dir().join(format!("ecc2-dashboard-{}.db", Uuid::new_v4()));
|
||||
let db = StateStore::open(&db_path)?;
|
||||
let now = Utc::now();
|
||||
@@ -5484,10 +5560,13 @@ diff --git a/src/next.rs b/src/next.rs
|
||||
let previous_scroll = dashboard.output_scroll_offset;
|
||||
|
||||
dashboard.selected_pane = Pane::Metrics;
|
||||
dashboard.last_metrics_height = 2;
|
||||
dashboard.scroll_up();
|
||||
dashboard.scroll_down();
|
||||
dashboard.scroll_down();
|
||||
|
||||
assert_eq!(dashboard.output_scroll_offset, previous_scroll);
|
||||
assert_eq!(dashboard.metrics_scroll_offset, 2);
|
||||
let _ = std::fs::remove_file(db_path);
|
||||
Ok(())
|
||||
}
|
||||
@@ -6989,6 +7068,8 @@ diff --git a/src/next.rs b/src/next.rs
|
||||
output_follow: true,
|
||||
output_scroll_offset: 0,
|
||||
last_output_height: 0,
|
||||
metrics_scroll_offset: 0,
|
||||
last_metrics_height: 0,
|
||||
search_input: None,
|
||||
spawn_input: None,
|
||||
search_query: None,
|
||||
|
||||
Reference in New Issue
Block a user