diff --git a/packages/ui/src/components/message-block.tsx b/packages/ui/src/components/message-block.tsx index 1d59cf88..0fd21b04 100644 --- a/packages/ui/src/components/message-block.tsx +++ b/packages/ui/src/components/message-block.tsx @@ -902,6 +902,7 @@ export default function MessageBlock(props: MessageBlockProps) { onDeleteMessagesUpTo={props.onDeleteMessagesUpTo} selectedMessageIds={props.selectedMessageIds} onToggleSelectedMessage={props.onToggleSelectedMessage} + onContentRendered={props.onContentRendered} /> @@ -1280,6 +1281,7 @@ interface ReasoningCardProps { onDeleteMessagesUpTo?: (messageId: string) => void | Promise selectedMessageIds?: () => Set onToggleSelectedMessage?: (messageId: string, selected: boolean) => void + onContentRendered?: () => void } function ReasoningCard(props: ReasoningCardProps) { @@ -1288,6 +1290,25 @@ function ReasoningCard(props: ReasoningCardProps) { const [deletingMessage, setDeletingMessage] = createSignal(false) const [deletingUpTo, setDeletingUpTo] = createSignal(false) const isSelectedForDeletion = () => Boolean(props.selectedMessageIds?.().has(props.messageId)) + let pendingRenderNotificationFrame: number | null = null + + const notifyContentRendered = () => { + if (!props.onContentRendered || typeof requestAnimationFrame !== "function") return + if (pendingRenderNotificationFrame !== null) { + cancelAnimationFrame(pendingRenderNotificationFrame) + } + pendingRenderNotificationFrame = requestAnimationFrame(() => { + pendingRenderNotificationFrame = null + props.onContentRendered?.() + }) + } + + onCleanup(() => { + if (pendingRenderNotificationFrame !== null) { + cancelAnimationFrame(pendingRenderNotificationFrame) + pendingRenderNotificationFrame = null + } + }) createEffect(() => { setExpanded(Boolean(props.defaultExpanded)) @@ -1356,6 +1377,12 @@ function ReasoningCard(props: ReasoningCardProps) { const viewHideLabel = () => expanded() ? t("messageBlock.reasoning.indicator.hide") : t("messageBlock.reasoning.indicator.view") + createEffect(() => { + if (!expanded()) return + reasoningText() + notifyContentRendered() + }) + const canDeleteMessage = () => Boolean(props.showDeleteMessage) && !deletingMessage() const handleDeleteMessage = async (event: MouseEvent) => {