mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
da86cedbd439d38fbd7e613e4e88f8f6f138debb
18 Commits
-
feat(tui): add reverse history search to composer (#17550)
## Problem The TUI had shell-style Up/Down history recall, but `Ctrl+R` did not provide the reverse incremental search workflow users expect from shells. Users needed a way to search older prompts without immediately replacing the current draft, and the interaction needed to handle async persistent history, repeated navigation keys, duplicate prompt text, footer hints, and preview highlighting without making the main composer file even harder to review. https://github.com/user-attachments/assets/5165affd-4c9a-46e9-adbd-89088f5f7b6b <img width="1227" height="722" alt="image" src="https://github.com/user-attachments/assets/8bc83289-eeca-47c7-b0c3-8975101901af" /> ## Mental model `Ctrl+R` opens a temporary search session owned by the composer. The footer line becomes the search input, the composer body previews the current match only after the query has text, and `Enter` accepts that preview as an editable draft while `Esc` restores the draft that existed before search started. The history layer provides a combined offset space over persistent and local history, but search navigation exposes unique prompt text rather than every physical history row. ## Non-goals This change does not rewrite stored history, change normal Up/Down browsing semantics, add fuzzy matching, or add persistent metadata for attachments in cross-session history. Search deduplication is deliberately scoped to the active Ctrl+R search session and uses exact prompt text, so case, whitespace, punctuation, and attachment-only differences are not normalized. ## Tradeoffs The implementation keeps search state in the existing composer and history state machines instead of adding a new cross-module controller. That keeps ownership local and testable, but it means the composer still coordinates visible search status, draft restoration, footer rendering, cursor placement, and match highlighting while `ChatComposerHistory` owns traversal, async fetch continuation, boundary clamping, and unique-result caching. Unique-result caching stores cloned `HistoryEntry` values so known matches can be revisited without cache lookups; this is simple and robust for interactive search sizes, but it is not a global history index. ## Architecture `ChatComposer` detects `Ctrl+R`, snapshots the current draft, switches the footer to `FooterMode::HistorySearch`, and routes search-mode keys before normal editing. Query edits call `ChatComposerHistory::search` with `restart = true`, which starts from the newest combined-history offset. Repeated `Ctrl+R` or Up searches older; Down searches newer through already discovered unique matches or continues the scan. Persistent history entries still arrive asynchronously through `on_entry_response`, where a pending search either accepts the response, skips a duplicate, or requests the next offset. The composer-facing pieces now live in `codex-rs/tui/src/bottom_pane/chat_composer/history_search.rs`, leaving `chat_composer.rs` responsible for routing and rendering integration instead of owning every search helper inline. `codex-rs/tui/src/bottom_pane/chat_composer_history.rs` remains the owner of stored history, combined offsets, async fetch state, boundary semantics, and duplicate suppression. Match highlighting is computed from the current composer text while search is active and disappears when the match is accepted. ## Observability There are no new logs or telemetry. The practical debug path is state inspection: `ChatComposer.history_search` tells whether the footer query is idle, searching, matched, or unmatched; `ChatComposerHistory.search` tracks selected raw offsets, pending persistent fetches, exhausted directions, and unique match cache state. If a user reports skipped or repeated results, first inspect the exact stored prompt text, the selected offset, whether an async persistent response is still pending, and whether a query edit restarted the search session. ## Tests The change is covered by focused `codex-tui` unit tests for opening search without previewing the latest entry, accepting and canceling search, no-match restoration, boundary clamping, footer hints, case-insensitive highlighting, local duplicate skipping, and persistent duplicate skipping through async responses. Snapshot coverage captures the footer-mode visual changes. Local verification used `just fmt`, `cargo test -p codex-tui history_search`, `cargo test -p codex-tui`, and `just fix -p codex-tui`.
Felipe Coury ·
2026-04-12 19:32:19 -03:00 -
Remove remaining custom prompt support (#16115)
## Summary - remove protocol and core support for discovering and listing custom prompts - simplify the TUI slash-command flow and command popup to built-in commands only - delete obsolete custom prompt tests, helpers, and docs references - clean up downstream event handling for the removed protocol events
Eric Traut ·
2026-03-28 13:49:37 -06:00 -
Rename tui_app_server to tui (#16104)
This is a follow-up to https://github.com/openai/codex/pull/15922. That previous PR deleted the old `tui` directory and left the new `tui_app_server` directory in place. This PR renames `tui_app_server` to `tui` and fixes up all references.
Eric Traut ·
2026-03-28 11:23:07 -06:00 -
Remove the legacy TUI split (#15922)
This is the part 1 of 2 PRs that will delete the `tui` / `tui_app_server` split. This part simply deletes the existing `tui` directory and marks the `tui_app_server` feature flag as removed. I left the `tui_app_server` feature flag in place for now so its presence doesn't result in an error. It is simply ignored. Part 2 will rename the `tui_app_server` directory `tui`. I did this as two parts to reduce visible code churn.
Eric Traut ·
2026-03-27 22:56:44 +00:00 -
tui: preserve kill buffer across submit and slash-command clears (#12006)
## Problem Before this change, composer paths that cleared the textarea after submit or slash-command dispatch also cleared the textarea kill buffer. That meant a user could `Ctrl+K` part of a draft, trigger a composer action that cleared the visible draft, and then lose the ability to `Ctrl+Y` the killed text back. This was especially awkward for workflows where the user wants to temporarily remove text, run a composer action such as changing reasoning level or dispatching a slash command, and then restore the killed text into the now-empty draft. ## Mental model This change separates visible draft state from editing-history state. The visible draft includes the current textarea contents and text elements that should be cleared when the composer submits or dispatches a command. The kill buffer is different: it represents the most recent killed text and should survive those composer-driven clears so the user can still yank it back afterward. After this change, submit and slash-command dispatch still clear the visible textarea contents, but they no longer erase the most recent kill. ## Non-goals This does not implement a multi-entry kill ring or change the semantics of `Ctrl+K` and `Ctrl+Y` beyond preserving the existing yank target across these clears. It also does not change how submit, slash-command parsing, prompt expansion, or attachment handling work, except that those flows no longer discard the textarea kill buffer as a side effect of clearing the draft. ## Tradeoffs The main tradeoff is that clearing the visible textarea is no longer equivalent to fully resetting all editing state. That is intentional here, because submit and slash-command dispatch are composer actions, not requests to forget the user's most recent kill. The benefit is better editing continuity. The cost is that callers must understand that full-buffer replacement resets visible draft state but not the kill buffer. ## Architecture The behavioral change is in `TextArea`: full-buffer replacement now rebuilds text and elements without clearing `kill_buffer`. `ChatComposer` already clears the textarea after successful submit and slash-command dispatch by calling into those textarea replacement paths. With this change, those existing composer flows inherit the new behavior automatically: the visible draft is cleared, but the last killed text remains available for `Ctrl+Y`. The tests cover both layers: - `TextArea` verifies that the kill buffer survives full-buffer replacement. - `ChatComposer` verifies that it survives submit. - `ChatComposer` also verifies that it survives slash-command dispatch. ## Observability There is no dedicated logging for kill-buffer preservation. The most direct way to reason about the behavior is to inspect textarea-wide replacement paths and confirm whether they treat the kill buffer as visible-buffer state or as editing-history state. If this regresses in the future, the likely failure mode is simple and user-visible: `Ctrl+Y` stops restoring text after submit or slash-command clears even though ordinary kill/yank still works within a single uninterrupted draft. ## Tests Added focused regression coverage for the new contract: - `kill_buffer_persists_across_set_text` - `kill_buffer_persists_after_submit` - `kill_buffer_persists_after_slash_command_dispatch` Local verification: - `just fmt` - `cargo test -p codex-tui` --------- Co-authored-by: Josh McKinney <joshka@openai.com>
rakan-oai ·
2026-03-03 02:06:08 +00:00 -
tui: preserve remote image attachments across resume/backtrack (#10590)
## Summary This PR makes app-server-provided image URLs first-class attachments in TUI, so they survive resume/backtrack/history recall and are resubmitted correctly. <img width="715" height="491" alt="Screenshot 2026-02-12 at 8 27 08 PM" src="https://github.com/user-attachments/assets/226cbd35-8f0c-4e51-a13e-459ef5dd1927" /> Can delete the attached image upon backtracking: <img width="716" height="301" alt="Screenshot 2026-02-12 at 8 27 31 PM" src="https://github.com/user-attachments/assets/4558d230-f1bd-4eed-a093-8e1ab9c6db27" /> In both history and composer, remote images are rendered as normal `[Image #N]` placeholders, with numbering unified with local images. ## What changed - Plumb remote image URLs through TUI message state: - `UserHistoryCell` - `BacktrackSelection` - `ChatComposerHistory::HistoryEntry` - `ChatWidget::UserMessage` - Show remote images as placeholder rows inside the composer box (above textarea), and in history cells. - Support keyboard selection/deletion for remote image rows in composer (`Up`/`Down`, `Delete`/`Backspace`). - Preserve remote-image-only turns in local composer history (Up/Down recall), including restore after backtrack. - Ensure submit/queue/backtrack resubmit include remote images in model input (`UserInput::Image`), and keep request shape stable for remote-image-only turns. - Keep image numbering contiguous across remote + local images: - remote images occupy `[Image #1]..[Image #M]` - local images start at `[Image #M+1]` - deletion renumbers consistently. - In protocol conversion, increment shared image index for remote images too, so mixed remote/local image tags stay in a single sequence. - Simplify restore logic to trust in-memory attachment order (no placeholder-number parsing path). - Backtrack/replay rollback handling now queues trims through `AppEvent::ApplyThreadRollback` and syncs transcript overlay/deferred lines after trims, so overlay/transcript state stays consistent. - Trim trailing blank rendered lines from user history rendering to avoid oversized blank padding. ## Docs + tests - Updated: `docs/tui-chat-composer.md` (remote image flow, selection/deletion, numbering offsets) - Added/updated tests across `tui/src/chatwidget/tests.rs`, `tui/src/app.rs`, `tui/src/app_backtrack.rs`, `tui/src/history_cell.rs`, and `tui/src/bottom_pane/chat_composer.rs` - Added snapshot coverage for remote image composer states, including deleting the first of two remote images. ## Validation - `just fmt` - `cargo test -p codex-tui` ## Codex author `codex fork 019c2636-1571-74a1-8471-15a3b1c3f49d`
Charley Cunningham ·
2026-02-13 14:54:06 -08:00 -
tui: keep history recall cursor at line end (#11295)
## Summary - keep cursor at end-of-line after Up/Down history recall - allow continued history navigation when recalled text cursor is at start or end boundary - add regression tests and document the history cursor contract in composer docs ## Testing - just fmt - cargo test -p codex-tui --lib history_navigation_leaves_cursor_at_end_of_line - cargo test -p codex-tui --lib should_handle_navigation_when_cursor_is_at_line_boundaries - cargo test -p codex-tui *(fails in existing integration test `suite::no_panic_on_startup::malformed_rules_should_not_panic` because `target/debug/codex` is not present in this environment)*
Josh McKinney ·
2026-02-10 17:21:46 +00:00 -
fix(tui): tab submits when no task running in steer mode (#10035)
When steer mode is enabled, Tab used to only queue while a task was running and otherwise did nothing. Treat Tab as an immediate submit when no task is running so input isn't dropped when the inflight turn ends mid-typing. Adds a regression test and updates docs/tooltips.
Josh McKinney ·
2026-02-10 00:39:09 +00:00 -
fix(tui): rehydrate drafts and restore image placeholders (#9040)
Fixes #9050 When a draft is stashed with Ctrl+C, we now persist the full draft state (text elements, local image paths, and pending paste payloads) in local history. Up/Down recall rehydrates placeholder elements and attachments so styling remains correct and large pastes still expand on submit. Persistent (cross‑session) history remains text‑only. Backtrack prefills now reuse the selected user message’s text elements and local image paths, so image placeholders/attachments rehydrate when rolling back. External editor replacements keep only attachments whose placeholders remain and then normalize image placeholders to `[Image #1]..[Image #N]` to keep the attachment mapping consistent. Docs: - docs/tui-chat-composer.md Testing: - just fix -p codex-tui - cargo test -p codex-tui Co-authored-by: Eric Traut <etraut@openai.com>
Chriss4123 ·
2026-02-07 20:08:45 -08:00 -
[Codex][CLI] Gate image inputs by model modalities (#10271)
###### Summary - Add input_modalities to model metadata so clients can determine supported input types. - Gate image paste/attach in TUI when the selected model does not support images. - Block submits that include images for unsupported models and show a clear warning. - Propagate modality metadata through app-server protocol/model-list responses. - Update related tests/fixtures. ###### Rationale - Models support different input modalities. - Clients need an explicit capability signal to prevent unsupported requests. - Backward-compatible defaults preserve existing behavior when modality metadata is absent. ###### Scope - codex-rs/protocol, codex-rs/core, codex-rs/tui - codex-rs/app-server-protocol, codex-rs/app-server - Generated app-server types / schema fixtures ###### Trade-offs - Default behavior assumes text + image when field is absent for compatibility. - Server-side validation remains the source of truth. ###### Follow-up - Non-TUI clients should consume input_modalities to disable unsupported attachments. - Model catalogs should explicitly set input_modalities for text-only models. ###### Testing - cargo fmt --all - cargo test -p codex-tui - env -u GITHUB_APP_KEY cargo test -p codex-core --lib - just write-app-server-schema - cargo run -p codex-cli --bin codex -- app-server generate-ts --out app-server-types - test against local backend <img width="695" height="199" alt="image" src="https://github.com/user-attachments/assets/d22dd04f-5eba-4db9-a7c5-a2506f60ec44" /> --------- Co-authored-by: Josh McKinney <joshka@openai.com>
Colin Young ·
2026-02-02 18:56:39 -08:00 -
Nicer highlighting of slash commands, /plan accepts prompt args and pasted images (#10269)
## Summary - Make typed slash commands become text elements when the user hits space, including paste‑burst spaces. - Enable `/plan` to accept inline args and submit them in plan mode, mirroring `/review` behavior and blocking submission while a task is running. - Preserve text elements/attachments for slash commands that take args. <img width="1510" height="500" alt="image" src="https://github.com/user-attachments/assets/446024df-b69a-4249-85db-1a85110e07f1" /> ## Changes - Add safe helper to insert element ranges in the textarea. - Extend command‑with‑args pipeline to carry text elements and reuse submission prep. - Update `/plan` dispatch to switch to plan mode then submit prompt + elements. - Document new composer behavior and add tests. ## Notes - `/plan` is blocked during active tasks (same as `/review`). - Slash‑command elementization recognizes built‑ins and `/prompts:` custom commands only. ## Codex author `codex fork 019c16d3-4520-7bb0-9b9d-48720d40a8ab`
Charley Cunningham ·
2026-02-02 09:53:29 -08:00 -
Restore image attachments/text elements when recalling input history (Up/Down) (#9628)
**Summary** - Up/Down input history now restores image attachments and text elements for local entries. - Composer history stores rich local entries (text + text elements + local image paths) while persistent history remains text-only. - Added tests to verify history recall rehydrates image placeholders and attachments in both `tui` and `tui2`. **Changes** - `tui/src/bottom_pane/chat_composer_history.rs`: store `HistoryEntry` (text + elements + image paths) for local history; adapt navigation + tests. - `tui2/src/bottom_pane/chat_composer_history.rs`: same as above. - `tui/src/bottom_pane/chat_composer.rs`: record rich history entries and restore them on Up/Down; update Ctrl+C history and tests. - `tui2/src/bottom_pane/chat_composer.rs`: same as above.
Charley Cunningham ·
2026-01-27 18:39:59 -08:00 -
Add composer config and shared menu surface helpers (#9891)
Centralize built-in slash-command gating and extract shared menu-surface helpers. - Add bottom_pane::slash_commands and reuse it from composer + command popup. - Introduce ChatComposerConfig + shared menu surface rendering without changing default behavior.
Ahmed Ibrahim ·
2026-01-26 23:16:29 +00:00 -
feat(tui): retire the tui2 experiment (#9640)
## Summary - Retire the experimental TUI2 implementation and its feature flag. - Remove TUI2-only config/schema/docs so the CLI stays on the terminal-native path. - Keep docs aligned with the legacy TUI while we focus on redraw-based improvements. ## Customer impact - Retires the TUI2 experiment and keeps Codex on the proven terminal-native UI while we invest in redraw-based improvements to the existing experience. ## Migration / compatibility - If you previously set tui2-related options in config.toml, they are now ignored and Codex continues using the existing terminal-native TUI (no action required). ## Context - What worked: a transcript-owned viewport delivered excellent resize rewrap and high-fidelity copy (especially for code). - Why stop: making that experience feel fully native across the environment matrix (terminal emulator, OS, input modality, multiplexer, font/theme, alt-screen behavior) creates a combinatorial explosion of edge cases. - What next: we are focusing on redraw-based improvements to the existing terminal-native TUI so scrolling, selection, and copy remain native while resize/redraw correctness improves. ## Testing - just write-config-schema - just fmt - cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs -p codex-core - cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs -p codex-cli - cargo check - cargo test -p codex-core - cargo test -p codex-cli
Josh McKinney ·
2026-01-22 01:02:29 +00:00 -
Ahmed Ibrahim ·
2026-01-20 20:54:15 -08:00 -
Prompt Expansion: Preserve Text Elements (#9518)
Summary - Preserve `text_elements` through custom prompt argument parsing and expansion (named and numeric placeholders). - Translate text element ranges through Shlex parsing using sentinel substitution, and rehydrate text + element ranges per arg. - Drop image attachments when their placeholder does not survive prompt expansion, keeping attachments consistent with rendered elements. - Mirror changes in TUI2 and expand tests for prompt parsing/expansion edge cases. Tests - placeholders with spaces as single tokens (positional + key=value, quoted + unquoted), - prompt expansion with image placeholders, - large paste + image arg combinations, - unused image arg dropped after expansion.
charley-oai ·
2026-01-20 18:30:20 -08:00 -
fix(tui): harden paste-burst state transitions (#9124)
User-facing symptom: On terminals that deliver pastes as rapid KeyCode::Char/Enter streams (notably Windows), paste-burst transient state can leak into the next input. Users can see Enter insert a newline when they meant to submit, or see characters appear late / handled through the wrong path. System problem: PasteBurst is time-based. Clearing only the classification window (e.g. via clear_window_after_non_char()) can erase last_plain_char_time without emitting buffered text. If a buffer is still non-empty after that, flush_if_due() no longer has a timeout clock to flush against, so the buffer can get "stuck" until another plain char arrives. This was surfaced while adding deterministic regression tests for paste-burst behavior. Fix: when disabling burst detection, defuse any in-flight burst state: flush held/buffered text through handle_paste() (so it follows normal paste integration), then clear timing and Enter suppression. Document the rationale inline and update docs/tui-chat-composer.md so "disable_paste_burst" matches the actual behavior.
Josh McKinney ·
2026-01-14 01:42:21 +00:00 -
fix(tui): document paste-burst state machine (#9020)
Add a narrative doc and inline rustdoc explaining how `ChatComposer` and `PasteBurst` compose into a single state machine on terminals that lack reliable bracketed paste (notably Windows). This documents the key states, invariants, and integration points (`handle_input_basic`, `handle_non_ascii_char`, tick-driven flush) so future changes are easier to reason about.
Josh McKinney ·
2026-01-13 11:48:31 -08:00