feat: add todo management feature; implement todo content tracking and display in the UI
This commit is contained in:
@@ -303,6 +303,20 @@ pub fn apply_action(state: &mut AppStateRest, action: Action) {
|
||||
state.misc.tick_count = state.misc.tick_count.wrapping_add(1);
|
||||
let now_ms = chrono::Utc::now().timestamp_millis();
|
||||
state.misc.drain_expired_toasts(now_ms);
|
||||
|
||||
if state.misc.tick_count.is_multiple_of(10) {
|
||||
let todo_path = state.session_dir.join("todo.md");
|
||||
if let Ok(content) = std::fs::read_to_string(&todo_path) {
|
||||
if content != state.misc.todo_content {
|
||||
state.misc.todo_content = content;
|
||||
state.dirty = true;
|
||||
}
|
||||
} else if !state.misc.todo_content.is_empty() {
|
||||
state.misc.todo_content.clear();
|
||||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Background API connectivity check — runs on a background thread
|
||||
// every ~1s while disconnected, every ~30s while connected, so the
|
||||
// status bar reflects real API availability without user input.
|
||||
|
||||
@@ -278,6 +278,7 @@ pub struct MiscState {
|
||||
pub editor: Option<super::super::mode::editor::EditorState>,
|
||||
pub api_connected: bool,
|
||||
pub tick_count: u64,
|
||||
pub todo_content: String,
|
||||
}
|
||||
|
||||
impl MiscState {
|
||||
@@ -294,6 +295,7 @@ impl MiscState {
|
||||
editor: None,
|
||||
api_connected: false,
|
||||
tick_count: 0,
|
||||
todo_content: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -116,7 +116,7 @@ fn run_single_process() -> Result<()> {
|
||||
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, crossterm::event::EnableMouseCapture)?;
|
||||
execute!(stdout, EnterAlternateScreen)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
terminal.clear()?;
|
||||
@@ -124,7 +124,7 @@ fn run_single_process() -> Result<()> {
|
||||
let run_result = run_loop(&mut state, &mut terminal);
|
||||
|
||||
let mut restore_stdout = io::stdout();
|
||||
let _ = execute!(restore_stdout, LeaveAlternateScreen, crossterm::event::DisableMouseCapture);
|
||||
let _ = execute!(restore_stdout, LeaveAlternateScreen);
|
||||
let _ = disable_raw_mode();
|
||||
|
||||
if let Err(e) = run_result {
|
||||
@@ -460,7 +460,7 @@ fn run_attach(session_id: &str) -> Result<()> {
|
||||
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, crossterm::event::EnableMouseCapture)?;
|
||||
execute!(stdout, EnterAlternateScreen)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
terminal.clear()?;
|
||||
@@ -544,7 +544,7 @@ fn run_attach(session_id: &str) -> Result<()> {
|
||||
})?;
|
||||
}
|
||||
|
||||
let _ = execute!(io::stdout(), LeaveAlternateScreen, crossterm::event::DisableMouseCapture);
|
||||
let _ = execute!(io::stdout(), LeaveAlternateScreen);
|
||||
let _ = disable_raw_mode();
|
||||
|
||||
let _ = client_state.settings.save();
|
||||
@@ -570,7 +570,7 @@ fn run_loop(
|
||||
let _ = terminal.clear();
|
||||
|
||||
let _ = disable_raw_mode();
|
||||
let _ = execute!(io::stdout(), LeaveAlternateScreen, crossterm::event::DisableMouseCapture);
|
||||
let _ = execute!(io::stdout(), LeaveAlternateScreen);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
+43
-5
@@ -29,6 +29,20 @@ use theme::Theme;
|
||||
pub fn draw(frame: &mut Frame, state: &crate::app::state::rest::AppStateRest) {
|
||||
let area = frame.area();
|
||||
|
||||
let show_todo = !state.misc.todo_content.is_empty() || state.misc.overlay == crate::app::state::types::Overlay::Todo;
|
||||
let (main_area, todo_area) = if show_todo && area.width > 60 {
|
||||
let h_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([
|
||||
Constraint::Min(40),
|
||||
Constraint::Length(30),
|
||||
])
|
||||
.split(area);
|
||||
(h_chunks[0], Some(h_chunks[1]))
|
||||
} else {
|
||||
(area, None)
|
||||
};
|
||||
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
@@ -36,26 +50,50 @@ pub fn draw(frame: &mut Frame, state: &crate::app::state::rest::AppStateRest) {
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.split(area);
|
||||
.split(main_area);
|
||||
|
||||
let main_area = chunks[0];
|
||||
let chat_area = chunks[0];
|
||||
let input_area = chunks[1];
|
||||
let status_area = chunks[2];
|
||||
|
||||
if state.misc.overlay.is_active() {
|
||||
if state.misc.overlay.is_active() && state.misc.overlay != crate::app::state::types::Overlay::Todo {
|
||||
let overlay = state.misc.overlay;
|
||||
render_overlay(frame, main_area, overlay, state);
|
||||
render_overlay(frame, chat_area, overlay, state);
|
||||
} else {
|
||||
render_main_panel(frame, main_area, state);
|
||||
render_main_panel(frame, chat_area, state);
|
||||
}
|
||||
|
||||
render_input_bar(frame, input_area, state);
|
||||
status::draw_status_bar(frame, status_area, state);
|
||||
|
||||
if let Some(todo_rect) = todo_area {
|
||||
render_todo_panel(frame, todo_rect, state);
|
||||
}
|
||||
|
||||
// Toast notifications at top-right (like Hyprland)
|
||||
render_toasts(frame, state);
|
||||
}
|
||||
|
||||
fn render_todo_panel(frame: &mut Frame, area: Rect, state: &crate::app::state::rest::AppStateRest) {
|
||||
let block = Block::default()
|
||||
.title(" Todo ")
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Theme::PRIMARY))
|
||||
.style(Style::default().bg(Theme::BG));
|
||||
|
||||
let content = if state.misc.todo_content.is_empty() {
|
||||
"No tasks yet."
|
||||
} else {
|
||||
&state.misc.todo_content
|
||||
};
|
||||
|
||||
let paragraph = Paragraph::new(content)
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: false });
|
||||
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
fn render_main_panel(frame: &mut Frame, area: Rect, state: &crate::app::state::rest::AppStateRest) {
|
||||
chat::draw_chat(frame, area, state);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user