From e255ad18ba99cbc454e9b48604b80f4962b09d89 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sun, 5 Jul 2026 01:54:13 -0700 Subject: [PATCH] Sessions Integration Feature Nucleic-Session: FC94353F-9F0D-4BCC-AD7C-0FB14E38C2E5 Co-authored-by: Nucleic --- .../NucleicRemote/Models/RemoteStore.swift | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift index 5ab7880..ed11a52 100644 --- a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift +++ b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift @@ -614,25 +614,38 @@ final class RemoteStore: ObservableObject { } + /// Unpair this device entirely — drop every paired Mac and its connection (the "Unpair this + /// device" button). Nothing left to show, so the app returns to the pairing intro. func unpair() { - // Forget + drop the active Mac's connection specifically (the switcher may have made it not - // the last). Other Macs' connections keep running. - if let id = activeHostID { - connections[id]?.teardown() - connections[id] = nil - IdentityStore.removePairedHost(id: id) + for (id, conn) in connections { conn.teardown(); connections[id] = nil } + for host in IdentityStore.pairedHosts() { IdentityStore.removePairedHost(id: host.fingerprint) } + IdentityStore.clearPairedHost() + finishUnpairIfEmpty() + } + + /// Forget one paired Mac (the Settings ▸ Mesh per-row remove): drop just its connection + + /// registry record. Other Macs keep running and stay in the merged view. + func unpair(_ hostID: String) { + connections[hostID]?.teardown() + connections[hostID] = nil + IdentityStore.removePairedHost(id: hostID) + if IdentityStore.pairedHosts().isEmpty { + finishUnpairIfEmpty() } else { - IdentityStore.clearPairedHost() - } - activeHostID = nil - // Mesh P3: if other Macs remain paired, switch to one (already connected) rather than unpairing. - if let next = IdentityStore.pairedHosts().last { - activeHostID = next.fingerprint - reconnect() - return + rebuildAggregate() + refreshAggregate() } + } + + /// Wind the app back to the unpaired state once no Macs remain: clear the open transcript + flat + /// aggregate and spin the embedded Tailscale node down. + private func finishUnpairIfEmpty() { + openSessionID = nil + openSessionHostID = nil + openEvents = []; openApprovals = []; openDiff = nil; diffLoading = false connectivity = .unpaired sessions = [] + dashboard = .empty // Nothing left to dial — spin the embedded Tailscale node down if it was running. Task { await TailnetNode.shared.stop() } tailnetStatus = nil