diff --git a/src/renderer/src/components/dashboard/build-dashboard-snapshot.ts b/src/renderer/src/components/dashboard/build-dashboard-snapshot.ts index 0962d760da9..c7c3c9711cc 100644 --- a/src/renderer/src/components/dashboard/build-dashboard-snapshot.ts +++ b/src/renderer/src/components/dashboard/build-dashboard-snapshot.ts @@ -14,6 +14,7 @@ import { resolveDashboardCardTerminalInput, type DashboardCardTerminalInputState } from './dashboard-card-terminal-input' +import { readDashboardClientHost } from './dashboard-client-host' import { getAgentRowConversationName } from '../../../../shared/agent-row-conversation-name' import { migrationUnsupportedToAgentStatusEntry } from '@/lib/migration-unsupported-agent-entry' import { applyAgentRowLineage } from './agent-row-lineage' @@ -61,14 +62,6 @@ export type DashboardSnapshotState = Pick< DashboardCardContextState & Partial -function readClientOsRelease(): string | undefined { - try { - return window.api?.platform?.get?.()?.osRelease - } catch { - return undefined - } -} - function bucketForState(state: DashboardAgentRow['state']): DashboardBucket { switch (state) { case 'working': @@ -136,13 +129,7 @@ export function buildDashboardSnapshot( options: { includeCardDetails?: boolean; includeFilterOptions?: boolean } = {} ): DashboardSnapshot { const cards: DashboardCard[] = [] - const clientUserAgent = typeof navigator === 'undefined' ? '' : navigator.userAgent - const clientPlatform = clientUserAgent.includes('Mac') - ? 'darwin' - : clientUserAgent.includes('Windows') - ? 'win32' - : 'linux' - const clientOsRelease = readClientOsRelease() + const clientHost = readDashboardClientHost() const repoIconsByRepoId: Record = {} const includeCardDetails = options.includeCardDetails !== false const generatedTitlesEnabled = state.settings?.tabAutoGenerateTitle === true @@ -294,9 +281,9 @@ export function buildDashboardSnapshot( cwd: row.tab.startupCwd ?? worktree.path, shellOverride: row.tab.shellOverride, launchAgent: row.tab.launchAgent, - clientPlatform, - userAgent: clientUserAgent, - osRelease: clientOsRelease + clientPlatform: clientHost.platform, + userAgent: clientHost.userAgent, + osRelease: clientHost.osRelease }) : null // Only repos that actually contribute a card ship their icon. diff --git a/src/renderer/src/components/dashboard/dashboard-client-host.ts b/src/renderer/src/components/dashboard/dashboard-client-host.ts new file mode 100644 index 00000000000..adb507b60da --- /dev/null +++ b/src/renderer/src/components/dashboard/dashboard-client-host.ts @@ -0,0 +1,32 @@ +/** + * The facts about the machine *rendering* the board, as opposed to the machine + * executing a pty. A card's preview terminal needs both: byte encodings follow + * the pty's host, while xterm's own ConPTY wrap-marker compat follows the client. + */ +export type DashboardClientHost = { + platform: NodeJS.Platform + userAgent: string + osRelease: string | undefined +} + +export function readDashboardClientHost(): DashboardClientHost { + const userAgent = typeof navigator === 'undefined' ? '' : navigator.userAgent + return { + platform: userAgent.includes('Mac') + ? 'darwin' + : userAgent.includes('Windows') + ? 'win32' + : 'linux', + userAgent, + // Why: absent in the pop-out renderer and in tests, where no preload rides along. + osRelease: readClientOsRelease() + } +} + +function readClientOsRelease(): string | undefined { + try { + return window.api?.platform?.get?.()?.osRelease + } catch { + return undefined + } +}