perf(chat): hidden panes stop polling, rows stop re-rendering, a covered terminal stops drawing
The Workspace keeps panes of other workspaces and tabs mounted (display:none), and every one of them kept following the live stream as if shown. - useOnScreen (IntersectionObserver): ConversationView, the outline and the 700 ms menu scan skip while their pane is hidden and catch up once on show. - A new_event refetches the tail at most every 1.5 s (leading + trailing). - MessageList: WorkGroup/MessageRow/EditedFiles compare only their own blocks and results, so a tail append no longer re-renders every row. - A terminal under the chat is moved off the viewport with a transform, so xterm pauses drawing without a resize. - PaneChat and TerminalView are memoized; LaneConsolePane passes a stable onExit. - RunningStep's clock and the outline poll skip unchanged or hidden updates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useRef, useState, type CSSProperties } from "react";
|
||||
import { memo, useCallback, useEffect, useRef, useState, type CSSProperties } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ArrowUp, FileText, Square, SquareCheck } from "lucide-react";
|
||||
import { isSubmitEnter } from "../../lib/keys";
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
type PaneMenu,
|
||||
} from "../../lib/panePaste";
|
||||
import { useLiveRefresh } from "../../hooks/useLiveRefresh";
|
||||
import { useOnScreen, isShown } from "../../hooks/useOnScreen";
|
||||
import { openInChanges } from "../../lib/openInChanges";
|
||||
import { toast } from "../../lib/toast";
|
||||
import { confirmDialog } from "../../lib/confirm";
|
||||
@@ -306,7 +307,10 @@ function PaneMenuCard({
|
||||
);
|
||||
}
|
||||
|
||||
export function PaneChat({
|
||||
/** Memoized: a lane_update re-renders the Workspace's pane tree, and every
|
||||
* prop here is a primitive or the pane's own run handle — nothing in a
|
||||
* frame about another lane reaches the chat. */
|
||||
export const PaneChat = memo(function PaneChat({
|
||||
run,
|
||||
sessionId,
|
||||
cwd,
|
||||
@@ -370,7 +374,9 @@ export function PaneChat({
|
||||
return () => ro.disconnect();
|
||||
}, []);
|
||||
const [list, setList] = useState<OutlineList | null>(null);
|
||||
const activity = useSessionOutline(sessionId);
|
||||
// Kept mounted in a hidden workspace or tab: its pollers skip until shown.
|
||||
const onScreen = useOnScreen(rootRef);
|
||||
const activity = useSessionOutline(sessionId, onScreen);
|
||||
// Open by default only in a spanning pane, and only once there is an output.
|
||||
const outlineOpen = outlineChoice ?? (panelOpen && activity.outputs.length > 0);
|
||||
const scope: PreviewScope = { laneId, laneCwd: laneCwd || cwd, cwd, sessionId };
|
||||
@@ -421,9 +427,12 @@ export function PaneChat({
|
||||
return JSON.stringify(next) === JSON.stringify(prev) ? prev : next;
|
||||
});
|
||||
read();
|
||||
const timer = window.setInterval(read, 700);
|
||||
// Nobody can answer a menu in a hidden tab or pane: no screen scan then.
|
||||
const timer = window.setInterval(() => {
|
||||
if (isShown(onScreen)) read();
|
||||
}, 700);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [runId]);
|
||||
}, [runId, onScreen]);
|
||||
const asking = waiting || menu !== null;
|
||||
// Gone until the pane redraws: no second press on a stale menu.
|
||||
const choose = (index: number) => {
|
||||
@@ -451,7 +460,10 @@ export function PaneChat({
|
||||
model: null,
|
||||
});
|
||||
const onContext = useCallback(
|
||||
(tokens: number, model: string | null) => setContext({ tokens, model }),
|
||||
(tokens: number, model: string | null) =>
|
||||
setContext((prev) =>
|
||||
prev.tokens === tokens && prev.model === model ? prev : { tokens, model }
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
@@ -678,4 +690,4 @@ export function PaneChat({
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user