diff --git a/packages/ui/src/components/diff-viewer.tsx b/packages/ui/src/components/diff-viewer.tsx index 79f1af18..2b8ad7b9 100644 --- a/packages/ui/src/components/diff-viewer.tsx +++ b/packages/ui/src/components/diff-viewer.tsx @@ -37,10 +37,10 @@ export function ToolCallDiffViewer(props: ToolCallDiffViewerProps) { if (!normalized) { return null } - + const language = getLanguageFromPath(props.filePath) || "text" const fileName = props.filePath || "diff" - + return { oldFile: { fileName, @@ -53,96 +53,47 @@ export function ToolCallDiffViewer(props: ToolCallDiffViewerProps) { hunks: [normalized], } }) - + let diffContainerRef: HTMLDivElement | undefined - let pendingCapture: number | undefined - let pendingContext: CaptureContext | undefined - let lastRenderedMarkup: string | undefined - let lastCachedHtml: string | undefined - - const clearPendingCapture = () => { - if (pendingCapture !== undefined) { - cancelAnimationFrame(pendingCapture) - pendingCapture = undefined - } - pendingContext = undefined - } - - const runCapture = (context: CaptureContext) => { - if (!diffContainerRef) { - props.onRendered?.() - return - } - - const markup = diffContainerRef.innerHTML - if (!markup) { - props.onRendered?.() - return - } - - const hasChanged = markup !== lastRenderedMarkup - if (hasChanged) { - lastRenderedMarkup = markup - if (context.cacheEntryParams) { - setCacheEntry(context.cacheEntryParams, { - text: context.diffText, - html: markup, - theme: context.theme, - mode: context.mode, - }) - } - } - - props.onRendered?.() - } - - const scheduleCapture = (context: CaptureContext) => { - clearPendingCapture() - pendingContext = context - pendingCapture = requestAnimationFrame(() => { - const activeContext = pendingContext - pendingContext = undefined - pendingCapture = undefined - if (activeContext) { - runCapture(activeContext) - } - }) - } - + let lastCapturedKey: string | undefined + + const contextKey = createMemo(() => { + const data = diffData() + if (!data) return "" + return `${props.theme}|${props.mode}|${props.diffText}` + }) + createEffect(() => { const cachedHtml = props.cachedHtml if (cachedHtml) { - clearPendingCapture() - if (cachedHtml !== lastCachedHtml) { - lastCachedHtml = cachedHtml - lastRenderedMarkup = cachedHtml - props.onRendered?.() + // When we are given cached HTML, we rely on the caller's cache + // and simply notify once rendered. + props.onRendered?.() + return + } + + const key = contextKey() + if (!key) return + if (!diffContainerRef) return + if (lastCapturedKey === key) return + + requestAnimationFrame(() => { + if (!diffContainerRef) return + const markup = diffContainerRef.innerHTML + if (!markup) return + lastCapturedKey = key + if (props.cacheEntryParams) { + setCacheEntry(props.cacheEntryParams, { + text: props.diffText, + html: markup, + theme: props.theme, + mode: props.mode, + }) } - return - } - - lastCachedHtml = undefined - - const data = diffData() - const theme = props.theme - const mode = props.mode - - if (!data) { - clearPendingCapture() - return - } - - scheduleCapture({ - theme, - mode, - diffText: props.diffText, - cacheEntryParams: props.cacheEntryParams, + props.onRendered?.() }) }) - onCleanup(() => { - clearPendingCapture() - }) return (
diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index e3a2c8bf..508ecffe 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -29,7 +29,8 @@ export function Markdown(props: MarkdownProps) { latestRequestedText = text - await initMarkdown(dark) + // Markdown initialization is now handled globally in App. + // initMarkdown is idempotent but we avoid per-part calls here. if (!highlightEnabled) { part.renderCache = undefined diff --git a/packages/ui/src/components/message-stream-v2.tsx b/packages/ui/src/components/message-stream-v2.tsx index 2eab5519..1ef395ec 100644 --- a/packages/ui/src/components/message-stream-v2.tsx +++ b/packages/ui/src/components/message-stream-v2.tsx @@ -295,28 +295,6 @@ export default function MessageStreamV2(props: MessageStreamV2Props) { return `${revisionValue}:${tailSignature}` }) - createEffect(() => { - const ids = new Set(messageIds()) - const cache = getSessionRenderCache(props.instanceId, props.sessionId) - for (const [key] of cache.messageBlocks) { - if (!ids.has(key)) { - cache.messageBlocks.delete(key) - } - } - for (const [key] of cache.messageItems) { - const messageId = key.split(":", 1)[0] - if (!ids.has(messageId)) { - cache.messageItems.delete(key) - } - } - for (const [key] of cache.toolItems) { - const messageId = key.split(":", 1)[0] - if (!ids.has(messageId)) { - cache.toolItems.delete(key) - } - } - }) - const scrollCache = useScrollCache({ instanceId: () => props.instanceId, sessionId: () => props.sessionId,