perf(desktop): move settled-preview derivation off the message root

The last status-dependent read at the message root, and the most expensive
one: the completedText selector flipped between '' while running and a full
messageContentText(content) join once settled, so every running <-> settled
transition re-ran the join for the whole message AND re-rendered the root. At
stream breadth N that is N joins plus N root re-renders per flip.

completedText and the previewTargets memo it feeds now live in a new
AssistantPreviewEmbeds leaf, mounted at the same position inside
[data-slot='aui_assistant-message-content']. Verified before moving that
previewTargets fed nothing else at the root -- its only consumer was its own
render block. The leaf renders the same wrapper div with the same classes, or
null when there are no targets, so the DOM is byte-identical; a component
boundary adds no node, so unlike StreamingMarker this needed no placement care
around the :first-child/:last-child rules.

The '' branch is preserved deliberately: it is the streaming-side optimization
that keeps the selector referentially stable so per-token flushes skip the
regex scan.

AssistantMessageBody now holds no status-dependent subscription at all -- what
remains is messageId, hasVisibleText, isInterim and turnDurationS, none of
which move on a pending flip.

Adds preview-embeds.test.tsx. The embed had no coverage, and the two cases are
written as a matched pair on the same selector -- present once settled, absent
while running -- so neither can pass vacuously.

