perf(desktop): memoize MarkdownText plugins to stop churning Streamdown
The inline `plugins={{ math: mathPlugin, ...(isStreaming ? {} : { code }) }}`
on `<StreamdownTextPrimitive>` constructed a new object literal on every
parent render. That broke `<Streamdown>`'s outer memo and forced its
internal `rehypePlugins` / `remarkPlugins` array useMemos to rebuild,
which propagates a new identity into every `<Block>` and defeats Block's
memoization for stable historical blocks.
After memoizing on `[isStreaming]` (the only real dimension of variance),
CPU profile during a 5 s synthetic stream on the 34 MB session shows
`parser` self-time dropping out of the top 10, `compile` cut roughly in
half, and `bn$1` / `m$1` (micromark internals) leaving the top entries.
Doesn't move the visible longtask count on its own — Streamdown's
per-Block parse cost still dominates whenever the last block's content
changes — but it removes a class of unnecessary re-parses for historical
blocks during streaming. See `scripts/profile-typing-lag.md` for the
full investigation.
This commit is contained in:
@@ -238,6 +238,16 @@ const HEADING_SIZES: Record<'h1' | 'h2' | 'h3' | 'h4', string> = {
|
||||
const MarkdownTextImpl = () => {
|
||||
const isStreaming = useAuiState(s => s.message.status?.type === 'running')
|
||||
|
||||
// Stable per-state plugin object. The previous inline `{ math: mathPlugin,
|
||||
// ...(isStreaming ? {} : { code }) }` created a new object identity on every
|
||||
// render, which churns Streamdown's outer memo + propagates new prop
|
||||
// identities into every Block. The plugin set really only varies on
|
||||
// `isStreaming`, so memoize on that.
|
||||
const plugins = useMemo(
|
||||
() => (isStreaming ? { math: mathPlugin } : { math: mathPlugin, code }),
|
||||
[isStreaming]
|
||||
)
|
||||
|
||||
const components = useMemo(
|
||||
() =>
|
||||
({
|
||||
@@ -331,7 +341,7 @@ const MarkdownTextImpl = () => {
|
||||
// on the SyntaxHighlighter component, so we don't pay code-block
|
||||
// tokenization on every token even with this set.
|
||||
parseIncompleteMarkdown
|
||||
plugins={{ math: mathPlugin, ...(isStreaming ? {} : { code }) }}
|
||||
plugins={plugins}
|
||||
preprocess={preprocessMarkdown}
|
||||
/>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user