From f4b71003e304f3257696c1fb257cda5a17e07f4d Mon Sep 17 00:00:00 2001 From: Droid Agent Date: Tue, 27 Jan 2026 14:27:35 +0000 Subject: [PATCH] fix(tui): enable paste in MCP server configuration modal Previously, when adding an MCP server (HTTP or stdio) or setting API keys, users could not paste text into the input fields. This was because the paste event (EngineEvent::Paste) was only routed to Form modals and the main input widget, not to the unified modal stack that includes the MCP manager modal. Changes: - Add handle_paste method to the Modal trait with default no-op impl - Add handle_paste method to ModalStack to delegate to top modal - Implement handle_paste in McpManagerModal for all text input modes: - AddStdioServer (name, command, args fields) - AddHttpServer (name, url fields) - SetAuth (api_key field) - SelectFromRegistry (search_query field) - Update event loop to check modal_stack for paste events first --- cortex-tui/src/modal/mcp_manager.rs | 40 +++++++++++++++++++++++++++++ cortex-tui/src/modal/mod.rs | 16 ++++++++++++ cortex-tui/src/runner/event_loop.rs | 7 +++-- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/cortex-tui/src/modal/mcp_manager.rs b/cortex-tui/src/modal/mcp_manager.rs index c0adf51d..658e36aa 100644 --- a/cortex-tui/src/modal/mcp_manager.rs +++ b/cortex-tui/src/modal/mcp_manager.rs @@ -1416,6 +1416,46 @@ impl Modal for McpManagerModal { "MCP Servers" } + fn handle_paste(&mut self, text: &str) -> bool { + match &mut self.mode { + McpMode::AddStdioServer { + name, + command, + args, + focus, + } => { + match focus { + AddStdioServerFocus::Name => name.push_str(text), + AddStdioServerFocus::Command => command.push_str(text), + AddStdioServerFocus::Args => args.push_str(text), + } + true + } + McpMode::AddHttpServer { name, url, focus } => { + match focus { + AddHttpServerFocus::Name => name.push_str(text), + AddHttpServerFocus::Url => url.push_str(text), + } + true + } + McpMode::SetAuth { + server_name: _, + api_key, + } => { + api_key.push_str(text); + true + } + McpMode::SelectFromRegistry { + selected: _, + search_query, + } => { + search_query.push_str(text); + true + } + _ => false, + } + } + fn desired_height(&self, max_height: u16, _width: u16) -> u16 { match &self.mode { McpMode::List => { diff --git a/cortex-tui/src/modal/mod.rs b/cortex-tui/src/modal/mod.rs index ae003dcb..8c9891d4 100644 --- a/cortex-tui/src/modal/mod.rs +++ b/cortex-tui/src/modal/mod.rs @@ -57,6 +57,12 @@ pub trait Modal: Send { /// Handle a key event, returning the result fn handle_key(&mut self, key: KeyEvent) -> ModalResult; + /// Handle pasted text. Returns true if the paste was handled. + /// Default implementation does nothing. + fn handle_paste(&mut self, _text: &str) -> bool { + false + } + /// Key hints to display at the bottom fn key_hints(&self) -> Vec<(&'static str, &'static str)>; @@ -243,6 +249,16 @@ impl ModalStack { } } + /// Handle pasted text, delegating to the top modal + /// Returns true if the paste was handled + pub fn handle_paste(&mut self, text: &str) -> bool { + if let Some(modal) = self.current_mut() { + modal.handle_paste(text) + } else { + false + } + } + /// Clear all modals from the stack pub fn clear(&mut self) { self.stack.clear(); diff --git a/cortex-tui/src/runner/event_loop.rs b/cortex-tui/src/runner/event_loop.rs index 9a8441e5..05060438 100644 --- a/cortex-tui/src/runner/event_loop.rs +++ b/cortex-tui/src/runner/event_loop.rs @@ -1672,10 +1672,13 @@ impl EventLoop { } EngineEvent::Paste(text) => { - // Check if a form modal is active and route paste to it - if let Some(crate::app::ActiveModal::Form(ref mut form_state)) = + // Check modal stack first (unified modal system) + if self.modal_stack.is_active() && self.modal_stack.handle_paste(&text) { + // Paste was handled by modal + } else if let Some(crate::app::ActiveModal::Form(ref mut form_state)) = self.app_state.active_modal { + // Check if a form modal is active and route paste to it form_state.handle_paste(&text); } else { // Otherwise insert pasted text into the main input widget