Skip to content

Commit 35ddd87

Browse files
committed
feat(tui): add History tab with output viewer, scrolling & tab switching
chore(deps): bump syn to 2.0.109; align windows-sys versions; set home to 0.5.11 chore: remove committed SQLite artifact (rust/local.db)
1 parent 444aafe commit 35ddd87

File tree

4 files changed

+316
-91
lines changed

4 files changed

+316
-91
lines changed

rust/Cargo.lock

Lines changed: 35 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/local.db

-204 KB
Binary file not shown.

rust/src/core.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,17 @@ pub fn run_search(files: Vec<PathBuf>, query: &str, top: usize, unique: bool) ->
6565
// ---------------------
6666
// TUI model
6767
// ---------------------
68+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69+
pub enum Tab {
70+
Main,
71+
History,
72+
}
73+
6874
#[derive(Clone)]
6975
pub struct HistoryEntry {
7076
pub cmd: String,
7177
pub exit_code: Option<i32>,
78+
pub output_lines: Vec<String>,
7279
}
7380

7481
pub struct App {
@@ -90,8 +97,14 @@ pub struct App {
9097
pub last_run_cmd: Option<String>,
9198

9299
// view state
100+
pub current_tab: Tab,
101+
pub selected_history_index: usize,
93102
pub pinned_output: bool, // middle-left shows output instead of suggestions
94103
pub recent_runs_area: Option<Rect>, // clickable area cache
104+
pub main_tab_area: Option<Rect>,
105+
pub history_tab_area: Option<Rect>,
106+
pub output_scroll: u16, // scroll offset for main tab output
107+
pub history_scroll: u16, // scroll offset for history tab output
95108

96109
// corpus
97110
pub corpus: Vec<String>,
@@ -102,18 +115,31 @@ pub struct App {
102115

103116
impl App {
104117
pub fn new(corpus: Vec<String>, top: usize, db: Option<SqlitePool>) -> Self {
118+
// Load recent history from database
119+
let history = if let Some(ref pool) = db {
120+
load_recent_history(pool, 100).unwrap_or_default()
121+
} else {
122+
Vec::new()
123+
};
124+
105125
Self {
106126
input: String::new(),
107127
cursor: 0,
108128
suggestions: Vec::new(),
109129
selected: 0,
110130
max_suggestions: top,
111-
history: Vec::new(),
131+
history,
112132
output_lines: Vec::new(),
113133
is_running: false,
114134
last_run_cmd: None,
135+
current_tab: Tab::Main,
136+
selected_history_index: 0,
115137
pinned_output: false,
116138
recent_runs_area: None,
139+
main_tab_area: None,
140+
history_tab_area: None,
141+
output_scroll: 0,
142+
history_scroll: 0,
117143
corpus,
118144
db,
119145
session_id: Self::generate_session_id(),
@@ -299,6 +325,38 @@ fn persist_history_entry(
299325
Ok(())
300326
}
301327

328+
fn load_recent_history(pool: &SqlitePool, limit: usize) -> Result<Vec<HistoryEntry>> {
329+
pool.query_collect(
330+
"SELECT command, output FROM history ORDER BY id DESC LIMIT ?1",
331+
vec![Value::Integer(limit as i64)],
332+
|row| {
333+
let command: String = row.get(0)?;
334+
let output_str: String = row.get(1).unwrap_or_default();
335+
336+
// Parse output to extract lines and exit code
337+
let mut output_lines = Vec::new();
338+
let mut exit_code = None;
339+
340+
for line in output_str.lines() {
341+
if line.starts_with("(exit code: ") && line.ends_with(")") {
342+
// Extract exit code from the last line
343+
if let Some(code_str) = line.strip_prefix("(exit code: ").and_then(|s| s.strip_suffix(")")) {
344+
exit_code = code_str.parse::<i32>().ok();
345+
}
346+
} else {
347+
output_lines.push(line.to_string());
348+
}
349+
}
350+
351+
Ok(HistoryEntry {
352+
cmd: command,
353+
exit_code,
354+
output_lines,
355+
})
356+
},
357+
)
358+
}
359+
302360
fn hash_command(command: &str) -> String {
303361
let mut hasher = Sha256::new();
304362
hasher.update(command.as_bytes());

0 commit comments

Comments
 (0)