perf(preview): load rich-content libraries on demand, two fewer parses per render, cached scroll samples - #949
Merged
Merged
Conversation
…ent uses them
The viewer imported full highlight.js (194 grammars), KaTeX + mhchem and
Mermaid on every mount, and enrichment waited for all three. Each library
is now imported by renderRichContent the first time the roots hold code,
math or a diagram, and each pass runs as soon as its own library is in.
highlight.js is the `common` build; other fenced grammars load per name
(aliases by registering every grammar once). The export shares the path.
vite build, rich-content chunks fetched on first render:
before: hljs 971 KB + Mermaid 605 KB + KaTeX/mhchem ~295 KB, every document
after: plain text 0; code 164 KB (+ the named grammar's chunk);
KaTeX and Mermaid only for math / diagrams
Total chunk bytes on disk +153 KB (per-grammar chunks).
…an unchanged host Every render serialized DOMPurify's tree to a string only for blockPatch to parse it again into a <template>. The preview now asks DOMPurify for a fragment (same MARKDOWN_SANITIZE_CONFIG) and the patch moves those nodes, so the sanitizer is still the last thing before the DOM, with no reparse after it. A tab switch whose host already holds the tab's HTML is no longer sanitized and diffed again. jsdom, ~1.15 MB markdown (1.78 MB HTML), one-word edit, min of 5: sanitize + patch 4.3-5.5 s -> 2.4-2.5 s process + sanitize + patch 5.0-5.4 s -> 4.3-4.5 s tab switch, same content 2.7-3.6 s -> skipped
collectLineSamples walked the whole preview on every scroll event, twice per event in split view. The table (elements only; boxes are still measured live) is now kept per root and dropped by invalidateAnchorMemos, which the patch already calls and which a fold toggle, a tab switch that skips the patch, and a Mermaid diagram replacing its <pre> now call too. jsdom, 17,430 blocks: 38.1 ms -> 0.08 ms per offset-to-line mapping.
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.
What this is
Three preview changes, one commit each.
lib/common. Other fenced languages load per file by name. An alias (docker,ps1) or unknown name registers every grammar once per window, since only the grammars know their aliases. Export shares the same loaders.blockPatchparsed it again in a<template>.sanitizeMarkdownFragmentnow returns DOMPurify's fragment (RETURN_DOM_FRAGMENT), andblockPatchmoves those nodes in. The sanitizer stays the last step before the DOM, and nothing reparses after it. Switching back to a tab whose preview already holds that content skips sanitize and patch.collectLineSampleswalked the whole preview on every scroll event, twice in split view. The element list is now cached per root, and positions are still measured live. It is invalidated throughinvalidateAnchorMemoson patch, fold, a tab switch that skips the patch, and a Mermaid diagram replacing its<pre>.Mechanism
Libraries a first render fetches (
vite build): any document before, hljs 971 KB + Mermaid 605 KB + KaTeX/mhchem ~295 KB. After: a plain-text document 0, code 164 KB plus its languages, KaTeX and Mermaid only when used.jsdom, 1.15 MB document, one word changed, min of 5: sanitize + patch 4.3–5.5 s → 2.4–2.5 s. Line lookup on 17,430 blocks 38.1 ms → 0.08 ms. jsdom is far slower than WebKit, so only the ratios mean anything.
Tests
Red without the change, green with it:
richContentLazyLoad.spec.tspreviewSanitize.spec.ts:<style>,<script>,onerrorandjavascript:through the fragment pathlineSamplesCache.spec.ts: cache, fold invalidation, diagram invalidationVerification
Rebased on master after #945–#947:
npm run check: 0 errorsnpm test: 1112/1112npx vitest run: 492/492npm run build: okNot verified: the Tauri app. Each preview now restores its reading position once after the first enrich, where before that only happened when the libraries arrived after the document.