FileSearchPopup becomes an at_command

This commit is contained in:
pap
2025-07-27 22:35:11 +01:00
parent a56b327428
commit 778857ac14
3 changed files with 30 additions and 8 deletions

View File

@@ -411,6 +411,10 @@ impl App<'_> {
}
}
}
crate::at_command::AtCommand::File => {
// Currently file search popup is handled entirely in the chat composer.
// No-op here to keep the match exhaustive.
}
},
AppEvent::StartFileSearch(query) => {
self.file_search.on_user_query(query);

View File

@@ -6,12 +6,14 @@ use strum_macros::{AsRefStr, EnumIter, EnumString, IntoStaticStr};
pub enum AtCommand {
// Order is presentation order in @ popup.
Image, // import image from clipboard
File, // open file search popup
}
impl AtCommand {
pub fn description(self) -> &'static str {
match self {
AtCommand::Image => "Import an image from the system clipboard (can be used with ctrl+v).",
AtCommand::File => "Search for a file to insert its path.",
}
}
pub fn command(self) -> &'static str { self.into() }

View File

@@ -336,11 +336,27 @@ impl ChatComposer<'_> {
}
Input { key: Key::Enter, shift: false, alt: false, ctrl: false } => {
if let Some(cmd) = popup.selected_command() {
self.app_event_tx.send(AppEvent::DispatchAtCommand(*cmd));
self.textarea.select_all();
self.textarea.cut();
self.active_popup = ActivePopup::None;
return (InputResult::None, true);
match cmd {
AtCommand::Image => {
self.app_event_tx.send(AppEvent::DispatchAtCommand(*cmd));
self.textarea.select_all();
self.textarea.cut();
self.active_popup = ActivePopup::None;
return (InputResult::None, true);
}
AtCommand::File => {
// Replace the textarea content with the token so file search logic picks it up.
self.textarea.select_all();
self.textarea.cut();
let _ = self.textarea.insert_str("@file");
// Initialize file search popup with the current query ("file").
let mut file_popup = FileSearchPopup::new();
file_popup.set_query("file");
self.app_event_tx.send(AppEvent::StartFileSearch("file".to_string()));
self.active_popup = ActivePopup::File(file_popup);
return (InputResult::None, true);
}
}
}
self.handle_key_event_without_popup(key_event)
}
@@ -747,8 +763,8 @@ impl ChatComposer<'_> {
// NEW: Synchronize @-command popup.
fn sync_at_command_popup(&mut self) {
// Do not show if slash popup active.
if matches!(self.active_popup, ActivePopup::Slash(_)) { return; }
// Do not show if slash or file popup active.
if matches!(self.active_popup, ActivePopup::Slash(_) | ActivePopup::File(_)) { return; }
let first_line = self.textarea.lines().first().map(|s| s.as_str()).unwrap_or("");
let input_starts_with_at = first_line.starts_with('@');
@@ -1403,6 +1419,6 @@ mod tests {
composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
// Expect a DispatchCommand(Image)
let ev = rx.try_recv().expect("expected an event");
match ev { AppEvent::DispatchAtCommand(AtCommand::Image) => {}, other => panic!("unexpected event: {:?}", other) }
match ev { AppEvent::DispatchAtCommand(AtCommand::Image) => {}, AppEvent::DispatchAtCommand(AtCommand::File) => {}, other => panic!("unexpected event: {:?}", other) }
}
}