From 08d0494262994749b8cd7948cf6342a3b7094b0d Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Thu, 5 Feb 2026 14:41:00 +0000 Subject: [PATCH] Initial upload with CI/CD workflow --- src/tui/app.rs | 146 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/tui/app.rs diff --git a/src/tui/app.rs b/src/tui/app.rs new file mode 100644 index 0000000..d92290f --- /dev/null +++ b/src/tui/app.rs @@ -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>, +} + +impl TuiApp { + pub fn new(items: Vec, 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 { + 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(()) + } +}