feat(chat): render assistant turns as interleaved comment/action bubbles (#3397)#3568
feat(chat): render assistant turns as interleaved comment/action bubbles (#3397)#3568rodboev wants to merge 4 commits into
Conversation
|
| Filename | Overview |
|---|---|
| static/ui.js | Adds isInterleavedTranscriptBubbles(), walkMessageParts() generator, and the interleaved rendering branch in renderMessages(); has an empty thinking-bubble DOM leak when _showThinking is false. |
| static/panels.js | Wires the new checkbox into the settings save/load flow but reads from window._interleavedTranscriptBubbles (a stale global) in saveSettings instead of the checkbox DOM element. |
| api/config.py | Adds interleaved_transcript_bubbles: False to defaults and bool-key set; correct and minimal. |
| static/boot.js | Reads interleaved_transcript_bubbles from settings into window._interleavedTranscriptBubbles at boot and in fallback defaults; follows the exact same pattern as existing flags. |
| static/i18n.js | Adds the two new i18n keys to all 12 locale objects, but all non-English locales receive the English strings verbatim. |
| static/index.html | Adds the settingsInterleavedTranscriptBubbles checkbox in the workspace settings section; consistent with adjacent checkboxes. |
| static/style.css | Adds seven .interleaved-bubble* CSS rules using existing design tokens; the transparent padding on .interleaved-bubble-thinking exposes the phantom-gap bug when thinking is hidden. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[renderMessages - assistant message] --> B{Interleaved mode AND not live?}
B -- No --> C[Standard single assistant-segment div]
B -- Yes --> D[Build toolResultsByTid index]
D --> E[walkMessageParts generator]
E --> F{part.type}
F -- comment --> G[renderMd bubble]
F -- thinking --> H{_showThinking?}
H -- true --> I[thinkingCard bubble]
H -- false --> J[Empty bubble appended - bug]
F -- cli-action --> K[tool name label bubble]
F -- result --> L[pre snippet bubble]
G & I & J & K & L --> M[appendChild to currentAssistantTurn]
M --> N[assistantSegments.set to lastElementChild]
C --> O[assistantSegments.set to seg]
Reviews (4): Last reviewed commit: "fix(chat): index Anthropic-format tool_r..." | Re-trigger Greptile
Cluster review (#3400 live-to-final epic) — concept + UX call needed before this can moveReviewed as part of the live-to-final cluster sweep. This is neither superseded nor a simple rework — it's a net-new rendering model that lands squarely in the territory #3400 is actively redesigning, so it needs a concept decision before code review. What it does: renders each assistant turn as interleaved comment/action bubbles (via a new Two things gate it:
Marking |
Thinking Path
renderMessages()renders each assistant turn as a monolithic block: prose, thinking, and tool cards stacked in a singleassistant-turndiv. Readers can't visually distinguish natural-language commentary from machine action.walkMessageParts()generator that yields typed sub-parts. This is the minimal API needed to render each part as its own bubble.simplified_tool_calling(set at boot from settings, updated viasaveSettings, triggersclearMessageRenderCache + renderMessages).!m._live) because the SSE live card insertion logic atui.js:7497-7503places tool cards inline bydata-live-tid; mixing that with a sub-part rebuild would produce duplicate nodes.walkMessagePartsgenerator handles both Anthropic content-array format (type=text, type=tool_use) and OpenAI top-level format (tool_calls array), matching the same format-detection logic already used in the fallback tool-card builder atui.js:6992-7073.What Changed
api/config.py: addsinterleaved_transcript_bubbles: Falseto_SETTINGS_DEFAULTS(line 5059) and to_SETTINGS_BOOL_KEYS(line 5211).static/boot.js: readswindow._interleavedTranscriptBubblesfrom settings at line 1697 (boot) and line 1795 (fallback defaults).static/ui.js: addsisInterleavedTranscriptBubbles()predicate andwalkMessageParts()generator beforeisSimplifiedToolCalling()at line 5720; wraps the assistant segment rendering block insiderenderMessages()with a conditional at line 6869.static/panels.js: syncswindow._interleavedTranscriptBubblesand triggers re-render aftersaveSettingsat line 6036; writes back to settings body inreadSettingsFromUiat line 7685.static/index.html: addssettingsInterleavedTranscriptBubblescheckbox in the workspace settings section at line 976.static/style.css: adds.interleaved-bubble*variant rules after.assistant-segment-anchorat line 3941.static/i18n.js: addssettings_label_interleaved_transcript_bubblesandsettings_desc_interleaved_transcript_bubbleskeys in all 12 locale objects.Why It Matters
Users reading CLI agent sessions can now see each thought, action, and result as a distinct bubble, making it easier to follow the agent's reasoning chain without parsing a dense mixed block.
Verification
Manual:
Risks / Follow-ups
window._interleavedTranscriptBubblesis a global flag designed for that follow-up.Model Used
Claude Opus 4.8 via Claude Code CLI
Closes #3397