Skip to main content

j_cli/command/todo/
mod.rs

1pub mod app;
2pub mod ui;
3
4use crate::config::YamlConfig;
5use crate::{error, info};
6use app::{
7    AppMode, TodoApp, TodoItem, handle_confirm_delete, handle_confirm_report, handle_help_mode,
8    handle_input_mode, handle_normal_mode, load_todo_list, save_todo_list,
9};
10use chrono::Local;
11use crossterm::{
12    event::{self, Event, KeyCode, KeyModifiers},
13    execute,
14    terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
15};
16use ratatui::{Terminal, backend::CrosstermBackend};
17use std::io;
18use ui::draw_ui;
19
20/// 处理 todo 命令: j todo [content...]
21pub fn handle_todo(content: &[String], config: &mut YamlConfig) {
22    if content.is_empty() {
23        run_todo_tui(config);
24        return;
25    }
26
27    let text = content.join(" ");
28    let text = text.trim().trim_matches('"').to_string();
29
30    if text.is_empty() {
31        error!("⚠️ 内容为空,无法添加待办");
32        return;
33    }
34
35    let mut list = load_todo_list();
36    list.items.push(TodoItem {
37        content: text.clone(),
38        done: false,
39        created_at: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
40        done_at: None,
41    });
42
43    if save_todo_list(&list) {
44        info!("✅ 已添加待办: {}", text);
45        let undone = list.items.iter().filter(|i| !i.done).count();
46        info!("📋 当前未完成待办: {} 条", undone);
47    }
48}
49
50/// 启动 TUI 待办管理界面
51fn run_todo_tui(config: &mut YamlConfig) {
52    match run_todo_tui_internal(config) {
53        Ok(_) => {}
54        Err(e) => {
55            error!("❌ TUI 启动失败: {}", e);
56        }
57    }
58}
59
60fn run_todo_tui_internal(config: &mut YamlConfig) -> io::Result<()> {
61    terminal::enable_raw_mode()?;
62    let mut stdout = io::stdout();
63    execute!(stdout, EnterAlternateScreen)?;
64
65    let backend = CrosstermBackend::new(stdout);
66    let mut terminal = Terminal::new(backend)?;
67
68    let mut app = TodoApp::new();
69    let mut last_input_len: usize = 0;
70
71    loop {
72        terminal.draw(|f| draw_ui(f, &mut app))?;
73
74        let current_input_len = app.input.chars().count();
75        if current_input_len != last_input_len {
76            app.preview_scroll = 0;
77            last_input_len = current_input_len;
78        }
79
80        if event::poll(std::time::Duration::from_millis(100))? {
81            if let Event::Key(key) = event::read()? {
82                // Alt+↑/↓ 预览区滚动(在 Adding/Editing 模式下)
83                if (app.mode == AppMode::Adding || app.mode == AppMode::Editing)
84                    && key.modifiers.contains(KeyModifiers::ALT)
85                {
86                    match key.code {
87                        KeyCode::Down => {
88                            app.preview_scroll = app.preview_scroll.saturating_add(1);
89                            continue;
90                        }
91                        KeyCode::Up => {
92                            app.preview_scroll = app.preview_scroll.saturating_sub(1);
93                            continue;
94                        }
95                        _ => {}
96                    }
97                }
98
99                match app.mode {
100                    AppMode::Normal => {
101                        if handle_normal_mode(&mut app, key) {
102                            break;
103                        }
104                    }
105                    AppMode::Adding => handle_input_mode(&mut app, key),
106                    AppMode::Editing => handle_input_mode(&mut app, key),
107                    AppMode::ConfirmDelete => handle_confirm_delete(&mut app, key),
108                    AppMode::ConfirmReport => handle_confirm_report(&mut app, key, config),
109                    AppMode::Help => handle_help_mode(&mut app, key),
110                }
111            }
112        }
113    }
114
115    terminal::disable_raw_mode()?;
116    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
117
118    Ok(())
119}