-
Notifications
You must be signed in to change notification settings - Fork 9.1k
queue slash commands in tui #14170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
queue slash commands in tui #14170
Changes from all commits
Commits
Show all changes
67 commits
Select commit
Hold shift + click to select a range
9478b34
queue slash commands in tui
charley-oai 072d5d9
remove dead tui helper methods
charley-oai a8f1f43
preserve queued slash actions on interrupt
charley-oai e5f1b84
queue interactive slash command selections
charley-oai 9ab49d2
narrow esc interrupts in tui
charley-oai 35e8aa9
Simplify queued slash command replay
charley-oai fa50564
tui: restore interactive slash queue behavior
charley-oai 122e147
tui: simplify interrupt status plumbing
charley-oai ed546bc
tui: preserve queued slash draft intent
charley-oai 393740d
tui: clarify live slash queueing branch
charley-oai 8267155
tui: document slash dispatch replay contract
charley-oai 173d0ef
tui: resolve interactive slash commands before queueing
charley-oai bb3e618
tui: clarify queue replay stop comment
charley-oai 217da0c
tui: make slash command helpers exhaustive
charley-oai 8c45c1a
tui: canonicalize interactive slash drafts
charley-oai bcb3555
tui: reject repeated model modifiers
charley-oai 7ea03de
tui: resume queued replay after idle slash actions
charley-oai ad586ba
tui: share slash command shlex codec
charley-oai 8fda4e0
tui: add slash command help page
charley-oai 6a278e9
tui: make slash help dismissible
charley-oai 528e3df
tui: let slash help grow with terminal
charley-oai 8fbc9d3
tui: let q dismiss slash help
charley-oai 77e1e80
tui: add slash help search and status
charley-oai 6e20c5a
tui: refine slash help search controls
charley-oai 0727144
tui: space slash help footer
charley-oai 56c7646
tui: shorten slash help close hint
charley-oai 541bc6e
tui: let shift-n go backward in slash help search
charley-oai e477780
tui: clear slash help search before closing
charley-oai 2268f98
tui: clean up slash help footer hints
charley-oai 93ead12
tui: block queued replay behind open popups
charley-oai efa6715
tui: cfg-gate realtime settings draft helper
charley-oai d8c0839
tui: reject unavailable queued slash drafts
charley-oai 7250e82
tui: preserve world-writable warning opt-out
charley-oai 96fe896
codex: fix CI failure on PR #14170
charley-oai 614936d
tui: fix duplicate windows sandbox import
charley-oai 3a6e3a9
tui: remove dead windows sandbox event
charley-oai 6487398
codex: fix windows slash help snapshots (#14170)
charley-oai ef2b09c
tui: preserve selected resume target path
charley-oai 7bcff58
tui: reject unavailable queued slash drafts on replay
charley-oai 295f7fe
tui: serialize exact resume picker targets
charley-oai 3b4b483
tui: route elevated approvals through setup flow
charley-oai a80a654
tui: honor resume cwd prompt exits
charley-oai 02f6733
tui: clear empty-arg slash drafts on dispatch
charley-oai 027524d
tui: classify slash commands by execution kind
charley-oai 7e91f70
tui: run legacy sandbox preflight before enable
charley-oai 5ebd348
tui: refresh windows slash help snapshots
charley-oai 51d1e06
tui: encode queued resume paths losslessly
charley-oai 39f436f
tui: complete legacy sandbox preflight asynchronously
charley-oai eb31929
tui: align slash help subagents naming
charley-oai 90e964f
tui: pause replay after personality updates
charley-oai d65960d
tui: restore multi-agents slash alias
charley-oai 1a54e52
tui: dedupe alias-expanded popup commands
charley-oai a6ffd1c
tui: gate slash help by runtime availability
charley-oai e0627c8
tui: reserve builtin aliases in command popup
charley-oai 6d0be7f
tui: preserve queued drafts across session switches
charley-oai f34490a
tui: gate inline feedback command
charley-oai 6cc1476
tui: fix windows selection action inference
charley-oai 8d3ed74
tui: defer windows sandbox popup selection
charley-oai 11d49d9
tui: queue smart approvals updates
charley-oai 3b8dbed
codex: address PR review feedback (#14170)
charley-oai 89f7c08
codex: address PR review feedback (#14170)
charley-oai 5d81980
codex: fix windows help snapshots (#14170)
charley-oai 9ec1452
tui: simplify slash command policy wiring
charley-oai d932510
tui: resume queued drafts after inline app events
charley-oai f701d9e
tui: match slash popup aliases
charley-oai ef28639
tui: accept plain p in help search
charley-oai 6c9bac8
tui: wait for async slash replay updates
charley-oai File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,8 +50,8 @@ | |
| //! | ||
| //! The numeric auto-submit path used by the slash popup performs the same pending-paste expansion | ||
| //! and attachment pruning, and clears pending paste state on success. | ||
| //! Slash commands with arguments (like `/plan` and `/review`) reuse the same preparation path so | ||
| //! pasted content and text elements are preserved when extracting args. | ||
| //! Slash commands with arguments (like `/model`, `/plan`, and `/review`) reuse the same | ||
| //! preparation path so pasted content and text elements are preserved when extracting args. | ||
| //! | ||
| //! # Remote Image Rows (Up/Down/Delete) | ||
| //! | ||
|
|
@@ -572,23 +572,6 @@ impl ChatComposer { | |
| self.sync_popups(); | ||
| } | ||
|
|
||
| pub(crate) fn take_mention_bindings(&mut self) -> Vec<MentionBinding> { | ||
| let elements = self.current_mention_elements(); | ||
| let mut ordered = Vec::new(); | ||
| for (id, mention) in elements { | ||
| if let Some(binding) = self.mention_bindings.remove(&id) | ||
| && binding.mention == mention | ||
| { | ||
| ordered.push(MentionBinding { | ||
| mention: binding.mention, | ||
| path: binding.path, | ||
| }); | ||
| } | ||
| } | ||
| self.mention_bindings.clear(); | ||
| ordered | ||
| } | ||
|
|
||
| pub fn set_collaboration_modes_enabled(&mut self, enabled: bool) { | ||
| self.collaboration_modes_enabled = enabled; | ||
| } | ||
|
|
@@ -2525,9 +2508,6 @@ impl ChatComposer { | |
| && let Some(cmd) = | ||
| slash_commands::find_builtin_command(name, self.builtin_command_flags()) | ||
| { | ||
| if self.reject_slash_command_if_unavailable(cmd) { | ||
| return Some(InputResult::None); | ||
| } | ||
| self.textarea.set_text_clearing_elements(""); | ||
| Some(InputResult::Command(cmd)) | ||
| } else { | ||
|
|
@@ -2553,13 +2533,6 @@ impl ChatComposer { | |
|
|
||
| let cmd = slash_commands::find_builtin_command(name, self.builtin_command_flags())?; | ||
|
|
||
| if !cmd.supports_inline_args() { | ||
| return None; | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Every command supports inline args now |
||
| if self.reject_slash_command_if_unavailable(cmd) { | ||
| return Some(InputResult::None); | ||
| } | ||
|
|
||
| let mut args_elements = | ||
| Self::slash_command_args_elements(rest, rest_offset, &self.textarea.text_elements()); | ||
| let trimmed_rest = rest.trim(); | ||
|
|
@@ -2573,10 +2546,10 @@ impl ChatComposer { | |
|
|
||
| /// Expand pending placeholders and extract normalized inline-command args. | ||
| /// | ||
| /// Inline-arg commands are initially dispatched using the raw draft so command rejection does | ||
| /// not consume user input. Once a command is accepted, this helper performs the usual | ||
| /// submission preparation (paste expansion, element trimming) and rebases element ranges from | ||
| /// full-text offsets to command-arg offsets. | ||
| /// Inline-arg commands are initially dispatched using the raw draft so command-specific | ||
| /// handling can decide whether to consume the input. Once a command is accepted, this helper | ||
| /// performs the usual submission preparation (paste expansion, element trimming) and rebases | ||
| /// element ranges from full-text offsets to command-arg offsets. | ||
| pub(crate) fn prepare_inline_args_submission( | ||
| &mut self, | ||
| record_history: bool, | ||
|
|
@@ -2593,20 +2566,6 @@ impl ChatComposer { | |
| Some((trimmed_rest.to_string(), args_elements)) | ||
| } | ||
|
|
||
| fn reject_slash_command_if_unavailable(&self, cmd: SlashCommand) -> bool { | ||
| if !self.is_task_running || cmd.available_during_task() { | ||
| return false; | ||
| } | ||
| let message = format!( | ||
| "'/{}' is disabled while a task is in progress.", | ||
| cmd.command() | ||
| ); | ||
| self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new( | ||
| history_cell::new_error_event(message), | ||
| ))); | ||
| true | ||
| } | ||
|
|
||
| /// Translate full-text element ranges into command-argument ranges. | ||
| /// | ||
| /// `rest_offset` is the byte offset where `rest` begins in the full text. | ||
|
|
@@ -6422,6 +6381,69 @@ mod tests { | |
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn slash_popup_help_first_for_root_ui() { | ||
| use ratatui::Terminal; | ||
| use ratatui::backend::TestBackend; | ||
|
|
||
| let (tx, _rx) = unbounded_channel::<AppEvent>(); | ||
| let sender = AppEventSender::new(tx); | ||
|
|
||
| let mut composer = ChatComposer::new( | ||
| true, | ||
| sender, | ||
| false, | ||
| "Ask Codex to do anything".to_string(), | ||
| false, | ||
| ); | ||
|
|
||
| type_chars_humanlike(&mut composer, &['/']); | ||
|
|
||
| let mut terminal = match Terminal::new(TestBackend::new(60, 8)) { | ||
| Ok(t) => t, | ||
| Err(e) => panic!("Failed to create terminal: {e}"), | ||
| }; | ||
| terminal | ||
| .draw(|f| composer.render(f.area(), f.buffer_mut())) | ||
| .unwrap_or_else(|e| panic!("Failed to draw composer: {e}")); | ||
|
|
||
| if cfg!(target_os = "windows") { | ||
| insta::with_settings!({ snapshot_suffix => "windows" }, { | ||
| insta::assert_snapshot!("slash_popup_root", terminal.backend()); | ||
| }); | ||
| } else { | ||
| insta::assert_snapshot!("slash_popup_root", terminal.backend()); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn slash_popup_help_first_for_root_logic() { | ||
| use super::super::command_popup::CommandItem; | ||
| let (tx, _rx) = unbounded_channel::<AppEvent>(); | ||
| let sender = AppEventSender::new(tx); | ||
| let mut composer = ChatComposer::new( | ||
| true, | ||
| sender, | ||
| false, | ||
| "Ask Codex to do anything".to_string(), | ||
| false, | ||
| ); | ||
| type_chars_humanlike(&mut composer, &['/']); | ||
|
|
||
| match &composer.active_popup { | ||
| ActivePopup::Command(popup) => match popup.selected_item() { | ||
| Some(CommandItem::Builtin(cmd)) => { | ||
| assert_eq!(cmd.command(), "help") | ||
| } | ||
| Some(CommandItem::UserPrompt(_)) => { | ||
| panic!("unexpected prompt selected for '/'") | ||
| } | ||
| None => panic!("no selected command for '/'"), | ||
| }, | ||
| _ => panic!("slash popup not active after typing '/'"), | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn slash_popup_model_first_for_mo_ui() { | ||
| use ratatui::Terminal; | ||
|
|
@@ -6678,7 +6700,7 @@ mod tests { | |
| } | ||
|
|
||
| #[test] | ||
| fn slash_command_disabled_while_task_running_keeps_text() { | ||
| fn slash_command_while_task_running_still_dispatches() { | ||
| use crossterm::event::KeyCode; | ||
| use crossterm::event::KeyEvent; | ||
| use crossterm::event::KeyModifiers; | ||
|
|
@@ -6700,24 +6722,16 @@ mod tests { | |
| let (result, _needs_redraw) = | ||
| composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); | ||
|
|
||
| assert_eq!(InputResult::None, result); | ||
| assert_eq!( | ||
| InputResult::CommandWithArgs( | ||
| SlashCommand::Review, | ||
| "these changes".to_string(), | ||
| Vec::new(), | ||
| ), | ||
| result | ||
| ); | ||
| assert_eq!("/review these changes", composer.textarea.text()); | ||
|
|
||
| let mut found_error = false; | ||
| while let Ok(event) = rx.try_recv() { | ||
| if let AppEvent::InsertHistoryCell(cell) = event { | ||
| let message = cell | ||
| .display_lines(80) | ||
| .into_iter() | ||
| .map(|line| line.to_string()) | ||
| .collect::<Vec<_>>() | ||
| .join("\n"); | ||
| assert!(message.contains("disabled while a task is in progress")); | ||
| found_error = true; | ||
| break; | ||
| } | ||
| } | ||
| assert!(found_error, "expected error history cell to be sent"); | ||
| assert!(rx.try_recv().is_err(), "no error should be emitted"); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
@@ -7626,7 +7640,7 @@ mod tests { | |
| composer.take_recent_submission_mention_bindings(), | ||
| mention_bindings | ||
| ); | ||
| assert!(composer.take_mention_bindings().is_empty()); | ||
| assert!(composer.mention_bindings().is_empty()); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was
"'/{}' is disabled while a task is in progress.",which we no longer emit