This document defines the current CodexUI interaction contract. AISuite and the Codex app-server own protocol and domain semantics; CodexUI owns only local presentation, input, selection, and scroll state.
PresentationModel is the sole retained authoritative store for normalized UI
state. The message view is a projection of its selected thread plus
client-local prompt admissions; cards and inspectors do not maintain a second
domain store.
The conversation has one semantic grouping level: an app-server turn contains its items in server order. Authoritative cards are keyed by stable thread, turn, and item IDs; local prompt cards are keyed by their submission IDs. The same keyed reconcile path handles initial display and updates, mutating a card in place when its visible data changes. An identical visible projection does not rebuild widgets or change geometry.
Local prompt admission resumes bottom following when the only pause was caused by composer overlay growth, so the complete pending prompt becomes visible. It never overrides a pause created by user scrolling.
Mouse-wheel and touchpad gestures use Qt's native platform/device scroll handling. CodexUI only records whether the resulting position follows the bottom or is owned by the user.
- The selected thread is identified by its stable app-server thread ID.
- Once selected, a hydrated thread remains visible in the sidebar for the session even when it is outside the ordinary top-level thread ordering; an authoritative removal still removes it, and other rows retain app-server ordering.
- Sending always targets the visibly selected thread. CodexUI validates the visible selection before dispatch and never creates a thread as an implicit fallback for missing or inconsistent selection state.
- A new thread is created only from an explicit New Thread intent. Its dialog captures the workspace, optional name, instructions, and ephemeral state.
- Background thread activity, list refreshes, reconnects, and creation by another frontend never change the user's selected thread.
- Selecting a thread hydrates it once per bridge connection even when the discovery result already contains an active turn. The full read is merged into the retained per-thread presentation, so live Plan, Agents, and Changes state cannot be erased by an incomplete reconstruction. Reload remains the explicit forced fresh-read action.
Submitting a prompt creates a client-local pending prompt card at the bottom of the destination thread immediately. The card uses a muted version of the normal blue user-card treatment, with a brighter blue highlight sweeping left and right across it until the app-server acknowledges the operation.
Each pending prompt has a process-wide client-local submission ID and remains
associated with its destination thread. It therefore remains visible when the
user switches threads and returns. On
successful acknowledgment, the card shows a short accepted sweep before it
becomes a normal user message. If the authoritative app-server item arrives
during that transition, it inherits the pending card's stable visual anchor and
replaces it after the 500-millisecond transition completes. Only the correlated
turn.start or turn.steer completion callback acknowledges a prompt;
conversation events never infer acknowledgment. Each operation carries a
unique clientUserMessageId, which binds the authoritative user item without
confusing identical prompt text. A failed submission remains visible with an
explicit error state.
The composer is cleared immediately after local admission and remains enabled. Users may enter additional prompts while earlier prompts await acknowledgment. CodexUI queues submissions per thread and dispatches them in order: only one unacknowledged prompt operation is in flight for a thread. After each result, the next queued prompt is sent using the app-server state produced by the preceding acknowledgment. Different threads remain independent.
Submission waits until the destination thread has completed its connection-
generation hydration. A provider-marked notLoaded thread is resumed before
the turn operation. If a submission still receives a transient thread-not-found
result, CodexUI performs one bounded resume-and-retry; a repeated failure is
shown on the pending card rather than retried indefinitely. If hydration has
failed, submission is rejected without clearing the composer draft; Reload
must succeed before that prompt can be admitted. A disconnect between admission
and dispatch leaves the pending card in place and unsent until bridge-open
re-drives it. An active resume prevents a concurrent hydration read or turn
operation for the same thread.
For an explicit new-thread draft, prompts entered while thread.create is in
flight remain attached to that draft. When creation succeeds, all pending
prompts move to the returned stable thread ID and are dispatched in order.
The message view smoothly follows incoming content only while it is already at the bottom. Consecutive geometry changes retarget one short, monotonic animation to the newest bottom. If the user scrolls upward, the animation stops immediately and automatic following pauses so the current text can be read. Returning to the bottom re-enables following.
Follow/pause mode and the visible-card/pixel-offset anchor are retained per thread and restored when the user switches back.
This policy applies to new messages, streaming updates, pending prompt cards, and card reconstruction. It is based on the scroll bar's actual bottom state, not on turn activity.
While following is paused, CodexUI anchors the first visible card and its pixel offset. Appends below the viewport keep the scrollbar value unchanged; card reflow or reconstruction restores that visual anchor after Qt completes layout. Incoming data therefore cannot move the user's reading position merely because content above or below it changed size. Protocol updates that do not change a card's visible projection do not rebuild that card. Multiple visible card changes from one refresh are applied as one paint-suppressed layout transaction with one anchor restoration, including streaming Command execution updates. Incoming deltas are coalesced to at most one reconcile per display interval; growing text and Command execution output are appended in place instead of being recopied and rebuilt for every delta. New authoritative cards are inserted at their server-ordered position without reconstructing retained cards. While following is paused, the effective history window expands with incoming cards so its visible anchor is not evicted; the requested bound is restored after following resumes.
User scrolling to the current bottom re-enables following. A generic Qt range clamp caused by card reflow does not count as user intent and cannot silently re-enable following. Composer contraction is the explicit exception: after its trailing space is removed, CodexUI recomputes whether the resulting clamped position is the new bottom.
The complete center region is wheel- and touchpad-scroll sensitive. Wheel events over non-scrollable center chrome and the horizontal splitter handles are forwarded to the message view. A nested scrollable control, such as Command execution output, consumes an event while it can scroll in that direction and hands an edge event back to the conversation.
The upcoming-turn controls are anchored to the bottom of the center pane. The prompt editor starts at one line, grows upward for multiline input, and stops at its configured maximum height, after which it scrolls internally.
The message-view layout reserves only the composer's canonical height. When prompt text, attachments, settings, or attention controls increase that height, the composer grows upward as an overlay: the viewport keeps its normal geometry and may be partly covered. An equal-height trailing spacer is added to the scrollable conversation content so the final card can still be moved into the visible region with the normal gap above the composer.
Growing this spacer preserves the current scrollbar value and does not move the messages automatically. Reaching its new maximum re-enables bottom-follow for subsequent content. When the composer returns to canonical height, the spacer is removed; Qt may clamp a former bottom position to the reduced range, after which the normal viewport state and bottom-follow policy apply again.
The card's visible label is Command execution.
Command execution output boxes are created only when output contains printable, non-whitespace text after terminal control sequences are ignored; empty, whitespace-only, and ANSI/control-only output has no output surface. A shown box has no non-content minimum height, grows from zero to a maximum of 220 pixels, and exposes a styled vertical scrollbar only when content exceeds that limit. Its content height is measured synchronously during the outer layout transaction. Streaming output, completion status, and metadata update the retained outer Command execution card in place; they do not replace it. Output follows its bottom while already at the bottom. A manual upward scroll pauses following until the user returns to the bottom. Each output card retains its own follow/pause position across in-place output updates.
The State and Protocol viewers use the common CodexUI scrollbar styling and show vertical scrollbars only when needed. The Protocol log occupies the expanding area of its tab; protocol statistics are displayed below the log. Protocol and State data are diagnostic presentation only and do not create domain authority. Plan, Agents, Changes, and Requests use retained per-thread presentation snapshots, so revisiting a materialized thread does not clear or flash those surfaces while unrelated frames arrive.
The application identity is codex-ui. The executable, desktop entry,
StartupWMClass, application icon name, and installed SVG icon use that same
identity so Linux desktop environments associate the running window with the
correct launcher and taskbar icon.
Long-running operations need scoped progress presentation rather than a global busy state. Candidate scopes include prompt acknowledgment, thread creation, and loading a long thread. Pending prompt acknowledgment already has its own animated highlight sweep. Any additional progress indicator must preserve input and navigation that can safely remain interactive, identify the operation it represents, and avoid suggesting that unrelated threads are blocked. No general spinner contract is defined yet.