Skip to content

feat(chat): render assistant turns as interleaved comment/action bubbles (#3397)#3568

Open
rodboev wants to merge 4 commits into
nesquena:masterfrom
rodboev:pr/interleaved-transcript-bubbles
Open

feat(chat): render assistant turns as interleaved comment/action bubbles (#3397)#3568
rodboev wants to merge 4 commits into
nesquena:masterfrom
rodboev:pr/interleaved-transcript-bubbles

Conversation

@rodboev

@rodboev rodboev commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Thinking Path

  • For CLI-origin and tool-heavy sessions, the current renderMessages() renders each assistant turn as a monolithic block: prose, thinking, and tool cards stacked in a single assistant-turn div. Readers can't visually distinguish natural-language commentary from machine action.
  • The issue author sketched a walkMessageParts() generator that yields typed sub-parts. This is the minimal API needed to render each part as its own bubble.
  • A default-off toggle is the right gate: existing sessions are unaffected, and users opt in per-instance. The toggle follows the same pattern as simplified_tool_calling (set at boot from settings, updated via saveSettings, triggers clearMessageRenderCache + renderMessages).
  • Live-streaming turns are excluded from the interleaved path (guarded by !m._live) because the SSE live card insertion logic at ui.js:7497-7503 places tool cards inline by data-live-tid; mixing that with a sub-part rebuild would produce duplicate nodes.
  • The walkMessageParts generator 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 at ui.js:6992-7073.

What Changed

  • api/config.py: adds interleaved_transcript_bubbles: False to _SETTINGS_DEFAULTS (line 5059) and to _SETTINGS_BOOL_KEYS (line 5211).
  • static/boot.js: reads window._interleavedTranscriptBubbles from settings at line 1697 (boot) and line 1795 (fallback defaults).
  • static/ui.js: adds isInterleavedTranscriptBubbles() predicate and walkMessageParts() generator before isSimplifiedToolCalling() at line 5720; wraps the assistant segment rendering block inside renderMessages() with a conditional at line 6869.
  • static/panels.js: syncs window._interleavedTranscriptBubbles and triggers re-render after saveSettings at line 6036; writes back to settings body in readSettingsFromUi at line 7685.
  • static/index.html: adds settingsInterleavedTranscriptBubbles checkbox in the workspace settings section at line 976.
  • static/style.css: adds .interleaved-bubble* variant rules after .assistant-segment-anchor at line 3941.
  • static/i18n.js: adds settings_label_interleaved_transcript_bubbles and settings_desc_interleaved_transcript_bubbles keys 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

pytest tests/test_config.py -v --timeout=60
pytest tests/ -v --timeout=60

Manual:

  • Enable toggle in Settings; verify a tool-heavy session renders as multiple bubbles per assistant turn.
  • Disable toggle; verify session reverts to standard block view.
  • Verify live streaming is unaffected (no duplicate nodes during active stream).

Risks / Follow-ups

Model Used

Claude Opus 4.8 via Claude Code CLI

Closes #3397

@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces an opt-in "interleaved transcript bubbles" mode that splits each assistant turn into individual comment, thinking, action, and result bubbles instead of rendering a single monolithic block. The feature is guarded by a default-off global flag set at boot, in the settings panel, and in _autosavePreferencesSettings.

  • walkMessageParts generator + render branch (ui.js): handles both Anthropic content-array and OpenAI top-level formats; live-streaming turns are explicitly excluded to avoid conflicts with existing SSE DOM insertion.
  • Settings wiring (panels.js, index.html, boot.js, config.py): adds the checkbox, default value, bool-key registration, and re-render trigger — following the same pattern as simplified_tool_calling.
  • CSS + i18n (style.css, i18n.js): seven new bubble variant rules and two new i18n keys added to all 12 locale objects (non-English strings are untranslated placeholders, as flagged in a prior thread).

Confidence Score: 3/5

Two defects in the new interleaved rendering path need fixing before merging: a stale-global read in saveSettings that can silently drop checkbox changes, and an empty bubble that renders a visible whitespace gap when thinking display is disabled.

The saveSettings function reads window._interleavedTranscriptBubbles rather than the checkbox element, so a user who clicks Save before the debounced autosave fires will have their change quietly overwritten. In the render loop, a thinking bubble div is unconditionally appended even when _showThinking is false, producing a padded transparent gap in every session that has thinking blocks.

static/panels.js (saveSettings stale-global read) and static/ui.js (empty thinking-bubble append)

Important Files Changed

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]
Loading

Reviews (4): Last reviewed commit: "fix(chat): index Anthropic-format tool_r..." | Re-trigger Greptile

Comment thread static/ui.js
Comment thread static/ui.js Outdated
Comment thread static/ui.js
Comment thread static/i18n.js
Comment thread static/ui.js
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Cluster review (#3400 live-to-final epic) — concept + UX call needed before this can move

Reviewed 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 walkMessageParts() generator), behind a default-off toggle, instead of the current monolithic assistant-turn block. That's a direct take on #3400's "tool activity should be visible, quiet, and close to the prose that caused it" goal — but it's one specific visual answer to it, and #3400/#3401 are converging on the Worklog-collapse model (compact activity summary above the final answer). We need to decide whether the bubble layout complements that direction or competes with it before investing in review.

Two things gate it:

  1. Concept fit (maintainer call): does interleaved bubbles belong alongside the shipped Worklog model, or does it pull the surface in a different direction? This is a @nesquena design decision, not something I should merge autonomously.
  2. Screenshot/UX evidence: it's a visible chat-rendering change, so per our standing rule it needs populated-env screenshots at desktop + mobile (and a live before/after) — not a diff read — even once the concept is greenlit. (It's also ~63 commits behind and will need a rebase onto the post-Redesign live-to-final assistant replies #3401 renderMessages().)

Marking ux + hold pending that concept decision. Not rejecting the idea — flagging that it overlaps an in-flight epic redesign and needs Nathan's direction on shape first. Thanks @rodboev.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hold ux User experience / visual polish

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: render session transcript as interleaved AI-comment / CLI-action bubbles

2 participants