Add AppKit paragraph rendering layer with full iOS parity - #106
Merged
Merged
Conversation
Replace the macOS placeholder (monospaced Text) with a proper NSViewRepresentable wrapping MTMathUILabel. iosMath already supports macOS via its MTConfig.h typedef layer. Uses intrinsicContentSize instead of sizeToFit since NSView lacks the latter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement ParagraphNSView (NSTextView subclass) with: - Word-by-word streaming fade animation via CADisplayLink - NSTextView delegate for link click handling - Custom context menu support via NSMenu - Appearance tracking via AppAppearance - LatexViewProviderMac for inline math rendering - InlineCitationViewProviderMac for citation attachments - Size caching and intrinsic content size management - View reuse cache (ParagraphNSViewCache) Replace the macOS ParagraphView placeholder with a proper NSViewRepresentable wrapping ParagraphNSView, mirroring the iOS UIViewRepresentable structure (coordinator, size cache, animation). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ct shared animation code Move all paragraph-related files under UI/Paragraph/: - UI/Paragraph/ParagraphView.swift (NSViewRepresentable + UIViewRepresentable) - UI/Paragraph/ParagraphAnimation.swift (shared FadeAnimationData, easeOut, constants) - UI/Paragraph/UIKit/ParagraphUIView.swift - UI/Paragraph/UIKit/ParagraphUIViewCache.swift - UI/Paragraph/AppKit/ParagraphNSView.swift - UI/Paragraph/AppKit/ParagraphNSViewCache.swift Extract duplicated animation code into ParagraphAnimation.swift: - FadeAnimationData struct - paragraphEaseOut() curve function - ParagraphAnimationConstants (fadeInDuration, delayBetweenWordsRatio) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move UIViewRepresentable to UIKit/ParagraphView+iOS.swift and NSViewRepresentable to AppKit/ParagraphView+macOS.swift. Suffixes avoid SPM duplicate filename errors within the same target. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add AppAppearance.update(style:) for UIKit and AppAppearance.update(appearance:) for AppKit, encapsulating the platform conversion logic. Update all callers in ParagraphUIView and ParagraphNSView to use the new convenience methods. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge LatexViewProvider (iOS) and LatexViewProviderMac into one class at UI/Paragraph/LatexViewProvider.swift. Platform differences are handled with minimal #if blocks for the init parentView type and sizeToFit vs intrinsicContentSize. Also consolidate the LatexAttachmentData.resolvedTextColor extension into the same file with a single computed property using MDColor. Add comment to AppAppearance.update(appearance:) explaining that bestMatch handles all dark/light variants. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…kdown into macos-paragraph-view
…yling - Remove InlineCitationViewProviderMac (no-op provider, citations use image-based rendering via InlineCitationAttachment.image) - Remove its UTType.url registration from setupView() - Remove empty configureVisualStyling placeholder on iOS Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…aphViewCache Single shared file at UI/Paragraph/ParagraphViewCache.swift with #if blocks only for the platform view type. Same API, same logic, one class name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a macOS AppKit paragraph rendering implementation (NSTextView-backed) intended to match the existing iOS paragraph renderer’s behavior, while reorganizing paragraph-related code into a cleaner platform-split structure and extracting shared animation / LaTeX attachment logic.
Changes:
- Added AppKit paragraph rendering (
ParagraphNSView+ cache +NSViewRepresentablewrapper) with animation, link handling, context menus, and accessibility. - Extracted shared paragraph animation primitives and unified LaTeX attachment view-provider code for iOS/macOS.
- Refactored appearance updates and introduced view reuse caches for paragraph views.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| Sources/MarkdownText/Utilities/AppAppearance.swift | Adds platform-specific helpers to update cached light/dark appearance. |
| Sources/MarkdownText/UI/Paragraph/UIKit/ParagraphView+iOS.swift | Cleans up platform gating and keeps iOS ParagraphView as a UIKit representable. |
| Sources/MarkdownText/UI/Paragraph/UIKit/ParagraphUIViewCache.swift | Introduces a simple reuse pool for ParagraphUIView. |
| Sources/MarkdownText/UI/Paragraph/UIKit/ParagraphUIView.swift | Moves shared animation / LaTeX logic out; uses centralized appearance update + shared animation constants. |
| Sources/MarkdownText/UI/Paragraph/ParagraphAnimation.swift | New shared constants + fade animation data + ease-out curve. |
| Sources/MarkdownText/UI/Paragraph/LatexViewProvider.swift | New cross-platform NSTextAttachmentViewProvider for LaTeX attachments. |
| Sources/MarkdownText/UI/Paragraph/AppKit/ParagraphView+macOS.swift | New macOS NSViewRepresentable wrapper around ParagraphNSView with size caching. |
| Sources/MarkdownText/UI/Paragraph/AppKit/ParagraphNSViewCache.swift | New reuse pool for ParagraphNSView. |
| Sources/MarkdownText/UI/Paragraph/AppKit/ParagraphNSView.swift | New AppKit NSTextView subclass implementing rendering/animation/menu/accessibility features. |
Introduce MDParagraphView (ParagraphUIView on iOS, ParagraphNSView on macOS) following the same pattern as MDFont/MDColor/MDImage. This eliminates all #if blocks from ParagraphViewCache. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create MacVariant with window-size-based configs (narrow/standard/wide) - Make SnapshotTestCase.assert() compile on both platforms using NSHostingController on macOS, UIHostingController on iOS - Guard DeviceVariant and ViewImageConfig with #if canImport(UIKit) - Wrap iOS-only test files with #if canImport(UIKit) so the test target compiles on macOS (parser/rewriter tests run on both) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add .macOS(.v14) to Package.swift platforms array, officially declaring macOS support for build and test. Add MarkdownTextMacSnapshotTests with 3 tests (lists, code blocks, tables) × 4 variants (narrow/standard × light/dark) = 12 reference snapshots recorded for macOS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove #if canImport(UIKit) guards from snapshot tests that don't actually need UIKit (BlockQuoteView, MarkdownText, OrderedList, UnorderedList). These tests now run on both iOS and macOS using the platform-appropriate variant (DeviceVariant on iOS, MacVariant on macOS). Delete the separate MarkdownTextMacSnapshotTests.swift — the existing tests cover the same scenarios on both platforms. Record 100 macOS reference snapshots across the shared test suites. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename variant output from 'standard-light' to 'macOS-standard-light' for clear platform distinction. Remove the narrow (400pt) variant — standard (800pt) light/dark is sufficient for macOS coverage. 50 reference snapshots (down from 100). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add clearCache() to ParagraphViewCache and call it in SnapshotTestCase.setUp() to prevent cached ParagraphNSView instances from leaking math attachment subviews across tests. Re-record all macOS snapshots with clean cache. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4 tasks
…rm TableViewSnapshotTests; sample app macOS support - Replace cacheDisplay(in:to:) with CGWindowListCreateImage for macOS snapshot capture — fixes garbled MTMathUILabel rendering in snapshots - Make TableViewSnapshotTests cross-platform (remove #if canImport(UIKit) wrapper, use MDColor/MDFont) - Add macOS target to sample app (project.yml + cross-platform source) - Re-record all macOS snapshots with correct math rendering Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add MDImage(sfSymbol:) convenience init for cross-platform SF Symbol usage - Add Color.dynamic(light:dark:) and Color.systemBackground extensions to library - Use NSAttributedString.isEqual(to:) in ParagraphView macOS Equatable - Rename DeviceVariant to DeviceiOSVariant - Use URL.fromMixedEncodingString in ParagraphNSView link handler - Remove dead NSTextViewDelegate conformance from ParagraphNSView Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add macOS support to SwiftStreamingMarkdown with a full AppKit rendering layer, snapshot testing infrastructure, and sample app support.
Core rendering
ParagraphNSView(NSTextView-based) with full iOS parity: attributed text, fade-in animations via CADisplayLink, link handling, context menus, and intrinsic content sizingParagraphViewCachefor macOS withclearCache()to prevent test pollution from the static singletonLatexViewProvidermacOS path usingNSColor(name:)dynamic colors andintrinsicContentSizeSnapshot testing
Snapshottingstrategy usingCGWindowListCreateImageinstead of the defaultcacheDisplay(in:to:)— fixes garbled MTMathUILabel rendering in macOS snapshotsMacVariantwith standard (800pt) and wide (1200pt) window sizes, light/dark color schemesSnapshotTestCaseextended with AppKitassert()overload and cache clearing in setUpTableViewSnapshotTestsmade cross-platform (removed#if canImport(UIKit)wrapper)Sample app
SwiftStreamingMarkdownSampleMactarget in project.yml (macOS 15.0)MDFont/MDColorusage,NSPasteboard/NSSavePanel,NSColor(name:)dynamic colors, conditional toolbar/navigation modifiersRefs #104
Validation
xcodebuild test -scheme SwiftStreamingMarkdown -destination "platform=macOS" -skipMacroValidation -disable-concurrent-testing— 60 tests, 0 failuresxcodebuild build -scheme SwiftStreamingMarkdownSampleMac -destination "platform=macOS" -skipMacroValidation— build succeededOSS readiness