Skip to content

feat: stashes current message with Ctrl+S#10173

Closed
fcoury wants to merge 21 commits intoopenai:mainfrom
fcoury:feat/stash
Closed

feat: stashes current message with Ctrl+S#10173
fcoury wants to merge 21 commits intoopenai:mainfrom
fcoury:feat/stash

Conversation

@fcoury
Copy link
Contributor

@fcoury fcoury commented Jan 29, 2026

Summary

Adds message stash to the composer with Ctrl+S.

This feature allows users to temporarily stash their current message, attachments and pending paste expansions and have it automatically restored after the next submission.

Motivation

There were a few occasions when I was preparing a large follow-up task for the agent while I noticed the agent was doing something undesired. Under normal circumstances I need to copy the text I had written, paste it somewhere else (not really because I use a clipboard manager, but still), enter the steering message, submit it and then paste the message I had in progress.

This PR would allow me hit Ctrl+S as soon as I realize the model needs steering and as soon as I send the steering message, the stashed message and all its attributes gets automatically restored.

Edge cases

We don't want the user to ever lose a message in progress, so if the composer isn't empty when we attempt to restore the stash, we stop, don't restore it and keep the stash untouched.

Copilot AI review requested due to automatic review settings January 29, 2026 17:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a message stash feature that allows users to temporarily save their current message draft (including text, attachments, and pending paste expansions) using Ctrl+S and have it automatically restored after submitting another message.

