Initial upload with CI/CD workflow
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-05 14:41:00 +00:00
parent 809a648393
commit 08d0494262

146
src/tui/app.rs Normal file
View File

@@ -0,0 +1,146 @@
use crate::tui::{render, TuiState};
use crossterm::{
event::{self, Event, KeyCode, KeyEventKind},
terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType},
ExecutableCommand,
};
use ratatui::{backend::CrosstermBackend, Terminal};
use std::io::Stdout;
use std::path::PathBuf;
use crate::models::TechDebtItem;
pub struct TuiApp {
state: TuiState,
terminal: Terminal<CrosstermBackend<Stdout>>,
}
impl TuiApp {
pub fn new(items: Vec<TechDebtItem>, path: PathBuf) -> Self {
let state = TuiState::new(items, path);
let terminal = Terminal::new(CrosstermBackend::new(std::io::stdout()))
.expect("Failed to create terminal");
Self { state, terminal }
}
pub fn run(&mut self) -> anyhow::Result<()> {
enable_raw_mode()?;
std::io::stdout().execute(Clear(ClearType::All))?;
loop {
self.terminal.draw(|f| render(f, &self.state))?;
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press {
match self.handle_input(key.code) {
Ok(should_exit) => {
if should_exit {
break;
}
}
Err(e) => {
eprintln!("Error handling input: {}", e);
}
}
}
}
}
disable_raw_mode()?;
std::io::stdout().execute(Clear(ClearType::All))?;
Ok(())
}
fn handle_input(&mut self, code: KeyCode) -> anyhow::Result<bool> {
let mut should_exit = false;
match code {
KeyCode::Char('q') | KeyCode::Esc => {
should_exit = true;
}
KeyCode::Tab => {
self.state.current_view = match self.state.current_view {
crate::tui::View::Dashboard => crate::tui::View::List,
crate::tui::View::List => crate::tui::View::Dashboard,
crate::tui::View::Detail => crate::tui::View::List,
crate::tui::View::Export => crate::tui::View::Dashboard,
};
}
KeyCode::Up => {
if self.state.selected_index > 0 {
self.state.selected_index -= 1;
}
}
KeyCode::Down => {
let filtered_len = self.state.filtered_items().len();
if filtered_len > 0 && self.state.selected_index < filtered_len - 1 {
self.state.selected_index += 1;
}
}
KeyCode::Enter => {
if self.state.current_view == crate::tui::View::List {
self.state.current_view = crate::tui::View::Detail;
}
}
KeyCode::Char('/') => {
self.enable_filter_mode()?;
}
KeyCode::Char('f') => {
self.enable_filter_mode()?;
}
KeyCode::Char('s') => {
self.state.sort_order = match self.state.sort_order {
crate::tui::SortOrder::Priority => crate::tui::SortOrder::File,
crate::tui::SortOrder::File => crate::tui::SortOrder::Line,
crate::tui::SortOrder::Line => crate::tui::SortOrder::Keyword,
crate::tui::SortOrder::Keyword => crate::tui::SortOrder::Priority,
};
}
KeyCode::Char('c') => {
self.state.filter_priority = None;
self.state.filter_text.clear();
}
KeyCode::Char('1') => {
self.state.filter_priority = Some(crate::models::Priority::Critical);
}
KeyCode::Char('2') => {
self.state.filter_priority = Some(crate::models::Priority::High);
}
KeyCode::Char('3') => {
self.state.filter_priority = Some(crate::models::Priority::Medium);
}
KeyCode::Char('4') => {
self.state.filter_priority = Some(crate::models::Priority::Low);
}
_ => {}
}
Ok(should_exit)
}
fn enable_filter_mode(&mut self) -> anyhow::Result<()> {
disable_raw_mode()?;
println!("Enter filter text (or press ESC to cancel): ");
std::io::stdout().flush()?;
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
input = input.trim_end().to_string();
if input.is_empty() || input.as_bytes().first() == Some(&0x1b) {
self.state.filter_text.clear();
} else {
self.state.filter_text = input;
}
enable_raw_mode()?;
std::io::stdout().execute(Clear(ClearType::All))?;
Ok(())
}
}