Opened a ~900KB markdown file — 11,176 lines, 5,567 inline links, 1,868 ATX headings, basically a long list of bookmarks grouped by domain — and the window is unresponsive for a second or two before scroll wakes up. Theme switches and search keystrokes re-trigger it.
I poked around the source. The hot spot looks like the VStack in MarkdownView.swift around line 127:
ScrollView {
VStack(alignment: .leading, spacing: 8) {
ForEach(cachedBlocks) { block in
blockView(for: block)
}
}
}
VStack lays out every child eagerly, so SwiftUI instantiates, measures (Core Text), and positions all 1,800+ blocks before painting the first frame. That's the freeze.
I tried swapping VStack for LazyVStack on a local branch and the open is sub-frame. There's a comment in MarkdownBlock.swift:10–13 showing you already debugged the LazyVStack id-getter hot path and made id a stored property, so the swap is safe now.
One catch worth knowing: textSelection(.enabled) inside a LazyVStack loses auto-scroll-during-drag, because off-screen rows aren't materialized when AppKit goes looking for the next selectable view. So the simple swap fixes the perf but trades away a native macOS interaction. There's probably a deeper fix worth its own issue (NSTextView-backed content view); I'm filing that separately.
Happy to send a PR with the LazyVStack change plus a two-pass scrollTo helper (needed because proxy.scrollTo(id, anchor:) to an unmaterialized item under-shoots on macOS 13) and a 150ms debounce on the search recompute path. Patch is small: +30/−9 lines, one file. Or you might want to take a different angle — say the word.
Repro file is just a long bullet list of [text](url) — date lines. Easy to synthesize, or I can attach the one I hit it with.
Opened a ~900KB markdown file — 11,176 lines, 5,567 inline links, 1,868 ATX headings, basically a long list of bookmarks grouped by domain — and the window is unresponsive for a second or two before scroll wakes up. Theme switches and search keystrokes re-trigger it.
I poked around the source. The hot spot looks like the
VStackinMarkdownView.swiftaround line 127:VStacklays out every child eagerly, so SwiftUI instantiates, measures (Core Text), and positions all 1,800+ blocks before painting the first frame. That's the freeze.I tried swapping
VStackforLazyVStackon a local branch and the open is sub-frame. There's a comment inMarkdownBlock.swift:10–13showing you already debugged theLazyVStackid-getter hot path and madeida stored property, so the swap is safe now.One catch worth knowing:
textSelection(.enabled)inside aLazyVStackloses auto-scroll-during-drag, because off-screen rows aren't materialized when AppKit goes looking for the next selectable view. So the simple swap fixes the perf but trades away a native macOS interaction. There's probably a deeper fix worth its own issue (NSTextView-backed content view); I'm filing that separately.Happy to send a PR with the
LazyVStackchange plus a two-passscrollTohelper (needed becauseproxy.scrollTo(id, anchor:)to an unmaterialized item under-shoots on macOS 13) and a 150ms debounce on the search recompute path. Patch is small: +30/−9 lines, one file. Or you might want to take a different angle — say the word.Repro file is just a long bullet list of
[text](url) — datelines. Easy to synthesize, or I can attach the one I hit it with.