koda_cli/widgets/
slash_menu.rs1use super::dropdown::{self, DropdownItem, DropdownState};
7use ratatui::text::Line;
8
9#[derive(Clone, Debug)]
11pub struct SlashCommand {
12 pub command: &'static str,
13 pub description: &'static str,
14 pub arg_hint: Option<&'static str>,
17}
18
19impl DropdownItem for SlashCommand {
20 fn label(&self) -> &str {
21 self.command
22 }
23 fn description(&self) -> String {
24 self.description.to_string()
25 }
26 fn matches_filter(&self, filter: &str) -> bool {
27 self.command.starts_with(filter)
28 }
29}
30
31pub fn from_input(
34 commands: &'static [(&'static str, &'static str, Option<&'static str>)],
35 input: &str,
36) -> Option<DropdownState<SlashCommand>> {
37 let items: Vec<SlashCommand> = commands
38 .iter()
39 .map(|(cmd, desc, arg_hint)| SlashCommand {
40 command: cmd,
41 description: desc,
42 arg_hint: *arg_hint,
43 })
44 .collect();
45 let mut dd = DropdownState::new(items, "\u{1f43b} Commands");
46 if dd.apply_filter(input) {
47 Some(dd)
48 } else {
49 None
50 }
51}
52
53pub fn build_menu_lines(state: &DropdownState<SlashCommand>) -> Vec<Line<'static>> {
55 dropdown::build_dropdown_lines(state)
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 const TEST_COMMANDS: &[(&str, &str, Option<&str>)] = &[
63 ("/agent", "Agents", Some("<name>")),
64 ("/compact", "Compact", None),
65 ("/diff", "Diff", None),
66 ("/exit", "Quit", None),
67 ("/expand", "Expand", None),
68 ("/model", "Pick model", None),
69 ];
70
71 #[test]
72 fn from_input_all() {
73 let state = from_input(TEST_COMMANDS, "/").unwrap();
74 assert_eq!(state.filtered.len(), 6);
75 }
76
77 #[test]
78 fn from_input_filtered() {
79 let state = from_input(TEST_COMMANDS, "/m").unwrap();
80 assert_eq!(state.filtered.len(), 1);
81 assert_eq!(state.filtered[0].command, "/model");
82 }
83
84 #[test]
85 fn from_input_no_match() {
86 assert!(from_input(TEST_COMMANDS, "/z").is_none());
87 }
88
89 #[test]
90 fn selected_command() {
91 let state = from_input(TEST_COMMANDS, "/").unwrap();
92 assert_eq!(state.selected_item().unwrap().command, "/agent");
93 }
94}