Changes:

  • Added stash functionality triggered by Ctrl+S to save current composer state
  • Implemented automatic restoration after message submission
  • Added visual indicator showing when a stash exists
  • Added hint message when trying to restore stash with non-empty composer

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
codex-rs/tui/src/chatwidget.rs Added stash field, Ctrl+S handling for restore, automatic restoration after submission, and stash indicator management
codex-rs/tui/src/chatwidget/tests.rs Added test for stash/restore cycle
codex-rs/tui/src/bottom_pane/mod.rs Added PreparedDraft export, restore_stash method, stash indicator integration, and composer_has_draft check
codex-rs/tui/src/bottom_pane/chat_composer.rs Refactored prepare logic to support stashing, added PreparedDraft struct, handle_stash method, and has_draft check
codex-rs/tui/src/bottom_pane/footer.rs Added CantUnstashHint footer mode and hint message
codex-rs/tui/src/bottom_pane/stash_indicator.rs New file implementing visual indicator for stash existence
codex-rs/tui/src/bottom_pane/snapshots/*.snap Snapshot tests for stash indicator rendering
Comments suppressed due to low confidence (1)

codex-rs/tui/src/chatwidget.rs:2541

  • The stash restoration logic is inconsistent between submitted and queued messages. When a message is submitted (InputResult::Submitted), the stash is automatically restored at line 2526. However, when a message is queued (InputResult::Queued), there is no corresponding restore_stash() call.

This means:

  1. User stashes content
  2. User queues a follow-up message while task is running
  3. Stash is NOT restored, even though a message was "sent"

For consistency with the PR description ("automatically restored after the next submission"), the stash should also be restored after queuing a message. Add a restore_stash() call after the queued message is handled, similar to line 2526.

                InputResult::Queued {
                    text,
                    text_elements,
                } => {
                    let user_message = UserMessage {
                        text,
                        local_images: self
                            .bottom_pane
                            .take_recent_submission_images_with_placeholders(),
                        text_elements,
                        mention_paths: self.bottom_pane.take_mention_paths(),
                    };
                    self.queue_user_message(user_message);
                }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@etraut-openai etraut-openai added the oai PRs contributed by OpenAI employees label Jan 30, 2026
@etraut-openai
Copy link
Collaborator

@codex review

Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

match expand_custom_prompt(&text, &text_elements, &self.custom_prompts) {
Ok(expanded) => expanded,
Err(err) => {
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
history_cell::new_error_event(err.user_message()),

P2 Badge Skip prompt expansion when stashing drafts

prepare_composer_text runs expand_custom_prompt (and returns an error) even in ForStash mode, so pressing Ctrl+S on an incomplete /prompts: line surfaces a validation error and refuses to stash. Stashing should be a lossless snapshot of the current draft, so it should bypass prompt expansion/validation in stash mode.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fcoury added 20 commits January 30, 2026 19:58
The message keeps stashed until the user submits the current message.
Right after sending the stashed message is restored.
Added word wrapping to the stash indicator and if Ctrl+S is pressed with
a stash present, recalls it.
The rebase from upstream/main introduced new FooterMode variants
(ComposerEmpty, ComposerHasDraft) and restructured several match
statements. This commit adds the CantUnstashHint variant to all
affected match statements and updates snapshots for the new footer
layout.
Refactors set_text_content_with_local_images_and_pending_pastes to accept
LocalImageAttachment structs instead of just paths, ensuring placeholder-to-path
mappings are preserved when restoring stashed drafts. This prevents images from
being lost during stash/unstash operations.

- Rewrite method to properly filter and map LocalImageAttachment by placeholders
- Remove intermediate wrapper method in BottomPane
- Add test coverage for image preservation
- Fix doc comment indentation in footer.rs
- Skip prompt expansion when stashing drafts
- Keep mention-path mappings in stashed drafts
- Image pruning was gated behind slash_commands_enabled
@joshka-oai
Copy link
Collaborator

Hey Felipe, great to see you here. I'm a fan of what you're doing over on https://github.com/fcoury/textual-rs/

I think we actually already have a version of this behavior today, but without any UI that makes it clear what’s happening.

You can reproduce it like this:

  1. Type tell me a storyEnter
  2. Type now anotherCtrl+C
  3. Type about a dinosaurEnter
  4. Press Up

At that point, the interrupted text comes back, which is useful — but pretty implicit. Framing this as a way to make it clearer what Ctrl+C does (rather than introducing a brand-new concept) might be the right angle here.

We’ve also touched nearby pieces of this before:

  • fix(tui): rehydrate drafts and restore image placeholders #9040 is still pending review (on my plate for this week) and will address the missing content that wasn’t being restored when stashed text came back.
  • There are also a few smaller, related PRs mentioned in the same thread that touch on adjacent pieces of this behavior.
  • We also previously closed feat: add prompt stashing #8918, which explored a similar direction from an internal OpenAI contributor outside the Codex team.

So conceptually, this feels aligned with prior work — the main gap now is making the behavior visible and understandable to users. Maybe we could add a small UX affordance for this: for example, display a brief note or tooltip when Ctrl+C is pressed explaining what just happened, or include a short mention in the startup help text. We already have a list of these onboarding tips, and this would be a good one to add there.

@fcoury
Copy link
Contributor Author

fcoury commented Feb 2, 2026

Hey Felipe, great to see you here. I'm a fan of what you're doing over on fcoury/textual-rs

Thank you and thanks for the work you do on ratatui as well! :-)

I think we actually already have a version of this behavior today, but without any UI that makes it clear what’s happening.
You can reproduce it like this:

Now that's a big surprise! Nice.

At that point, the interrupted text comes back, which is useful — but pretty implicit. Framing this as a way to make it clearer what Ctrl+C does (rather than introducing a brand-new concept) might be the right angle here.

I just played with it and it even saves images, very cool.

So conceptually, this feels aligned with prior work — the main gap now is making the behavior visible and understandable to users. Maybe we could add a small UX affordance for this: for example, display a brief note or tooltip when Ctrl+C is pressed explaining what just happened, or include a short mention in the startup help text. We already have a list of these onboarding tips, and this would be a good one to add there.

Agreed. Feel free to close this PR or let me know if you want me to and I'll be glad to work on the smaller scope note/tooltip addition PR in the future.

Thank you!

@joshka-oai
Copy link
Collaborator

I'm not 100% certain exactly what the right behavior that makes this clear is but it's obviously a needed behavior (having got two PRs for it - both reasonable). We need to make the current behavior more obvious. Feel free to throw together some ideas on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

oai PRs contributed by OpenAI employees

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants