Fix paragraph/heading width hugging; add LLM Chat sample demo - #162
Conversation
Remove the `HStack { ParagraphView(); Spacer() }` wrapper from the
.paragraph and .heading cases in SingleBlockView. The unqualified
Spacer() forced blocks to report the full offered width upward,
breaking ancestors that hug content to the text's natural width
(e.g. chat bubbles). ParagraphView already renders left-aligned and
the outer VStack is `.leading`, so alignment is unchanged.
Also align the snapshot CanvasView harness to `.leading` and
re-record the affected iOS references.
Add an interactive "LLM Chat" sample demo (LLMChatView +
LLMChatViewModel + non-isolated LLMChatInteractor) showcasing
role-aligned bubbles with progressively streamed rich Markdown.
Fixes #157
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Not ready to approve
The sample chat code introduces compile-time issues (missing imports / incorrect onChange closure signature) and a concurrency safety concern around concurrent sends sharing mutable state and a parser instance.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR fixes a core SwiftUI layout bug where paragraph and heading blocks inflated their reported width (breaking width-hugging containers like chat bubbles), updates the snapshot canvas alignment to reflect the corrected hugging behavior, and adds a new “LLM Chat” sample screen to exercise streaming, rich Markdown rendering.
Changes:
- Fix width reporting for
.paragraphand.headingblocks by removing theHStack { …; Spacer() }wrapper inSingleBlockView. - Update the snapshot test canvas to use a leading-aligned
VStackso reference images match the new hugging behavior. - Add a new “LLM Chat” featured demo (view + view model + interactor) to demonstrate streaming-style incremental Markdown updates.
File summaries
| File | Description |
|---|---|
| Tests/MarkdownTextTests/SnapshotTestFoundation/CanvasView.swift | Aligns the snapshot harness container to .leading to match the corrected hugging layout. |
| Sources/MarkdownText/UI/BlockView.swift | Removes the Spacer()-inflated layout wrapper for headings/paragraphs so blocks can report their natural width. |
| Examples/SwiftStreamingMarkdownSample/SwiftStreamingMarkdownSample/NavigationView.swift | Adds a “Featured” section linking to the new LLM Chat demo. |
| Examples/SwiftStreamingMarkdownSample/SwiftStreamingMarkdownSample/LLMChatViewModel.swift | Introduces main-actor chat transcript + draft state for the demo. |
| Examples/SwiftStreamingMarkdownSample/SwiftStreamingMarkdownSample/LLMChatView.swift | Implements the chat UI and incremental scrolling behavior. |
| Examples/SwiftStreamingMarkdownSample/SwiftStreamingMarkdownSample/LLMChatInteractor.swift | Provides mock streaming replies and shared Markdown parsing/render configuration. |
Review details
- Files reviewed: 6/52 changed files
- Comments generated: 4
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Copy the 26 affected macOS-standard light/dark references (13 test methods) recorded by the Record macOS Snapshots workflow, matching the iOS references re-recorded after removing the paragraph/heading Spacer wrapper. Refs #157 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Constrain the conversation column and composer to a centered 640pt max width on macOS so the chat doesn't stretch across a wide window; iOS keeps full width. Refs #157 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Fix the core rendering bug where paragraph and heading blocks always report the full offered width, and add an interactive sample demo that exercises the corrected hugging behavior.
Sources/MarkdownText/UI/BlockView.swift— remove theHStack { ParagraphView(...); Spacer() }wrapper from the.paragraphand.headingcases ofSingleBlockView. The unqualifiedSpacer()always consumed whatever width its parentHStackwas offered, so the block reported that inflated width upward — breaking any ancestor that hugs content to the text's natural width (e.g. a chat bubble capping itself at a fraction of the screen).ParagraphViewalready renders left-aligned andBlockView's outerVStackis.leading, so left alignment is unchanged. Callers wanting full-width fill can still apply.frame(maxWidth: .infinity, alignment: .leading)from the outside.Tests/.../SnapshotTestFoundation/CanvasView.swift— align the snapshot harnessVStackto.leadingso references reflect the hugging layout.LLMChatView+@MainActor LLMChatViewModel+ non-isolatedLLMChatInteractor) — a typical chat interface with trailing user bubbles and leading bot bubbles, a text composer, and rotating mock responses (text, citations, images, code) rendered viaDocumentViewfrom progressively parsedRenderableDocuments to simulate streaming. Linked from a new "Featured" section inNavigationView.Fixes #157
Validation
make lint— 0 violations in 139 filesNote: macOS snapshot references were not re-recorded locally (per the snapshot-tests skill, macOS variants must be produced by the
Record macOS Snapshotsworkflow to avoid subpixel mismatches). They still need to be backfilled before CI's macOS snapshot job will pass.OSS readiness