From e60da7ccb1962424f7fdcdc922536beaf041de22 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sun, 5 Jul 2026 01:55:48 -0700 Subject: [PATCH] Sessions Integration Feature Nucleic-Session: FC94353F-9F0D-4BCC-AD7C-0FB14E38C2E5 Co-authored-by: Nucleic --- .../NucleicRemote/Models/RemoteStore.swift | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift index 10cf465..9aa8d83 100644 --- a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift +++ b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift @@ -1188,6 +1188,60 @@ extension Data { } } +extension DashboardSnapshot { + /// Merge every connected Mac's dashboard into one aggregate projection (mesh P3), so Home, + /// Projects, and To-Dos show all your Macs together: projects and to-dos concatenated, activity + /// summed per day, counts summed (active-days recomputed from the merged activity), subscription + /// usage taken from the first Mac that reports it (a user's Macs share one account, so their + /// windows match — summing would double-count), and provider status feeds unioned by provider + /// (provider status is global, not per-Mac). + static func merged(_ snapshots: some Collection) -> DashboardSnapshot { + let snaps = Array(snapshots) + guard snaps.count != 1 else { return snaps[0] } + guard !snaps.isEmpty else { return .empty } + + let projects = snaps.flatMap(\.projects) + let todos = snaps.flatMap(\.todos) + + // Activity summed per start-of-day. + var byDay: [Date: (count: Int, tokens: Int)] = [:] + for snap in snaps { + for day in snap.activity { + let key = Calendar.current.startOfDay(for: day.day) + var entry = byDay[key] ?? (0, 0) + entry.count += day.count + entry.tokens += day.tokens + byDay[key] = entry + } + } + let activity = byDay + .map { ActivityDay(day: $0.key, count: $0.value.count, tokens: $0.value.tokens) } + .sorted { $0.day < $1.day } + + let counts = DashboardCounts( + projects: projects.count, + chats: snaps.reduce(0) { $0 + $1.counts.chats }, + activeChats: snaps.reduce(0) { $0 + $1.counts.activeChats }, + messages: snaps.reduce(0) { $0 + $1.counts.messages }, + activeDays: activity.filter { $0.count > 0 || $0.tokens > 0 }.count, + tokens: snaps.reduce(0) { $0 + $1.counts.tokens }) + + let usage = snaps.compactMap(\.usage).first + + var seenProviders = Set() + var statusFeeds: [WireStatusFeed] = [] + for snap in snaps { + for feed in snap.statusFeeds where seenProviders.insert(feed.provider).inserted { + statusFeeds.append(feed) + } + } + + return DashboardSnapshot( + counts: counts, activity: activity, projects: projects, + todos: todos, usage: usage, statusFeeds: statusFeeds) + } +} + extension WireSessionSummary { /// A copy with selected fields overridden — the demo simulator's only way to "mutate" a /// summary, whose stored properties are all `let`. Nullable fields use a double optional so a