diff --git a/webviews/src/agent-session/acpmux/App.tsx b/webviews/src/agent-session/acpmux/App.tsx index 18583faf2d17..dd833f48f894 100644 --- a/webviews/src/agent-session/acpmux/App.tsx +++ b/webviews/src/agent-session/acpmux/App.tsx @@ -1981,13 +1981,16 @@ export function VirtualTranscript({ let lastTurnId: string | undefined; for (let index = rows.length - 1; index >= 0 && !lastTurnId; index -= 1) if (rows[index]!.kind === "turnSummary") lastTurnId = rows[index]!.id; - const markTurns = () => { + const markTurns = useCallback(() => { for (const node of thread.current?.querySelectorAll(".acpmux-row") ?? []) { node.toggleAttribute("data-last-turn", node.dataset.rowId === lastTurnId); node.toggleAttribute("data-turn-revealed", node.dataset.rowId === pointedTurn.current); } - }; - useLayoutEffect(markTurns); + }, [lastTurnId]); + // Mark only when the mounted rows, their range, or the last turn changes. The old no-deps + // layout effect scanned every commit, including scroll/follow state commits that cannot change + // these attributes. `pointAt` still calls the callback directly for pointer-driven updates. + useLayoutEffect(markTurns, [markTurns, rows, range.first, range.last]); // Older history pages in before the reader reaches it: whenever the view is within two viewports // of the first loaded row, including a tail too short to fill them. The anchoring above keeps the // reader's row in place as each page lands.