use super::state::{SkillsDialogState, matching};
use crate::brain::skills::Skill;
use crate::tui::app::App;
use crate::tui::events::AppMode;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyOutcome {
Consumed,
Close,
Execute(String),
NotConsumed,
}
pub async fn handle_key(app: &mut App, key: KeyEvent) {
let skills = app.skills.clone();
match decide(&mut app.skills_dialog, &skills, key) {
KeyOutcome::Consumed | KeyOutcome::NotConsumed => {}
KeyOutcome::Close => {
app.mode = AppMode::Chat;
}
KeyOutcome::Execute(body) => {
app.mode = AppMode::Chat;
super::actions::execute(app, body);
}
}
}
pub fn decide(state: &mut SkillsDialogState, skills: &[Skill], key: KeyEvent) -> KeyOutcome {
match key.code {
KeyCode::Esc => KeyOutcome::Close,
KeyCode::Enter => {
let visible = matching(skills, &state.filter);
match visible.get(state.selected_index) {
Some(s) => KeyOutcome::Execute(s.body.clone()),
None => KeyOutcome::Consumed,
}
}
KeyCode::Tab | KeyCode::Down => {
move_selection(state, skills, 1);
KeyOutcome::Consumed
}
KeyCode::BackTab | KeyCode::Up => {
move_selection(state, skills, -1);
KeyOutcome::Consumed
}
KeyCode::Backspace => {
state.filter.pop();
state.selected_index = 0;
KeyOutcome::Consumed
}
KeyCode::Char(c) => {
if key.modifiers.contains(KeyModifiers::CONTROL) {
return KeyOutcome::NotConsumed;
}
state.filter.push(c);
state.selected_index = 0;
KeyOutcome::Consumed
}
_ => KeyOutcome::NotConsumed,
}
}
fn move_selection(state: &mut SkillsDialogState, skills: &[Skill], delta: i32) {
let visible = matching(skills, &state.filter);
let count = visible.len();
if count == 0 {
state.selected_index = 0;
return;
}
let count_i = count as i32;
let cur = state.selected_index.min(count - 1) as i32;
let next = ((cur + delta) % count_i + count_i) % count_i;
state.selected_index = next as usize;
}