Follow-up from #34 review. Perf concern, not a correctness bug.
ChatView.svelte calls renderMarkdown(t.text) inline inside the {#each turns} block on every reactive tick. In a 1700-step Claude session, that can be 500+ assistant turns × [marked.parse + DOMPurify.sanitize] per re-render. Svelte 5's $derived won't memoize across this loop; the pipeline re-runs whenever any unrelated state mutates preview (tree-query typing, selection change, view-mode toggle).
Fix
Cache the rendered HTML on the ChatTurn itself at flatten time, so subsequent renders are a trivial property read:
// in tree.ts::flattenChatHead
const textHtml = c.text ? renderMarkdown(c.text) : "";
const thinkingHtml = c.thinking ? renderMarkdown(c.thinking) : "";
// → store on ChatTurn, use in ChatView without re-calling renderMarkdown
This changes flattenChatHead from classify-only to render-too, which couples it to the markdown pipeline — acceptable given ChatTurn is a UI-model type.
Alternative: Map<stepId, string> memo external to the flatten, keyed by step.id + text hash.
Same treatment wanted
diff.raw.split("\n") in the tool block — cache the split on the turn.
viz.ts::renderCard also calls renderMarkdown(bodyText) for graph cards on every render(). Graph re-renders are less frequent but not free.
Follow-up from #34 review. Perf concern, not a correctness bug.
ChatView.sveltecallsrenderMarkdown(t.text)inline inside the{#each turns}block on every reactive tick. In a 1700-step Claude session, that can be 500+ assistant turns × [marked.parse + DOMPurify.sanitize] per re-render. Svelte 5's $derived won't memoize across this loop; the pipeline re-runs whenever any unrelated state mutatespreview(tree-query typing, selection change, view-mode toggle).Fix
Cache the rendered HTML on the
ChatTurnitself at flatten time, so subsequent renders are a trivial property read:This changes
flattenChatHeadfrom classify-only to render-too, which couples it to the markdown pipeline — acceptable givenChatTurnis a UI-model type.Alternative:
Map<stepId, string>memo external to the flatten, keyed by step.id + text hash.Same treatment wanted
diff.raw.split("\n")in the tool block — cache the split on the turn.viz.ts::renderCardalso callsrenderMarkdown(bodyText)for graph cards on everyrender(). Graph re-renders are less frequent but not free.