Skip to content

Add Character Streaming text animation - #150

Open
Mahyar (Mac) McDonald (theontho) wants to merge 7 commits into
mainfrom
character-streaming-rework
Open

Add Character Streaming text animation#150
Mahyar (Mac) McDonald (theontho) wants to merge 7 commits into
mainfrom
character-streaming-rework

Conversation

@theontho

@theontho Mahyar (Mac) McDonald (theontho) commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Screen.Recording.2026-07-17.at.9.36.35.AM.mov

Add Character Streaming as an explicit Markdown text-animation mode that smooths chunky server updates while preserving attributed rendering, interaction, layout, and accessibility.

  • Replace the Boolean animation API with .none, .fade, and .characterStreaming, without compatibility aliases
  • Buffer attributed composed graphemes and release exactly one per adaptive 18–4.5ms scheduler opportunity
  • Preserve terminal withholding, completion drain, replacement/restyle behavior, normalization safety, Reduce Motion, and bounded state
  • Render the full rise, scale, blur-to-sharp, and fade transform through UIKit and AppKit TextKit paths
  • Keep Standard Fade available and make Character Streaming the sample default

Closes #149

Validation

  • make lint — 137 files, 0 violations
  • Focused iOS ParagraphAnimationTests and ParagraphViewTests — 39 tests passed
  • Focused macOS ParagraphAnimationTests and ParagraphNSViewTests — 31 tests passed
  • make test — 116 XCTest tests and 39 Swift Testing tests passed
  • make build-sample — succeeded
  • Verified Character Streaming visually on an iPhone 17 Pro simulator running iOS 26.2

OSS readiness

  • No secrets, internal URLs, private identifiers, or product-only service names were added.
  • Public docs, fixtures, or notices were updated if behavior or dependencies changed.
  • Third-party dependency changes (adds, removes, version bumps) are intentional and reviewed. (no third-party dependency changes)
  • Streaming/incomplete markdown behavior remains covered by fixtures or tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace suffix fading with a one-grapheme attributed streaming pipeline, adaptive cadence, full platform transforms, selectable sample styles, and deterministic coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a649c2f7-fe53-4216-9f15-b5aa7237934a
Scope terminal withholding to the structural tail paragraph, preserve scheduler deadlines and fade completion, keep streaming measurement bounded, support live Reduce Motion changes, and align UIKit/AppKit transforms.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a649c2f7-fe53-4216-9f15-b5aa7237934a
Compare streamed replacements by exact UTF-16, clamp released offsets to grapheme boundaries, preserve Fade in attachment table cells, and update the public animation example.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a649c2f7-fe53-4216-9f15-b5aa7237934a
Preserve release cadence across drained queues, coalesce contextual glyph clusters, and render true bitmap blur before the sharp crossfade on UIKit and AppKit.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 78f1a116-c298-4c20-8669-dcded414d291

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new explicit streamed-text animation API (.none, .fade, .characterStreaming) and implements character-by-character streaming using TextKit so streamed Markdown updates appear smoother while preserving attributed rendering, interaction, layout, and accessibility across iOS and macOS.

Changes:

  • Replaces the previous boolean animation flag with MarkdownRenderConfig.TextAnimation and updates builders/docs/sample to use the new API.
  • Implements Character Streaming state/scheduling and custom TextKit glyph rendering (rise/scale/blur→sharp/fade) for UIKit/AppKit paragraph views.
  • Threads stream-completion and “tail branch” environment signals through the render tree so only the correct trailing paragraph withholds/drains the terminal grapheme, and adds/updates tests accordingly.
