From 233e37b2bd3ab5539cf2218557f4dbdb05634a34 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Tue, 15 Sep 2026 14:59:38 -0700 Subject: [PATCH] feat(native-chat): show the workspace name on each reconnect row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A row read `codex · folder:8f3a1c22-… · 8 hours ago`. Recognising which chats would reconnect is the entire point of the list, and at twenty rows a UUID identifies nothing. No RPC or host change was needed: the renderer can already resolve this id. Resolved the way automation dispatch resolves the same id space (resolveAutomationDispatchWorkspace) -- a folder workspace by its full `folder:` key via getKnownWorktreeById, a git worktree by its bare `repoId::path` id via allWorktrees. Both return a Worktree, whose displayName is a required field, and DetectedWorktree extends Worktree so either shape answers. Falls back to the id when nothing resolves, which is what the row showed before and also covers the window before the worktree store has hydrated. The lookup lives in a per-row subcomponent because a hook cannot run inside `map`, and its selector returns a primitive string so repeated selector runs cannot churn referential equality. --- .../NativeChatResumeOnRestartModal.tsx | 99 +++++++++++++------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/src/renderer/src/components/NativeChatResumeOnRestartModal.tsx b/src/renderer/src/components/NativeChatResumeOnRestartModal.tsx index 38a2fe622a9..2d243cdc5da 100644 --- a/src/renderer/src/components/NativeChatResumeOnRestartModal.tsx +++ b/src/renderer/src/components/NativeChatResumeOnRestartModal.tsx @@ -15,6 +15,7 @@ import { useAppStore } from '../store' import { callStructuredAgentSession } from '@/runtime/structured-agent-session-client' import { translate } from '@/i18n/i18n' import { formatUiRelativeTime } from '@/i18n/relative-time-format' +import { parseWorkspaceKey } from '../../../shared/workspace-scope' /** * What would be reconnected, shown before anything runs. @@ -66,6 +67,69 @@ function announceResumed(count: number): void { ) } +/** + * A workspace id is not a name. `folder:` identifies nothing at twenty rows, and recognising + * which chats would reconnect is the entire point of this list. + * + * Resolved the way automation dispatch already resolves the same id space: a folder workspace is + * keyed by its full `folder:` key, a git worktree by its bare `repoId::path` id. Falls back + * to the id — what the row showed before — when nothing is resolvable, which also covers the window + * before the worktree store has hydrated. + */ +function useWorkspaceLabel(workspaceId: string): string { + // Returns a primitive, so the selector re-runs freely without churning referential equality. + return useAppStore((store) => { + const scope = parseWorkspaceKey(workspaceId) + const worktree = + scope?.type === 'folder' + ? store.getKnownWorktreeById(workspaceId) + : store.allWorktrees().find((entry) => entry.id === workspaceId) + return worktree?.displayName ?? workspaceId + }) +} + +/** Its own component because a hook cannot run inside `map`, and the label needs one per row. */ +function ResumeOnRestartRow({ + candidate, + listedAt, + busy, + onReconnect +}: { + candidate: ResumeCandidate + listedAt: number + busy: boolean + onReconnect: () => void +}): React.JSX.Element { + const workspaceLabel = useWorkspaceLabel(candidate.workspaceId) + return ( +
  • +
    +

    + {candidate.latestPrompt.trim() || + translate('auto.components.NativeChatResumeOnRestartModal.untitled', 'Untitled chat')} +

    + {/* Age matters: the TTL is 24h, so an eight-hour-old offer must not look like one from a + minute ago. */} +

    + {`${candidate.agent} · ${workspaceLabel} · ${formatUiRelativeTime( + candidate.recordedAt - listedAt + )}`} +

    +
    + +
  • + ) +} + export function NativeChatResumeOnRestartModal(): React.JSX.Element | null { const structuredEnabled = useAppStore( (store) => store.settings?.experimentalStructuredNativeChat === true @@ -207,34 +271,13 @@ export function NativeChatResumeOnRestartModal(): React.JSX.Element | null { className="flex min-h-0 flex-col gap-1 overflow-y-auto scrollbar-sleek rounded-md border bg-muted/35 p-1.5" > {candidates.map((candidate) => ( -
  • -
    -

    - {candidate.latestPrompt.trim() || - translate( - 'auto.components.NativeChatResumeOnRestartModal.untitled', - 'Untitled chat' - )} -

    - {/* Age matters: the TTL is 24h, so an eight-hour-old offer must not look like one - from a minute ago. */} -

    - {`${candidate.agent} · ${candidate.workspaceId} · ${formatUiRelativeTime( - candidate.recordedAt - listedAt - )}`} -

    -
    - -
  • + void resume([candidate.sessionId])} + /> ))}