diff --git a/src/web-ui/src/app/components/panels/content-canvas/ContentCanvas.tsx b/src/web-ui/src/app/components/panels/content-canvas/ContentCanvas.tsx index 8a657b9fd7..5dc6cf56f6 100644 --- a/src/web-ui/src/app/components/panels/content-canvas/ContentCanvas.tsx +++ b/src/web-ui/src/app/components/panels/content-canvas/ContentCanvas.tsx @@ -86,7 +86,7 @@ export const ContentCanvas: React.FC = ({ const canvasScopeKey = useCanvasStore(state => state.scopeKey); const { workspace: currentWorkspace } = useCurrentWorkspace(); const activeSessionId = useSyncExternalStore( - flowChatStore.subscribe.bind(flowChatStore), + notify => flowChatStore.subscribeSelector(s => s.activeSessionId, () => notify()), () => flowChatStore.getState().activeSessionId, () => flowChatStore.getState().activeSessionId, ); diff --git a/src/web-ui/src/app/hooks/useSessionTabLabels.ts b/src/web-ui/src/app/hooks/useSessionTabLabels.ts index 2aca119a24..47bc18764b 100644 --- a/src/web-ui/src/app/hooks/useSessionTabLabels.ts +++ b/src/web-ui/src/app/hooks/useSessionTabLabels.ts @@ -44,7 +44,12 @@ function readSessionTabLabels( } function subscribeToLabels(notify: () => void): () => void { - const unsubscribe = flowChatStore.subscribe(notify); + // Use subscribeSelector to only trigger when the sessions Map identity changes or surface changes, + // preventing unrelated store state changes from broadcasting to tab labels. + const unsubscribe = flowChatStore.subscribeSelector( + state => state.sessions, + () => notify(), + ); const unsubscribeSurfaces = flowChatStore.subscribeSurfaceSessions(notify); return () => { unsubscribe(); unsubscribeSurfaces(); }; } diff --git a/src/web-ui/src/app/hooks/useSessionTerminalDirectory.ts b/src/web-ui/src/app/hooks/useSessionTerminalDirectory.ts index 7f37a550c1..a2b81a3d97 100644 --- a/src/web-ui/src/app/hooks/useSessionTerminalDirectory.ts +++ b/src/web-ui/src/app/hooks/useSessionTerminalDirectory.ts @@ -38,12 +38,17 @@ export function activeSessionTerminalDirectory(workspaceId: string | undefined): ); } -const subscribeFlowChatStore = (listener: () => void) => flowChatStore.subscribe(listener); +function subscribeActiveTerminalDirectory(workspaceId: string | undefined, notify: () => void): () => void { + return flowChatStore.subscribeSelector( + () => activeSessionTerminalDirectory(workspaceId), + () => notify(), + ); +} /** Terminal default cwd for `workspaceId`, tracking the active session. */ export function useSessionTerminalDirectory(workspaceId: string | undefined): string | undefined { return useSyncExternalStore( - subscribeFlowChatStore, + listener => subscribeActiveTerminalDirectory(workspaceId, listener), () => activeSessionTerminalDirectory(workspaceId), () => undefined, ); diff --git a/src/web-ui/src/app/scenes/session/AuxPane.tsx b/src/web-ui/src/app/scenes/session/AuxPane.tsx index 04a6a19cc8..901ca5c031 100644 --- a/src/web-ui/src/app/scenes/session/AuxPane.tsx +++ b/src/web-ui/src/app/scenes/session/AuxPane.tsx @@ -48,7 +48,7 @@ const AuxPane = forwardRef( ({ workspacePath, externalToolbar = false, isSceneActive = true, terminalResizeSuspended = false, resizeSuspended = false, isFullscreen = false, onToggleFullscreen }, ref) => { const { t } = useI18n('components'); const activeSessionId = useSyncExternalStore( - flowChatStore.subscribe.bind(flowChatStore), + notify => flowChatStore.subscribeSelector(s => s.activeSessionId, () => notify()), () => flowChatStore.getState().activeSessionId, () => flowChatStore.getState().activeSessionId, ); diff --git a/src/web-ui/src/flow_chat/hooks/useActiveSessionState.ts b/src/web-ui/src/flow_chat/hooks/useActiveSessionState.ts index 0e4b9fc6f0..d21712ba6a 100644 --- a/src/web-ui/src/flow_chat/hooks/useActiveSessionState.ts +++ b/src/web-ui/src/flow_chat/hooks/useActiveSessionState.ts @@ -38,10 +38,23 @@ export const useActiveSessionState = (): ActiveSessionState => { }; }, [surfaceId, sessionId]); const subscribe = useCallback((notify: () => void) => { - const stopStore = flowChatStore.subscribe(notify); - const stopMachine = stateMachineManager.subscribeGlobal(notify); + // Only wake when target session entity or activeSessionId reference changes, + // avoiding re-evaluation on unrelated background sessions' stream flushes. + const stopStore = flowChatStore.subscribeSelector( + state => { + const targetId = sessionId !== undefined && surfaceId !== undefined + ? (surfaceId === getActiveSurfaceId() ? sessionId : undefined) + : state.activeSessionId ?? ''; + return targetId ? state.sessions.get(targetId) : undefined; + }, + () => notify(), + ); + const stopMachine = stateMachineManager.subscribeGlobal((changedSessionId) => { + const activeId = sessionId ?? flowChatStore.getState().activeSessionId; + if (!activeId || changedSessionId === activeId) notify(); + }); const stopSurface = onSurfaceActivated(notify); return () => { stopStore(); stopMachine(); stopSurface(); }; - }, []); + }, [surfaceId, sessionId]); return useSyncExternalStore(subscribe, getSnapshot, getSnapshot); };