feat: implement interactive lesson management and update command handling
This commit is contained in:
@@ -1,20 +1,13 @@
|
||||
//! Slash-command parser that maps TUI `/foo` input lines into `Command`
|
||||
//! variants for the action dispatch system.
|
||||
|
||||
use crate::app::mode::ModeKind;
|
||||
|
||||
/// A parsed slash command from the TUI input buffer.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Command {
|
||||
Help,
|
||||
Quit,
|
||||
LessonCreate(String),
|
||||
LessonExport(String),
|
||||
LessonImport(String),
|
||||
LessonAccept(String),
|
||||
LessonReject(String),
|
||||
LessonList,
|
||||
Mode(ModeKind),
|
||||
LessonInteractive,
|
||||
McpOpen,
|
||||
Clear,
|
||||
ClearConfirm,
|
||||
Login { provider: String },
|
||||
@@ -54,31 +47,13 @@ pub fn parse_command(text: &str) -> Command {
|
||||
"/quit" => Command::Quit,
|
||||
"/clear" if arg1.is_empty() => Command::ClearConfirm,
|
||||
"/clear" => Command::Clear,
|
||||
"/mode" if arg1.is_empty() => Command::Unknown("/mode requires a subcommand: chat, bash, help, settings".to_string()),
|
||||
"/mode" => {
|
||||
let mode = match arg1 {
|
||||
"chat" | "c" => ModeKind::Chat,
|
||||
"bash" | "b" => ModeKind::Bash,
|
||||
"workflow" | "w" => ModeKind::Workflow,
|
||||
"help" | "h" => ModeKind::Help,
|
||||
"settings" | "s" => ModeKind::Settings,
|
||||
_ => return Command::Unknown(format!("unknown mode: {}", arg1)),
|
||||
};
|
||||
Command::Mode(mode)
|
||||
}
|
||||
"/lesson" if arg1 == "export" && !arg2.is_empty() => Command::LessonExport(arg2.to_string()),
|
||||
"/lesson" if arg1 == "import" && !arg2.is_empty() => Command::LessonImport(arg2.to_string()),
|
||||
"/lesson" if arg1 == "list" || arg1 == "ls" => Command::LessonList,
|
||||
"/lesson" if arg1 == "accept" && !arg2.is_empty() => Command::LessonAccept(arg2.to_string()),
|
||||
"/lesson" if arg1 == "reject" && !arg2.is_empty() => Command::LessonReject(arg2.to_string()),
|
||||
"/lesson" if !arg1.is_empty() => Command::LessonCreate(arg1.to_string()),
|
||||
"/lesson" => Command::LessonList,
|
||||
"/lesson" => Command::LessonInteractive,
|
||||
"/login" if arg1.is_empty() => Command::Login { provider: String::new() },
|
||||
"/login" if !arg1.is_empty() => Command::Login { provider: arg1.to_string() },
|
||||
"/edit" if !arg1.is_empty() => Command::Edit(arg1.to_string()),
|
||||
"/edit" => Command::Edit(".".to_string()),
|
||||
"/mcp" if arg1.is_empty() => {
|
||||
Command::Mode(ModeKind::Mcp)
|
||||
Command::McpOpen
|
||||
}
|
||||
"/mcp" if arg1 == "add" && !arg2.is_empty() => {
|
||||
let rest = arg2.trim();
|
||||
|
||||
@@ -69,6 +69,70 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
||||
}
|
||||
}
|
||||
|
||||
if state.misc.overlay == Overlay::Learning {
|
||||
match key.code {
|
||||
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
return vec![Action::QuitConfirm];
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
return vec![Action::CloseOverlay];
|
||||
}
|
||||
KeyCode::Up => {
|
||||
let items = crate::app::mode::learning::get_learning_items(state);
|
||||
let n = items.len();
|
||||
state.misc.selected_index = if state.misc.selected_index == 0 { n.saturating_sub(1) } else { state.misc.selected_index - 1 };
|
||||
state.dirty = true;
|
||||
return vec![];
|
||||
}
|
||||
KeyCode::Down => {
|
||||
let items = crate::app::mode::learning::get_learning_items(state);
|
||||
let n = items.len();
|
||||
state.misc.selected_index = if n == 0 { 0 } else { (state.misc.selected_index + 1) % n };
|
||||
state.dirty = true;
|
||||
return vec![];
|
||||
}
|
||||
KeyCode::Enter | KeyCode::Char('a') => {
|
||||
let items = crate::app::mode::learning::get_learning_items(state);
|
||||
if let Some(item) = items.get(state.misc.selected_index) {
|
||||
match item {
|
||||
crate::app::mode::learning::LearningItem::Pending { name, .. } => {
|
||||
return vec![Action::LessonAccept { name: name.clone() }];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return vec![];
|
||||
}
|
||||
KeyCode::Char('r') => {
|
||||
let items = crate::app::mode::learning::get_learning_items(state);
|
||||
if let Some(item) = items.get(state.misc.selected_index) {
|
||||
match item {
|
||||
crate::app::mode::learning::LearningItem::Pending { name, .. } => {
|
||||
return vec![Action::LessonReject { name: name.clone() }];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return vec![];
|
||||
}
|
||||
KeyCode::Char('d') | KeyCode::Delete | KeyCode::Backspace => {
|
||||
let items = crate::app::mode::learning::get_learning_items(state);
|
||||
if let Some(item) = items.get(state.misc.selected_index) {
|
||||
match item {
|
||||
crate::app::mode::learning::LearningItem::Pending { name, .. } => {
|
||||
return vec![Action::LessonReject { name: name.clone() }];
|
||||
}
|
||||
crate::app::mode::learning::LearningItem::Stored { name, .. } => {
|
||||
return vec![Action::LessonDelete { name: name.clone() }];
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec![];
|
||||
}
|
||||
_ => return vec![],
|
||||
}
|
||||
}
|
||||
|
||||
match key.code {
|
||||
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
vec![Action::QuitConfirm]
|
||||
|
||||
Reference in New Issue
Block a user