TUI: completed modal view can remain mounted after paste-driven completion
Summary
BottomPane::handle_paste() can leave a completed modal flow mounted while re-enabling the composer.
That creates an inconsistent TUI state: the composer becomes editable again, but rendering and key routing can still prefer stale view state because the completed flow was never torn down.
Current code
In codex-rs/tui/src/bottom_pane/mod.rs:
pub fn handle_paste(&mut self, pasted: String) {
if let Some(view) = self.view_stack.last_mut() {
let needs_redraw = view.handle_paste(pasted);
if view.is_complete() {
self.on_active_view_complete();
}
if needs_redraw {
self.request_redraw();
}
} else {
...
}
}
on_active_view_complete() re-enables the composer, but unlike the key-event completion path the paste path does not tear down the completed modal flow first.
Why this is a bug
The key-event path already treats view_complete as completion of the active modal flow:
- tear down the completed flow from
view_stack - then call
on_active_view_complete()
The paste path only performs the second half.
That means the following invariants can be violated after a paste-driven completion:
- stale view state can remain mounted in
view_stack - composer input has already been re-enabled
- rendering can still show stale active-view state
- subsequent key routing can still prefer stale view state instead of the composer
This is especially easy to see when more than one view is stacked: even if the top view completed, the flow has not been torn down consistently before composer state is restored.
Minimal deterministic repro
A unit-level repro is straightforward:
- Create a
BottomPane - Disable composer input
- Push a lower blocking
BottomPaneView - Push a top
BottomPaneViewwhose:
handle_paste()returnstrueis_complete()returnstrue
- Call
handle_paste("hello")
Expected:
- the completed modal flow is torn down consistently with the key-event completion path
- composer is re-enabled
- next normal keypress goes to the composer
Actual:
- composer is re-enabled
- stale view state can remain mounted
Expected behavior
If a modal view completes during paste handling, Codex should tear down the completed modal flow before restoring composer state, just like the key-event completion path already does.
Additional context
I found this while investigating TUI/input freeze behavior in the CLI. The separate input-stream recovery bug is tracked in #12645; this issue is specifically about paste-driven modal teardown semantics.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