Scroll memory an embedder can keep
New onPersistScrollOffset / restoreScrollOffset on NativeTextViewWrapper, plus a real dismantleNSView. An embedder that routed away from the editor and back — to a home screen, a tag page — reopened every note at the top. Note-to-note was always fine.
Three things had to give, each sufficient on its own: the engine's per-document offsets live on the coordinator, which dies with the view; nothing recorded the offset on the way out, because there was no dismantleNSView in the package at all; and the restore was gated on a document switch, which a remount is not (makeCoordinator seeds documentId, so the first update pass never looks like one).
Teardown hands the offset over now, and the restore is latched rather than gated — the first pass after a remount still carries the embedder's empty buffer, so it has to try again on the pass that brings the real content. The latch gives up once the text is non-empty: a missed restore is one wrong offset, an armed latch teleports the reader minutes later. Both closures are asked at call time rather than snapshotted, so an embedder's own retention rules can see changes made on the way out. Passing neither leaves behavior unchanged.
Inline parsing is linear in the spans per region
Every pass after the first consulted the claimed ranges by scanning the whole array — once per character in scanEscapes and collectDelimiterRuns, once per candidate in scanLinkFamily — and buildTree decided containment by testing each span against every other. The passes walk the string left to right and claimed ranges never partially overlap, so a cursor over the sorted ranges answers both questions in amortised constant time.
A paragraph of 240 code spans parses in 0.5 ms rather than 33 ms; 6× the spans now costs 6× the parse instead of ~30×. Affects every claimed-span construct — code, escapes, links, images, wiki links, inline LaTeX, emphasis, extension spans. No parse result changes.
Copied URLs and emails arrive as real links
The editor styler linkifies bare URLs with NSDataDetector, but the HTML renderer emitted them as plain text — so the pasteboard's HTML, RTF and web-archive flavors carried no anchor at all, and whether a copied URL arrived clickable was left to the receiving app. MarkdownHTMLRenderer now wraps detector matches in <a href> (emails as mailto:) using the same system detector as the styler, and the RTF and web-archive flavors are derived from that HTML, so all three inherit the link.
Explicit [title](url) links were always correct. A URL-shaped run inside a link's own title stays plain so anchors never nest, and code spans remain excluded, matching the styler.
Changed
==highlight==fills the line box. AppKit paints.backgroundColorover ascent + descent only, so the marker fell short of the line height and a highlight that wrapped came out as a stack of bands. The newNSAttributedString.Key.markdownBlockBackgroundis painted across the whole line box byMarkdownTextLayoutFragmentand is available to embedder extensions wherever a fill should read as a block.
Fixed
- Markdown link labels may hold inline code and escaped punctuation —
[`App`](/tmp/App.swift:56)stayed literal, because the link pass rejected every candidate overlapping an already claimed span, including one lying entirely inside the label. - Initially narrow tables reflow when the editor width shrinks, instead of retaining stale image geometry until an unrelated full restyle.
Known limitations
- Table cells render no inline markup on the copy path: bold, explicit links and autolinked URLs inside a cell reach the pasteboard as raw markdown text.
Full changelog: 0.11.0...0.12.0
Contributors
by @wildthink — inline parse cost linear in the spans per region (#140).
by @YishenTu — narrow tables reflow after a width change (#117).
by @yukihiratype2 — inline code spans and escaped punctuation in link labels (#118).
by @xandaaaa — autolinked URLs and emails in the HTML renderer (#144).
by @luca-chen198 — scroll memory across an unmount (#143) and the line-box highlight.