Smooth WebUI streaming with state-driven viewport motion (#4696)

This commit is contained in:
chengyongru
2026-07-26 00:18:24 +08:00
committed by GitHub
parent 9a7debcb48
commit b0ef759e2c
28 changed files with 3202 additions and 670 deletions
+19 -6
View File
@@ -92,7 +92,13 @@ export function ThreadMessages({
unit.type === "activity"
&& next?.type === "message"
&& next.message.role === "assistant";
const deferOffscreenRender =
index < units.length - 1
&& (
unit.type === "activity"
? !liveActivityClusterIndices.has(index)
: unit.message.role === "assistant" && !unit.message.isStreaming
);
const userPromptId =
unit.type === "message" && unit.message.role === "user"
? unit.message.id
@@ -110,6 +116,7 @@ export function ThreadMessages({
marginTop={marginTop}
userPromptId={userPromptId}
hasBodyBelow={hasBodyBelow}
deferOffscreenRender={deferOffscreenRender}
isTurnStreaming={liveActivityClusterIndices.has(index)}
forkIndex={forkIndex}
showForkBoundary={index === forkBoundaryAfterUnitIndex}
@@ -131,6 +138,7 @@ interface ThreadDisplayUnitProps {
marginTop: string;
userPromptId?: string;
hasBodyBelow: boolean;
deferOffscreenRender: boolean;
isTurnStreaming: boolean;
forkIndex?: number;
showForkBoundary: boolean;
@@ -147,6 +155,7 @@ const ThreadDisplayUnit = memo(function ThreadDisplayUnit({
marginTop,
userPromptId,
hasBodyBelow,
deferOffscreenRender,
isTurnStreaming,
forkIndex,
showForkBoundary,
@@ -157,17 +166,20 @@ const ThreadDisplayUnit = memo(function ThreadDisplayUnit({
onOpenFilePreview,
onForkFromMessage,
}: ThreadDisplayUnitProps) {
// Introducing content-visibility after a unit has painted can move the
// browser's scroll anchor. Only units deferred on their first render may
// remain deferred.
const hasRenderedEagerlyRef = useRef(!deferOffscreenRender);
if (!deferOffscreenRender) hasRenderedEagerlyRef.current = true;
const stableDeferOffscreenRender =
deferOffscreenRender && !hasRenderedEagerlyRef.current;
const onForkFromHere = useCallback(() => {
if (forkIndex !== undefined) onForkFromMessage?.(forkIndex);
}, [forkIndex, onForkFromMessage]);
const deferOffscreenRender = unit.type === "activity"
? !isTurnStreaming
: unit.message.role === "assistant" && !unit.message.isStreaming;
return (
<>
<div
className={`${marginTop}${deferOffscreenRender ? " thread-render-unit" : ""}`}
className={`${marginTop}${stableDeferOffscreenRender ? " thread-render-unit" : ""}`}
data-user-prompt-id={userPromptId}
>
{unit.type === "activity" ? (
@@ -206,6 +218,7 @@ function threadDisplayUnitPropsEqual(
&& previous.marginTop === next.marginTop
&& previous.userPromptId === next.userPromptId
&& previous.hasBodyBelow === next.hasBodyBelow
&& previous.deferOffscreenRender === next.deferOffscreenRender
&& previous.isTurnStreaming === next.isTurnStreaming
&& previous.forkIndex === next.forkIndex
&& previous.showForkBoundary === next.showForkBoundary