From 198f951798827a4172b701015ffcd0ddecf28700 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Mon, 3 Aug 2026 22:23:28 -0700 Subject: [PATCH] [eric] canvas: expanding a chat paints the shell first and mounts the transcript next frame, the click's 630ms paint block is gone --- .../app/pages/Dashboard/cards/AgentCard.tsx | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx b/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx index 9a70ad62..ebf67cae 100644 --- a/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx +++ b/frontend/src/app/pages/Dashboard/cards/AgentCard.tsx @@ -703,6 +703,14 @@ const AgentCard: React.FC = ({ ); // Drafts collapse to the pill like everything else; only a pending approval keeps the full card, since you have to see what you are approving. const pillMode = !expanded && !hasPending && !tileZone; + + // Two-phase expand: mounting a long transcript synchronously inside the expand click blocked its paint for ~630ms (the measured INP worst case), so the click paints the expanded shell first and the chat mounts on the next frame. + const [chatMounted, setChatMounted] = useState(false); + useEffect(() => { + if (!expanded) { setChatMounted(false); return undefined; } + const raf = requestAnimationFrame(() => setChatMounted(true)); + return () => cancelAnimationFrame(raf); + }, [expanded]); // Glass bubble + fullscreen scrim are both dark in either theme, so the title goes light on them. const titleColor = expanded ? GLASS_SURFACE_TEXT : c.text.primary; // The answer a finished turn actually spoke, for pills with no widget/plan to show. Only the last assistant say, never a tool line. @@ -1240,17 +1248,21 @@ const AgentCard: React.FC = ({ }} > - dispatch(collapseSession(session.id))} - embedded - fullscreenChat={isFullscreen} - autoFocus={autoFocusInput} - isGlowing={isGlowingRedux && !glowFading} - onDismissGlow={dismissGlow} - onBranch={onBranch ? (newId: string) => onBranch(session.id, newId) : undefined} - /> + {chatMounted ? ( + dispatch(collapseSession(session.id))} + embedded + fullscreenChat={isFullscreen} + autoFocus={autoFocusInput} + isGlowing={isGlowingRedux && !glowFading} + onDismissGlow={dismissGlow} + onBranch={onBranch ? (newId: string) => onBranch(session.id, newId) : undefined} + /> + ) : ( + + )} )}