-
{environment.name}
+
{hostDisplay.title}
) : null}
+ {hostDescriptorText ? (
+
{hostDescriptorText}
+ ) : null}
{environment.connectionDependency === 'ssh-tunnel'
? translate(
diff --git a/src/shared/host-display-resolution.test.ts b/src/shared/host-display-resolution.test.ts
new file mode 100644
index 00000000000..f13f54d11c0
--- /dev/null
+++ b/src/shared/host-display-resolution.test.ts
@@ -0,0 +1,58 @@
+import { describe, expect, it } from 'vitest'
+import { resolveHostDisplay } from './host-display-resolution'
+
+describe('resolveHostDisplay', () => {
+ it('titles with the caller label and shows a disagreeing machine name beneath it', () => {
+ expect(
+ resolveHostDisplay({
+ name: 'Windows-Low Spec',
+ machineName: 'm4airs-Air',
+ platform: 'darwin'
+ })
+ ).toEqual({
+ title: 'Windows-Low Spec',
+ descriptorLine: 'macOS · m4airs-Air'
+ })
+ })
+
+ it('never titles with the machine name, whatever the label state', () => {
+ // The title-fallback bug class: a late descriptor must not be able to retitle a row.
+ for (const name of ['Host 2', 'Desk']) {
+ expect(
+ resolveHostDisplay({ name, machineName: 'm4airs-Air', platform: 'darwin' }).title
+ ).toBe(name)
+ }
+ })
+
+ it('keeps the OS visible when the shown name is the machine name', () => {
+ expect(
+ resolveHostDisplay({
+ name: 'm4airs-Air',
+ machineName: 'm4airs-Air',
+ platform: 'darwin'
+ })
+ ).toEqual({ title: 'm4airs-Air', descriptorLine: 'macOS' })
+ })
+
+ it('labels whichever descriptor half an older host reported', () => {
+ expect(resolveHostDisplay({ name: 'Desk', platform: 'linux' })).toEqual({
+ title: 'Desk',
+ descriptorLine: 'Linux'
+ })
+ expect(resolveHostDisplay({ name: 'Desk', machineName: 'build-box' })).toEqual({
+ title: 'Desk',
+ descriptorLine: 'build-box'
+ })
+ expect(resolveHostDisplay({ name: 'Desk' })).toEqual({
+ title: 'Desk',
+ descriptorLine: null
+ })
+ })
+
+ it('normalizes blank inputs', () => {
+ expect(resolveHostDisplay({ name: ' ', machineName: ' ' })).toEqual({
+ title: 'Host',
+ descriptorLine: null
+ })
+ })
+})
diff --git a/src/shared/host-display-resolution.ts b/src/shared/host-display-resolution.ts
new file mode 100644
index 00000000000..b35dce5e5e1
--- /dev/null
+++ b/src/shared/host-display-resolution.ts
@@ -0,0 +1,37 @@
+import { hostPlatformDisplayName } from './host-platform-label'
+
+export type HostDisplayResolutionInput = {
+ /** The label the caller already owns: a stored resolved name, or a desktop-local server label. */
+ name: string
+ machineName?: string | null
+ platform?: NodeJS.Platform | null
+}
+
+export type HostDisplayResolution = {
+ title: string
+ /**
+ * "OS · machine name" when the machine name differs from the shown title, the OS alone when it
+ * matches (the OS is always shown when known), or null when the host reported neither.
+ */
+ descriptorLine: string | null
+}
+
+/**
+ * The one display rule for host rows and headers. The title is always the caller's own label —
+ * a machine name can appear only on the descriptor line, never as the title, so a late-arriving
+ * descriptor can never retitle a row.
+ */
+export function resolveHostDisplay(input: HostDisplayResolutionInput): HostDisplayResolution {
+ const title = normalize(input.name) ?? 'Host'
+ const machineName = normalize(input.machineName)
+ const descriptorLine =
+ [hostPlatformDisplayName(input.platform ?? null), machineName === title ? null : machineName]
+ .filter(Boolean)
+ .join(' · ') || null
+ return { title, descriptorLine }
+}
+
+function normalize(value: string | null | undefined): string | null {
+ const trimmed = value?.trim()
+ return trimmed ? trimmed : null
+}
diff --git a/src/shared/host-platform-label.ts b/src/shared/host-platform-label.ts
new file mode 100644
index 00000000000..5df8f78d1d0
--- /dev/null
+++ b/src/shared/host-platform-label.ts
@@ -0,0 +1,19 @@
+const PLATFORM_LABELS: Record = {
+ aix: 'AIX',
+ android: 'Android',
+ cygwin: 'Cygwin',
+ darwin: 'macOS',
+ freebsd: 'FreeBSD',
+ haiku: 'Haiku',
+ linux: 'Linux',
+ netbsd: 'NetBSD',
+ openbsd: 'OpenBSD',
+ sunos: 'Solaris',
+ win32: 'Windows'
+}
+
+export function hostPlatformDisplayName(
+ platform: NodeJS.Platform | null | undefined
+): string | null {
+ return platform ? PLATFORM_LABELS[platform] : null
+}