From 67767296b2460224406ec94d8df1057913f9046d Mon Sep 17 00:00:00 2001 From: Leo Li Date: Wed, 21 Oct 2026 19:47:09 -0700 Subject: [PATCH 1/2] cmux NEXT tabs: test that a remote tab's machine badge takes its sidebar name's color An SSH host named "lab" at leo@huge-blue badges its remote terminal and remote-localhost browser tabs "huge-blue"; the badge hashed that text and wore another MachineColor than the machine's "lab" sidebar rows. Co-Authored-By: Claude Opus 5.5 Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fddf-7670-8279-1a511ab46d6f&host=cosmic-gray --- .../TabMachineColorTests.swift | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/TabMachineColorTests.swift diff --git a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/TabMachineColorTests.swift b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/TabMachineColorTests.swift new file mode 100644 index 000000000000..ee7df4b015a9 --- /dev/null +++ b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/TabMachineColorTests.swift @@ -0,0 +1,45 @@ +import CmuxNextDaemonService +import CmuxNextRemote +import CmuxNextRemoteLocalhostService +import Foundation +import Testing +@testable import CmuxNextApp + +/// A machine wears one color (MachineColor, #22763), hashed from its sidebar name: a tab's +/// machine badge takes it from `MachineRegistry.tabColorName`, so remote terminals and +/// remote-localhost browser tabs match the machine's sidebar rows and composer chip even +/// where the badge shows other text. An SSH host named "lab" at leo@huge-blue badges its +/// tabs "huge-blue"; they hashed "huge-blue" and wore another color than the "lab" rows. +@MainActor +struct TabMachineColorTests { + static func sshMachine(_ machines: MachineRegistry) throws -> SSHMachineSession { + let host = try SSHHost(destination: SSHDestination(parsing: "leo@huge-blue.tail1.ts.net")).named("lab") + let session = SSHMachineSession(host: host, binary: URL(fileURLWithPath: "/usr/bin/false"), + paths: SSHPaths(root: URL(fileURLWithPath: "/private/tmp/cn-tab-machine-color-unused")), + environment: { [:] }) + machines.add(session) + return session + } + + @Test func anSSHHostsTabBadgeTakesTheColorOfItsSidebarName() throws { + let machines = MachineRegistry(local: DaemonService()) + let id = try Self.sshMachine(machines).machineID + let (badge, sidebar) = (machines.machineBadge(id), machines.machineName(id)) + #expect(badge == "huge-blue" && sidebar == "lab", "the badge shows the destination, the sidebar the label") + #expect(machines.tabColorName(id, badge: badge) == "lab", "a terminal tab on the host") + // A remote-localhost browser tab badges the machine's localhost with the same short name. + let localhost = RemoteLocalhostService(machines: machines, environment: [:]) + let browser = localhost.machineName(of: try #require(machines.anyDaemon(machine: id))) + #expect(machines.tabColorName(id, badge: browser) == "lab", "a browser tab seeing the host's localhost") + } + + @Test func otherBadgesKeepTheirOwnTextsColor() throws { + let machines = MachineRegistry(local: DaemonService()) + let id = try Self.sshMachine(machines).machineID + #expect(machines.tabColorName(id, badge: "this Mac") == nil, "a This Mac chip names no machine of the tree") + #expect(machines.tabColorName(id, badge: nil) == nil) + #expect(machines.tabColorName("vm-unknown", badge: "vm-unknown") == nil, "a machine not known here") + #expect(machines.tabColorName(MachineRegistry.localID, badge: "huge-blue") == nil) + #expect(machines.tabColorName(nil, badge: "huge-blue") == nil) + } +} From 064c81ba9768453e8af48cda6b5b22a0909bc6d9 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Wed, 21 Oct 2026 19:56:42 -0700 Subject: [PATCH 2/2] cmux NEXT tabs: a remote tab's machine badge wears its sidebar name's color Remote SSH terminal tabs and remote-localhost browser tabs set only the badge text (an SSH host's destination, "huge-blue"), so TabCell hashed that text and the tab wore another MachineColor than the machine's sidebar rows and composer chip (its label, "lab"). MachineRegistry.tabColorName gives the sidebar name when the badge is the machine's own short name; PaneController sets it as TabItem.machineName for both kinds. Other badges (a "this Mac" chip) and machines not known here keep the badge text's color. Co-Authored-By: Claude Opus 5.5 Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fddf-7670-8279-1a511ab46d6f&host=cosmic-gray --- .../Sources/CmuxNextApp/Cloud/MachineRegistry.swift | 9 +++++++++ .../CmuxNext/Sources/CmuxNextApp/PaneController.swift | 4 ++-- .../CmuxNextApp/Remote/RemoteTerminalService.swift | 10 ++++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/MachineRegistry.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/MachineRegistry.swift index 315973ad704e..89620125792a 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/MachineRegistry.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/MachineRegistry.swift @@ -94,6 +94,15 @@ final class MachineRegistry { session(machineID)?.machine.title ?? sshSession(machineID)?.host.label ?? server(machineID)?.name } + /// The name a tab's machine badge takes its color from (`TabItem.machineName`): the sidebar + /// name of `machineID` when `badge` is that machine's short name (an SSH host's badge shows + /// its destination, the sidebar its label), so the tab wears its rows' MachineColor. Nil for + /// any other badge (a "this Mac" chip) or a machine not known here: the badge's own text. + func tabColorName(_ machineID: String?, badge: String?) -> String? { + guard let machineID, let badge, badge == machineBadge(machineID) else { return nil } + return machineName(machineID) + } + /// What `daemon` can do for the app. A remote daemon never needs the /// home-only capabilities, nor, once the local daemon keeps personal /// state (`profiles-v1`), workspace groups and saved tab groups. diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/PaneController.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/PaneController.swift index fd589d93dac2..c495f3f3b9c7 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/PaneController.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/PaneController.swift @@ -213,7 +213,7 @@ final class PaneController: SurfacePresenter, PresentablePane { item.isDormant = services.cache.dormantTabs.contains(tab.id) if tab.kind == .remoteTerminal { // Its terminal runs on another machine: that machine's name. - item.machineBadge = services.remoteTerminals.badge(for: tab) + (item.machineBadge, item.machineName) = services.remoteTerminals.badge(for: tab) item.icon = .icon(.terminal) } else if tab.kind != .browser { (item.machineBadge, item.machineName) = (machine, machine.flatMap { _ in services.machines.machineName(daemon.machineID) }) @@ -222,7 +222,7 @@ final class PaneController: SurfacePresenter, PresentablePane { // A browser tab names the machine whose localhost it sees. let engine: BrowserEngineKind = tab.browserEngine == BrowserEngineTag.cef.rawValue ? .cef : .webkit let badge = services.remoteLocalhost.badge(for: tab, url: tab.url.flatMap(URL.init(string:)), engine: engine) - (item.machineBadge, item.machineBadgeHelp) = (badge?.text, badge?.help) + (item.machineBadge, item.machineBadgeHelp, item.machineName) = (badge?.text, badge?.help, services.machines.tabColorName(daemon.machineID, badge: badge?.text)) // Incognito tabs have only a placeholder record in the daemon. let incognito = services.cache.browserTabs.isIncognitoTab(tab.id) if incognito { diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Remote/RemoteTerminalService.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Remote/RemoteTerminalService.swift index 89c16cff7277..59aa67cf052b 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Remote/RemoteTerminalService.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Remote/RemoteTerminalService.swift @@ -111,10 +111,12 @@ final class RemoteTerminalService { /// The placeholder tab `key` shows now, if any. func existingPlaceholder(_ key: String) -> RemoteTerminalPlaceholderView? { placeholders[key] } - /// The machine name on a remote-terminal tab (the subtle machine badge). - func badge(for tab: TabModel) -> String? { - guard let ref = tab.remote else { return nil } - return machineName(ref) + /// The machine name on a remote-terminal tab (the subtle machine badge), and the name its + /// color comes from (`MachineRegistry.tabColorName`: the machine's sidebar name). + func badge(for tab: TabModel) -> (text: String?, colorName: String?) { + guard let ref = tab.remote else { return (nil, nil) } + let text = machineName(ref) + return (text, services.machines.tabColorName(services.machines.daemon(session: ref.sessionID)?.machineID, badge: text)) } func machineName(_ ref: RemoteTerminalRef) -> String {