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