mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-10 19:33:37 +08:00
feat: add ecc2 output time filters
This commit is contained in:
@@ -32,6 +32,31 @@ impl OutputStream {
|
|||||||
pub struct OutputLine {
|
pub struct OutputLine {
|
||||||
pub stream: OutputStream,
|
pub stream: OutputStream,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
|
pub timestamp: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputLine {
|
||||||
|
pub fn new(
|
||||||
|
stream: OutputStream,
|
||||||
|
text: impl Into<String>,
|
||||||
|
timestamp: impl Into<String>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
stream,
|
||||||
|
text: text.into(),
|
||||||
|
timestamp: timestamp.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_current_timestamp(stream: OutputStream, text: impl Into<String>) -> Self {
|
||||||
|
Self::new(stream, text, chrono::Utc::now().to_rfc3339())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn occurred_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
|
chrono::DateTime::parse_from_rfc3339(&self.timestamp)
|
||||||
|
.ok()
|
||||||
|
.map(|timestamp| timestamp.with_timezone(&chrono::Utc))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -70,10 +95,7 @@ impl SessionOutputStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_line(&self, session_id: &str, stream: OutputStream, text: impl Into<String>) {
|
pub fn push_line(&self, session_id: &str, stream: OutputStream, text: impl Into<String>) {
|
||||||
let line = OutputLine {
|
let line = OutputLine::with_current_timestamp(stream, text);
|
||||||
stream,
|
|
||||||
text: text.into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut buffers = self.lock_buffers();
|
let mut buffers = self.lock_buffers();
|
||||||
@@ -145,5 +167,6 @@ mod tests {
|
|||||||
assert_eq!(event.session_id, "session-1");
|
assert_eq!(event.session_id, "session-1");
|
||||||
assert_eq!(event.line.stream, OutputStream::Stderr);
|
assert_eq!(event.line.stream, OutputStream::Stderr);
|
||||||
assert_eq!(event.line.text, "problem");
|
assert_eq!(event.line.text, "problem");
|
||||||
|
assert!(event.line.occurred_at().is_some());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -961,9 +961,9 @@ impl StateStore {
|
|||||||
|
|
||||||
pub fn get_output_lines(&self, session_id: &str, limit: usize) -> Result<Vec<OutputLine>> {
|
pub fn get_output_lines(&self, session_id: &str, limit: usize) -> Result<Vec<OutputLine>> {
|
||||||
let mut stmt = self.conn.prepare(
|
let mut stmt = self.conn.prepare(
|
||||||
"SELECT stream, line
|
"SELECT stream, line, timestamp
|
||||||
FROM (
|
FROM (
|
||||||
SELECT id, stream, line
|
SELECT id, stream, line, timestamp
|
||||||
FROM session_output
|
FROM session_output
|
||||||
WHERE session_id = ?1
|
WHERE session_id = ?1
|
||||||
ORDER BY id DESC
|
ORDER BY id DESC
|
||||||
@@ -976,11 +976,13 @@ impl StateStore {
|
|||||||
.query_map(rusqlite::params![session_id, limit as i64], |row| {
|
.query_map(rusqlite::params![session_id, limit as i64], |row| {
|
||||||
let stream: String = row.get(0)?;
|
let stream: String = row.get(0)?;
|
||||||
let text: String = row.get(1)?;
|
let text: String = row.get(1)?;
|
||||||
|
let timestamp: String = row.get(2)?;
|
||||||
|
|
||||||
Ok(OutputLine {
|
Ok(OutputLine::new(
|
||||||
stream: OutputStream::from_db_value(&stream),
|
OutputStream::from_db_value(&stream),
|
||||||
text,
|
text,
|
||||||
})
|
timestamp,
|
||||||
|
))
|
||||||
})?
|
})?
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ pub async fn run(db: StateStore, cfg: Config) -> Result<()> {
|
|||||||
(_, KeyCode::Char('v')) => dashboard.toggle_output_mode(),
|
(_, KeyCode::Char('v')) => dashboard.toggle_output_mode(),
|
||||||
(_, KeyCode::Char('c')) => dashboard.toggle_conflict_protocol_mode(),
|
(_, KeyCode::Char('c')) => dashboard.toggle_conflict_protocol_mode(),
|
||||||
(_, KeyCode::Char('e')) => dashboard.toggle_output_filter(),
|
(_, KeyCode::Char('e')) => dashboard.toggle_output_filter(),
|
||||||
|
(_, KeyCode::Char('f')) => dashboard.cycle_output_time_filter(),
|
||||||
(_, KeyCode::Char('m')) => dashboard.merge_selected_worktree().await,
|
(_, KeyCode::Char('m')) => dashboard.merge_selected_worktree().await,
|
||||||
(_, KeyCode::Char('M')) => dashboard.merge_ready_worktrees().await,
|
(_, KeyCode::Char('M')) => dashboard.merge_ready_worktrees().await,
|
||||||
(_, KeyCode::Char('l')) => dashboard.cycle_pane_layout(),
|
(_, KeyCode::Char('l')) => dashboard.cycle_pane_layout(),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use chrono::{Duration, Utc};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{
|
widgets::{
|
||||||
@@ -72,6 +73,7 @@ pub struct Dashboard {
|
|||||||
selected_merge_readiness: Option<worktree::MergeReadiness>,
|
selected_merge_readiness: Option<worktree::MergeReadiness>,
|
||||||
output_mode: OutputMode,
|
output_mode: OutputMode,
|
||||||
output_filter: OutputFilter,
|
output_filter: OutputFilter,
|
||||||
|
output_time_filter: OutputTimeFilter,
|
||||||
selected_pane: Pane,
|
selected_pane: Pane,
|
||||||
selected_session: usize,
|
selected_session: usize,
|
||||||
show_help: bool,
|
show_help: bool,
|
||||||
@@ -123,6 +125,14 @@ enum OutputFilter {
|
|||||||
ErrorsOnly,
|
ErrorsOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum OutputTimeFilter {
|
||||||
|
AllTime,
|
||||||
|
Last15Minutes,
|
||||||
|
LastHour,
|
||||||
|
Last24Hours,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct PaneAreas {
|
struct PaneAreas {
|
||||||
sessions: Rect,
|
sessions: Rect,
|
||||||
@@ -201,6 +211,7 @@ impl Dashboard {
|
|||||||
selected_merge_readiness: None,
|
selected_merge_readiness: None,
|
||||||
output_mode: OutputMode::SessionOutput,
|
output_mode: OutputMode::SessionOutput,
|
||||||
output_filter: OutputFilter::All,
|
output_filter: OutputFilter::All,
|
||||||
|
output_time_filter: OutputTimeFilter::AllTime,
|
||||||
selected_pane: Pane::Sessions,
|
selected_pane: Pane::Sessions,
|
||||||
selected_session: 0,
|
selected_session: 0,
|
||||||
show_help: false,
|
show_help: false,
|
||||||
@@ -472,7 +483,11 @@ impl Dashboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn output_title(&self) -> String {
|
fn output_title(&self) -> String {
|
||||||
let filter = self.output_filter_label();
|
let filter = format!(
|
||||||
|
"{}{}",
|
||||||
|
self.output_filter.title_suffix(),
|
||||||
|
self.output_time_filter.title_suffix()
|
||||||
|
);
|
||||||
if let Some(input) = self.search_input.as_ref() {
|
if let Some(input) = self.search_input.as_ref() {
|
||||||
return format!(" Output{filter} /{input}_ ");
|
return format!(" Output{filter} /{input}_ ");
|
||||||
}
|
}
|
||||||
@@ -490,17 +505,14 @@ impl Dashboard {
|
|||||||
format!(" Output{filter} ")
|
format!(" Output{filter} ")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_filter_label(&self) -> &'static str {
|
|
||||||
match self.output_filter {
|
|
||||||
OutputFilter::All => "",
|
|
||||||
OutputFilter::ErrorsOnly => " errors",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn empty_output_message(&self) -> &'static str {
|
fn empty_output_message(&self) -> &'static str {
|
||||||
match self.output_filter {
|
match (self.output_filter, self.output_time_filter) {
|
||||||
OutputFilter::All => "Waiting for session output...",
|
(OutputFilter::All, OutputTimeFilter::AllTime) => "Waiting for session output...",
|
||||||
OutputFilter::ErrorsOnly => "No stderr output for this session yet.",
|
(OutputFilter::ErrorsOnly, OutputTimeFilter::AllTime) => {
|
||||||
|
"No stderr output for this session yet."
|
||||||
|
}
|
||||||
|
(OutputFilter::All, _) => "No output lines in the selected time range.",
|
||||||
|
(OutputFilter::ErrorsOnly, _) => "No stderr output in the selected time range.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,7 +623,7 @@ impl Dashboard {
|
|||||||
|
|
||||||
fn render_status_bar(&self, frame: &mut Frame, area: Rect) {
|
fn render_status_bar(&self, frame: &mut Frame, area: Rect) {
|
||||||
let base_text = format!(
|
let base_text = format!(
|
||||||
" [n]ew session [a]ssign re[b]alance global re[B]alance dra[i]n inbox [g]lobal dispatch coordinate [G]lobal [v]iew diff conflict proto[c]ol [e]rrors [m]erge merge ready [M] auto-worktree [t] auto-merge [w] toggle [p]olicy [,/.] dispatch limit [s]top [u]resume [x]cleanup prune inactive [X] [d]elete [r]efresh [Tab] switch pane [j/k] scroll [+/-] resize [l]ayout {} [T]heme {} [?] help [q]uit ",
|
" [n]ew session [a]ssign re[b]alance global re[B]alance dra[i]n inbox [g]lobal dispatch coordinate [G]lobal [v]iew diff conflict proto[c]ol [e]rrors time [f]ilter [m]erge merge ready [M] auto-worktree [t] auto-merge [w] toggle [p]olicy [,/.] dispatch limit [s]top [u]resume [x]cleanup prune inactive [X] [d]elete [r]efresh [Tab] switch pane [j/k] scroll [+/-] resize [l]ayout {} [T]heme {} [?] help [q]uit ",
|
||||||
self.layout_label(),
|
self.layout_label(),
|
||||||
self.theme_label()
|
self.theme_label()
|
||||||
);
|
);
|
||||||
@@ -683,6 +695,7 @@ impl Dashboard {
|
|||||||
" v Toggle selected worktree diff in output pane",
|
" v Toggle selected worktree diff in output pane",
|
||||||
" c Show conflict-resolution protocol for selected conflicted worktree",
|
" c Show conflict-resolution protocol for selected conflicted worktree",
|
||||||
" e Toggle output filter between all lines and stderr only",
|
" e Toggle output filter between all lines and stderr only",
|
||||||
|
" f Cycle output time filter between all/15m/1h/24h",
|
||||||
" m Merge selected ready worktree into base and clean it up",
|
" m Merge selected ready worktree into base and clean it up",
|
||||||
" M Merge all ready inactive worktrees and clean them up",
|
" M Merge all ready inactive worktrees and clean them up",
|
||||||
" l Cycle pane layout and persist it",
|
" l Cycle pane layout and persist it",
|
||||||
@@ -1724,6 +1737,23 @@ impl Dashboard {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cycle_output_time_filter(&mut self) {
|
||||||
|
if self.output_mode != OutputMode::SessionOutput {
|
||||||
|
self.set_operator_note(
|
||||||
|
"output time filters are only available in session output view".to_string(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output_time_filter = self.output_time_filter.next();
|
||||||
|
self.recompute_search_matches();
|
||||||
|
self.sync_output_scroll(self.last_output_height.max(1));
|
||||||
|
self.set_operator_note(format!(
|
||||||
|
"output time filter set to {}",
|
||||||
|
self.output_time_filter.label()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn toggle_auto_dispatch_policy(&mut self) {
|
pub fn toggle_auto_dispatch_policy(&mut self) {
|
||||||
self.cfg.auto_dispatch_unread_handoffs = !self.cfg.auto_dispatch_unread_handoffs;
|
self.cfg.auto_dispatch_unread_handoffs = !self.cfg.auto_dispatch_unread_handoffs;
|
||||||
match self.cfg.save() {
|
match self.cfg.save() {
|
||||||
@@ -2192,7 +2222,9 @@ impl Dashboard {
|
|||||||
fn visible_output_lines(&self) -> Vec<&OutputLine> {
|
fn visible_output_lines(&self) -> Vec<&OutputLine> {
|
||||||
self.selected_output_lines()
|
self.selected_output_lines()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|line| self.output_filter.matches(line.stream))
|
.filter(|line| {
|
||||||
|
self.output_filter.matches(line.stream) && self.output_time_filter.matches(line)
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2864,6 +2896,60 @@ impl OutputFilter {
|
|||||||
OutputFilter::ErrorsOnly => "errors",
|
OutputFilter::ErrorsOnly => "errors",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn title_suffix(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
OutputFilter::All => "",
|
||||||
|
OutputFilter::ErrorsOnly => " errors",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputTimeFilter {
|
||||||
|
fn next(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::AllTime => Self::Last15Minutes,
|
||||||
|
Self::Last15Minutes => Self::LastHour,
|
||||||
|
Self::LastHour => Self::Last24Hours,
|
||||||
|
Self::Last24Hours => Self::AllTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches(self, line: &OutputLine) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::AllTime => true,
|
||||||
|
Self::Last15Minutes => line
|
||||||
|
.occurred_at()
|
||||||
|
.map(|timestamp| timestamp >= Utc::now() - Duration::minutes(15))
|
||||||
|
.unwrap_or(false),
|
||||||
|
Self::LastHour => line
|
||||||
|
.occurred_at()
|
||||||
|
.map(|timestamp| timestamp >= Utc::now() - Duration::hours(1))
|
||||||
|
.unwrap_or(false),
|
||||||
|
Self::Last24Hours => line
|
||||||
|
.occurred_at()
|
||||||
|
.map(|timestamp| timestamp >= Utc::now() - Duration::hours(24))
|
||||||
|
.unwrap_or(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::AllTime => "all time",
|
||||||
|
Self::Last15Minutes => "last 15m",
|
||||||
|
Self::LastHour => "last 1h",
|
||||||
|
Self::Last24Hours => "last 24h",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title_suffix(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::AllTime => "",
|
||||||
|
Self::Last15Minutes => " last 15m",
|
||||||
|
Self::LastHour => " last 1h",
|
||||||
|
Self::Last24Hours => " last 24h",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionSummary {
|
impl SessionSummary {
|
||||||
@@ -3320,10 +3406,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
dashboard.session_output_cache.insert(
|
dashboard.session_output_cache.insert(
|
||||||
"focus-12345678".to_string(),
|
"focus-12345678".to_string(),
|
||||||
vec![OutputLine {
|
vec![test_output_line(OutputStream::Stdout, "last useful output")],
|
||||||
stream: OutputStream::Stdout,
|
|
||||||
text: "last useful output".to_string(),
|
|
||||||
}],
|
|
||||||
);
|
);
|
||||||
dashboard.selected_diff_summary = Some("1 file changed, 2 insertions(+)".to_string());
|
dashboard.selected_diff_summary = Some("1 file changed, 2 insertions(+)".to_string());
|
||||||
dashboard.selected_diff_preview = vec![
|
dashboard.selected_diff_preview = vec![
|
||||||
@@ -4160,18 +4243,9 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
dashboard.session_output_cache.insert(
|
dashboard.session_output_cache.insert(
|
||||||
"focus-12345678".to_string(),
|
"focus-12345678".to_string(),
|
||||||
vec![
|
vec![
|
||||||
OutputLine {
|
test_output_line(OutputStream::Stdout, "alpha"),
|
||||||
stream: OutputStream::Stdout,
|
test_output_line(OutputStream::Stdout, "beta"),
|
||||||
text: "alpha".to_string(),
|
test_output_line(OutputStream::Stdout, "alpha tail"),
|
||||||
},
|
|
||||||
OutputLine {
|
|
||||||
stream: OutputStream::Stdout,
|
|
||||||
text: "beta".to_string(),
|
|
||||||
},
|
|
||||||
OutputLine {
|
|
||||||
stream: OutputStream::Stdout,
|
|
||||||
text: "alpha tail".to_string(),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
dashboard.last_output_height = 2;
|
dashboard.last_output_height = 2;
|
||||||
@@ -4207,18 +4281,9 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
dashboard.session_output_cache.insert(
|
dashboard.session_output_cache.insert(
|
||||||
"focus-12345678".to_string(),
|
"focus-12345678".to_string(),
|
||||||
vec![
|
vec![
|
||||||
OutputLine {
|
test_output_line(OutputStream::Stdout, "alpha-1"),
|
||||||
stream: OutputStream::Stdout,
|
test_output_line(OutputStream::Stdout, "beta"),
|
||||||
text: "alpha-1".to_string(),
|
test_output_line(OutputStream::Stdout, "alpha-2"),
|
||||||
},
|
|
||||||
OutputLine {
|
|
||||||
stream: OutputStream::Stdout,
|
|
||||||
text: "beta".to_string(),
|
|
||||||
},
|
|
||||||
OutputLine {
|
|
||||||
stream: OutputStream::Stdout,
|
|
||||||
text: "alpha-2".to_string(),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
dashboard.search_query = Some(r"alpha-\d".to_string());
|
dashboard.search_query = Some(r"alpha-\d".to_string());
|
||||||
@@ -4304,14 +4369,8 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
dashboard.session_output_cache.insert(
|
dashboard.session_output_cache.insert(
|
||||||
"focus-12345678".to_string(),
|
"focus-12345678".to_string(),
|
||||||
vec![
|
vec![
|
||||||
OutputLine {
|
test_output_line(OutputStream::Stdout, "stdout line"),
|
||||||
stream: OutputStream::Stdout,
|
test_output_line(OutputStream::Stderr, "stderr line"),
|
||||||
text: "stdout line".to_string(),
|
|
||||||
},
|
|
||||||
OutputLine {
|
|
||||||
stream: OutputStream::Stderr,
|
|
||||||
text: "stderr line".to_string(),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4342,18 +4401,9 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
dashboard.session_output_cache.insert(
|
dashboard.session_output_cache.insert(
|
||||||
"focus-12345678".to_string(),
|
"focus-12345678".to_string(),
|
||||||
vec![
|
vec![
|
||||||
OutputLine {
|
test_output_line(OutputStream::Stdout, "alpha stdout"),
|
||||||
stream: OutputStream::Stdout,
|
test_output_line(OutputStream::Stderr, "alpha stderr"),
|
||||||
text: "alpha stdout".to_string(),
|
test_output_line(OutputStream::Stderr, "beta stderr"),
|
||||||
},
|
|
||||||
OutputLine {
|
|
||||||
stream: OutputStream::Stderr,
|
|
||||||
text: "alpha stderr".to_string(),
|
|
||||||
},
|
|
||||||
OutputLine {
|
|
||||||
stream: OutputStream::Stderr,
|
|
||||||
text: "beta stderr".to_string(),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
dashboard.output_filter = OutputFilter::ErrorsOnly;
|
dashboard.output_filter = OutputFilter::ErrorsOnly;
|
||||||
@@ -4366,6 +4416,73 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
assert_eq!(dashboard.visible_output_text(), "alpha stderr\nbeta stderr");
|
assert_eq!(dashboard.visible_output_text(), "alpha stderr\nbeta stderr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cycle_output_time_filter_keeps_only_recent_lines() {
|
||||||
|
let mut dashboard = test_dashboard(
|
||||||
|
vec![sample_session(
|
||||||
|
"focus-12345678",
|
||||||
|
"planner",
|
||||||
|
SessionState::Running,
|
||||||
|
None,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
)],
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
dashboard.session_output_cache.insert(
|
||||||
|
"focus-12345678".to_string(),
|
||||||
|
vec![
|
||||||
|
test_output_line_minutes_ago(OutputStream::Stdout, "recent line", 5),
|
||||||
|
test_output_line_minutes_ago(OutputStream::Stdout, "older line", 45),
|
||||||
|
test_output_line_minutes_ago(OutputStream::Stdout, "stale line", 180),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
dashboard.cycle_output_time_filter();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
dashboard.output_time_filter,
|
||||||
|
OutputTimeFilter::Last15Minutes
|
||||||
|
);
|
||||||
|
assert_eq!(dashboard.visible_output_text(), "recent line");
|
||||||
|
assert_eq!(dashboard.output_title(), " Output last 15m ");
|
||||||
|
assert_eq!(
|
||||||
|
dashboard.operator_note.as_deref(),
|
||||||
|
Some("output time filter set to last 15m")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn search_matches_respect_time_filter() {
|
||||||
|
let mut dashboard = test_dashboard(
|
||||||
|
vec![sample_session(
|
||||||
|
"focus-12345678",
|
||||||
|
"planner",
|
||||||
|
SessionState::Running,
|
||||||
|
None,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
)],
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
dashboard.session_output_cache.insert(
|
||||||
|
"focus-12345678".to_string(),
|
||||||
|
vec![
|
||||||
|
test_output_line_minutes_ago(OutputStream::Stdout, "alpha recent", 10),
|
||||||
|
test_output_line_minutes_ago(OutputStream::Stdout, "beta recent", 10),
|
||||||
|
test_output_line_minutes_ago(OutputStream::Stdout, "alpha stale", 180),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
dashboard.output_time_filter = OutputTimeFilter::Last15Minutes;
|
||||||
|
dashboard.search_query = Some("alpha.*".to_string());
|
||||||
|
dashboard.last_output_height = 1;
|
||||||
|
|
||||||
|
dashboard.recompute_search_matches();
|
||||||
|
|
||||||
|
assert_eq!(dashboard.search_matches, vec![0]);
|
||||||
|
assert_eq!(dashboard.visible_output_text(), "alpha recent\nbeta recent");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn stop_selected_uses_session_manager_transition() -> Result<()> {
|
async fn stop_selected_uses_session_manager_transition() -> Result<()> {
|
||||||
let db_path = std::env::temp_dir().join(format!("ecc2-dashboard-{}.db", Uuid::new_v4()));
|
let db_path = std::env::temp_dir().join(format!("ecc2-dashboard-{}.db", Uuid::new_v4()));
|
||||||
@@ -5056,6 +5173,22 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
assert_eq!(dashboard.theme_palette().row_highlight_bg, Color::Gray);
|
assert_eq!(dashboard.theme_palette().row_highlight_bg, Color::Gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_output_line(stream: OutputStream, text: &str) -> OutputLine {
|
||||||
|
OutputLine::new(stream, text, Utc::now().to_rfc3339())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_output_line_minutes_ago(
|
||||||
|
stream: OutputStream,
|
||||||
|
text: &str,
|
||||||
|
minutes_ago: i64,
|
||||||
|
) -> OutputLine {
|
||||||
|
OutputLine::new(
|
||||||
|
stream,
|
||||||
|
text,
|
||||||
|
(Utc::now() - chrono::Duration::minutes(minutes_ago)).to_rfc3339(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn test_dashboard(sessions: Vec<Session>, selected_session: usize) -> Dashboard {
|
fn test_dashboard(sessions: Vec<Session>, selected_session: usize) -> Dashboard {
|
||||||
let selected_session = selected_session.min(sessions.len().saturating_sub(1));
|
let selected_session = selected_session.min(sessions.len().saturating_sub(1));
|
||||||
let cfg = Config::default();
|
let cfg = Config::default();
|
||||||
@@ -5093,6 +5226,7 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
selected_merge_readiness: None,
|
selected_merge_readiness: None,
|
||||||
output_mode: OutputMode::SessionOutput,
|
output_mode: OutputMode::SessionOutput,
|
||||||
output_filter: OutputFilter::All,
|
output_filter: OutputFilter::All,
|
||||||
|
output_time_filter: OutputTimeFilter::AllTime,
|
||||||
selected_pane: Pane::Sessions,
|
selected_pane: Pane::Sessions,
|
||||||
selected_session,
|
selected_session,
|
||||||
show_help: false,
|
show_help: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user