TUI: clipboard image paste for text-only models saves the PNG, then discards its path — insert the path as text instead
Summary
When the active model does not advertise image input, pasting a clipboard image in the TUI writes the image to a temp file and then throws the path away, leaving only a warning. Every neighboring code path in the same situation preserves the path as plain text. Making the clipboard-paste branch do the same would let text-only models (paired with vision-bridge tooling) keep working, with a very small change.
Current behavior (rust-v0.149.1)
- Ctrl+V with an image on the clipboard always writes a temp PNG first:
paste_image_to_temp_png()createscodex-clipboard-*.pngbefore any modality check (clipboard_paste.rs#L121). attach_imagethen checkscurrent_model_supports_images(); when false it emits a warning and returns, discarding the freshly saved path (chatwidget/interaction.rs#L204). The file stays on disk; its path survives only in debug logs.
Inconsistency with neighboring paths
- Pasting an image file path (rather than a bitmap) into the composer with a text-only model falls through to
insert_str, so the path is kept as plain text (chat_composer.rs#L1151). - Core request assembly for text-only models also preserves the path:
strip_images_when_unsupportedreplaces only theInputImagepixels with a placeholder, while the<local_image [Image #N] path="...">text tag survives (context_manager/normalize.rs#L317, protocol/models.rs#L1739).
So the design elsewhere is consistently "no pixels for text-only models, but keep the path as text". The TUI clipboard-paste branch is the one place that drops it.
Proposed change
In attach_image's unsupported branch, insert the already-saved temp path into the composer as plain text (same treatment as a pasted image file path), optionally keeping the existing warning. No request-level changes needed: core already strips pixels for text-only models.
Why it matters
Users on text-only models increasingly pair Codex with vision-bridge skills/tools that read an image from a path appearing in the conversation. Today the same user action (Ctrl+V) works in flows where the path label survives, but dead-ends in the TUI even though the bytes are already on disk. Reference report: https://github.com/liustack/modlens/issues/78
Suggested test coverage
- Text-only model + clipboard bitmap → composer contains the temp PNG path as text; submitted request contains no
input_image. - Image-capable model keeps the current attachment behavior.
- Paths with spaces; WSL (PowerShell fallback already returns a converted path).
2 Comments
Out of curiosity, which model(s) are you using that don't have vision capabilities?
Verified local patch evidence for openai/codex#40595
I reproduced the text-only-model branch at unit level and validated a minimal local patch against the current public TUI code.
Root cause
paste_image_to_temp_png()saves the clipboard bitmap before modality checking.ChatWidget::attach_image()then warns and returns when the active model does not advertise image support, leaving the composer unchanged and discarding the only user-visible reference to the saved PNG.Tested behavior
The local patch inserts the already-saved path into the composer as plain text in the unsupported-image branch, keeps the existing warning, and leaves image attachments empty. Submitting the draft produces a normal
UserInput::Text, not an image payload.Verification on the repository-pinned Rust 1.95.0 toolchain:
Changed public-source locations:
codex-rs/tui/src/chatwidget/interaction.rscodex-rs/tui/src/chatwidget/tests/composer_submission.rsThe focused test verifies all four boundaries: path text is retained, no local image attachment is created, the existing warning is emitted, and submission remains text-only.
Per the repository contribution policy, I have not opened a code PR. I can provide the two-file patch if useful to the maintainers.