Skip to content

feat(ios): word-paced fade-in for streaming assistant prose#100884

Merged
steipete merged 1 commit into
mainfrom
feat/ios-streaming-reveal-nash
Jul 6, 2026
Merged

feat(ios): word-paced fade-in for streaming assistant prose#100884
steipete merged 1 commit into
mainfrom
feat/ios-streaming-reveal-nash

Conversation

@steipete

@steipete steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Related: #100699

What Problem This Solves

Streamed assistant prose in the iOS/macOS chat currently appears in raw chunks — text pops in a delta at a time, which reads as jittery compared to modern chat clients.

Why This Change Was Made

Adds a word-paced fade-in for streaming assistant prose in the shared renderer. The pacing core is a pure, deterministic module (no timers, callers pass now): appended words get staggered 40ms reveals with a 120ms fade, a burst catch-up rule compresses backlogs so the queue clears within 400ms, reveal is strictly monotonic (a shown word never un-shows), and non-append rewrites (e.g., markdown that reformats earlier text when a ** closes) reveal instantly and restart pacing. Only the last prose block of the streaming bubble animates; code, tables, and math keep their existing render-plain-until-complete behavior. Markdown is parsed once per delta and cached — per-frame work is opacity restyling of a bounded 24-word tail only, and the driving TimelineView stops when nothing is fading. Reduce Motion bypasses the effect entirely. Final transcript rendering is untouched.

User Impact

Streaming responses read smoothly word by word instead of popping in chunks; users with Reduce Motion enabled keep the current instant behavior; completed messages render exactly as before.

Evidence

  • swift test --package-path apps/shared/OpenClawKit — full suite green (453 Swift Testing + 21 XCTest). New ChatStreamingRevealTests (word boundaries incl. Unicode/CJK, staggered deadlines, burst catch-up cap, monotonicity, rewrite reset); ChatStreamReplayTests extended with consecutive streamed-turn convergence (final transcript text always equals the full streamed text, no state leaks between turns).
  • iOS xcodebuild build-for-testing green; streaming-bubble smoke case added to SwiftUIRenderSmokeTests.
  • swiftformat --lint via the iOS filelist — clean; i18n inventory unchanged (native:i18n:sync idempotent on this diff).
  • Structured review (codex) — one lifecycle finding fixed during development, second pass clean.
  • Known gap: live visual smoothness/Reduce Motion not observed on-device in this environment; logic-level proof only.

@openclaw-barnacle openclaw-barnacle Bot added app: ios App: ios scripts Repository scripts size: L maintainer Maintainer-authored PR labels Jul 6, 2026

@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: 6959ac4d49

ℹ️ 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".


