Companion to the perf issue I'm filing alongside this one.
The current per-block rendering — Text(AttributedString) inside a ScrollView { VStack { ForEach } } — hits two ceilings on long markdown:
- Layout cost scales with block count, not viewport, so big docs freeze on open.
- SwiftUI's
Text selection is per-view. Cross-block selection doesn't work, and textSelection(.enabled) doesn't get AppKit's auto-scroll-during-drag.
Swapping VStack to LazyVStack (suggested in the perf issue) addresses (1) but worsens (2), since off-screen rows aren't there to be selected.
A different shape worth considering: render the parsed markdown to a single NSAttributedString and host it in an NSTextView wrapped via NSViewRepresentable. The text view handles line layout, lazy display, selection with auto-scroll, find-bar integration, and macOS Services without extra work. Code blocks, tables, mermaid diagrams, math — those can live in a parallel block-level layer, or get baked in as text attachments if you want them inside the selectable run.
Real trade-offs:
- Bigger refactor, especially for the table/code/mermaid block views.
- Search highlight gets reimplemented against
NSTextView's temporary attribute APIs.
- ToC scroll-to-heading becomes scroll-to-character-range.
Upside: any document opens instantly, native macOS selection (find, lookup, share, drag), and probably less SwiftUI surface area overall. It's the right primitive for a viewer of long markdown.
Not asking for this on a deadline — just flagging the path. Happy to prototype if you're ever interested.
Companion to the perf issue I'm filing alongside this one.
The current per-block rendering —
Text(AttributedString)inside aScrollView { VStack { ForEach } }— hits two ceilings on long markdown:Textselection is per-view. Cross-block selection doesn't work, andtextSelection(.enabled)doesn't get AppKit's auto-scroll-during-drag.Swapping
VStacktoLazyVStack(suggested in the perf issue) addresses (1) but worsens (2), since off-screen rows aren't there to be selected.A different shape worth considering: render the parsed markdown to a single
NSAttributedStringand host it in anNSTextViewwrapped viaNSViewRepresentable. The text view handles line layout, lazy display, selection with auto-scroll, find-bar integration, and macOS Services without extra work. Code blocks, tables, mermaid diagrams, math — those can live in a parallel block-level layer, or get baked in as text attachments if you want them inside the selectable run.Real trade-offs:
NSTextView's temporary attribute APIs.Upside: any document opens instantly, native macOS selection (find, lookup, share, drag), and probably less SwiftUI surface area overall. It's the right primitive for a viewer of long markdown.
Not asking for this on a deadline — just flagging the path. Happy to prototype if you're ever interested.