From faf85e038cf4b4daca2536dd10da45b18a24801e Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 10 Apr 2026 18:48:36 +0700 Subject: [PATCH] Fix host switcher status syncing --- packages/app/src/components/left-sidebar.tsx | 99 ++++++++++++-------- 1 file changed, 62 insertions(+), 37 deletions(-) diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx index c3eb01e31..55594f13d 100644 --- a/packages/app/src/components/left-sidebar.tsx +++ b/packages/app/src/components/left-sidebar.tsx @@ -5,8 +5,8 @@ import { useState, useEffect, useRef, - useSyncExternalStore, type Dispatch, + type ReactElement, type RefObject, type SetStateAction, } from "react"; @@ -44,8 +44,8 @@ import { import { useSidebarAnimation } from "@/contexts/sidebar-animation-context"; import { useWindowControlsPadding } from "@/utils/desktop-window"; import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region"; -import { Combobox } from "@/components/ui/combobox"; -import { getHostRuntimeStore, useHosts } from "@/runtime/host-runtime"; +import { Combobox, ComboboxItem, type ComboboxOption } from "@/components/ui/combobox"; +import { useHostRuntimeSnapshot, useHosts } from "@/runtime/host-runtime"; import { formatConnectionStatus } from "@/utils/daemons"; import { HEADER_INNER_HEIGHT, @@ -69,18 +69,12 @@ interface LeftSidebarProps { selectedAgentId?: string; } -interface HostOption { - id: string; - label: string; - description: string; -} - interface SidebarSharedProps { theme: SidebarTheme; activeServerId: string | null; activeHostLabel: string; activeHostStatusColor: string; - hostOptions: HostOption[]; + hostOptions: ComboboxOption[]; hostTriggerRef: RefObject; isHostPickerOpen: boolean; setIsHostPickerOpen: Dispatch>; @@ -95,6 +89,12 @@ interface SidebarSharedProps { handleHostSelect: (nextServerId: string) => void; handleOpenProject: () => void; handleSettings: () => void; + renderHostOption: (input: { + option: ComboboxOption; + selected: boolean; + active: boolean; + onPress: () => void; + }) => ReactElement; } interface MobileSidebarProps extends SidebarSharedProps { @@ -124,28 +124,6 @@ export const LeftSidebar = memo(function LeftSidebar({ const closeToAgent = usePanelStore((state) => state.closeToAgent); const pathname = usePathname(); const daemons = useHosts(); - const runtime = getHostRuntimeStore(); - const runtimeConnectionStatusSignature = useSyncExternalStore( - (onStoreChange) => runtime.subscribeAll(onStoreChange), - () => - daemons - .map( - (daemon) => - `${daemon.serverId}:${ - runtime.getSnapshot(daemon.serverId)?.connectionStatus ?? "connecting" - }`, - ) - .join("|"), - () => - daemons - .map( - (daemon) => - `${daemon.serverId}:${ - runtime.getSnapshot(daemon.serverId)?.connectionStatus ?? "connecting" - }`, - ) - .join("|"), - ); const activeServerIdFromPath = useMemo(() => parseServerIdFromPathname(pathname), [pathname]); const activeDaemon = useMemo(() => { if (daemons.length === 0) { @@ -165,8 +143,9 @@ export const LeftSidebar = memo(function LeftSidebar({ const trimmed = activeDaemon.label?.trim(); return trimmed && trimmed.length > 0 ? trimmed : activeDaemon.serverId; }, [activeDaemon]); + const activeHostSnapshot = useHostRuntimeSnapshot(activeServerId ?? ""); const activeHostStatus = activeServerId - ? (runtime.getSnapshot(activeServerId)?.connectionStatus ?? "connecting") + ? (activeHostSnapshot?.connectionStatus ?? "connecting") : "idle"; const activeHostStatusColor = activeHostStatus === "online" @@ -179,11 +158,25 @@ export const LeftSidebar = memo(function LeftSidebar({ daemons.map((daemon) => ({ id: daemon.serverId, label: daemon.label?.trim() || daemon.serverId, - description: formatConnectionStatus( - runtime.getSnapshot(daemon.serverId)?.connectionStatus ?? "connecting", - ), })), - [daemons, runtime, runtimeConnectionStatusSignature], + [daemons], + ); + const renderHostOption = useCallback( + ({ option, selected, active, onPress }: { + option: ComboboxOption; + selected: boolean; + active: boolean; + onPress: () => void; + }) => ( + + ), + [], ); const hostTriggerRef = useRef(null); const [isHostPickerOpen, setIsHostPickerOpen] = useState(false); @@ -276,6 +269,7 @@ export const LeftSidebar = memo(function LeftSidebar({ toggleProjectCollapsed, handleRefresh, handleHostSelect, + renderHostOption, }; if (isCompactLayout) { @@ -305,6 +299,33 @@ export const LeftSidebar = memo(function LeftSidebar({ ); }); +function HostSwitchOption({ + serverId, + label, + selected, + active, + onPress, +}: { + serverId: string; + label: string; + selected: boolean; + active: boolean; + onPress: () => void; +}) { + const snapshot = useHostRuntimeSnapshot(serverId); + const connectionStatus = snapshot?.connectionStatus ?? "connecting"; + + return ( + + ); +} + function SessionsButton({ onPress }: { onPress: () => void }) { const { theme } = useUnistyles(); const pathname = usePathname(); @@ -361,6 +382,7 @@ function MobileSidebar({ toggleProjectCollapsed, handleRefresh, handleHostSelect, + renderHostOption, handleOpenProject, handleSettings, insetsTop, @@ -606,6 +628,7 @@ function MobileSidebar({ options={hostOptions} value={activeServerId ?? ""} onSelect={handleHostSelect} + renderOption={renderHostOption} searchable={false} title="Switch host" searchPlaceholder="Search hosts..." @@ -639,6 +662,7 @@ function DesktopSidebar({ toggleProjectCollapsed, handleRefresh, handleHostSelect, + renderHostOption, handleOpenProject, handleSettings, insetsTop, @@ -789,6 +813,7 @@ function DesktopSidebar({ options={hostOptions} value={activeServerId ?? ""} onSelect={handleHostSelect} + renderOption={renderHostOption} searchable={false} title="Switch host" searchPlaceholder="Search hosts..."