From ef6f2a2a1a3f2a2d79aa8376cddfa7e702ea2bce Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Sun, 11 Oct 2026 05:02:52 -0400 Subject: [PATCH] fix(mobile): name run targets and the desktop's own row as the host list does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Run on picker showed a server's raw environment id behind "Remote ·" and always a monitor glyph; it now takes the host list's badge labels and icon, as the desktop's picker does. Server rows also made the desktop row read "This computer" because its platform was only read for a mixed desktop catalog. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- mobile/src/components/HostKindIcon.tsx | 10 ++++ .../src/components/NewWorktreeFormSheet.tsx | 2 +- .../src/components/NewWorktreeModal.test.tsx | 21 +++++++- mobile/src/components/NewWorktreeModal.tsx | 7 +-- .../components/NewWorktreeModalController.tsx | 2 + .../components/NewWorktreeModalDrawers.tsx | 5 +- .../NewWorktreeProjectTargetFields.tsx | 7 +-- mobile/src/components/WorktreeListRow.tsx | 21 ++------ .../new-workspace-project-targets.test.ts | 32 +++++++------ .../new-workspace-project-targets.ts | 33 ++++++------- .../components/new-worktree-modal-types.ts | 2 + .../src/host-screen/host-screen-overlays.tsx | 2 + .../host-server-workspaces-wiring.test.ts | 21 +++++++- .../host-ssh-health-badge-wiring.test.ts | 4 +- .../host-screen/use-host-display-worktrees.ts | 48 +++++++++++++------ .../host-screen/use-host-screen-controller.ts | 6 ++- 16 files changed, 142 insertions(+), 81 deletions(-) create mode 100644 mobile/src/components/HostKindIcon.tsx diff --git a/mobile/src/components/HostKindIcon.tsx b/mobile/src/components/HostKindIcon.tsx new file mode 100644 index 00000000000..11fb9bb0b55 --- /dev/null +++ b/mobile/src/components/HostKindIcon.tsx @@ -0,0 +1,10 @@ +import { Monitor, Server } from 'lucide-react-native' +import { parseExecutionHostId } from '../../../src/shared/execution-host' +import { colors } from '../theme/mobile-theme' + +/** The desktop's own machine is a monitor, any other host a server, as on the desktop's run targets. */ +export function HostKindIcon({ hostId, size }: { hostId: string | undefined; size: number }) { + // Rows from hosts that predate hostId stamping are local: a remote row always carries one. + const Icon = (parseExecutionHostId(hostId)?.kind ?? 'local') === 'local' ? Monitor : Server + return +} diff --git a/mobile/src/components/NewWorktreeFormSheet.tsx b/mobile/src/components/NewWorktreeFormSheet.tsx index 895b601b84b..3f4f8a725e2 100644 --- a/mobile/src/components/NewWorktreeFormSheet.tsx +++ b/mobile/src/components/NewWorktreeFormSheet.tsx @@ -24,7 +24,7 @@ export function NewWorktreeFormSheet(props: { loading: boolean hasRepos: boolean project: Selection | null - runTarget: Selection | null + runTarget: (Selection & { hostId: string }) | null projectBadgeColor: string | null selectedRepoIsGit: boolean selectedRepoConnectionId: string | null diff --git a/mobile/src/components/NewWorktreeModal.test.tsx b/mobile/src/components/NewWorktreeModal.test.tsx index 459fdf4a4f2..5d1bbf450ff 100644 --- a/mobile/src/components/NewWorktreeModal.test.tsx +++ b/mobile/src/components/NewWorktreeModal.test.tsx @@ -184,7 +184,7 @@ describe('NewWorktreeModal project targets', () => { expect(pickerItems(renderer, 'Run on')).toEqual([ expect.objectContaining({ label: LOCAL_HOST_LABEL, detail: '/src/orca' }), expect.objectContaining({ - label: 'SSH · build-server', + label: 'build-server', detail: '/home/dev/orca' }) ]) @@ -349,6 +349,7 @@ describe('NewWorktreeModal project targets', () => { client: requestsOnly(desktop), serverClients: new Map([['runtime:env-1', requestsOnly(server)]]), hostId: 'host-1', + hostLabelById: new Map([['runtime:env-1', 'scratch-server']]), onCreated, onClose: () => {} }) @@ -357,10 +358,26 @@ describe('NewWorktreeModal project targets', () => { await flushUpdates() const runTargets = pickerItems(renderer, 'Run on') - expect(runTargets.map((item) => item.detail)).toEqual(['/src/orca', '/srv/orca']) + expect(runTargets.map((item) => [item.label, item.detail])).toEqual([ + [LOCAL_HOST_LABEL, '/src/orca'], + ['scratch-server', '/srv/orca'] + ]) const serverTarget = runTargetPicker(renderer) + const iconOf = (item: unknown) => { + let icon: ReactTestRenderer | undefined + act(() => { + icon = create(serverTarget.props.renderIcon(item)) + }) + return icon?.toJSON() + } + expect(serverTarget.props.items.map(iconOf)).toMatchObject([ + { type: 'Monitor' }, + { type: 'Server' } + ]) await act(async () => serverTarget.props.onSelect(serverTarget.props.items[1])) await flushUpdates() + // The Run on field shows the picked server's glyph, as its row does. + expect(renderer.root.findAll((node) => node.type === 'Server')).toHaveLength(1) expect(pickerItems(renderer, 'Run on')[0]?.label).toBe(LOCAL_HOST_LABEL) act(() => sourceInputs(renderer)[0]!.props.onChangeText('srv-wt')) const form = renderer.root.find((node) => typeof node.props.onCreate === 'function') diff --git a/mobile/src/components/NewWorktreeModal.tsx b/mobile/src/components/NewWorktreeModal.tsx index 1c3a310a674..222083fd70b 100644 --- a/mobile/src/components/NewWorktreeModal.tsx +++ b/mobile/src/components/NewWorktreeModal.tsx @@ -69,6 +69,7 @@ function NewWorktreeModalContent(props: NewWorktreeModalProps) { client: desktopClient, serverClients, hostId, + hostLabelById, existingWorktreePaths, existingWorktrees, openExternalUrl, @@ -182,11 +183,11 @@ function NewWorktreeModalContent(props: NewWorktreeModalProps) { const selectedProject = projectPickerItems.find((project) => project.id === selectedProjectId) ?? null const runTargetPickerItems = useMemo( - () => buildNewWorkspaceRunTargetOptions(repos, selectedProjectId, hostPlatform), - [hostPlatform, repos, selectedProjectId] + () => buildNewWorkspaceRunTargetOptions(repos, selectedProjectId, hostPlatform, hostLabelById), + [hostLabelById, hostPlatform, repos, selectedProjectId] ) const selectedRunTarget = selectedRepo - ? getNewWorkspaceRunTarget(selectedRepo, hostPlatform) + ? getNewWorkspaceRunTarget(selectedRepo, hostPlatform, hostLabelById) : null const needsSetupChoice = Boolean(setupScript.setupCommand) && setupScript.setupRunPolicy === 'ask' const canCreate = diff --git a/mobile/src/components/NewWorktreeModalController.tsx b/mobile/src/components/NewWorktreeModalController.tsx index f73556bdc48..c00dfc03e9e 100644 --- a/mobile/src/components/NewWorktreeModalController.tsx +++ b/mobile/src/components/NewWorktreeModalController.tsx @@ -20,6 +20,7 @@ export const NewWorktreeModalController = forwardRef props.onRepoChange(item.repo)} onClose={props.onTransitionToForm} - renderIcon={() => } + renderIcon={(item) => } /> void onOpenRunTarget: () => void @@ -33,7 +34,7 @@ export function NewWorktreeProjectTargetFields({ Run on - + diff --git a/mobile/src/components/WorktreeListRow.tsx b/mobile/src/components/WorktreeListRow.tsx index 6a01c8f8766..3faaf00635a 100644 --- a/mobile/src/components/WorktreeListRow.tsx +++ b/mobile/src/components/WorktreeListRow.tsx @@ -1,21 +1,14 @@ import { memo } from 'react' -import { - Bell, - ChevronDown, - ChevronRight, - GitBranch, - GitPullRequest, - Monitor, - Server -} from 'lucide-react-native' +import { Bell, ChevronDown, ChevronRight, GitBranch, GitPullRequest } from 'lucide-react-native' import { Pressable, StyleSheet, Text, View } from 'react-native' -import { parseExecutionHostId, type ExecutionHostId } from '../../../src/shared/execution-host' +import type { ExecutionHostId } from '../../../src/shared/execution-host' import type { AgentWorkingMode } from '../../../src/shared/agent-status-types' import type { RuntimeWorktreeAgentRow } from '../../../src/shared/runtime-types' import type { MobileRenderableRepoIcon } from '../host-screen/host-screen-reply-schema' import { triggerMediumImpact } from '../platform/haptics' import { colors, radii, spacing, typography } from '../theme/mobile-theme' import { AgentSpinner } from './AgentSpinner' +import { HostKindIcon } from './HostKindIcon' import { MobileRepoIcon } from './MobileRepoIcon' import { WorktreeAgentList } from './WorktreeAgentList' import { WorktreeMetaGlyphs, prStateColor } from './WorktreeMetaGlyphs' @@ -173,13 +166,7 @@ function WorktreeListRowComponent({ .filter(Boolean) .join(', ')} > - {/* Rows from hosts that predate hostId stamping are local: a remote row always carries one. */} - {(parseExecutionHostId(item.hostContextHostId ?? item.hostId)?.kind ?? 'local') === - 'local' ? ( - - ) : ( - - )} + {item.hostContextLabel} diff --git a/mobile/src/components/new-workspace-project-targets.test.ts b/mobile/src/components/new-workspace-project-targets.test.ts index 18c379f2d14..d1e83550e9c 100644 --- a/mobile/src/components/new-workspace-project-targets.test.ts +++ b/mobile/src/components/new-workspace-project-targets.test.ts @@ -43,16 +43,16 @@ describe('new workspace project targets', () => { expect(options[0]).toMatchObject({ label: 'orca', detail: 'stablyai/orca' }) }) - it('labels local, SSH, and paired runtime targets', () => { + it('labels local, SSH, and paired runtime targets as the host list names them', () => { expect( getNewWorkspaceRunTarget({ id: 'local', displayName: 'orca', path: '/src/orca' }, 'darwin') - ).toEqual({ label: LOCAL_HOST_LABEL, detail: '/src/orca' }) + ).toEqual({ hostId: 'local', label: LOCAL_HOST_LABEL, detail: '/src/orca' }) expect( getNewWorkspaceRunTarget({ id: 'local', displayName: 'orca', path: 'C:\\src\\orca' }) - ).toEqual({ label: 'This computer', detail: 'C:\\src\\orca' }) + ).toMatchObject({ label: 'This computer' }) expect( getNewWorkspaceRunTarget({ id: 'local', displayName: 'orca', path: 'C:\\src\\orca' }, 'win32') - ).toEqual({ label: 'Local Windows', detail: 'C:\\src\\orca' }) + ).toMatchObject({ label: 'Local Windows' }) expect( getNewWorkspaceRunTarget({ id: 'ssh', @@ -60,15 +60,19 @@ describe('new workspace project targets', () => { path: 'C:\\src\\orca', executionHostId: 'ssh:Windows%20VM' }) - ).toEqual({ label: 'SSH · Windows VM', detail: 'C:\\src\\orca' }) - expect( - getNewWorkspaceRunTarget({ - id: 'runtime', - displayName: 'orca', - path: '/src/orca', - executionHostId: 'runtime:devbox' - }) - ).toEqual({ label: 'Remote · devbox', detail: '/src/orca' }) + ).toEqual({ hostId: 'ssh:Windows%20VM', label: 'Windows VM', detail: 'C:\\src\\orca' }) + const server = { + id: 'runtime', + displayName: 'orca', + path: '/src/orca', + executionHostId: 'runtime:20269ff3-60aa-4e79-a581-513a3f8e394b' + } + const labels = new Map([[server.executionHostId, 'scratch-server']] as const) + expect(getNewWorkspaceRunTarget(server, 'darwin', labels)).toEqual({ + hostId: server.executionHostId, + label: 'scratch-server', + detail: '/src/orca' + }) }) it('shows one target per host when the project has multiple local worktrees', () => { @@ -90,7 +94,7 @@ describe('new workspace project targets', () => { expect.objectContaining({ id: 'local', label: LOCAL_HOST_LABEL, detail: '/src/orca-a' }), expect.objectContaining({ id: 'ssh:build-server', - label: 'SSH · build-server', + label: 'build-server', detail: '/home/dev/orca' }) ]) diff --git a/mobile/src/components/new-workspace-project-targets.ts b/mobile/src/components/new-workspace-project-targets.ts index 20c46aafd3b..c3f5d436a40 100644 --- a/mobile/src/components/new-workspace-project-targets.ts +++ b/mobile/src/components/new-workspace-project-targets.ts @@ -1,14 +1,10 @@ import type { Repo } from '../../../src/shared/repo-types' -import { - getExecutionHostLabel, - getLocalExecutionHostLabel, - getRepoExecutionHostId, - parseExecutionHostId -} from '../../../src/shared/execution-host' +import { getRepoExecutionHostId, type ExecutionHostId } from '../../../src/shared/execution-host' import { getProjectIdentityKey, getProjectProviderIdentity } from '../../../src/shared/project-host-setup-projection' +import { getHostContextLabel } from '../../../src/shared/worktree/host-context-labels' type WorkspaceRepo = Pick & Partial< @@ -24,6 +20,7 @@ export type NewWorkspaceProjectOption = { export type NewWorkspaceRunTargetOption = { id: string + hostId: ExecutionHostId label: string detail: string repo: TRepo @@ -58,24 +55,23 @@ export function buildNewWorkspaceProjectOptions( }) } +/** Named like the host list's badge; the desktop's picker shows the bare host label too. */ export function getNewWorkspaceRunTarget( repo: WorkspaceRepo, - localPlatform: NodeJS.Platform | null = null + localPlatform: NodeJS.Platform | null = null, + hostLabelById?: ReadonlyMap ): { + hostId: ExecutionHostId label: string detail: string } { const hostId = getRepoExecutionHostId(repo) - const host = parseExecutionHostId(hostId) - const hostLabel = getExecutionHostLabel(hostId) - if (host?.kind === 'ssh') { - return { label: `SSH · ${hostLabel}`, detail: repo.path } - } - if (host?.kind === 'runtime') { - return { label: `Remote · ${hostLabel}`, detail: repo.path } - } return { - label: localPlatform ? getLocalExecutionHostLabel(localPlatform) : 'This computer', + hostId, + label: getHostContextLabel(hostId, { + hostLabelById, + hostPlatform: localPlatform + }), detail: repo.path } } @@ -83,7 +79,8 @@ export function getNewWorkspaceRunTarget( export function buildNewWorkspaceRunTargetOptions( repos: readonly TRepo[], projectId: string | null, - localPlatform: NodeJS.Platform | null = null + localPlatform: NodeJS.Platform | null = null, + hostLabelById?: ReadonlyMap ): NewWorkspaceRunTargetOption[] { if (!projectId) { return [] @@ -98,7 +95,7 @@ export function buildNewWorkspaceRunTargetOptions( // Keyed by host: a repo id names one repo per host, so two hosts can share one. options.set(hostId, { id: hostId, - ...getNewWorkspaceRunTarget(repo, localPlatform), + ...getNewWorkspaceRunTarget(repo, localPlatform, hostLabelById), repo }) } diff --git a/mobile/src/components/new-worktree-modal-types.ts b/mobile/src/components/new-worktree-modal-types.ts index 13cfd38b101..b943d06b608 100644 --- a/mobile/src/components/new-worktree-modal-types.ts +++ b/mobile/src/components/new-worktree-modal-types.ts @@ -24,6 +24,8 @@ export type NewWorktreeModalProps = { /** The desktop's servers this phone can reach now; their repos are offered and created there. */ serverClients?: ReadonlyMap hostId?: string + /** The host list's badge labels, so a run target reads as its row does. */ + hostLabelById?: ReadonlyMap existingWorktreePaths?: readonly string[] existingWorktrees?: readonly { repoId: string; branch: string }[] openExternalUrl: (url: string) => void diff --git a/mobile/src/host-screen/host-screen-overlays.tsx b/mobile/src/host-screen/host-screen-overlays.tsx index 153c90cc3ce..2eda461fa43 100644 --- a/mobile/src/host-screen/host-screen-overlays.tsx +++ b/mobile/src/host-screen/host-screen-overlays.tsx @@ -31,6 +31,7 @@ export function HostScreenOverlays({ controller }: { controller: HostScreenContr existingWorktreePaths, hostCapabilities, hostId, + hostLabels, settings, showNewWorktree, state @@ -229,6 +230,7 @@ export function HostScreenOverlays({ controller }: { controller: HostScreenContr client={client} serverClients={serverClients} hostId={hostId} + hostLabelById={hostLabels.hostLabelById} existingWorktreePaths={existingWorktreePaths} existingWorktrees={state.worktrees} // The seam, not react-native's `Linking`: this screen is in the tasks page closure, and diff --git a/mobile/src/host-screen/host-server-workspaces-wiring.test.ts b/mobile/src/host-screen/host-server-workspaces-wiring.test.ts index 39dfea137dc..9813d2a6a5e 100644 --- a/mobile/src/host-screen/host-server-workspaces-wiring.test.ts +++ b/mobile/src/host-screen/host-server-workspaces-wiring.test.ts @@ -18,11 +18,15 @@ vi.mock('../transport/use-worktree-resync', () => ({ })) import { MOBILE_DESKTOP_RELAY_RUNTIME_CAPABILITY } from '../../../src/shared/mobile-desktop-relay-contract' +import { + recordHostDescriptor, + resetHostDescriptorStoreForTests +} from '../transport/host-descriptor-store' import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes' import type { RpcResponse } from '../transport/types' import { readVisibleHostIds } from '../worktree/visible-host-rows' import type { Worktree } from '../worktree/workspace-list-types' -import { useHostDisplayWorktrees } from './use-host-display-worktrees' +import { useHostDisplayWorktrees, useHostLabelSources } from './use-host-display-worktrees' import { useHostScreenState } from './use-host-screen-state' import { useHostWorktreeActions } from './use-host-worktree-actions' import { useHostWorktreeCatalog } from './use-host-worktree-catalog' @@ -145,7 +149,7 @@ async function mountScreen(first: FakeSession, hostCapabilities: string[]): Prom }) held.state = state held.fetch = catalog.fetchWorktrees - held.rows = useHostDisplayWorktrees('connected', state) + held.rows = useHostDisplayWorktrees('connected', state, useHostLabelSources('host-1', state)) return null } await act(async () => { @@ -213,6 +217,19 @@ describe("a desktop's server workspaces on the phone", () => { ]) }) + it("names the desktop's own row for the desktop's platform once servers mix the list", async () => { + recordHostDescriptor('host-1', { machineName: null, platform: 'darwin' }) + try { + const screen = await mountScreen(desktop(), RELAYS) + expect(screen.rows()[0]).toMatchObject({ + worktreeId: 'mac-wt', + hostContextLabel: 'Local Mac' + }) + } finally { + resetHostDescriptorStoreForTests() + } + }) + it('keeps showing today’s view under an older desktop or an older shell', async () => { const oldDesktop = desktop() const oldShell = desktop({ carriesExecutionHost: false }) diff --git a/mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts b/mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts index ffd7e73f63a..90ff4e31cd8 100644 --- a/mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts +++ b/mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts @@ -11,7 +11,7 @@ vi.mock('../cache/repo-cache', () => ({ setCachedRepos: () => {} })) import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes' import type { RpcResponse } from '../transport/types' import type { Worktree } from '../worktree/workspace-list-types' -import { useHostDisplayWorktrees } from './use-host-display-worktrees' +import { useHostDisplayWorktrees, useHostLabelSources } from './use-host-display-worktrees' import { useHostRepoMetadata } from './use-host-repo-metadata' import { useHostScreenState } from './use-host-screen-state' @@ -68,7 +68,7 @@ async function displayedRows(args: { state.clientRef.current = client setWorktrees = state.setWorktrees seen.refresh = useHostRepoMetadata({ client, connState: 'connected', hostId: 'host-1', state }) - seen.rows = useHostDisplayWorktrees('connected', state) + seen.rows = useHostDisplayWorktrees('connected', state, useHostLabelSources('host-1', state)) return null } await act(async () => { diff --git a/mobile/src/host-screen/use-host-display-worktrees.ts b/mobile/src/host-screen/use-host-display-worktrees.ts index 3631d1f4cc3..86f85c9c94d 100644 --- a/mobile/src/host-screen/use-host-display-worktrees.ts +++ b/mobile/src/host-screen/use-host-display-worktrees.ts @@ -1,14 +1,21 @@ import { useMemo } from 'react' import type { ExecutionHostId } from '../../../src/shared/execution-host' +import { useHostDescriptor } from '../transport/host-descriptor-store' import type { ConnectionState } from '../transport/types' import { serverHostHealth } from '../worktree/server-workspaces' import { filterVisibleHostRows } from '../worktree/visible-host-rows' -import { applyWorktreeHostContextLabels } from '../worktree/worktree-host-context-labels' +import { + applyWorktreeHostContextLabels, + type HostLabelSources +} from '../worktree/worktree-host-context-labels' import { applyWorktreeRowDisplayState } from '../worktree/worktree-host-row-identity' import type { HostScreenState } from './use-host-screen-state' -/** The rows the host list renders: the live or last-known catalog with display state and host badges. */ -export function useHostDisplayWorktrees(connState: ConnectionState, state: HostScreenState) { +/** What names each host on this screen: the row badges and the new-workspace run targets. */ +export function useHostLabelSources( + hostId: string | undefined, + state: HostScreenState +): HostLabelSources { const { serverWorkspaces } = state // Why: a server's label and health come with its rows, alongside the desktop's SSH ones. const hostLabelById = useMemo( @@ -19,36 +26,49 @@ export function useHostDisplayWorktrees(connState: ConnectionState, state: HostS () => withServerHosts(state.hostHealthById, serverWorkspaces.hosts, serverHostHealth), [state.hostHealthById, serverWorkspaces.hosts] ) + const descriptorPlatform = useHostDescriptor(hostId)?.platform ?? null + // Why: metadata reads the platform only for a mixed desktop catalog; server rows mix it too. + const hostPlatform = state.hostPlatform ?? descriptorPlatform + return useMemo( + () => ({ + repoHostIdByRepoId: state.repoHostIdByRepoId, + hostLabelById, + hostPlatform, + hostHealthById + }), + [state.repoHostIdByRepoId, hostLabelById, hostPlatform, hostHealthById] + ) +} + +/** The rows the host list renders: the live or last-known catalog with display state and host badges. */ +export function useHostDisplayWorktrees( + connState: ConnectionState, + state: HostScreenState, + labels: HostLabelSources +) { return useMemo(() => { // Why: live `worktrees` is authoritative only while connected; under the amber // mount default, connecting/handshaking must keep the pre-reconnect list too. const base = connState === 'connected' ? state.worktrees : state.lastKnownWorktrees const rows = filterVisibleHostRows( - [...base, ...serverWorkspaces.worktrees], + [...base, ...state.serverWorkspaces.worktrees], state.visibleHostIds, state.repoHostIdByRepoId ) return applyWorktreeHostContextLabels( applyWorktreeRowDisplayState(rows, state.sleptIds, state.optimisticActiveWorktreeIdentity), - { - repoHostIdByRepoId: state.repoHostIdByRepoId, - hostLabelById, - hostPlatform: state.hostPlatform, - hostHealthById - } + labels ) }, [ connState, state.worktrees, state.lastKnownWorktrees, - serverWorkspaces.worktrees, + state.serverWorkspaces.worktrees, state.visibleHostIds, state.sleptIds, state.optimisticActiveWorktreeIdentity, state.repoHostIdByRepoId, - hostLabelById, - state.hostPlatform, - hostHealthById + labels ]) } diff --git a/mobile/src/host-screen/use-host-screen-controller.ts b/mobile/src/host-screen/use-host-screen-controller.ts index b3e8620b54f..cbb91f89b3f 100644 --- a/mobile/src/host-screen/use-host-screen-controller.ts +++ b/mobile/src/host-screen/use-host-screen-controller.ts @@ -15,7 +15,7 @@ import { useRelayRecoveryStatus } from '../transport/client-context-connection-metrics' import { useWorkspaceSections } from '../worktree/use-workspace-sections' -import { useHostDisplayWorktrees } from './use-host-display-worktrees' +import { useHostDisplayWorktrees, useHostLabelSources } from './use-host-display-worktrees' import { useHostRepoMetadata } from './use-host-repo-metadata' import { useHostScreenIdentity } from './use-host-screen-identity' import { useHostScreenState } from './use-host-screen-state' @@ -103,7 +103,8 @@ export function useHostScreenController({ } const showNewWorktree = resolvedRouteActionState.showNewWorktree - const displayWorktrees = useHostDisplayWorktrees(connState, state) + const hostLabels = useHostLabelSources(hostId, state) + const displayWorktrees = useHostDisplayWorktrees(connState, state, hostLabels) const sectionsResult = useWorkspaceSections({ displayWorktrees, sortMode: state.sortMode, @@ -135,6 +136,7 @@ export function useHostScreenController({ hostCapabilities, hostDisplay, hostId, + hostLabels, insets, isReadOnly: connState === 'auth-failed', isWideLayout,