Skip to content

Commit 17d3d68

Browse files
Merge pull request #41 from gitcoder89431/33-implement-query-gating-for-provider-validation
feat: Enhance provider configuration handling and UI feedback
2 parents 2cec2df + 69a26da commit 17d3d68

File tree

3 files changed

+173
-5
lines changed

3 files changed

+173
-5
lines changed

src/events.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl EventHandler {
5353
match key_event.code {
5454
KeyCode::Char('q') => Ok(AppEvent::Quit),
5555
KeyCode::Esc => Ok(AppEvent::CloseSettings),
56-
KeyCode::Char('s') | KeyCode::Char('S') => Ok(AppEvent::OpenSettings),
56+
KeyCode::Char(',') | KeyCode::Char('s') | KeyCode::Char('S') => Ok(AppEvent::OpenSettings),
5757
KeyCode::Up | KeyCode::Char('k') => Ok(AppEvent::NavigateUp),
5858
KeyCode::Down | KeyCode::Char('j') => Ok(AppEvent::NavigateDown),
5959
KeyCode::Enter | KeyCode::Char(' ') => Ok(AppEvent::Select),
@@ -87,6 +87,8 @@ pub enum AppState {
8787
Main,
8888
/// Settings modal active
8989
Settings,
90+
/// Waiting for provider configuration - no valid providers available
91+
WaitingForConfig,
9092
/// Application is shutting down gracefully
9193
Quitting,
9294
/// Application encountered an error

src/settings.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ pub enum ProviderType {
2121
OpenRouter,
2222
}
2323

24+
impl std::fmt::Display for ProviderType {
25+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26+
match self {
27+
ProviderType::Local => write!(f, "Local Provider"),
28+
ProviderType::OpenRouter => write!(f, "OpenRouter"),
29+
}
30+
}
31+
}
32+
2433
/// Provider configuration with validation status
2534
#[derive(Debug, Clone)]
2635
pub struct ProviderConfig {
@@ -1120,6 +1129,49 @@ impl Settings {
11201129
},
11211130
}
11221131
}
1132+
1133+
/// Check if at least one provider is valid and ready for queries
1134+
pub fn has_valid_provider(&self) -> bool {
1135+
self.local_provider.validation_status == ValidationStatus::Valid
1136+
|| self.openrouter_provider.validation_status == ValidationStatus::Valid
1137+
}
1138+
1139+
/// Get list of available (valid) providers
1140+
pub fn get_available_providers(&self) -> Vec<ProviderType> {
1141+
let mut providers = Vec::new();
1142+
1143+
if self.local_provider.validation_status == ValidationStatus::Valid {
1144+
providers.push(ProviderType::Local);
1145+
}
1146+
1147+
if self.openrouter_provider.validation_status == ValidationStatus::Valid {
1148+
providers.push(ProviderType::OpenRouter);
1149+
}
1150+
1151+
providers
1152+
}
1153+
1154+
/// Get provider configuration status for UI display
1155+
pub fn get_provider_status_summary(&self) -> Vec<(ProviderType, ValidationStatus, String)> {
1156+
vec![
1157+
(
1158+
ProviderType::Local,
1159+
self.local_provider.validation_status.clone(),
1160+
match &self.local_provider.endpoint_url {
1161+
Some(url) => url.clone(),
1162+
None => "Not configured".to_string(),
1163+
}
1164+
),
1165+
(
1166+
ProviderType::OpenRouter,
1167+
self.openrouter_provider.validation_status.clone(),
1168+
match &self.openrouter_provider.api_key {
1169+
Some(_) => "API configured".to_string(),
1170+
None => "Not configured".to_string(),
1171+
}
1172+
),
1173+
]
1174+
}
11231175
}
11241176

11251177
impl Default for Settings {

src/ui/app.rs

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,20 @@ pub struct App {
4242
impl 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\nAn error occurred:\n{}\n\nPress 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

Comments
 (0)