From 4d79d1823dc97e385b2e3049539f5fe02539411d Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 19:33:49 -0700 Subject: [PATCH 1/8] Add Cloud workspaces to Cmd-P switcher --- Resources/Localizable.xcstrings | 59 +++++++++++++++++++++ Sources/ContentView.swift | 92 ++++++++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 1 deletion(-) diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 100b491092b0..3a1f0e108421 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -607970,6 +607970,65 @@ } } } + }, + "commandPalette.kind.cloudWorkspace": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Cloud Workspace" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Cloud-Arbeitsbereich" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Espace de travail cloud" + } + }, + "ar": { + "stringUnit": { + "state": "translated", + "value": "مساحة عمل سحابية" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Espacio de trabajo en la nube" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "雲端工作區" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "云工作区" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "클라우드 워크스페이스" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "クラウドワークスペース" + } + } + } } }, "version": "1.0" diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 8cbbff5f8f64..e9978f7b2966 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -5640,6 +5640,7 @@ struct ContentView: View { return hasher.finalize() } let windowContexts = commandPaletteSwitcherWindowContexts() + let cloudWorkspaceTargets = commandPaletteCloudWorkspaceTargets() let fingerprintContexts = windowContexts.map { context in CommandPaletteSwitcherFingerprintContext( windowId: context.windowId, @@ -5672,7 +5673,13 @@ struct ContentView: View { } ) } - return CommandPaletteSwitcherFingerprintContext.fingerprint(windowContexts: fingerprintContexts) + var fingerprint = CommandPaletteSwitcherFingerprintContext.fingerprint(windowContexts: fingerprintContexts) + for target in cloudWorkspaceTargets { + fingerprint = fingerprint &* 31 &+ target.machine.rawValue.hashValue + fingerprint = fingerprint &* 31 &+ target.workspace.id.hashValue + fingerprint = fingerprint &* 31 &+ target.workspace.name.hashValue + } + return fingerprint } private static func commandPaletteHighlightedTitleText(_ title: String, matchedIndices: Set) -> Text { @@ -5743,6 +5750,59 @@ struct ContentView: View { } } + private struct CommandPaletteCloudWorkspaceTarget { + let machine: SurfaceMachineID + let workspace: SurfaceRemoteWorkspace + let group: SurfaceResourceGroup + } + + private func commandPaletteCloudWorkspaceTargets() -> [CommandPaletteCloudWorkspaceTarget] { + guard CloudMachinesFeature.isEnabled else { return [] } + let catalog = SurfaceCatalog.shared + let snapshot = catalog.snapshot + let allNodes = CloudTreeNodeBuilder.nodes( + machines: [], + snapshot: snapshot, + localWorkspaces: [], + includeLocalMachine: false + ) + let sidebarNodes = catalog.sidebarNodes() + let sidebarWorkspaceIDs = Set( + CloudTreeNodeBuilder.flattened(sidebarNodes).compactMap { node -> String? in + guard case .workspace(let machine, let workspace, _, _, _) = node.kind else { return nil } + return "\(machine.rawValue):\(workspace.id)" + } + ) + + let orderedNodes = CloudTreeNodeBuilder.flattened(sidebarNodes) + + CloudTreeNodeBuilder.flattened(allNodes).filter { node in + guard case .workspace(let machine, let workspace, _, _, _) = node.kind else { return false } + return !sidebarWorkspaceIDs.contains("\(machine.rawValue):\(workspace.id)") + } + + var seen = Set() + return orderedNodes.compactMap { node in + guard case .workspace(let machine, let workspace, _, _, _) = node.kind, + let group = node.dragGroup, + seen.insert("\(machine.rawValue):\(workspace.id)").inserted else { return nil } + return CommandPaletteCloudWorkspaceTarget(machine: machine, workspace: workspace, group: group) + } + } + + private func openCommandPaletteCloudWorkspace(_ target: CommandPaletteCloudWorkspaceTarget) { + let actions = CloudTreeNodeActions.bound( + navigationHost: AppDelegate.makeCloudTerminalNavigationHost(), + catalog: { SurfaceCatalog.shared }, + selectedWorkspaceID: { self.tabManager.selectedTabId }, + selectLocalWorkspace: { workspaceID in self.tabManager.selectedTabId = workspaceID }, + onDidMutate: {}, + onFailure: { _ in NSSound.beep() }, + refresh: {}, + workspaceCreationHost: { CloudWorkspaceCreationHost(manager: self.tabManager) } + ) + actions.openWorkspace(target.machine, target.workspace, target.group) + } + private func commandPaletteSwitcherEntries(includeSurfaces: Bool) -> [CommandPaletteCommand] { if let snapshot = commandPaletteCurrentWorkSnapshot { return commandPaletteCurrentWorkEntries(snapshot: snapshot) @@ -5854,6 +5914,36 @@ struct ContentView: View { } } + let cloudWorkspaceKind = String(localized: "commandPalette.kind.cloudWorkspace", defaultValue: "Cloud Workspace") + let cloudWorkspaceTargets = commandPaletteCloudWorkspaceTargets() + for target in cloudWorkspaceTargets { + let machineName = SurfaceCatalog.shared.machineInfo(for: target.machine)?.name ?? target.machine.rawValue + let title = target.workspace.name + let commandID = "switcher.cloudWorkspace.\(target.machine.rawValue).\(target.workspace.id)" + let keywords = CommandPaletteSwitcherSearchIndexer( + baseKeywords: [ + "cloud", "workspace", "remote", "vm", "open", "go", "switch", title, machineName + ], + detail: .workspace + ).keywords + entries.append( + CommandPaletteCommand( + id: commandID, + rank: nextRank, + title: title, + subtitle: Self.commandPaletteSwitcherSubtitle(base: cloudWorkspaceKind + " • " + machineName, windowLabel: nil), + shortcutHint: nil, + kindLabel: cloudWorkspaceKind, + keywords: keywords, + dismissOnRun: true, + action: { [weak self] in + self?.openCommandPaletteCloudWorkspace(target) + } + ) + ) + nextRank += 1 + } + return entries } From 1dbcb2468dfde18ebe976f6d8114099925e7a052 Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 19:52:19 -0700 Subject: [PATCH 2/8] Fix Cloud switcher imports and target caching --- Resources/Localizable.xcstrings | 66 +++++++++++++++++++++++++++++++++ Sources/ContentView.swift | 23 +++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 3a1f0e108421..50aa7672a4f2 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -608027,6 +608027,72 @@ "state": "translated", "value": "クラウドワークスペース" } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Radni prostor u oblaku" + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Cloud-arbejdsområde" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Spazio di lavoro cloud" + } + }, + "km": { + "stringUnit": { + "state": "translated", + "value": "កន្លែងធ្វើការលើ Cloud" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Skyarbeidsområde" + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Obszar roboczy w chmurze" + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Workspace na nuvem" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Облачная рабочая область" + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "เวิร์กสเปซบนคลาวด์" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Bulut çalışma alanı" + } + }, + "uk": { + "stringUnit": { + "state": "translated", + "value": "Хмарний робочий простір" + } } } } diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index e9978f7b2966..7f4cad62c279 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -19,6 +19,7 @@ import CmuxSidebarProviderKit import CmuxExtensionSidebarExamples import CmuxSettingsUI import CmuxSidebar +import CmuxSurfaceCatalogModel import CmuxSidebarRemoteRender import CmuxSwiftRender import CmuxSwiftRenderUI @@ -1003,6 +1004,8 @@ struct ContentView: View { @State private var commandPaletteSearchCorpus: [CommandPaletteSearchCorpusEntry] = [] @State private var commandPaletteSearchCorpusByID: [String: CommandPaletteSearchCorpusEntry] = [:] @State private var commandPaletteSearchCommandsByID: [String: CommandPaletteCommand] = [:] + @State private var commandPaletteCloudWorkspaceTargetsCache: [CommandPaletteCloudWorkspaceTarget] = [] + @State private var commandPaletteCloudWorkspaceTargetsCacheKey: Int? private var isCommandPalettePresented: Bool { commandPaletteOverlayState.isCommandPalettePresented @@ -5760,6 +5763,21 @@ struct ContentView: View { guard CloudMachinesFeature.isEnabled else { return [] } let catalog = SurfaceCatalog.shared let snapshot = catalog.snapshot + var keyHasher = Hasher() + for machine in snapshot.machines where !machine.id.isLocal { + keyHasher.combine(machine.id.rawValue) + keyHasher.combine(machine.name) + for workspace in machine.remoteWorkspaces ?? [] { + keyHasher.combine(workspace.id) + keyHasher.combine(workspace.name) + keyHasher.combine(workspace.index) + } + } + keyHasher.combine(String(describing: catalog.sidebarOrganization.state)) + let cacheKey = keyHasher.finalize() + if commandPaletteCloudWorkspaceTargetsCacheKey == cacheKey { + return commandPaletteCloudWorkspaceTargetsCache + } let allNodes = CloudTreeNodeBuilder.nodes( machines: [], snapshot: snapshot, @@ -5781,12 +5799,15 @@ struct ContentView: View { } var seen = Set() - return orderedNodes.compactMap { node in + let targets = orderedNodes.compactMap { node in guard case .workspace(let machine, let workspace, _, _, _) = node.kind, let group = node.dragGroup, seen.insert("\(machine.rawValue):\(workspace.id)").inserted else { return nil } return CommandPaletteCloudWorkspaceTarget(machine: machine, workspace: workspace, group: group) } + commandPaletteCloudWorkspaceTargetsCacheKey = cacheKey + commandPaletteCloudWorkspaceTargetsCache = targets + return targets } private func openCommandPaletteCloudWorkspace(_ target: CommandPaletteCloudWorkspaceTarget) { From 0de888d924e7844453daac355ce52b88121093d4 Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:06:22 -0700 Subject: [PATCH 3/8] Fix Cloud switcher entry compilation --- Sources/ContentView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 7f4cad62c279..eec4b3b283e5 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -5799,7 +5799,7 @@ struct ContentView: View { } var seen = Set() - let targets = orderedNodes.compactMap { node in + let targets: [CommandPaletteCloudWorkspaceTarget] = orderedNodes.compactMap { node in guard case .workspace(let machine, let workspace, _, _, _) = node.kind, let group = node.dragGroup, seen.insert("\(machine.rawValue):\(workspace.id)").inserted else { return nil } @@ -5945,6 +5945,7 @@ struct ContentView: View { baseKeywords: [ "cloud", "workspace", "remote", "vm", "open", "go", "switch", title, machineName ], + metadata: CommandPaletteSwitcherSearchMetadata(), detail: .workspace ).keywords entries.append( From 8652d4a18bfba7daee7a7cbaca255dac0bdefef0 Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:13:26 -0700 Subject: [PATCH 4/8] Keep Cloud switcher fingerprint path lightweight --- Sources/ContentView.swift | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index eec4b3b283e5..6cbd2af3dc45 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -1005,7 +1005,7 @@ struct ContentView: View { @State private var commandPaletteSearchCorpusByID: [String: CommandPaletteSearchCorpusEntry] = [:] @State private var commandPaletteSearchCommandsByID: [String: CommandPaletteCommand] = [:] @State private var commandPaletteCloudWorkspaceTargetsCache: [CommandPaletteCloudWorkspaceTarget] = [] - @State private var commandPaletteCloudWorkspaceTargetsCacheKey: Int? + @State private var commandPaletteCloudWorkspaceTargetsFingerprint: Int? private var isCommandPalettePresented: Bool { commandPaletteOverlayState.isCommandPalettePresented @@ -5643,7 +5643,6 @@ struct ContentView: View { return hasher.finalize() } let windowContexts = commandPaletteSwitcherWindowContexts() - let cloudWorkspaceTargets = commandPaletteCloudWorkspaceTargets() let fingerprintContexts = windowContexts.map { context in CommandPaletteSwitcherFingerprintContext( windowId: context.windowId, @@ -5677,11 +5676,7 @@ struct ContentView: View { ) } var fingerprint = CommandPaletteSwitcherFingerprintContext.fingerprint(windowContexts: fingerprintContexts) - for target in cloudWorkspaceTargets { - fingerprint = fingerprint &* 31 &+ target.machine.rawValue.hashValue - fingerprint = fingerprint &* 31 &+ target.workspace.id.hashValue - fingerprint = fingerprint &* 31 &+ target.workspace.name.hashValue - } + fingerprint = fingerprint &* 31 &+ (commandPaletteCloudWorkspaceTargetsFingerprint ?? 0) return fingerprint } @@ -5763,21 +5758,6 @@ struct ContentView: View { guard CloudMachinesFeature.isEnabled else { return [] } let catalog = SurfaceCatalog.shared let snapshot = catalog.snapshot - var keyHasher = Hasher() - for machine in snapshot.machines where !machine.id.isLocal { - keyHasher.combine(machine.id.rawValue) - keyHasher.combine(machine.name) - for workspace in machine.remoteWorkspaces ?? [] { - keyHasher.combine(workspace.id) - keyHasher.combine(workspace.name) - keyHasher.combine(workspace.index) - } - } - keyHasher.combine(String(describing: catalog.sidebarOrganization.state)) - let cacheKey = keyHasher.finalize() - if commandPaletteCloudWorkspaceTargetsCacheKey == cacheKey { - return commandPaletteCloudWorkspaceTargetsCache - } let allNodes = CloudTreeNodeBuilder.nodes( machines: [], snapshot: snapshot, @@ -5805,7 +5785,14 @@ struct ContentView: View { seen.insert("\(machine.rawValue):\(workspace.id)").inserted else { return nil } return CommandPaletteCloudWorkspaceTarget(machine: machine, workspace: workspace, group: group) } - commandPaletteCloudWorkspaceTargetsCacheKey = cacheKey + var fingerprintHasher = Hasher() + for target in targets { + fingerprintHasher.combine(target.machine.rawValue) + fingerprintHasher.combine(target.workspace.id) + fingerprintHasher.combine(target.workspace.name) + target.group.remoteWorkspaceID.map(fingerprintHasher.combine) + } + commandPaletteCloudWorkspaceTargetsFingerprint = fingerprintHasher.finalize() commandPaletteCloudWorkspaceTargetsCache = targets return targets } @@ -10139,6 +10126,8 @@ struct ContentView: View { } private func openCommandPaletteSwitcher() { + commandPaletteCloudWorkspaceTargetsCache = [] + commandPaletteCloudWorkspaceTargetsFingerprint = nil handleCommandPaletteListRequest(scope: .switcher) } From 1de8630f58080eb32ae98114375d303642de0521 Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:13:44 -0700 Subject: [PATCH 5/8] Clarify Cloud target fingerprint hashing --- Sources/ContentView.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 6cbd2af3dc45..6d2671881862 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -5790,7 +5790,9 @@ struct ContentView: View { fingerprintHasher.combine(target.machine.rawValue) fingerprintHasher.combine(target.workspace.id) fingerprintHasher.combine(target.workspace.name) - target.group.remoteWorkspaceID.map(fingerprintHasher.combine) + if let remoteWorkspaceID = target.group.remoteWorkspaceID { + fingerprintHasher.combine(remoteWorkspaceID) + } } commandPaletteCloudWorkspaceTargetsFingerprint = fingerprintHasher.finalize() commandPaletteCloudWorkspaceTargetsCache = targets From 16bac02bf169d3e0744363107d5b59ae3a9f41c4 Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:26:41 -0700 Subject: [PATCH 6/8] Fix Cloud switcher action capture --- Sources/ContentView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 6d2671881862..eec161b65657 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -5947,8 +5947,8 @@ struct ContentView: View { kindLabel: cloudWorkspaceKind, keywords: keywords, dismissOnRun: true, - action: { [weak self] in - self?.openCommandPaletteCloudWorkspace(target) + action: { + self.openCommandPaletteCloudWorkspace(target) } ) ) From b8f11d2dd54689e027c454553e51a61c191f887c Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:57:57 -0700 Subject: [PATCH 7/8] Refresh Cloud switcher cache with catalog changes --- Sources/ContentView.swift | 87 +++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index eec161b65657..90752561840d 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -1006,6 +1006,8 @@ struct ContentView: View { @State private var commandPaletteSearchCommandsByID: [String: CommandPaletteCommand] = [:] @State private var commandPaletteCloudWorkspaceTargetsCache: [CommandPaletteCloudWorkspaceTarget] = [] @State private var commandPaletteCloudWorkspaceTargetsFingerprint: Int? + @State private var commandPaletteCloudWorkspaceTargetsCacheRevision: UInt64 = 0 + @State private var commandPaletteCloudWorkspaceTargetsCachedRevision: UInt64? private var isCommandPalettePresented: Bool { commandPaletteOverlayState.isCommandPalettePresented @@ -2689,6 +2691,17 @@ struct ContentView: View { } }) + view = AnyView(view.onReceive( + NotificationCenter.default.publisher(for: SurfaceCatalog.didChangeNotification, object: SurfaceCatalog.shared) + ) { _ in + invalidateCommandPaletteCloudWorkspaceTargets() + }) + view = AnyView(view.onReceive( + NotificationCenter.default.publisher(for: CloudSidebarOrganizationStore.didChangeNotification, object: SurfaceCatalog.shared.sidebarOrganization) + ) { _ in + invalidateCommandPaletteCloudWorkspaceTargets() + }) + view = AnyView(view.onChange(of: tabManager.selectedTabId) { newValue in // SwiftUI may deliver an earlier selection after the model has // already advanced again. Reconcile the current model value once, @@ -5071,10 +5084,14 @@ struct ContentView: View { private var commandPaletteCurrentSearchFingerprint: Int { let scope = commandPaletteListScope + let cloudWorkspaceTargets = scope == .switcher + ? commandPaletteCloudWorkspaceTargets() + : nil return commandPaletteEntriesFingerprint( for: scope, includeSurfaces: commandPaletteSwitcherIncludesSurfaceEntries, - commandsContext: scope == .commands ? commandPaletteCachedCommandsContext() : nil + commandsContext: scope == .commands ? commandPaletteCachedCommandsContext() : nil, + cloudWorkspaceTargets: cloudWorkspaceTargets ) } @@ -5182,20 +5199,25 @@ struct ContentView: View { private func commandPaletteEntries(for scope: CommandPaletteListScope) -> [CommandPaletteCommand] { commandPaletteEntries( for: scope, - includeSurfaces: commandPaletteSwitcherIncludesSurfaceEntries + includeSurfaces: commandPaletteSwitcherIncludesSurfaceEntries, + cloudWorkspaceTargets: scope == .switcher ? commandPaletteCloudWorkspaceTargets() : nil ) } private func commandPaletteEntries( for scope: CommandPaletteListScope, includeSurfaces: Bool, - commandsContext: CommandPaletteCommandsContext? = nil + commandsContext: CommandPaletteCommandsContext? = nil, + cloudWorkspaceTargets: [CommandPaletteCloudWorkspaceTarget]? = nil ) -> [CommandPaletteCommand] { switch scope { case .commands: return commandPaletteCommands(commandsContext: commandsContext ?? commandPaletteCachedCommandsContext()) case .switcher: - return commandPaletteSwitcherEntries(includeSurfaces: includeSurfaces) + return commandPaletteSwitcherEntries( + includeSurfaces: includeSurfaces, + cloudWorkspaceTargets: cloudWorkspaceTargets + ) } } @@ -5231,10 +5253,14 @@ struct ContentView: View { let commandsContext = scope == .commands ? commandPaletteCommandsContext(terminalOpenTargets: terminalOpenTargets) : nil + let cloudWorkspaceTargets = scope == .switcher + ? commandPaletteCloudWorkspaceTargets() + : nil let fingerprint = commandPaletteEntriesFingerprint( for: scope, includeSurfaces: includeSurfaces, - commandsContext: commandsContext + commandsContext: commandsContext, + cloudWorkspaceTargets: cloudWorkspaceTargets ) guard force || cachedCommandPaletteScope != scope || cachedCommandPaletteFingerprint != fingerprint else { return @@ -5243,7 +5269,8 @@ struct ContentView: View { let entries = commandPaletteEntries( for: scope, includeSurfaces: includeSurfaces, - commandsContext: commandsContext + commandsContext: commandsContext, + cloudWorkspaceTargets: cloudWorkspaceTargets ) commandPaletteSearchCommandsByID = CommandPaletteSearchOrchestrator.firstValueDictionary( entries, @@ -5616,7 +5643,8 @@ struct ContentView: View { private func commandPaletteEntriesFingerprint( for scope: CommandPaletteListScope, includeSurfaces: Bool, - commandsContext: CommandPaletteCommandsContext? = nil + commandsContext: CommandPaletteCommandsContext? = nil, + cloudWorkspaceTargets: [CommandPaletteCloudWorkspaceTarget]? = nil ) -> Int { switch scope { case .commands: @@ -5624,7 +5652,10 @@ struct ContentView: View { commandsContext: commandsContext ?? commandPaletteCachedCommandsContext() ) case .switcher: - return commandPaletteSwitcherEntriesFingerprint(includeSurfaces: includeSurfaces) + return commandPaletteSwitcherEntriesFingerprint( + includeSurfaces: includeSurfaces, + cloudWorkspaceTargets: cloudWorkspaceTargets + ) } } @@ -5635,7 +5666,10 @@ struct ContentView: View { return hasher.finalize() } - private func commandPaletteSwitcherEntriesFingerprint(includeSurfaces: Bool) -> Int { + private func commandPaletteSwitcherEntriesFingerprint( + includeSurfaces: Bool, + cloudWorkspaceTargets: [CommandPaletteCloudWorkspaceTarget]? = nil + ) -> Int { if commandPaletteCurrentWorkSnapshot != nil { var hasher = Hasher() hasher.combine("current-work") @@ -5676,6 +5710,7 @@ struct ContentView: View { ) } var fingerprint = CommandPaletteSwitcherFingerprintContext.fingerprint(windowContexts: fingerprintContexts) + _ = cloudWorkspaceTargets ?? commandPaletteCloudWorkspaceTargets() fingerprint = fingerprint &* 31 &+ (commandPaletteCloudWorkspaceTargetsFingerprint ?? 0) return fingerprint } @@ -5756,6 +5791,9 @@ struct ContentView: View { private func commandPaletteCloudWorkspaceTargets() -> [CommandPaletteCloudWorkspaceTarget] { guard CloudMachinesFeature.isEnabled else { return [] } + if commandPaletteCloudWorkspaceTargetsCachedRevision == commandPaletteCloudWorkspaceTargetsCacheRevision { + return commandPaletteCloudWorkspaceTargetsCache + } let catalog = SurfaceCatalog.shared let snapshot = catalog.snapshot let allNodes = CloudTreeNodeBuilder.nodes( @@ -5788,14 +5826,15 @@ struct ContentView: View { var fingerprintHasher = Hasher() for target in targets { fingerprintHasher.combine(target.machine.rawValue) + let machineName = catalog.machineInfo(for: target.machine)?.name ?? target.machine.rawValue + fingerprintHasher.combine(machineName) fingerprintHasher.combine(target.workspace.id) fingerprintHasher.combine(target.workspace.name) - if let remoteWorkspaceID = target.group.remoteWorkspaceID { - fingerprintHasher.combine(remoteWorkspaceID) - } + fingerprintHasher.combine(target.group) } commandPaletteCloudWorkspaceTargetsFingerprint = fingerprintHasher.finalize() commandPaletteCloudWorkspaceTargetsCache = targets + commandPaletteCloudWorkspaceTargetsCachedRevision = commandPaletteCloudWorkspaceTargetsCacheRevision return targets } @@ -5813,7 +5852,23 @@ struct ContentView: View { actions.openWorkspace(target.machine, target.workspace, target.group) } - private func commandPaletteSwitcherEntries(includeSurfaces: Bool) -> [CommandPaletteCommand] { + private func invalidateCommandPaletteCloudWorkspaceTargets() { + commandPaletteCloudWorkspaceTargetsCacheRevision &+= 1 + commandPaletteCloudWorkspaceTargetsCachedRevision = nil + commandPaletteCloudWorkspaceTargetsCache = [] + commandPaletteCloudWorkspaceTargetsFingerprint = nil + guard isCommandPalettePresented, + commandPaletteListScope == .switcher else { return } + scheduleCommandPaletteResultsRefresh( + query: commandPaletteQuery, + forceSearchCorpusRefresh: true + ) + } + + private func commandPaletteSwitcherEntries( + includeSurfaces: Bool, + cloudWorkspaceTargets: [CommandPaletteCloudWorkspaceTarget]? = nil + ) -> [CommandPaletteCommand] { if let snapshot = commandPaletteCurrentWorkSnapshot { return commandPaletteCurrentWorkEntries(snapshot: snapshot) } @@ -5925,8 +5980,7 @@ struct ContentView: View { } let cloudWorkspaceKind = String(localized: "commandPalette.kind.cloudWorkspace", defaultValue: "Cloud Workspace") - let cloudWorkspaceTargets = commandPaletteCloudWorkspaceTargets() - for target in cloudWorkspaceTargets { + for target in cloudWorkspaceTargets ?? commandPaletteCloudWorkspaceTargets() { let machineName = SurfaceCatalog.shared.machineInfo(for: target.machine)?.name ?? target.machine.rawValue let title = target.workspace.name let commandID = "switcher.cloudWorkspace.\(target.machine.rawValue).\(target.workspace.id)" @@ -10128,8 +10182,7 @@ struct ContentView: View { } private func openCommandPaletteSwitcher() { - commandPaletteCloudWorkspaceTargetsCache = [] - commandPaletteCloudWorkspaceTargetsFingerprint = nil + invalidateCommandPaletteCloudWorkspaceTargets() handleCommandPaletteListRequest(scope: .switcher) } From a1ccd64d7f98bde02d9873f148900f8e6cf3b85c Mon Sep 17 00:00:00 2001 From: Austin Wang <38676809+austinywang@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:07:21 -0700 Subject: [PATCH 8/8] Keep Cloud palette fingerprint render-pure --- Sources/ContentView.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 90752561840d..e55ab2a63012 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -5084,14 +5084,11 @@ struct ContentView: View { private var commandPaletteCurrentSearchFingerprint: Int { let scope = commandPaletteListScope - let cloudWorkspaceTargets = scope == .switcher - ? commandPaletteCloudWorkspaceTargets() - : nil return commandPaletteEntriesFingerprint( for: scope, includeSurfaces: commandPaletteSwitcherIncludesSurfaceEntries, commandsContext: scope == .commands ? commandPaletteCachedCommandsContext() : nil, - cloudWorkspaceTargets: cloudWorkspaceTargets + cloudWorkspaceTargets: nil ) } @@ -5710,7 +5707,6 @@ struct ContentView: View { ) } var fingerprint = CommandPaletteSwitcherFingerprintContext.fingerprint(windowContexts: fingerprintContexts) - _ = cloudWorkspaceTargets ?? commandPaletteCloudWorkspaceTargets() fingerprint = fingerprint &* 31 &+ (commandPaletteCloudWorkspaceTargetsFingerprint ?? 0) return fingerprint } @@ -5802,7 +5798,9 @@ struct ContentView: View { localWorkspaces: [], includeLocalMachine: false ) - let sidebarNodes = catalog.sidebarNodes() + let sidebarNodes = CloudSidebarOrganizationTree(nodes: allNodes).arrange( + using: catalog.sidebarOrganization.state + ) let sidebarWorkspaceIDs = Set( CloudTreeNodeBuilder.flattened(sidebarNodes).compactMap { node -> String? in guard case .workspace(let machine, let workspace, _, _, _) = node.kind else { return nil }