Show a summary per file
File Description
Sources/MarkdownText/Models/MarkdownRenderConfig.swift Introduces TextAnimation enum and updates config initialization/defaults.
Sources/MarkdownText/Models/MarkdownRenderConfig+Builders.swift Updates builder API to withTextAnimation and adjusts config propagation (needs follow-up fixes).
Sources/MarkdownText/UI/Paragraph/ParagraphAnimation.swift Adds shared constants and core Character Streaming state/logic.
Sources/MarkdownText/UI/Paragraph/CharacterStreamingLayoutManager.swift Adds custom layout manager to render per-glyph transforms including blur crossfade.
Sources/MarkdownText/UI/Paragraph/UIKit/ParagraphUIView.swift Implements TextKit-driven fade/character-streaming in UITextView, plus reuse/animation lifecycle.
Sources/MarkdownText/UI/Paragraph/AppKit/ParagraphNSView.swift Implements TextKit-driven fade/character-streaming in NSTextView.
Sources/MarkdownText/UI/Paragraph/UIKit/ParagraphView+iOS.swift Wires environment (reduce motion/stream completion/tail) into UIKit paragraph view and updates size caching.
Sources/MarkdownText/UI/Paragraph/AppKit/ParagraphView+macOS.swift Wires environment (reduce motion/stream completion/tail) into AppKit paragraph view and updates size caching.
Sources/MarkdownText/UI/Paragraph/ParagraphViewCache.swift Extends caching to reuse views appropriate for character streaming support.
Sources/MarkdownText/UI/BlockView.swift Propagates tail-branch environment down the renderable tree.
Sources/MarkdownText/UI/OrderedListView.swift Propagates tail-branch environment through ordered list children.
Sources/MarkdownText/UI/UnorderedListView.swift Propagates tail-branch environment through unordered list children.
Sources/MarkdownText/UI/DocumentView.swift Resets view identity when animation mode changes; adds env keys for stream completion and tail-branch.
Sources/MarkdownText/StreamedMarkdownView.swift Publishes and injects stream completion state into the environment.
Sources/MarkdownText/UI/TableView.swift Refactors table cell rendering to route animation correctly, including attachment-specific behavior.
Tests/MarkdownTextTests/ParagraphAnimationTests.swift New unit tests covering Character Streaming state, transforms, and invariants.
Tests/MarkdownTextTests/ParagraphViewTests.swift Adds UIKit integration tests for character streaming, accessibility, sizing, and rendering metrics.
Tests/MarkdownTextTests/ParagraphNSViewTests.swift Adds AppKit integration tests mirroring UIKit behavior.
Tests/MarkdownTextTests/CharacterStreamingRenderTestSupport.swift Adds image-metrics helpers used by blur/alpha tests.
Tests/MarkdownTextTests/TableViewTests.swift Adds mapping test for table attachment animation behavior.
README.md Updates usage example to new withTextAnimation API.
Examples/SwiftStreamingMarkdownSample/* Adds settings/UI to select streaming animation and makes Character Streaming the sample default.
Sources/MarkdownText/TextTransition/* Removes prior SwiftUI text-transition-based fade implementation.

Review details

  • Files reviewed: 30/30 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread Sources/MarkdownText/UI/Paragraph/UIKit/ParagraphUIView.swift
Comment thread Sources/MarkdownText/UI/Paragraph/AppKit/ParagraphNSView.swift
Comment thread Sources/MarkdownText/Models/MarkdownRenderConfig+Builders.swift
Settle detached paragraph animations, preserve image configuration across builders, and bound glyph blur rendering work with coherent per-release caches.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 78f1a116-c298-4c20-8669-dcded414d291
@theontho
Mahyar (Mac) McDonald (theontho) marked this pull request as draft July 19, 2026 21:41
Character Streaming no longer uses the per-word animation utility, leaving it unreachable.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 78f1a116-c298-4c20-8669-dcded414d291
@theontho
Mahyar (Mac) McDonald (theontho) marked this pull request as ready for review July 19, 2026 22:18
@junyan72

Copy link
Copy Markdown
Contributor

I'll take a look. The attached video has some noticeable rendering defects(for example, look at the baseline of "Heading", and the 3rd line's render position jittered during streaming) that could be relevant to the new layout.

@junyan72

Copy link
Copy Markdown
Contributor

Having Copilot to look for a simpler approach in #152 without entirely swapping to custom layout manager.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Character Streaming text animation

3 participants