diff --git a/packages/app/e2e/helpers/settings.ts b/packages/app/e2e/helpers/settings.ts index 40f128572..7b8c63bda 100644 --- a/packages/app/e2e/helpers/settings.ts +++ b/packages/app/e2e/helpers/settings.ts @@ -393,9 +393,9 @@ export async function expectLocalHostEntryFirst(page: Page, _serverId: string): await expect(sidebar).toBeVisible({ timeout: 15_000 }); // Single-host fixture: the picker is a non-interactive chip (no dropdown to - // open) that surfaces the local host by its label. The "Local" marker only - // appears on dropdown rows in the multi-host case, which this fixture does not - // exercise. + // open) that surfaces the local host by its label. The per-row connection + // endpoint only appears on dropdown rows in the multi-host case, which this + // fixture does not exercise. const picker = sidebar.getByTestId("settings-host-picker"); await expect(picker).toBeVisible(); await expect(picker.getByText(TEST_HOST_LABEL, { exact: true })).toBeVisible(); diff --git a/packages/app/e2e/settings-host-page.spec.ts b/packages/app/e2e/settings-host-page.spec.ts index a25786de1..e20480724 100644 --- a/packages/app/e2e/settings-host-page.spec.ts +++ b/packages/app/e2e/settings-host-page.spec.ts @@ -118,7 +118,7 @@ test.describe("Settings host page", () => { await expectHostActionCards(page, serverId); }); - test("sidebar pins the local daemon host first with a Local marker", async ({ page }) => { + test("sidebar pins the local daemon host first", async ({ page }) => { const serverId = getServerId(); // Simulate the Electron desktop bridge so `useIsLocalDaemon` resolves the diff --git a/packages/app/src/components/hosts/host-picker.tsx b/packages/app/src/components/hosts/host-picker.tsx index 86101d857..d39bd0b5b 100644 --- a/packages/app/src/components/hosts/host-picker.tsx +++ b/packages/app/src/components/hosts/host-picker.tsx @@ -1,11 +1,12 @@ import { useCallback, useMemo, type ReactElement, type ReactNode } from "react"; -import { Pressable, Text, View } from "react-native"; +import { Pressable, View } from "react-native"; import type { GestureResponderEvent } from "react-native"; import { Plus, Server, Settings } from "lucide-react-native"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { HostStatusDot } from "@/components/host-status-dot"; import { Combobox, ComboboxItem, type ComboboxProps } from "@/components/ui/combobox"; import { useLocalDaemonServerId } from "@/hooks/use-is-local-daemon"; +import { useHostRuntimeSnapshot, type ActiveConnection } from "@/runtime/host-runtime"; import { orderHostsLocalFirst } from "@/types/host-connection"; import { ADD_HOST_OPTION_ID, @@ -30,30 +31,48 @@ export function HostStatusDotSlot({ serverId }: { serverId: string }): ReactElem ); } +// Standard secure/plain web ports carry no information in the host display, so +// "relay.paseo.sh:443" reads as "relay.paseo.sh" while "127.0.0.1:6767" is kept. +function formatConnectionEndpoint(endpoint: string): string { + return endpoint.replace(/:(?:443|80)$/, ""); +} + +// Socket/pipe transports have no host:port — their endpoint is a filesystem +// path, so they read as "Local". TCP and relay show the address being used. +function formatActiveConnectionLabel(connection: ActiveConnection): string { + if (connection.type === "directSocket" || connection.type === "directPipe") { + return "Local"; + } + return formatConnectionEndpoint(connection.endpoint); +} + export interface HostPickerOptionProps { serverId: string; label: string; - isLocal: boolean; + showActiveConnection: boolean; selected?: boolean; active: boolean; onPress: () => void; onOpenHostSettings?: (serverId: string) => void; - localMarkerTestID?: string; testID?: string; } export function HostPickerOption({ serverId, label, - isLocal, + showActiveConnection, selected, active, onPress, onOpenHostSettings, - localMarkerTestID, testID, }: HostPickerOptionProps): ReactElement { const { theme } = useUnistyles(); + const activeConnection = useHostRuntimeSnapshot(serverId)?.activeConnection ?? null; + const connectionLabel = + showActiveConnection && activeConnection + ? formatActiveConnectionLabel(activeConnection) + : undefined; const leadingSlot = useMemo(() => , [serverId]); const handleSettingsPress = useCallback( (event: GestureResponderEvent) => { @@ -63,31 +82,20 @@ export function HostPickerOption({ [onOpenHostSettings, serverId], ); const trailingSlot = useMemo(() => { - if (!isLocal && !onOpenHostSettings) return undefined; + if (!onOpenHostSettings) return undefined; return ( - <> - {isLocal ? ( - - Local - - ) : null} - {onOpenHostSettings ? ( - - - - ) : null} - + + + ); }, [ handleSettingsPress, - isLocal, label, - localMarkerTestID, onOpenHostSettings, theme.colors.foregroundMuted, theme.iconSize.sm, @@ -96,6 +104,7 @@ export function HostPickerOption({ return ( void; - showLocalMarker?: boolean; + showActiveConnection?: boolean; onOpenHostSettings?: (serverId: string) => void; searchable?: boolean; title?: string; @@ -157,7 +166,6 @@ export interface HostPickerProps { desktopMinWidth?: number; addHostTestID?: string; hostOptionTestID?: (serverId: string) => string; - hostLocalMarkerTestID?: (serverId: string) => string; children: ReactNode; } @@ -171,7 +179,7 @@ export function HostPicker({ includeAllHost, includeAddHost, onAddHost, - showLocalMarker, + showActiveConnection, onOpenHostSettings, searchable, title, @@ -179,7 +187,6 @@ export function HostPicker({ desktopMinWidth, addHostTestID, hostOptionTestID, - hostLocalMarkerTestID, children, }: HostPickerProps): ReactElement { const localServerId = useLocalDaemonServerId(); @@ -243,23 +250,20 @@ export function HostPicker({ ); }, [ addHostTestID, - hostLocalMarkerTestID, hostOptionTestID, - localServerId, onOpenHostSettings, - showLocalMarker, + showActiveConnection, handleOpenHostSettings, ], ); @@ -292,9 +296,4 @@ const styles = StyleSheet.create((theme) => ({ alignItems: "center", justifyContent: "center", }, - localMarker: { - fontSize: theme.fontSize.xs, - color: theme.colors.foregroundMuted, - marginLeft: theme.spacing[1], - }, })); diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx index 00b027236..6c6fd9c09 100644 --- a/packages/app/src/components/left-sidebar.tsx +++ b/packages/app/src/components/left-sidebar.tsx @@ -290,10 +290,6 @@ function sidebarHostOptionTestID(serverId: string): string { return `sidebar-host-row-${serverId}`; } -function sidebarHostLocalMarkerTestID(serverId: string): string { - return `sidebar-host-local-marker-${serverId}`; -} - function FooterIconButton({ buttonRef, onPress, @@ -365,13 +361,12 @@ function SidebarHostPicker({ anchorRef={triggerRef} includeAddHost onAddHost={onAddHost} - showLocalMarker + showActiveConnection onOpenHostSettings={onOpenHostSettings} searchable desktopMinWidth={240} addHostTestID="sidebar-host-add" hostOptionTestID={sidebarHostOptionTestID} - hostLocalMarkerTestID={sidebarHostLocalMarkerTestID} > . The local host is listed first and tagged "Local"; an "Add host" - * row is always reachable from the list — even with a single host. + * . The local host is listed first, each row shows the connection it + * is using right now; an "Add host" row is always reachable from the list — + * even with a single host. */ function HostPicker({ activeServerId, sortedHosts, onSelectHost, onAddHost }: HostPickerProps) { const { t } = useTranslation(); @@ -906,7 +907,6 @@ function HostPicker({ activeServerId, sortedHosts, onSelectHost, onAddHost }: Ho sortedHosts.find((host) => host.serverId === activeServerId) ?? sortedHosts[0] ?? null; const handleOpen = useCallback(() => setIsOpen(true), []); - const hostLocalMarkerTestID = useCallback(() => "settings-host-local-marker", []); const hostOptionTestID = useCallback( (serverId: string) => `settings-host-picker-item-${serverId}`, [], @@ -929,12 +929,11 @@ function HostPicker({ activeServerId, sortedHosts, onSelectHost, onAddHost }: Ho anchorRef={triggerRef} includeAddHost onAddHost={onAddHost} - showLocalMarker + showActiveConnection searchable={false} title={t("settings.hostPicker.switchHost")} desktopMinWidth={240} addHostTestID="settings-add-host" - hostLocalMarkerTestID={hostLocalMarkerTestID} hostOptionTestID={hostOptionTestID} >