Zed agent UI lessons for EXEPERT
This note records what makes Zed's agent conversation feel compact and smooth, how its visible Thinking and Raw Input surfaces work, and which parts are useful for EXEPERT.
The comparison was verified against the local Zed reference at commit
d9ad6aff67e4 and the EXEPERT source on August 28, 2026. EXEPERT uses a
modified, vendored GPUI layer while its resolved Zed dependencies remain pinned
to cc053a4a6fa2fd0e8793201ed9099466af1be0b1, so the two applications share a
rendering lineage but not an identical API or source snapshot.
Zed renders structured thought chunks supplied by an agent protocol. It does not infer, reconstruct, or expose a model's private hidden reasoning. EXEPERT must preserve the same boundary: show only provider-authorized reasoning summaries or ordinary status text, and never relabel hidden chain-of-thought as user-visible content.
The short answer
Zed's perceived quality comes from three layers working together:
- A structured event model distinguishes messages, thoughts, tool activity, and generation state.
- A quiet visual hierarchy keeps assistant prose flat while thoughts and tool details use compact disclosures and timeline lines.
- A virtualized variable-height list updates and remeasures only the entries that changed, while stable state preserves scroll position and tail follow.
EXEPERT now applies that third layer to both Chat sources. Codex and Native Chat share one adaptive transcript core, while the persistent EXEPERT | ZED selector changes presentation semantics without changing models, transports, privacy, or stored messages.
Implemented EXEPERT and ZED presentation contract
The Chat header exposes one global presentation preference from
CodexUiPreferences.chat_presentation. Missing settings default to
EXEPERT, and the additive field remains compatible with settings schema
version 2. Both Native Chat and Codex read the same value. Below 720 px of Chat
stage width, the visible tabs become an opaque two-option menu so the header
does not jump or clip.
Changing presentation closes geometry-dependent message and composer popups, then remeasures the affected stable rows. It does not clear drafts, switch chats or tasks, stop speech, cancel a turn, alter attachments, or issue a network request. Logical list bookmarks therefore survive style changes.
EXEPERT preserves the established card and disclosure hierarchy. ZED uses EXEPERT's own palette, type, icons, and Rust/GPUI components with:
- compact 8 px user cards inside a centered 760 px reading column;
- flat assistant prose without a repeated assistant label;
- quiet timeline guides and borderless contextual actions;
- a 12 px composer with no shadow;
- a stable three-dot Working row before the first visible Native answer token; and
- compact
Thinkingand developer-activity disclosures in Codex.
This is a presentation comparison, not a performance tier. Both modes use the same optimized transcript implementation.
How Zed presents agent thinking
The ACP thread model stores assistant content as distinct message and thought chunks. A live thought can therefore be rendered independently from the final answer instead of parsing special markers from prose.
Zed provides four display policies through ThinkingBlockDisplay:
| Policy | Behavior |
|---|---|
Auto | Expands the currently streaming thought, then collapses it when the stream moves on unless the user explicitly toggled it. |
Preview | Opens the live thought in a constrained preview; an explicit user toggle reveals the full body. |
AlwaysExpanded | Starts thoughts open while still respecting an explicit user override. |
AlwaysCollapsed | Starts thoughts closed while still allowing the user to open one. |
The visual treatment is intentionally quiet: a muted thinking icon and label, a small disclosure affordance, and a thin left guide beside the open body. The preview is height-constrained and fades at its edge. A separate synthetic generation row uses a small Braille-frame spinner while the agent is working.
The apparent smooth dropdown is primarily fast native re-layout with stable state. In the inspected source, the body is conditionally mounted and the chevron changes; there is no general-purpose animated height tween for this disclosure. That distinction matters because EXEPERT can achieve the same calm feel without animating a large, continuously changing Markdown region.
A safe EXEPERT equivalent
For Codex, the matching UI should be a Working status plus an expandable
Reasoning summary that is fed only by the App Server's summary events. The
existing protocol boundary already opts out of item/reasoning/textDelta and
removes raw or encrypted reasoning fields before state reduction.
For Native Chat, a thinking surface is truthful only when the selected backend provides an explicit, displayable summary field. Otherwise EXEPERT should show a generic activity state such as Working, not fabricated internal thoughts.
Why Zed scrolling feels smooth
Zed does not lay out the full conversation on every streamed update. Its conversation view uses GPUI's variable-height virtual list and keeps stable entry state outside the visible row tree.
The important mechanics are:
ListStatetracks variable row heights and the current scroll anchor.- A
SumTreefinds the visible item range without scanning every message. - Only visible rows plus an overdraw buffer are rendered.
- Cached measurements are reused for unchanged entries.
- A streaming update remeasures its affected row rather than rebuilding the entire transcript.
- Insertions and removals splice the entry list at the affected range.
- Tail follow is conditional, so inspecting older content is not interrupted by incoming tokens.
The overdraw buffer is deliberate. Pre-rendering a limited region above and below the viewport prevents blank flashes when a wheel or touchpad moves faster than a frame can be measured.
How EXEPERT compares
EXEPERT Codex already follows the same architecture in
desktop/exepert-desktop/src/ui/codex_transcript.rs:
- a GPUI
ListStatevirtualizes the transcript; - stable row keys and revisions preserve row identity;
- prefix/suffix reconciliation splices only the changed range;
- only changed rows are remeasured;
- 512 px of overdraw protects fast scrolling;
- a 48 px near-bottom threshold controls automatic tail follow;
- each task retains its own scroll bookmark; and
- non-precision wheel input receives a bounded 140 ms glide, while precision input and reduced-motion behavior remain direct.
Completed agent Markdown also retains a stable TextView identity so unchanged
responses are not reparsed on every parent repaint.
This means a Zed-like Codex experience should preserve the existing EXEPERT transcript engine. Replacing it would add risk without addressing the main visual differences.
Native Chat now projects setup, empty, message, footer, failure, and streaming
states into stable NativeTranscriptRow values and renders them through the
same adaptive engine. Bookmarks are keyed by ChatId, so returning to an older
chat restores its logical reading position. Native SSE deltas are drained in
the existing 40 ms batch and update one bounded provisional row once per UI
batch. The provisional buffer is process-local, capped at 512 KiB, and is
atomically replaced by the authoritative stored response. It is never
persisted, sent back as history, or described as reasoning.
How Zed styles Raw Input
Zed converts tool input into Markdown before rendering it:
nullproduces no block;- booleans, numbers, and strings render as plain content; and
- objects and arrays are pretty-printed into a fenced
jsoncode block.
The surrounding tool timeline uses a small muted, buffer-font label such as Raw Input, a thin left guide, compact spacing, and an inset code surface. The shared Markdown code renderer supplies syntax highlighting, horizontal scrolling, and hover actions for copy and wrapping. The result feels like part of the conversation rather than a separate diagnostics console.
EXEPERT should borrow this presentation only for explicitly safe structured data. Its Codex reducer currently keeps bounded, allowlisted developer details and strips raw reasoning, encrypted content, credentials, and unrestricted protocol payloads. A future details card should retain that policy:
- render only allowlisted keys;
- pretty-print bounded JSON;
- cap string length, collection size, and nesting depth;
- exclude prompts, authorization headers, tokens, encrypted fields, and raw reasoning;
- label task-level metrics as task-level rather than response-level; and
- provide wrap and copy controls without copying hidden fields.
Calling an allowlisted projection Structured details is more accurate than calling it Raw Input. The latter label should be used only when the entire displayed value is both intentionally exposed by the provider and safe to show.
Visual hierarchy worth adopting
Zed's assistant output is mostly flat rather than placed in a large bubble. User prompts retain a compact card treatment, while assistant prose, thoughts, and tool activity share one aligned reading column. Tool events use small icons, muted labels, and vertical guides; detailed content appears only after a disclosure. A centered maximum content width prevents long lines without making short replies look like oversized cards.
For EXEPERT, the useful translation is:
- keep user messages compact and right-aligned;
- keep authoritative agent prose flat and left-aligned;
- use a quiet icon disclosure instead of prominent
EXPAND/COLLAPSEtext; - show one synthetic Working row during generation;
- let an
AutoorPreviewpolicy manage provider-authorized summaries; - present tool and developer activity as a restrained timeline; and
- keep contextual actions under completed responses rather than inside the prose surface.
These changes can make the agent feel more alive without pretending that a UI animation is evidence of consciousness or emotion.
Current architecture comparison
| Concern | Zed reference | EXEPERT Codex | EXEPERT Native Chat |
|---|---|---|---|
| Runtime | Native Rust/GPUI | Native Rust/modified GPUI | Native Rust/modified GPUI |
| Conversation model | Structured agent entries and chunks | Server-authoritative thread, turn, and item events | User/assistant messages with bounded metadata |
| Visible thinking | Provider/ACP thought chunks | Provider-authorized reasoning summaries only | Generic working state unless a backend supplies a displayable summary |
| Raw reasoning | Does not imply reconstruction of hidden reasoning | Explicitly opted out and stripped | Not part of the message contract |
| Transcript layout | Variable-height virtual list | Shared variable-height virtual list | Shared variable-height virtual list |
| Incremental update | Splice/remeasure the affected entry | Stable-key reconciliation and changed-range remeasurement | Stable rows, changed-range remeasurement, and one provisional streaming row |
| Tail behavior | Follow only while anchored at the end | Near-bottom follow, per-task bookmark, unseen-content state | ResponseFollowState and scroll-to-bottom state |
| Tool details | Raw values rendered through Markdown/code blocks | Bounded allowlisted developer projection | No equivalent rich tool timeline yet |
| Primary opportunity | Mature baseline | Continue measuring disclosure usability | Add structured backend events only when providers expose truthful safe fields |
Implemented privacy and disclosure boundary
- Codex
Thinkingconsumes only the reducer's authorizedReasoningSummary; the App Server raw-reasoning notification remains opted out and recursively stripped. - An actively streaming summary may open automatically in a 256 px faded preview. It collapses when it becomes authoritative unless the user made an explicit expansion choice. Explicit expansion shows the full summary, and explicit collapse overrides automatic preview.
- Developer
Structured detailsare generated only from the reducer's bounded allowlisteditem.detailsvalue. Wrap and copy never consult raw protocol payloads. - Native Working and provisional text represent request state and visible answer tokens. They are not an inferred thought trace.
- Presentation expansion state, provisional output, and Codex automatic preview state are process-local and excluded from settings and diagnostics.
Status-rail cleanup
Human collaboration now enters from the desktop status toolbar rather than a
floating bubble over workspace content. The stable
collaboration-popup-trigger is a 28×20, 2 px-radius action at the non-shrinking
right edge of the status rail's left group. It retains the room connection,
peer state, connected accent, unread badge, tooltip, accessible label, and
focus handle. Its 16 px user-group glyph is byte-identical to the checked-out
Zed assets/icons/user_group.svg reference at commit
d9ad6aff67e47de43abb270d22de75dd950f1b48, isolated under
assets/third_party/zed/ with both the repository-level GPLv3 text and the
source icon directory's Lucide ISC notice. That icon-directory notice does not
enumerate whether user_group.svg is Lucide-derived, so EXEPERT records the
ambiguity instead of declaring which license controls the glyph. The production
asset source also embeds both notices and THIRD_PARTY_NOTICES.md so the
standalone executable retains the legal materials. The collaboration panel
remains bottom-right and opens 12 px above the rail.
The visible EXEPERT brand now occupies the first passive status slot instead of
the primary title bar. The primary title area remains a native drag surface,
its Windows TitlebarOptions title remains EXEPERT, and the detached Inspector
and Runtime Log title bars are unchanged. During ambient activity the flexible
status slot renders muted ACTIVITY / followed by one accented AMBIENT; its
flexible width lets that activity text truncate before collaboration and build
controls at narrow sizes.
The adjacent desktop-version-badge also uses EXEPERT's sharp 2 px radius.
Collaboration and build-details surfaces are mutually exclusive visually:
opening one dismisses the other, but dismissing the collaboration panel does
not leave or end the room. Left/Right toolbar navigation moves between the two
interactive controls. Benchmark mode omits collaboration and presents the
version as non-interactive status text.
Source map
The following local files support this comparison. Symbol names are more durable than line numbers as the reference checkouts evolve.
| Topic | Local source |
|---|---|
| Zed message and thought chunks; raw JSON conversion | repo-reference/zed/crates/acp_thread/src/acp_thread.rs (AssistantMessageChunk, markdown_for_raw_output) |
| Zed thought expansion policies | repo-reference/zed/crates/settings_content/src/agent.rs and repo-reference/zed/crates/agent_ui/src/entry_view_state.rs |
| Zed thought, spinner, message, and tool rendering | repo-reference/zed/crates/agent_ui/src/conversation_view/thread_view.rs |
| Zed incremental entry reconciliation | repo-reference/zed/crates/agent_ui/src/conversation_view.rs |
| Zed virtual-list range and measurement engine | repo-reference/zed/crates/gpui/src/elements/list.rs |
| Zed code-block copy and wrap controls | repo-reference/zed/crates/markdown/src/markdown.rs |
| Zed user-group glyph | repo-reference/zed/assets/icons/user_group.svg at d9ad6aff67e47de43abb270d22de75dd950f1b48 |
| Bundled glyph, GPL text, and icon-directory ISC notice | desktop/exepert-desktop/assets/third_party/zed/ |
| EXEPERT shared transcript reconciliation and scroll state | desktop/exepert-desktop/src/ui/transcript.rs |
| EXEPERT Native and Codex transcript views | desktop/exepert-desktop/src/ui/native_chat_transcript.rs and desktop/exepert-desktop/src/ui/codex_transcript.rs |
| EXEPERT stable Codex Markdown rows | desktop/exepert-desktop/src/ui/codex.rs |
| EXEPERT raw-reasoning firewall | desktop/exepert-desktop/src/codex/protocol.rs |
| EXEPERT authoritative Codex reduction | desktop/exepert-desktop/src/codex/state.rs |
| EXEPERT Native Chat bounds and message model | desktop/exepert-desktop/src/chat.rs |
No Zed Rust code is copied to implement these lessons. The sole copied asset is
the unmodified user-group SVG described above; all surrounding Rust/GPUI
components remain original EXEPERT code. THIRD_PARTY_NOTICES.md records its
pinned provenance, bundled-file checksums, both potentially relevant upstream
license notices, and the requirement for legal review before public release.
That attribution is not a claim that either license conclusively applies, that
the asset is compatible with EXEPERT's MIT distribution, or that the repository
as a whole is license-cleared.