diff --git a/packages/app/src/hooks/use-providers-snapshot.ts b/packages/app/src/hooks/use-providers-snapshot.ts index 2dccfe618..f7a4a12f4 100644 --- a/packages/app/src/hooks/use-providers-snapshot.ts +++ b/packages/app/src/hooks/use-providers-snapshot.ts @@ -30,13 +30,19 @@ interface UseProvidersSnapshotResult { refetchIfStale: () => void; } +interface UseProvidersSnapshotOptions { + enabled?: boolean; +} + export function useProvidersSnapshot( serverId: string | null, cwd?: string | null, + options: UseProvidersSnapshotOptions = {}, ): UseProvidersSnapshotResult { const queryClient = useQueryClient(); const client = useHostRuntimeClient(serverId ?? ""); const isConnected = useHostRuntimeIsConnected(serverId ?? ""); + const enabled = options.enabled ?? true; const normalizedCwd = cwd?.trim() || undefined; const normalizedCwdKey = normalizeProvidersSnapshotCwdKey(normalizedCwd); const supportsSnapshot = useSessionStore( @@ -50,7 +56,7 @@ export function useProvidersSnapshot( const snapshotQuery = useQuery({ queryKey, - enabled: Boolean(supportsSnapshot && serverId && client && isConnected), + enabled: Boolean(enabled && supportsSnapshot && serverId && client && isConnected), staleTime: 60_000, queryFn: async () => { if (!client) { @@ -71,7 +77,7 @@ export function useProvidersSnapshot( const { mutateAsync: refreshSnapshot, isPending: isRefreshing } = refreshMutation; useEffect(() => { - if (!supportsSnapshot || !client || !isConnected || !serverId) { + if (!enabled || !supportsSnapshot || !client || !isConnected || !serverId) { return; } @@ -89,7 +95,16 @@ export function useProvidersSnapshot( requestId: "providers_snapshot_update", }); }); - }, [client, isConnected, normalizedCwdKey, queryClient, queryKey, serverId, supportsSnapshot]); + }, [ + client, + enabled, + isConnected, + normalizedCwdKey, + queryClient, + queryKey, + serverId, + supportsSnapshot, + ]); const refresh = useCallback( async (providers?: AgentProvider[]) => { diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx index 884feafd9..75e1e8d9a 100644 --- a/packages/app/src/screens/workspace/workspace-screen.tsx +++ b/packages/app/src/screens/workspace/workspace-screen.tsx @@ -131,6 +131,10 @@ type WorkspaceScreenProps = { workspaceId: string; }; +type WorkspaceScreenContentProps = WorkspaceScreenProps & { + isRouteFocused: boolean; +}; + function trimNonEmpty(value: string | null | undefined): string | null { if (typeof value !== "string") { return null; @@ -566,13 +570,13 @@ function useStableTabDescriptorMap(tabDescriptors: WorkspaceTabDescriptor[]) { export function WorkspaceScreen({ serverId, workspaceId }: WorkspaceScreenProps) { const isFocused = useIsFocused(); - if (!isFocused) { - return ; - } - return ( - + ); } @@ -604,7 +608,11 @@ function useCloseTabs(): UseCloseTabsResult { return { closingTabIds, closeTab }; } -function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) { +function WorkspaceScreenContent({ + serverId, + workspaceId, + isRouteFocused, +}: WorkspaceScreenContentProps) { const { theme } = useUnistyles(); const insets = useSafeAreaInsets(); const mainBackgroundColor = theme.colors.surfaceWorkspace; @@ -646,11 +654,15 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) // Warm the server-side provider snapshot for this workspace cwd so the model // picker is ready when opened. Consumers share the same query cache key. - useProvidersSnapshot(normalizedServerId, workspaceDirectory); + useProvidersSnapshot(normalizedServerId, workspaceDirectory, { + enabled: isRouteFocused, + }); const [pendingTerminalCreateInput, setPendingTerminalCreateInput] = useState<{ paneId?: string; } | null>(null); - const canCreateTerminalNow = Boolean(client && isConnected && workspaceDirectory); + const canCreateTerminalNow = Boolean( + isRouteFocused && client && isConnected && workspaceDirectory, + ); const workspaceAgentVisibility = useStoreWithEqualityFn( useSessionStore, @@ -669,7 +681,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) type ListTerminalsPayload = ListTerminalsResponse["payload"]; const terminalsQuery = useQuery({ queryKey: terminalsQueryKey, - enabled: Boolean(client && isConnected) && Boolean(workspaceDirectory), + enabled: Boolean(isRouteFocused && client && isConnected && workspaceDirectory), queryFn: async () => { if (!client || !workspaceDirectory) { throw new Error("Host is not connected"); @@ -736,7 +748,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) const { archiveAgent } = useArchiveAgent(); useEffect(() => { - if (!client || !isConnected || !workspaceDirectory) { + if (!isRouteFocused || !client || !isConnected || !workspaceDirectory) { return; } @@ -758,9 +770,11 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) unsubscribeChanged(); client.unsubscribeTerminals({ cwd: workspaceDirectory }); }; - }, [client, isConnected, queryClient, terminalsQueryKey, workspaceDirectory]); + }, [client, isConnected, isRouteFocused, queryClient, terminalsQueryKey, workspaceDirectory]); - const isCheckoutQueryEnabled = Boolean(client && isConnected) && Boolean(workspaceDirectory); + const isCheckoutQueryEnabled = Boolean( + isRouteFocused && client && isConnected && workspaceDirectory, + ); const checkoutQuery = useQuery({ queryKey: checkoutStatusQueryKey( normalizedServerId, @@ -851,8 +865,11 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) }, [isGitCheckout, normalizedServerId, workspaceDirectory]); useEffect(() => { + if (!isRouteFocused) { + return; + } setActiveExplorerCheckout(activeExplorerCheckout); - }, [activeExplorerCheckout, setActiveExplorerCheckout]); + }, [activeExplorerCheckout, isRouteFocused, setActiveExplorerCheckout]); const openExplorerForWorkspace = useCallback(() => { if (!activeExplorerCheckout) { @@ -876,7 +893,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) }); useEffect(() => { - if (isWeb || !isExplorerOpen) { + if (!isRouteFocused || isWeb || !isExplorerOpen) { return; } @@ -889,7 +906,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) }); return () => handler.remove(); - }, [closeToAgent, isExplorerOpen]); + }, [closeToAgent, isExplorerOpen, isRouteFocused]); const persistenceKey = useMemo( () => @@ -979,14 +996,20 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) }, [focusedPaneTabState.activeTab]); useEffect(() => { + if (!isRouteFocused) { + return; + } setFocusedAgentId(normalizedServerId, focusedPaneAgentId); - }, [focusedPaneAgentId, normalizedServerId, setFocusedAgentId]); + }, [focusedPaneAgentId, isRouteFocused, normalizedServerId, setFocusedAgentId]); useEffect(() => { + if (!isRouteFocused) { + return; + } return () => { setFocusedAgentId(normalizedServerId, null); }; - }, [normalizedServerId, setFocusedAgentId]); + }, [isRouteFocused, normalizedServerId, setFocusedAgentId]); const ensureWorkspaceTab = useCallback( function ensureWorkspaceTab(target: WorkspaceTabTarget): string | null { @@ -1018,6 +1041,9 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) ); useEffect(() => { + if (!isRouteFocused) { + return; + } if (!normalizedServerId || !normalizedWorkspaceId || !persistenceKey) { return; } @@ -1040,6 +1066,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) }); }, [ hasHydratedAgents, + isRouteFocused, pendingByDraftId, persistenceKey, reconcileWorkspaceTabs, @@ -1077,6 +1104,9 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) const emptyWorkspaceSeedRef = useRef(null); const autoOpenedSetupTabWorkspaceRef = useRef(null); useEffect(() => { + if (!isRouteFocused) { + return; + } if (!persistenceKey) { return; } @@ -1099,12 +1129,16 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) normalizedWorkspaceId, openWorkspaceDraftTab, persistenceKey, + isRouteFocused, terminals.length, tabs.length, workspaceAgentVisibility.activeAgentIds.size, ]); useEffect(() => { + if (!isRouteFocused) { + return; + } if (!persistenceKey) { return; } @@ -1146,6 +1180,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) autoOpenedSetupTabWorkspaceRef.current = persistenceKey; }, [ hasSetupTab, + isRouteFocused, normalizedWorkspaceId, openWorkspaceTabInBackground, persistenceKey, @@ -1763,7 +1798,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) "workspace.tab.navigate-relative", "workspace.terminal.new", ] as const, - enabled: Boolean(normalizedServerId && normalizedWorkspaceId), + enabled: Boolean(isRouteFocused && normalizedServerId && normalizedWorkspaceId), priority: 100, isActive: () => true, handle: handleWorkspaceTabAction, @@ -1784,7 +1819,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) "workspace.pane.move-tab.down", "workspace.pane.close", ] as const, - enabled: Boolean(normalizedServerId && normalizedWorkspaceId), + enabled: Boolean(isRouteFocused && normalizedServerId && normalizedWorkspaceId), priority: 100, isActive: () => true, handle: handleWorkspacePaneAction, @@ -1794,11 +1829,11 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) const canRenderDesktopPaneSplits = supportsDesktopPaneSplits(); const shouldRenderDesktopPaneFallback = !isMobile && !canRenderDesktopPaneSplits; useEffect(() => { - if (isNative || typeof document === "undefined" || activeTabDescriptor) { + if (!isRouteFocused || isNative || typeof document === "undefined" || activeTabDescriptor) { return; } document.title = "Workspace"; - }, [activeTabDescriptor]); + }, [activeTabDescriptor, isRouteFocused]); const buildPaneContentModel = useCallback( (input: { tab: WorkspaceTabDescriptor; @@ -1810,7 +1845,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) tab: input.tab, normalizedServerId, normalizedWorkspaceId, - isPaneFocused: input.isPaneFocused, + isPaneFocused: isRouteFocused && input.isPaneFocused, onOpenTab: (target) => { if (!persistenceKey) { return; @@ -1846,6 +1881,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) [ handleCloseTabById, handleOpenFileFromChat, + isRouteFocused, focusWorkspacePane, navigateToTabId, normalizedServerId, @@ -1919,8 +1955,8 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) @@ -2029,7 +2065,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) return ( - {isWeb && activeTabDescriptor ? ( + {isRouteFocused && isWeb && activeTabDescriptor ? ( - {(!isFocusModeEnabled || isMobile) && + {isRouteFocused && + (!isFocusModeEnabled || isMobile) && (workspaceDirectory ? (