Skip to content

fix(ui): keep streamed talk voice-turn transcripts readable#102573

Merged
steipete merged 2 commits into
mainfrom
claude/text-wrapping-bug-4e4b88
Jul 9, 2026
Merged

fix(ui): keep streamed talk voice-turn transcripts readable#102573
steipete merged 2 commits into
mainfrom
claude/text-wrapping-bug-4e4b88

Conversation

@steipete

@steipete steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes #102556.

During a realtime Talk session in the macOS app (Control UI inside WKWebView), the streaming assistant voice turn rendered one character per line: the turn box froze at the width of the first transcript delta (~114px) and grew thousands of pixels tall. Independently, the streamed transcript text itself was corrupted with injected spaces ("Chat G PT", "con vers ational").

Why This Change Was Made

Two independent bugs stacked on the same surface:

  1. WebKit-only layout collapse. .agent-chat__voice-turn was a CSS grid sized shrink-to-fit as a flex item of the voice-turns column. WebKit (Mac/iOS WKWebView) never re-runs the grid flex item's intrinsic sizing while streamed text grows, so the turn kept the width computed for the first one-character delta and every subsequent character wrapped to its own line. Chromium recomputes correctly, which is why existing browser tests never caught it. Converting the turn to a flex row (same visual: speaker | text | streaming dot, baseline-aligned, hugging content up to 780px) sidesteps the WebKit invalidation bug in both engines; probed grid variants (auto track, plain 1fr) stayed collapsed. Side effect: the fixed 12px third grid column is no longer reserved on finished turns that have no streaming dot.

  2. Assistant transcript merge mangling (all engines). mergeRealtimeTranscriptText applied a word-boundary spacing heuristic designed for user ASR re-statements to assistant audio-transcript deltas. Assistant deltas are verbatim fragments that split inside words (OpenAI response.output_audio_transcript.delta, Google Live outputTranscription; the server-side Google provider concatenates them verbatim in extensions/google/realtime-voice-provider.ts), so the heuristic injected a space between letter-adjacent fragments. Assistant entries now merge as strictly verbatim fragment streams (no synthesized characters, which would corrupt content like "Version 1.2" or "docs.openclaw.ai" at punctuation boundaries), and finals handle both provider shapes: OpenAI-style full-transcript finals replace, Google-Live-style tail-fragment finals append. User ASR merge heuristics are unchanged; user transcripts from shipped providers arrive with leading whitespace or as re-statements, which the existing path handles.

User Impact

Talk mode transcripts in the macOS/iOS app and all browsers render as normal wrapped text while streaming, and words are no longer broken apart ("I'm ChatGPT, a conversational AI model ..." instead of a one-character-per-line column of "I ' m C h a t G P T ...").

Before (WebKit): https://raw.githubusercontent.com/steipete/openclaw-pr-assets/main/talk-voice-turn-wrapping/before-webkit-collapsed.png
After (WebKit): https://raw.githubusercontent.com/steipete/openclaw-pr-assets/main/talk-voice-turn-wrapping/after-webkit-fixed.png

Evidence

  • Reproduced against the real Control UI (vite dev server + mock gateway, gateway-relay Talk session, per-character assistant transcript deltas) in Playwright WebKit 26.5: assistant turn measured 114x5238px with a 3px text column before the fix; 780x56px after. Chromium measures identically after the fix (780x56) and the user turn keeps hugging its content (273px).
  • Transcript merge unit-proofed: per-token deltas ["I","'m"," Chat","G","PT",...] previously merged to "I'm Chat G PT, a con vers ational ...", now "I'm ChatGPT, a conversational AI".
  • New regression tests:
    • ui/src/pages/chat/realtime-talk-conversation.test.ts: verbatim per-token and per-character assistant delta merging (including punctuation boundaries like "Version 1.2 is on docs.openclaw.ai"), OpenAI-style authoritative final replacement, Google-Live-style tail-fragment final append (9/9 pass).
    • ui/src/e2e/browser-talk-start-stop.e2e.test.ts: drives a full gateway-relay Talk session through the mock gateway with per-character assistant deltas and asserts the rendered voice-turn text is unmangled plus sane turn geometry (3/3 pass).
  • Existing talk conversation + talk e2e suites pass; the punctuation-spacing heuristic test now exercises the user ASR path, which keeps that behavior.

Talk voice turns collapsed to one character per line in the macOS app
(WKWebView) while streaming: WebKit never re-runs shrink-to-fit intrinsic
sizing for a grid flex-item as its content grows, so the turn froze at
first-delta width. Convert the turn to a flex row (same visual layout).

Assistant transcript deltas also merged through the user-ASR word-boundary
spacing heuristic, mangling verbatim fragments ("Chat G PT"). Assistant
entries now concatenate fragments verbatim, space only sentence-punctuation
joins, and accept both full-transcript and tail-fragment finals.

Fixes #102556
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui size: S maintainer Maintainer-authored PR labels Jul 9, 2026
Drop the sentence-punctuation separator: it invented characters in real
content (Version 1. 2, docs.openclaw. ai) and Google Live tail-finals
could never heal it. The punctuation-spacing heuristic remains user-only.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aab4ab7f9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (looksLikeTranscriptReplacement(existing, incoming)) {
return incoming;
}
return `${existing}${assistantFragmentSeparator(existing, incoming)}${incoming}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve spaces for Google Live text-part finals

When the provider-websocket Google Live path emits an assistant outputTranscription and then a final modelTurn.parts[].text on the same turn, as implemented in ui/src/pages/chat/realtime-talk-google-live.ts:242-269 and exercised by the fixture at ui/src/pages/chat/realtime-talk-google-live.test.ts:284-293, the final text part reaches this append branch. Because assistantFragmentSeparator only inserts spaces after sentence punctuation, an existing partial like hi followed by final text there now renders as hithere; before this change the shared merge path inserted the word-boundary space. Keep the verbatim merge limited to actual transcript deltas, or preserve spacing for these final text-part joins.

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix(ui): keep streamed talk voice-turn transcripts readable This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@steipete
steipete merged commit f141af7 into main Jul 9, 2026
88 of 90 checks passed
@steipete
steipete deleted the claude/text-wrapping-bug-4e4b88 branch July 9, 2026 09:07
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 10, 2026
…#102573)

* fix(ui): keep streamed talk voice-turn transcripts readable

Talk voice turns collapsed to one character per line in the macOS app
(WKWebView) while streaming: WebKit never re-runs shrink-to-fit intrinsic
sizing for a grid flex-item as its content grows, so the turn froze at
first-delta width. Convert the turn to a flex row (same visual layout).

Assistant transcript deltas also merged through the user-ASR word-boundary
spacing heuristic, mangling verbatim fragments ("Chat G PT"). Assistant
entries now concatenate fragments verbatim, space only sentence-punctuation
joins, and accept both full-transcript and tail-fragment finals.

Fixes openclaw#102556

* fix(ui): keep assistant talk fragments strictly verbatim

Drop the sentence-punctuation separator: it invented characters in real
content (Version 1. 2, docs.openclaw. ai) and Google Live tail-finals
could never heal it. The punctuation-spacing heuristic remains user-only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Talk voice-turn transcript collapses to one character per line in the macOS app while streaming

1 participant