@@ -42,13 +42,20 @@ pub struct App {
4242impl App {
4343 /// Create a new application instance with the given theme
4444 pub fn new ( theme : Theme ) -> Self {
45+ let settings_manager = SettingsManager :: new ( ) ;
46+ let initial_state = if settings_manager. get ( ) . has_valid_provider ( ) {
47+ AppState :: Main
48+ } else {
49+ AppState :: WaitingForConfig
50+ } ;
51+
4552 Self {
46- state : AppState :: Main ,
53+ state : initial_state ,
4754 previous_state : AppState :: Main ,
4855 theme,
4956 layout : AppLayout :: new ( ) . expect ( "Failed to create layout" ) ,
5057 event_handler : EventHandler :: default ( ) ,
51- settings : SettingsManager :: new ( ) ,
58+ settings : settings_manager ,
5259 modal_state : None ,
5360 last_size : None ,
5461 }
@@ -132,19 +139,22 @@ impl App {
132139 self . state = AppState :: Quitting ;
133140 }
134141 AppEvent :: OpenSettings => {
142+ // Settings can be opened from any state
135143 self . enter_settings ( ) ;
136144 }
137145 AppEvent :: CloseSettings => {
138146 // Only close settings if we're in settings mode
139147 if matches ! ( self . state, AppState :: Settings ) {
140148 self . exit_settings ( ) ;
149+ // After closing settings, check provider readiness
150+ self . check_provider_readiness ( ) ;
141151 } else {
142152 // If not in settings, ESC means quit
143153 self . state = AppState :: Quitting ;
144154 }
145155 }
146156 AppEvent :: NavigateUp => {
147- // Only handle navigation in settings modal
157+ // Only handle navigation in settings modal and main state
148158 if matches ! ( self . state, AppState :: Settings )
149159 && let Some ( ref mut modal_state) = self . modal_state
150160 {
@@ -153,9 +163,10 @@ impl App {
153163 let selected_theme = modal_state. selected_theme ( ) ;
154164 self . theme . set_variant ( selected_theme) ;
155165 }
166+ // In WaitingForConfig state, navigation is ignored
156167 }
157168 AppEvent :: NavigateDown => {
158- // Only handle navigation in settings modal
169+ // Only handle navigation in settings modal and main state
159170 if matches ! ( self . state, AppState :: Settings )
160171 && let Some ( ref mut modal_state) = self . modal_state
161172 {
@@ -164,6 +175,7 @@ impl App {
164175 let selected_theme = modal_state. selected_theme ( ) ;
165176 self . theme . set_variant ( selected_theme) ;
166177 }
178+ // In WaitingForConfig state, navigation is ignored
167179 }
168180 AppEvent :: Select => {
169181 // Only handle selection in settings modal
@@ -177,12 +189,18 @@ impl App {
177189 }
178190 // Close modal after selection
179191 self . exit_settings ( ) ;
192+ // Check provider readiness after theme change
193+ self . check_provider_readiness ( ) ;
180194 }
195+ // In WaitingForConfig state, selection is ignored
181196 }
182197 AppEvent :: SettingsAction ( action) => {
183198 // Handle settings actions and apply theme changes immediately
184199 if let Err ( e) = self . handle_settings_action ( action) {
185200 self . state = AppState :: Error ( format ! ( "Settings error: {}" , e) ) ;
201+ } else {
202+ // After any settings action, check provider readiness
203+ self . check_provider_readiness ( ) ;
186204 }
187205 }
188206 AppEvent :: Resize ( width, height) => {
@@ -340,6 +358,76 @@ impl App {
340358 AppState :: Error ( error) => {
341359 format ! ( "Application Error\n \n An error occurred:\n {}\n \n Press ESC or q to quit." , error)
342360 }
361+ AppState :: WaitingForConfig => {
362+ let provider_status = self . settings ( ) . get_provider_status_summary ( ) ;
363+ let available_providers = self . settings ( ) . get_available_providers ( ) ;
364+
365+ let status_display = if provider_status. is_empty ( ) {
366+ " No providers configured yet" . to_string ( )
367+ } else {
368+ provider_status. iter ( )
369+ . map ( |( provider, status, _) | {
370+ let status_icon = match status {
371+ crate :: settings:: ValidationStatus :: Valid => "✅" ,
372+ crate :: settings:: ValidationStatus :: Invalid => "❌" ,
373+ crate :: settings:: ValidationStatus :: Checking => "⏳" ,
374+ crate :: settings:: ValidationStatus :: Unchecked => "⚪" ,
375+ } ;
376+ format ! ( " {} {}" , status_icon, provider)
377+ } )
378+ . collect :: < Vec < _ > > ( )
379+ . join ( "\n " )
380+ } ;
381+
382+ format ! ( r#"
383+
384+ ╔═══════════════════════════════════════════════════════════════╗
385+ ║ ║
386+ ║ ⚙️ PROVIDER CONFIGURATION REQUIRED ⚙️ ║
387+ ║ ║
388+ ╚═══════════════════════════════════════════════════════════════╝
389+
390+ Welcome to Agentic! Before you can start using the AI orchestration
391+ features, you need to configure at least one AI provider.
392+
393+ PROVIDER STATUS
394+ ══════════════════════════════════════════════════════════════════
395+
396+ {}
397+
398+ AVAILABLE PROVIDERS
399+ ══════════════════════════════════════════════════════════════════
400+
401+ {}
402+
403+ CONFIGURATION STEPS
404+ ══════════════════════════════════════════════════════════════════
405+
406+ 1. Press ',' to open the Settings panel
407+ 2. Navigate to Provider Configuration
408+ 3. Add your API keys for one or more providers
409+ 4. Test the configuration
410+ 5. Return here to start using Agentic
411+
412+ KEY BINDINGS
413+ ══════════════════════════════════════════════════════════════════
414+
415+ , (comma) - Open Settings Panel
416+ ESC / q - Quit Application
417+ T - Toggle Theme (Dark/Light)
418+
419+ "# ,
420+ status_display,
421+ if available_providers. is_empty( ) {
422+ " No providers configured yet" . to_string( )
423+ } else {
424+ available_providers. iter( )
425+ . map( |p| format!( " • {}" , p) )
426+ . collect:: <Vec <_>>( )
427+ . join( "\n " )
428+ }
429+ )
430+ }
343431 } ;
344432
345433 let main_block = Block :: default ( )
@@ -381,6 +469,10 @@ impl App {
381469 ) ,
382470 AppState :: Quitting => "Application shutting down gracefully..." . to_string ( ) ,
383471 AppState :: Error ( _) => "Error state - Press ESC/q to quit" . to_string ( ) ,
472+ AppState :: WaitingForConfig => format ! (
473+ ", (comma): Settings | ESC/q: Quit | T: Theme Toggle | Current: [{}] | Configure Provider Required" ,
474+ current_theme
475+ ) ,
384476 } ;
385477
386478 let paragraph = Paragraph :: new ( footer_text)
@@ -438,4 +530,26 @@ impl App {
438530 self . settings . reset_to_defaults ( ) ;
439531 self . settings . get ( ) . apply_theme ( & mut self . theme ) ;
440532 }
533+
534+ /// Check provider readiness and update app state accordingly
535+ pub fn check_provider_readiness ( & mut self ) {
536+ if !self . settings . get ( ) . has_valid_provider ( ) {
537+ if self . state == AppState :: Main {
538+ self . state = AppState :: WaitingForConfig ;
539+ }
540+ } else {
541+ if self . state == AppState :: WaitingForConfig {
542+ self . state = AppState :: Main ;
543+ }
544+ }
545+ }
546+
547+ /// Handle validation event results and update provider status
548+ pub fn update_provider_status ( & mut self , validation_event : crate :: settings:: ValidationEvent ) {
549+ // Update the provider status through settings
550+ self . settings . get_mut ( ) . handle_validation_event ( validation_event) ;
551+
552+ // Check if we need to change app state
553+ self . check_provider_readiness ( ) ;
554+ }
441555}
0 commit comments