Behavior-identical; invalidation scope only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Royalaid
2026-08-18 20:36:33 -07:00
committed by Teknium
parent db6c282c91
commit ced7d5e087
2 changed files with 148 additions and 22 deletions
@@ -172,21 +172,6 @@ const AssistantMessageBody: FC<AssistantMessageProps> = ({ onBranchInNewChat, on
// stable across the 30 Hz delta stream, so this adds no per-token renders).
const turnDurationS = useAuiState(s => s.message.metadata?.custom?.durationS as number | undefined)
// Preview targets only materialize once the turn completes — while running
// the selector returns '' (stable), so per-token flushes skip the regex
// scan and the re-render it would cause.
const completedText = useAuiState(s =>
s.message.status?.type === 'running' ? '' : messageContentText(s.message.content)
)
const previewTargets = useMemo(() => {
if (!completedText || !/(https?:\/\/|file:\/\/)/i.test(completedText)) {
return []
}
return pickPrimaryPreviewTarget(extractPreviewTargets(completedText))
}, [completedText])
const getMessageText = useCallback(() => messageContentText(messageRuntime.getState().content), [messageRuntime])
// useEnterAnimation consults `enabled` ONLY when its callback ref fires,
@@ -219,13 +204,7 @@ const AssistantMessageBody: FC<AssistantMessageProps> = ({ onBranchInNewChat, on
{/* Todos render in the composer status stack now, not inline. */}
{MESSAGE_PARTS}
<AssistantStatusSlot />
{previewTargets.length > 0 && (
<div className="mt-3 flex flex-wrap gap-2">
{previewTargets.map(target => (
<PreviewAttachment key={target} source="explicit-link" target={target} />
))}
</div>
)}
<AssistantPreviewEmbeds />
<MessagePrimitive.Error>
<ErrorPrimitive.Root
className="mt-1.5 flex items-start gap-1.5 text-[0.78rem] leading-5 text-[color-mix(in_srgb,var(--dt-destructive)_78%,var(--ui-text-secondary))]"
@@ -302,6 +281,53 @@ const AssistantStatusSlot: FC = () => {
return <TurnActivityIndicator />
}
/**
* PERF leaf: owns the settled-text selector that feeds the link previews.
*
* This was the last status-dependent read at the message root, and the most
* expensive one: the selector flips between '' while running and the full
* `messageContentText(content)` join once settled, so every running <-> settled
* transition re-ran the join for the whole message AND re-rendered the root.
* At stream breadth N that is N joins plus N root re-renders per flip. Reading
* it here confines both to this leaf, which renders nothing at all in the
* common case.
*
* The streaming-side optimization is unchanged and still the point of the ''
* branch: preview targets only materialize once the turn completes, so while
* running the selector returns a stable '' and per-token flushes skip the
* regex scan and the re-render it would cause.
*
* Renders exactly what the root used to render at this position — the same
* wrapper div with the same classes, or nothing when there are no targets —
* so the DOM is byte-identical either way. A component boundary adds no node
* of its own, so unlike StreamingMarker this needs no placement care.
*/
const AssistantPreviewEmbeds: FC = () => {
const completedText = useAuiState(s =>
s.message.status?.type === 'running' ? '' : messageContentText(s.message.content)
)
const previewTargets = useMemo(() => {
if (!completedText || !/(https?:\/\/|file:\/\/)/i.test(completedText)) {
return []
}
return pickPrimaryPreviewTarget(extractPreviewTargets(completedText))
}, [completedText])
if (previewTargets.length === 0) {
return null
}
return (
<div className="mt-3 flex flex-wrap gap-2">
{previewTargets.map(target => (
<PreviewAttachment key={target} source="explicit-link" target={target} />
))}
</div>
)
}
/**
* PERF leaf: owns the `settledParts` selector so the tail's settle stops
* re-rendering the message root. This is the one status-derived selector that
@@ -0,0 +1,100 @@
// Link previews moved off the message root into the AssistantPreviewEmbeds
// leaf, because the selector behind them (`'' while running`, the full
// `messageContentText(content)` join once settled) flipped on every
// running <-> settled transition and re-rendered the root with it.
//
// Two things are pinned here. That the embed still renders at all — the move
// was verbatim JSX and had no coverage before. And that it renders ONLY once
// the turn settles: the '' branch is a deliberate streaming optimization (it
// keeps the selector referentially stable so per-token flushes skip the regex
// scan), so a rewrite that drops it would make previews flicker in mid-stream
// with nothing to catch it.
import { AssistantRuntimeProvider, type ThreadMessage, useExternalStoreRuntime } from '@assistant-ui/react'
import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { Thread } from '.'
const createdAt = new Date('2026-05-01T00:00:00.000Z')
class TestResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
}
vi.stubGlobal('ResizeObserver', TestResizeObserver)
vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) =>
window.setTimeout(() => callback(performance.now()), 0)
)
vi.stubGlobal('cancelAnimationFrame', (id: number) => window.clearTimeout(id))
vi.stubGlobal('CSS', { escape: (str: string) => str })
Element.prototype.scrollTo = function scrollTo() {}
Element.prototype.animate = function animate() {
return { cancel() {}, finished: Promise.resolve() } as unknown as Animation
}
afterEach(() => {
cleanup()
})
const assistantMetadata = { unstable_state: null, unstable_annotations: [], unstable_data: [], steps: [], custom: {} }
function user(id: string, text: string): ThreadMessage {
return {
id,
role: 'user',
content: [{ type: 'text', text }],
attachments: [],
createdAt,
metadata: { custom: {} }
} as ThreadMessage
}
function assistant(id: string, text: string, running: boolean): ThreadMessage {
return {
id,
role: 'assistant',
content: text ? [{ type: 'text', text }] : [],
status: running ? { type: 'running' } : { type: 'complete', reason: 'stop' },
createdAt,
metadata: assistantMetadata
} as ThreadMessage
}
function Harness({ messages }: { messages: ThreadMessage[] }) {
const runtime = useExternalStoreRuntime<ThreadMessage>({
messages,
isRunning: messages.at(-1)?.status?.type === 'running',
onNew: async () => {}
})
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>
)
}
const TARGET = 'https://example.com/docs'
const WITH_PREVIEW = `Serving now: [Preview: example](#preview/${TARGET})`
describe('settled-turn link previews', () => {
it('renders the embed once the turn has settled', async () => {
const { container } = render(<Harness messages={[user('u1', 'start it'), assistant('a1', WITH_PREVIEW, false)]} />)
await screen.findByText('Serving now:', { exact: false })
expect(container.querySelector(`[title="${TARGET}"]`)).toBeTruthy()
})
it('does not render the embed while the turn is still running', async () => {
const { container } = render(<Harness messages={[user('u1', 'start it'), assistant('a1', WITH_PREVIEW, true)]} />)
await screen.findByText('Serving now:', { exact: false })
expect(container.querySelector(`[title="${TARGET}"]`)).toBeNull()
})
})