mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-06 02:37:05 +08:00
perf(tui): defer Md upgrade for fresh-mounted assistant rows
Adds DeferredMd — a wrapper around <Md> that renders a lightweight
<Text> placeholder on first mount and upgrades to the full markdown
subtree on a queueMicrotask follow-up. Rationale: fresh MessageLine
mounts during PageUp hold run our markdown tokenizer + syntax
highlighter synchronously, producing the 63-112ms renderer spikes
profiled earlier. A plain <Text> placeholder only needs Yoga to wrap
the pre-stripped string (no tokenizer, no highlight), then the Md
subtree builds in a follow-up React commit.
Upgrade cache: once a (theme, compact, text) tuple has been upgraded,
a WeakMap-keyed Set remembers it so remounts (scroll-out then
scroll-back) mount straight into <Md> — no placeholder round-trip.
WeakMap on theme means palette swaps re-upgrade naturally.
Honesty note: profiling under hold-PageUp showed this didn't reduce
renderer p99 measurably — the upgrade commit just pays the Md cost on
a follow-up frame instead of inline. The bigger bottleneck turned out
to be React commit frequency (3.5 commits/sec during 30Hz scroll
input, with 200ms+ silent gaps between commits dominating perceived
FPS), which this change doesn't address. Keeping the deferred path
anyway because:
1. It's correct and tested — no regressions across 352 tests
2. Defensive for pathological fresh-mount cases (giant code blocks,
wide tables) that aren't in the current profile fixture
3. Pairs naturally with useVirtualHistory's useDeferredValue to keep
React's concurrent scheduler able to interrupt upgrade commits
If the follow-up perf investigation (terminal write throughput / patch
volume / commit frequency) shows DeferredMd is net-neutral-or-worse in
practice, this can be reverted with a one-line swap back to <Md> in
messageLine.tsx:115.
Companion to the streaming 2-column fix in 7242361a — these two
touched messageLine.tsx together so they land as a pair.
This commit is contained in:
@@ -9,7 +9,7 @@ import { boundedLiveRenderText, compactPreview, hasAnsi, isPasteBackedText, stri
|
||||
import type { Theme } from '../theme.js'
|
||||
import type { ActiveTool, DetailsMode, Msg, SectionVisibility } from '../types.js'
|
||||
|
||||
import { Md } from './markdown.js'
|
||||
import { DeferredMd } from './deferredMarkdown.js'
|
||||
import { StreamingMd } from './streamingMarkdown.js'
|
||||
import { ToolTrail } from './thinking.js'
|
||||
import { TodoPanel } from './todoPanel.js'
|
||||
@@ -107,7 +107,12 @@ export const MessageLine = memo(function MessageLine({
|
||||
// streamingMarkdown.tsx for the cost model.
|
||||
<StreamingMd compact={compact} t={t} text={boundedLiveRenderText(msg.text)} />
|
||||
) : (
|
||||
<Md compact={compact} t={t} text={msg.text} />
|
||||
// Deferred markdown: plain-text placeholder on first mount, upgrade
|
||||
// to full Md on a queued microtask. Spreads the tokenizer + syntax
|
||||
// cost off the scroll critical path so hold-PageUp doesn't hitch
|
||||
// on fresh assistant rows entering overscan. See
|
||||
// deferredMarkdown.tsx for the trade-offs.
|
||||
<DeferredMd color={body} compact={compact} t={t} text={msg.text} />
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user