diff --git a/src/cortex-tui/src/app/state.rs b/src/cortex-tui/src/app/state.rs index 4a4d0b71..c9071d91 100644 --- a/src/cortex-tui/src/app/state.rs +++ b/src/cortex-tui/src/app/state.rs @@ -11,7 +11,7 @@ use crate::permissions::PermissionMode; use crate::question::QuestionState; use crate::selection::TextSelection; use crate::views::tool_call::{ContentSegment, ToolCallDisplay}; -use crate::widgets::ToastManager; +use crate::widgets::{ToastManager, ToastPosition}; use super::approval::{ApprovalState, PendingToolResult}; use super::autocomplete::AutocompleteState; @@ -209,7 +209,7 @@ impl AppState { // provider_picker removed: provider is now always "cortex" model_picker: crate::widgets::ModelPickerState::new(), text_selection: TextSelection::new(), - toasts: ToastManager::new(), + toasts: ToastManager::new().with_position(ToastPosition::BottomLeft), permission_mode: PermissionMode::default(), tool_calls: Vec::new(), pending_tool_results: Vec::new(), diff --git a/src/cortex-tui/src/commands/registry/builtin.rs b/src/cortex-tui/src/commands/registry/builtin.rs index 88ecfb58..99db09b4 100644 --- a/src/cortex-tui/src/commands/registry/builtin.rs +++ b/src/cortex-tui/src/commands/registry/builtin.rs @@ -550,25 +550,7 @@ pub fn register_builtin_commands(registry: &mut CommandRegistry) { true, )); - // NOTE: /provider and /providers are deprecated. Cortex is now a unified platform. - // These commands are hidden from help but still work (with deprecation warning). - registry.register(CommandDef::hidden( - "provider", - &["prov"], - "[Deprecated] Switch provider - Cortex is now a unified platform", - "/provider ", - CommandCategory::Model, - true, - )); - registry.register(CommandDef::hidden( - "providers", - &["lp", "list-providers"], - "[Deprecated] List available providers - Cortex is now a unified platform", - "/providers", - CommandCategory::Model, - false, - )); registry.register(CommandDef::new( "temperature", @@ -601,44 +583,7 @@ pub fn register_builtin_commands(registry: &mut CommandRegistry) { false, )); - // NOTE: /mcp-tools, /mcp-auth, /mcp-reload, /mcp-logs are deprecated. - // All MCP management is now centralized in the interactive /mcp panel. - // These commands are hidden but still work for backwards compatibility. - registry.register(CommandDef::hidden( - "mcp-tools", - &["tools", "lt"], - "[Deprecated] Use /mcp instead - List MCP tools", - "/mcp-tools [server]", - CommandCategory::Mcp, - true, - )); - - registry.register(CommandDef::hidden( - "mcp-auth", - &["auth"], - "[Deprecated] Use /mcp instead - Authenticate MCP server", - "/mcp-auth ", - CommandCategory::Mcp, - true, - )); - - registry.register(CommandDef::hidden( - "mcp-reload", - &["reload"], - "[Deprecated] Use /mcp instead - Reload MCP servers", - "/mcp-reload [server]", - CommandCategory::Mcp, - true, - )); - registry.register(CommandDef::hidden( - "mcp-logs", - &[], - "[Deprecated] Use /mcp instead - View MCP server logs", - "/mcp-logs [server]", - CommandCategory::Mcp, - true, - )); // ======================================== // DEBUG COMMANDS @@ -698,23 +643,7 @@ pub fn register_builtin_commands(registry: &mut CommandRegistry) { false, )); - registry.register(CommandDef::hidden( - "crash", - &[], - "Test crash handler", - "/crash", - CommandCategory::Debug, - false, - )); - registry.register(CommandDef::hidden( - "eval", - &[], - "Evaluate expression", - "/eval ", - CommandCategory::Debug, - true, - )); // ======================================== // DEVELOPMENT & TOOLS COMMANDS diff --git a/src/cortex-tui/src/modal/alert.rs b/src/cortex-tui/src/modal/alert.rs deleted file mode 100644 index ce760212..00000000 --- a/src/cortex-tui/src/modal/alert.rs +++ /dev/null @@ -1,224 +0,0 @@ -//! Alert Modals for Authentication -//! -//! Provides interactive alerts when authentication is required, -//! guiding users to authenticate via `cortex login`. - -use super::{Modal, ModalAction, ModalResult}; -use crate::ui::text_utils::truncate_with_ellipsis; -use crate::widgets::ActionBar; -use cortex_core::style::{CYAN_PRIMARY, SURFACE_1, TEXT, TEXT_DIM, VOID, WARNING}; -use crossterm::event::{KeyCode, KeyEvent}; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Layout, Rect}, - style::Style, - widgets::{Block, Borders, Clear, Widget}, -}; - -// ============================================================================ -// AUTHENTICATION ALERT MODAL -// ============================================================================ - -/// Alert shown when authentication is required -pub struct AuthAlertModal { - selected_option: usize, // 0=OK, 1=Cancel -} - -impl Default for AuthAlertModal { - fn default() -> Self { - Self::new() - } -} - -impl AuthAlertModal { - pub fn new() -> Self { - Self { selected_option: 0 } - } -} - -impl Modal for AuthAlertModal { - fn title(&self) -> &str { - "Authentication Required" - } - - fn desired_height(&self, _max_height: u16, _width: u16) -> u16 { - 10 // Fixed height for alert - } - - fn render(&self, area: Rect, buf: &mut Buffer) { - // Clear and draw border - Clear.render(area, buf); - - let block = Block::default() - .title(" Authentication Required ") - .borders(Borders::ALL) - .border_style(Style::default().fg(WARNING)) - .style(Style::default().bg(SURFACE_1)); - - let inner = block.inner(area); - block.render(area, buf); - - // Too small to render anything meaningful - if inner.height < 4 || inner.width < 20 { - return; - } - - // Minimal fallback for narrow terminals - if inner.height < 6 || inner.width < 40 { - let msg = - truncate_with_ellipsis("Auth Required", inner.width.saturating_sub(2) as usize); - buf.set_string(inner.x + 1, inner.y + 1, &msg, Style::default().fg(WARNING)); - return; - } - - // Layout: message area + action bar - let chunks = Layout::vertical([ - Constraint::Length(4), // Message - Constraint::Length(1), // Spacer - Constraint::Min(1), // Options - Constraint::Length(1), // Action bar - ]) - .split(inner); - - // Message - truncate if needed - let max_msg_width = inner.width.saturating_sub(4) as usize; - let msg1 = - truncate_with_ellipsis("Please run `cortex login` to authenticate.", max_msg_width); - let msg2 = truncate_with_ellipsis( - "This will open your browser to complete authentication.", - max_msg_width, - ); - - buf.set_string( - chunks[0].x + 2, - chunks[0].y, - &msg1, - Style::default().fg(TEXT), - ); - buf.set_string( - chunks[0].x + 2, - chunks[0].y + 2, - &msg2, - Style::default().fg(TEXT_DIM), - ); - - // Options - let options = [("OK", 0), ("Cancel", 1)]; - for (label, idx) in options.iter() { - let y = chunks[2].y; - let x_offset = if *idx == 0 { 2 } else { 12 }; - let is_selected = *idx == self.selected_option; - - let prefix = if is_selected { ">" } else { " " }; - let bg = if is_selected { CYAN_PRIMARY } else { SURFACE_1 }; - let fg = if is_selected { VOID } else { TEXT }; - - buf.set_string( - chunks[2].x + x_offset, - y, - prefix, - Style::default().fg(CYAN_PRIMARY), - ); - buf.set_string( - chunks[2].x + x_offset + 2, - y, - *label, - Style::default().fg(fg).bg(bg), - ); - } - - // Action bar - let bar = ActionBar::new() - .hint("←/→", "navigate") - .hint("Enter", "select") - .hint("Esc", "cancel"); - bar.render(chunks[3], buf); - } - - fn handle_key(&mut self, key: KeyEvent) -> ModalResult { - match key.code { - KeyCode::Left => { - self.selected_option = 0; - ModalResult::Continue - } - KeyCode::Right => { - self.selected_option = 1; - ModalResult::Continue - } - KeyCode::Enter => { - if self.selected_option == 0 { - ModalResult::Action(ModalAction::Custom("run_cortex_login".to_string())) - } else { - ModalResult::Close - } - } - KeyCode::Esc => ModalResult::Close, - _ => ModalResult::Continue, - } - } - - fn key_hints(&self) -> Vec<(&'static str, &'static str)> { - vec![("←/→", "navigate"), ("Enter", "select"), ("Esc", "cancel")] - } -} - -// Legacy alias for backward compatibility -pub type ProviderAlertModal = AuthAlertModal; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_auth_alert_creation() { - let alert = AuthAlertModal::new(); - assert_eq!(alert.selected_option, 0); - assert_eq!(alert.title(), "Authentication Required"); - } - - #[test] - fn test_auth_alert_default() { - let alert = AuthAlertModal::default(); - assert_eq!(alert.selected_option, 0); - } - - #[test] - fn test_navigation() { - let mut alert = AuthAlertModal::new(); - - // Navigate right - alert.handle_key(KeyEvent::from(KeyCode::Right)); - assert_eq!(alert.selected_option, 1); - - // Navigate left - alert.handle_key(KeyEvent::from(KeyCode::Left)); - assert_eq!(alert.selected_option, 0); - } - - #[test] - fn test_escape_closes() { - let mut alert = AuthAlertModal::new(); - let result = alert.handle_key(KeyEvent::from(KeyCode::Esc)); - assert!(matches!(result, ModalResult::Close)); - } - - #[test] - fn test_enter_on_ok_triggers_login() { - let mut alert = AuthAlertModal::new(); - assert_eq!(alert.selected_option, 0); // OK is selected by default - let result = alert.handle_key(KeyEvent::from(KeyCode::Enter)); - assert!(matches!( - result, - ModalResult::Action(ModalAction::Custom(ref s)) if s == "run_cortex_login" - )); - } - - #[test] - fn test_enter_on_cancel_closes() { - let mut alert = AuthAlertModal::new(); - alert.handle_key(KeyEvent::from(KeyCode::Right)); // Select Cancel - assert_eq!(alert.selected_option, 1); - let result = alert.handle_key(KeyEvent::from(KeyCode::Enter)); - assert!(matches!(result, ModalResult::Close)); - } -} diff --git a/src/cortex-tui/src/modal/mod.rs b/src/cortex-tui/src/modal/mod.rs index 26ec744a..3bcf5282 100644 --- a/src/cortex-tui/src/modal/mod.rs +++ b/src/cortex-tui/src/modal/mod.rs @@ -14,7 +14,6 @@ use ratatui::{ use std::path::PathBuf; // Re-export modal implementations -pub mod alert; pub mod commands; pub mod help; pub mod login; @@ -25,7 +24,6 @@ pub mod providers; pub mod sessions; pub mod upgrade; -pub use alert::{AuthAlertModal, ProviderAlertModal}; pub use commands::CommandsModal; pub use help::HelpModal; pub use login::{LoginModal, LoginState}; diff --git a/src/cortex-tui/src/runner/event_loop/modal.rs b/src/cortex-tui/src/runner/event_loop/modal.rs index 5a8d058a..d42b9bfe 100644 --- a/src/cortex-tui/src/runner/event_loop/modal.rs +++ b/src/cortex-tui/src/runner/event_loop/modal.rs @@ -277,9 +277,7 @@ impl EventLoop { } } ModalAction::ConfigureProvider(_provider_id) => { - use crate::modal::AuthAlertModal; - let alert = AuthAlertModal::new(); - self.modal_stack.push(Box::new(alert)); + self.app_state.toasts.warning("Please run `cortex login` to authenticate"); } ModalAction::ExecuteCommand(cmd) => { let cmd_str = if cmd.starts_with('/') { diff --git a/src/cortex-tui/src/runner/event_loop/streaming.rs b/src/cortex-tui/src/runner/event_loop/streaming.rs index a5a13138..90cffad7 100644 --- a/src/cortex-tui/src/runner/event_loop/streaming.rs +++ b/src/cortex-tui/src/runner/event_loop/streaming.rs @@ -62,10 +62,8 @@ impl EventLoop { self.app_state.queued_count() ); - // Open the authentication alert modal - use crate::modal::AuthAlertModal; - let alert = AuthAlertModal::new(); - self.modal_stack.push(Box::new(alert)); + // Show authentication required toast notification + self.app_state.toasts.error("Authentication required. Please run `cortex login` to authenticate."); return Ok(()); }