vtcode-tui 0.99.1

Reusable TUI primitives and session API for VT Code-style terminal interfaces
use anyhow::Result;
use ratatui::crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers};

use super::SelectionInterrupted;

pub(super) enum SelectionAction {
    Continue,
    Select,
    Cancel,
}

pub(super) fn handle_event(
    event: Event,
    total: usize,
    selected_index: &mut usize,
    number_buffer: &mut String,
) -> Result<SelectionAction> {
    match event {
        Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
            KeyCode::Up | KeyCode::Char('k') => {
                if *selected_index == 0 {
                    *selected_index = total - 1;
                } else {
                    *selected_index -= 1;
                }
                number_buffer.clear();
            }
            KeyCode::Down | KeyCode::Char('j') => {
                *selected_index = (*selected_index + 1) % total;
                number_buffer.clear();
            }
            KeyCode::Home => {
                *selected_index = 0;
                number_buffer.clear();
            }
            KeyCode::End => {
                *selected_index = total - 1;
                number_buffer.clear();
            }
            KeyCode::PageUp => {
                let step = 5.min(total - 1);
                if *selected_index < step {
                    *selected_index = 0;
                } else {
                    *selected_index -= step;
                }
                number_buffer.clear();
            }
            KeyCode::PageDown => {
                let step = 5.min(total - 1);
                *selected_index = (*selected_index + step).min(total - 1);
                number_buffer.clear();
            }
            KeyCode::Enter | KeyCode::Tab => return Ok(SelectionAction::Select),
            KeyCode::Esc => return Ok(SelectionAction::Cancel),
            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                return Err(SelectionInterrupted.into());
            }
            KeyCode::Char(c) if c.is_ascii_digit() => {
                number_buffer.push(c);
                if let Ok(index) = number_buffer.parse::<usize>()
                    && (1..=total).contains(&index)
                {
                    *selected_index = index - 1;
                }
                if number_buffer.len() >= total.to_string().len() {
                    number_buffer.clear();
                }
            }
            KeyCode::Backspace => {
                number_buffer.pop();
            }
            _ => {}
        },
        Event::Resize(_, _) => {
            number_buffer.clear();
        }
        _ => {}
    }

    Ok(SelectionAction::Continue)
}