private func proseText(_ prose: ChatMarkdownProse, index: Int) -> SwiftUI.Text {
guard let reveal = self.reveal, reveal.blockIndex == index else {
return SwiftUI.Text(prose.attributed)

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.

P1 Badge Restore branded-font coverage for prose Text helpers

When OpenClawTypographyTests.iOS app text and control calls keep branded font boundaries runs, it scans shared chat UI sources and only accepts a Text(...) call when a branded font token or .font(self.font) appears within the next 12 lines. This new helper returns SwiftUI.Text here, and the new concatenated SwiftUI.Text calls in revealedText have the same shape, so the typography audit reports these new lines even though blockView applies the font farther away; keep the font application local to the helper or add a narrow audit allowance before this lands.

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 6, 2026, 10:32 AM ET / 14:32 UTC.

Summary
The branch adds a shared SwiftUI streaming assistant text path that snapshots markdown and fades the last prose block word by word, with reveal/replay/smoke tests and iOS filelist coverage.

PR surface: Other +651. Total +651 across 7 files.

Reproducibility: yes. for the review blockers: source inspection and the existing typography audit path show the introduced Text offenders, and a delayed hel to hello world transition follows the line 72 filter to a skipped fading range. The live visual smoothness claim remains unproven.

Review metrics: 1 noteworthy metric.

  • Typography guard offenders: 3 introduced Text calls. The new SwiftUI.Text helper constructions are visible to the existing iOS branded-font audit before merge.

Root-cause cluster
Relationship: partial_overlap
Canonical: #100699
Summary: The linked issue is the broader canonical mobile chat polish bundle; this PR implements only the iOS streaming reveal slice and does not resolve the whole issue.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦪 silver shellfish
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Fix or narrowly allow the new SwiftUI.Text helper calls so the iOS typography audit stays green.
  • [P1] Handle delayed mid-word extensions in the reveal state machine and add a focused regression test for character-token chunking.
  • [P1] Add a short redacted simulator/device recording or equivalent live artifact showing streaming fade-in and Reduce Motion behavior.

Proof guidance:

  • [P1] Needs real behavior proof before merge: Missing: the PR body says live smoothness and Reduce Motion were not observed; add a redacted simulator/device recording or equivalent live artifact showing the changed streaming path, then update the PR body to trigger re-review or ask a maintainer to comment @clawsweeper re-review.

Risk before merge

  • [P1] The PR body reports logic/build proof only and explicitly leaves the visible streaming smoothness plus Reduce Motion behavior unobserved in a simulator or device recording.
  • [P1] This adds default motion to the stable mobile chat surface, so maintainer UX acceptance remains separate from fixing the code-level blockers.

Maintainer options:

  1. Decide the mitigation before merge
    Land the streaming reveal only after repairing the typography audit and mid-word chunk pacing, adding real simulator/device proof for normal and Reduce Motion paths, and getting maintainer UX acceptance.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] Manual review is needed because the narrow code blockers are coupled to missing contributor visual proof and a product-direction call for default chat motion.

Maintainer decision needed

  • Question: Should OpenClaw accept word-paced streaming reveal in the shared iOS/macOS chat renderer after the code blockers and visual proof gap are resolved?
  • Rationale: Automation can identify the concrete Swift blockers, but it cannot decide the product acceptability of new default chat motion in the stable mobile surface.
  • Likely owner: steipete — He owns the linked mobile polish issue and has the strongest recent history on the affected mobile chat renderer path.
  • Options:
    • Accept after fixes and proof (recommended): Repair the typography audit and delayed mid-word reveal behavior, add real visual proof for streaming and Reduce Motion, then continue maintainer UX review.
    • Defer to the polish bundle: Keep the broader mobile polish issue open and pause this PR until streaming reveal is explicitly prioritized.
    • Request a narrower prototype: Ask for a smaller visual demo or design review before taking renderer changes into the main app.

Security
Cleared: No concrete security or supply-chain concern found; the diff adds Swift UI/rendering code, tests, and one existing filelist entry without dependencies, lockfiles, secrets, workflows, or new network behavior.

Review findings

  • [P1] Restore local branded-font coverage for prose Text — apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift:89
  • [P2] Fade characters appended within an existing word — apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatStreamingReveal.swift:72
Review details

Best possible solution:

Land the streaming reveal only after repairing the typography audit and mid-word chunk pacing, adding real simulator/device proof for normal and Reduce Motion paths, and getting maintainer UX acceptance.

Do we have a high-confidence way to reproduce the issue?

Yes for the review blockers: source inspection and the existing typography audit path show the introduced Text offenders, and a delayed hel to hello world transition follows the line 72 filter to a skipped fading range. The live visual smoothness claim remains unproven.

Is this the best way to solve the issue?

No as submitted: the shared renderer is a plausible owner boundary, but the patch must fix the typography audit, fade delayed mid-word extensions, and add real visual proof before it is the best merge shape.

Full review comments:

  • [P1] Restore local branded-font coverage for prose Text — apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift:89
    The previous typography blocker is still present. OpenClawTypographyTests scans each Text/Label call and only accepts the ChatMarkdownRenderer exception when .font(self.font) appears within the next 12 lines; this helper returns SwiftUI.Text(prose.attributed) here, and the new concatenated SwiftUI.Text calls in revealedText have the same audit shape, so the focused typography audit will flag them before merge.
    Confidence: 0.95
  • [P2] Fade characters appended within an existing word — apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatStreamingReveal.swift:72
    Late catch: when a stream chunk extends a word after the previous partial word's fade deadline has expired, the old word is dropped and this predicate skips the expanded range because its lower bound is still before oldCharacterCount (for example, hel followed later by hello world). Those newly appended characters render fully opaque while later words fade, so arbitrary token chunking still produces mid-word pops.
    Confidence: 0.91
    Late finding: first raised on code an earlier review cycle already covered.

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 94a0d2ec3b79.

Label changes

Label justifications:

  • P3: This PR is mobile chat polish with code/proof blockers, not an urgent regression or blocked setup path.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Missing: the PR body says live smoothness and Reduce Motion were not observed; add a redacted simulator/device recording or equivalent live artifact showing the changed streaming path, then update the PR body to trigger re-review or ask a maintainer to comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Other +651. Total +651 across 7 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 7 669 18 +651
Total 7 669 18 +651

What I checked:

Likely related people:

  • steipete: Peter Steinberger authored the linked mobile polish issue and has recent merged history on the shared chat renderer and iOS typography surfaces that this PR changes. (role: recent area contributor and likely product owner; confidence: high; commits: 2a741bebb54f, 88f1ec38d4a5; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift, apps/ios/Tests/OpenClawTypographyTests.swift)
  • vincentkoc: Current-main blame attributes the baseline shared chat renderer, Swift filelist, and typography audit logic around the affected paths to Vincent Koc's commit. (role: baseline introducer / adjacent owner; confidence: medium; commits: cbf9acff374c; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift, scripts/ios-write-swift-filelist.mjs, apps/ios/Tests/OpenClawTypographyTests.swift)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (1 earlier review cycle)
  • reviewed 2026-07-06T13:03:14.643Z sha 6959ac4 :: needs real behavior proof before merge. :: [P1] Restore local branded-font coverage for prose Text

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. labels Jul 6, 2026
@steipete
steipete force-pushed the feat/ios-streaming-reveal-nash branch from 6959ac4 to 0ad535f Compare July 6, 2026 14:10

@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: 0ad535f7bd

ℹ️ 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".

Comment on lines +71 to +72
let appendedRanges = ranges.filter {
$0.lowerBound >= oldCharacterCount && !trackedStarts.contains($0.lowerBound)

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 Fade characters appended within an existing word

When a stream chunk extends the current word after its previous fade deadline has expired, the old range is dropped above and this predicate skips the updated word because its lower bound is still before oldCharacterCount (for example, "hel" followed later by "hello world"). The newly appended characters then render fully opaque while later words fade, so arbitrary token/character chunking still produces visible mid-word pops; treat the expanded trailing word as continued/new fading text instead of only accepting ranges that start after the old character count.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit 83f0527 into main Jul 6, 2026
85 checks passed
@steipete
steipete deleted the feat/ios-streaming-reveal-nash branch July 6, 2026 14:51
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

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

Labels

app: ios App: ios maintainer Maintainer-authored PR P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. scripts Repository scripts size: L status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant