11use super :: {
2- chat:: render_chat,
2+ chat:: { render_chat, AutocompleteParams } ,
33 footer:: render_footer,
44 header:: render_header,
55 model_selection_modal:: { render_model_selection_modal, ModelSelectionParams } ,
@@ -127,6 +127,8 @@ pub struct App {
127127 coaching_tip : ( String , String ) ,
128128 local_tokens_used : u32 , // Token count for current local request
129129 cloud_tokens_used : u32 , // Token count for current cloud request
130+ show_autocomplete : bool ,
131+ autocomplete_index : usize ,
130132}
131133
132134impl App {
@@ -158,6 +160,8 @@ impl App {
158160 coaching_tip : ( String :: new ( ) , String :: new ( ) ) ,
159161 local_tokens_used : 0 ,
160162 cloud_tokens_used : 0 ,
163+ show_autocomplete : false ,
164+ autocomplete_index : 0 ,
161165 }
162166 }
163167
@@ -510,6 +514,11 @@ impl App {
510514 self . mode ,
511515 & self . edit_buffer ,
512516 self . agent_status ,
517+ AutocompleteParams {
518+ show : self . show_autocomplete ,
519+ commands : & self . get_filtered_slash_commands ( ) ,
520+ selected_index : self . autocomplete_index ,
521+ } ,
513522 ) ;
514523 }
515524 } ) ?;
@@ -711,11 +720,18 @@ impl App {
711720 self . attempt_start ( ) ;
712721 }
713722 }
714- // TODO: Handle 'a' for About mode
723+ KeyCode :: Char ( 'a' ) => {
724+ // Show About modal - same as /about command
725+ self . coaching_tip = (
726+ "About RuixenOS v0.1.0" . to_string ( ) ,
727+ "🎯 The Curiosity Machine\n Transforming queries into thoughtful Ruixen inquiries since 2025.\n Built with Rust, ratatui, and endless wonder." . to_string ( ) ,
728+ ) ;
729+ self . mode = AppMode :: CoachingTip ;
730+ }
715731 _ => { }
716732 } ,
717733 AppMode :: Settings => match key. code {
718- KeyCode :: Char ( 'r' ) => self . mode = AppMode :: Normal ,
734+ KeyCode :: Esc => self . mode = AppMode :: Normal ,
719735 KeyCode :: Char ( 's' ) => {
720736 if let Err ( e) = self . settings . save ( ) {
721737 eprintln ! ( "Warning: Failed to save settings: {}" , e) ;
@@ -863,15 +879,49 @@ impl App {
863879 // Return to Normal mode
864880 self . mode = AppMode :: Normal ;
865881 self . edit_buffer . clear ( ) ;
882+ self . show_autocomplete = false ;
866883 }
867884 KeyCode :: Enter => {
868- // Process chat message
869- if !self . edit_buffer . is_empty ( ) {
885+ if self . show_autocomplete
886+ && !self . get_filtered_slash_commands ( ) . is_empty ( )
887+ {
888+ // Apply selected autocomplete suggestion
889+ let filtered = self . get_filtered_slash_commands ( ) ;
890+ let selected_command = & filtered[ self . autocomplete_index ] . 0 ;
891+ self . edit_buffer = selected_command. clone ( ) ;
892+ self . show_autocomplete = false ;
893+ } else if !self . edit_buffer . is_empty ( ) {
870894 self . handle_chat_message ( ) ;
895+ self . show_autocomplete = false ;
896+ }
897+ }
898+ KeyCode :: Tab => {
899+ if self . show_autocomplete
900+ && !self . get_filtered_slash_commands ( ) . is_empty ( )
901+ {
902+ // Apply selected autocomplete suggestion
903+ let filtered = self . get_filtered_slash_commands ( ) ;
904+ let selected_command = & filtered[ self . autocomplete_index ] . 0 ;
905+ self . edit_buffer = selected_command. clone ( ) ;
906+ self . show_autocomplete = false ;
907+ }
908+ }
909+ KeyCode :: Up if self . show_autocomplete => {
910+ if self . autocomplete_index > 0 {
911+ self . autocomplete_index -= 1 ;
912+ }
913+ }
914+ KeyCode :: Down if self . show_autocomplete => {
915+ let filtered_commands = self . get_filtered_slash_commands ( ) ;
916+ if self . autocomplete_index
917+ < filtered_commands. len ( ) . saturating_sub ( 1 )
918+ {
919+ self . autocomplete_index += 1 ;
871920 }
872921 }
873922 KeyCode :: Backspace => {
874923 self . edit_buffer . pop ( ) ;
924+ self . update_autocomplete ( ) ;
875925 }
876926 KeyCode :: Char ( 'v' ) if key. modifiers . contains ( KeyModifiers :: CONTROL ) => {
877927 // Ctrl+V: Allow pasting in chat
@@ -881,6 +931,7 @@ impl App {
881931 }
882932 KeyCode :: Char ( c) => {
883933 self . edit_buffer . push ( c) ;
934+ self . update_autocomplete ( ) ;
884935 }
885936 _ => { }
886937 } ,
@@ -1000,8 +1051,13 @@ impl App {
10001051 } ,
10011052 AppMode :: CoachingTip => match key. code {
10021053 KeyCode :: Enter | KeyCode :: Esc => {
1003- // Return to chat mode to try again
1004- self . mode = AppMode :: Chat ;
1054+ // About modal should return to main menu, errors return to chat
1055+ if self . coaching_tip . 0 . contains ( "About RuixenOS" ) {
1056+ self . mode = AppMode :: Normal ;
1057+ } else {
1058+ // Error messages return to chat to try again
1059+ self . mode = AppMode :: Chat ;
1060+ }
10051061 }
10061062 _ => { }
10071063 } ,
@@ -1152,20 +1208,54 @@ impl App {
11521208 "/quit" | "/exit" => {
11531209 self . should_quit = true ;
11541210 }
1155- "/theme" => {
1156- self . theme . toggle ( ) ;
1157- self . settings . theme = self . theme . variant ( ) ;
1158- if let Err ( e) = self . settings . save ( ) {
1159- eprintln ! ( "Warning: Failed to save settings: {}" , e) ;
1160- }
1161- }
11621211 _ => {
11631212 // Unknown command - could show help message or ignore
1164- println ! ( "Unknown command: {}" , command) ;
1213+ self . coaching_tip = (
1214+ "Unknown Command" . to_string ( ) ,
1215+ format ! (
1216+ "Command '{}' not recognized. Try /settings or /quit" ,
1217+ command
1218+ ) ,
1219+ ) ;
1220+ self . mode = AppMode :: CoachingTip ;
11651221 }
11661222 }
11671223 }
11681224
1225+ fn update_autocomplete ( & mut self ) {
1226+ if self . edit_buffer . starts_with ( '/' ) {
1227+ let filtered = self . get_filtered_slash_commands ( ) ;
1228+ self . show_autocomplete = !filtered. is_empty ( ) ;
1229+ self . autocomplete_index = 0 ; // Reset selection to top
1230+ } else {
1231+ self . show_autocomplete = false ;
1232+ }
1233+ }
1234+
1235+ fn get_filtered_slash_commands ( & self ) -> Vec < ( String , String ) > {
1236+ // Only 2 slash commands - About is main menu only
1237+ let available_commands = vec ! [
1238+ (
1239+ "/settings" . to_string( ) ,
1240+ "Configure app settings" . to_string( ) ,
1241+ ) ,
1242+ ( "/quit" . to_string( ) , "Exit the application" . to_string( ) ) ,
1243+ ] ;
1244+
1245+ if self . edit_buffer == "/" {
1246+ // Show all commands when just "/" is typed
1247+ available_commands
1248+ } else if self . edit_buffer . starts_with ( '/' ) {
1249+ // Filter commands based on what's typed
1250+ available_commands
1251+ . into_iter ( )
1252+ . filter ( |( cmd, _) | cmd. starts_with ( & self . edit_buffer ) )
1253+ . collect ( )
1254+ } else {
1255+ vec ! [ ]
1256+ }
1257+ }
1258+
11691259 fn start_editing_current_selection ( & mut self ) {
11701260 match self . settings_selection {
11711261 SettingsSelection :: Endpoint => {
0 commit comments