fix(tui): restore cancelled prompt on ctrl-c#1764
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a feature to restore the last submitted prompt to the composer when a request is cancelled via Ctrl+C. It adds a last_submitted_prompt field to the App state, updates it during message dispatch and steering, and provides a restoration method with accompanying tests. Feedback suggests using more idiomatic Rust by applying as_deref instead of cloned when retrieving the prompt and recommends adding test coverage for the steer_user_message function.
|
Follow-up commit What changed:
Verified after the commit:
|
|
Follow-up commit
Verified after the change:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d34c031c47
ℹ️ 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".
aboimpinto
left a comment
There was a problem hiding this comment.
Great work on this — focused, well-tested, and hits the right behaviour. A few observations:
✅ What Works Well
- Recording the last prompt in
dispatch_user_message/steer_user_messageis the right place — it captures exactly what was sent, not what's in the composer at cancel time. - Only restores into an empty composer — this is the correct edge-case handling. If the user started typing something new while waiting, their draft is preserved instead of being overwritten by the old prompt. This directly addresses @superzmy's concern from the issue discussion.
- Race condition fix — the
tokio::select!withbiasedordering on the cancel token beforecreate_message_streamis clean. Prevents the ugly case where Ctrl+C lands beforeTurnStartedresets the engine token. - Late stream event suppression —
suppress_stream_events_until_turn_completeis a good safeguard. Prevents stale deltas from leaking into the UI after the user has already cancelled and moved on. - Test coverage — 6 new test functions covering the core paths: prompt recording, Ctrl+C disposition, late event suppression, and the Windows isolation fixes.
💡 One Thought (non-blocking)
The ESC vs Ctrl+C differentiation is now implicit: ESC cancels but doesn't restore (the composer keeps whatever draft the user had), while Ctrl+C cancels AND restores the last sent prompt into an empty composer. This is a clean split and aligns with what we discussed in #1757. Might be worth a one-line note in the keybindings docs at some point so users discover it.
✅ Verified
- Gates pass (fmt, clippy, build, workspace tests)
- Manual TUI verification on Windows confirms the described behaviour
- No regression risk — the change is scoped to the cancel path
Nice work @nightt5879! 🚀

Summary
TurnStartedresets the engine token.returns in the Windows keyboard enhancement helper so workspace clippy passes.Root cause: submitting a prompt clears the composer, while the active-turn cancel path only stopped the request and updated status text. It did not keep a recoverable copy of the submitted input, and queued stream events could still render after the UI had returned focus to the composer.
Testing
cargo fmt --all -- --checkcargo buildcargo clippy --workspace --all-targets --all-features -- -D warningscargo test -p deepseek-tui --bin deepseek-tui restore_last_submitted_promptcargo test -p deepseek-tui --bin deepseek-tui dispatch_user_message_records_prompt_for_cancel_restorecargo test -p deepseek-tui --bin deepseek-tui ctrl_c_dispositioncargo test -p deepseek-tui --bin deepseek-tui local_cancel_marks_late_stream_events_for_suppressioncargo test --workspace --all-featuresdeepseek --provider deepseek --model deepseek-v4-flashRequest cancelled.Checklist
Closes #1757