Merge nucleic/gentle-slate-seal into dev

This commit is contained in:
2026-07-12 05:39:59 -07:00
parent 3da6338b0a
commit 954eafebcd
4 changed files with 215 additions and 3 deletions
@@ -90,6 +90,27 @@ final class RemoteStore: ObservableObject {
.compactMapValues { RunnerPresence.decode($0) }
}
// MARK: Mesh info (read-only projections for the Mesh Info screen)
/// Live connectivity for a directly-paired Mac, keyed by its `PairedHost.fingerprint` — the
/// same id `connections` is keyed by. `nil` when no connection object exists for it yet.
func connectivity(forPairedHost fingerprint: String) -> Connectivity? {
connections[fingerprint]?.connectivity
}
/// The mesh's runner-presence cards (Macs / cloud runners advertising capacity), sorted by
/// host name — the phone's window into the Carbon dispatch candidate pool. Keyed by the full
/// `deviceID` (distinct from `PairedHost.fingerprint`), so it's presented as its own section
/// rather than cross-matched against the directly-paired Macs.
var meshRunnerCards: [(hostID: String, presence: RunnerPresence)] {
runnerPresenceByHost
.map { (hostID: $0.key, presence: $0.value) }
.sorted {
$0.presence.hostName.localizedCaseInsensitiveCompare($1.presence.hostName)
== .orderedAscending
}
}
/// Whether the composer should offer "Auto (Mesh)" for the project the phone has selected:
/// some connected host advertising `canAcknowledgeDispatch` (other than the project's own
/// owner) holds a matching repo. The descriptor comes from the owning host's presence card.
@@ -76,8 +76,8 @@ private struct ActivityFeedRow: View {
store.route(to: event.sessionID)
} label: {
HStack(alignment: .top, spacing: 10) {
Image(systemName: Self.icon(for: event.kind))
.foregroundStyle(Self.tint(for: event.kind))
Image(systemName: ActivityFeedIcon.icon(for: event.kind))
.foregroundStyle(ActivityFeedIcon.tint(for: event.kind))
.font(.subheadline)
.frame(width: 20)
.padding(.top, 2)
@@ -105,12 +105,16 @@ private struct ActivityFeedRow: View {
}
private var subtitle: String {
var parts: [String] = [Self.headline(for: event)]
var parts: [String] = [ActivityFeedIcon.headline(for: event)]
if !event.projectName.isEmpty { parts.append(event.projectName) }
if !event.hostName.isEmpty { parts.append(event.hostName) }
return parts.joined(separator: " · ")
}
}
/// The shared icon / tint / headline vocabulary for a fleet-activity `FeedEvent`, used by both the
/// activity feed rows and the Mesh Info screen's event log so they read identically.
enum ActivityFeedIcon {
static func headline(for event: FeedEvent) -> String {
switch event.kind {
case .finished: "Finished"
@@ -0,0 +1,180 @@
import SwiftUI
import NucleicProtocol
/// The phone's **Mesh Info** screen — the iOS analogue of the Mac's Carbon Mesh Viewer, reached
/// from Settings ▸ Mesh ▸ "Mesh info". A read-only diagnostics surface over the mesh as this phone
/// sees it: overall connection health, the Macs this phone is directly paired with (live status +
/// transport), the mesh's runner-presence cards (the Carbon dispatch candidate pool with capacity /
/// projects), and a live tail of fleet activity. Structural metadata only — never prompt/draft
/// content, matching the Mac viewer's redaction invariant.
///
/// The phone links only `NucleicProtocol`, not the Mac's mesh engine, so this renders from
/// `RemoteStore` projections (`connectivity`, `meshRunnerCards`, `activityFeed`) rather than the
/// Mac's `MeshDiagnosticsLog`. Two id namespaces are in play — directly-paired Macs are keyed by
/// `PairedHost.fingerprint`, presence cards by the full `deviceID` — so they're shown as separate
/// sections instead of being cross-matched.
struct MeshInfoView: View {
@EnvironmentObject var store: RemoteStore
private var pairedHosts: [PairedHost] { IdentityStore.pairedHosts() }
private var runnerCards: [(hostID: String, presence: RunnerPresence)] { store.meshRunnerCards }
var body: some View {
Form {
statusSection
yourMacsSection
runnerCardsSection
activitySection
}
.navigationTitle("Mesh info")
.navigationBarTitleDisplayMode(.inline)
}
// MARK: Status
@ViewBuilder
private var statusSection: some View {
Section {
LabeledContent("Connection", value: store.connectivity.label)
LabeledContent("Paired Macs", value: "\(pairedHosts.count)")
if !runnerCards.isEmpty {
LabeledContent("Runner cards", value: "\(runnerCards.count)")
}
} header: {
Text("Status")
} footer: {
Text("The mesh as this device sees it. Read-only — structural metadata only.")
}
}
// MARK: Your Macs
@ViewBuilder
private var yourMacsSection: some View {
if !pairedHosts.isEmpty {
Section("Your Macs") {
ForEach(pairedHosts, id: \.fingerprint) { host in
let connectivity = store.connectivity(forPairedHost: host.fingerprint)
HStack(spacing: 10) {
statusDot(online: connectivity?.isLive == true)
Image(systemName: "desktopcomputer").foregroundStyle(.secondary)
VStack(alignment: .leading, spacing: 2) {
Text(host.hostName.isEmpty ? "Mac" : host.hostName)
Text(connectivity?.label ?? "Not connected")
.font(.footnote).foregroundStyle(.secondary)
}
Spacer()
}
}
}
}
}
// MARK: Runner presence cards
@ViewBuilder
private var runnerCardsSection: some View {
if !runnerCards.isEmpty {
Section {
ForEach(runnerCards, id: \.hostID) { card in
RunnerCardRow(presence: card.presence)
}
} header: {
Text("Runner capacity")
} footer: {
Text("Hosts advertising capacity for Carbon-dispatched chats, and the projects "
+ "they hold.")
}
}
}
// MARK: Recent activity
@ViewBuilder
private var activitySection: some View {
Section("Recent mesh activity") {
if store.activityFeed.isEmpty {
Text("No mesh events yet.")
.font(.footnote).foregroundStyle(.secondary)
} else {
// Newest first, capped — this is a diagnostics tail, not the full feed.
ForEach(Array(store.activityFeed.reversed().prefix(50))) { item in
MeshEventRow(item: item)
}
}
}
}
// MARK: Bits
private func statusDot(online: Bool) -> some View {
Circle()
.fill(online ? Color.green : Color.secondary.opacity(0.4))
.frame(width: 8, height: 8)
}
}
/// One runner-presence card: host name + kind, load (active / capacity), and its held projects.
private struct RunnerCardRow: View {
let presence: RunnerPresence
private var capacityLabel: String {
let cap = presence.maxConcurrent.map(String.init) ?? "∞"
return "\(presence.activeSessionCount)/\(cap)"
}
var body: some View {
HStack(spacing: 10) {
Image(systemName: presence.kind == .cloud ? "cloud" : "desktopcomputer")
.foregroundStyle(.secondary)
VStack(alignment: .leading, spacing: 2) {
Text(presence.hostName.isEmpty ? "Host" : presence.hostName)
Text("\(presence.projects.count) project(s)"
+ (presence.acceptsMeshDispatch ? " · accepts dispatch" : " · dispatch off"))
.font(.footnote).foregroundStyle(.secondary)
}
Spacer()
Text(capacityLabel)
.font(.footnote.monospacedDigit())
.foregroundStyle(.secondary)
}
}
}
/// One mesh event row (a fleet-activity `FeedEvent`) — the phone's equivalent of the Mac viewer's
/// event-log rows. Reuses the activity feed's icon/tint/headline vocabulary.
private struct MeshEventRow: View {
let item: ActivityFeedItem
private var event: FeedEvent { item.event }
var body: some View {
HStack(alignment: .top, spacing: 10) {
Image(systemName: ActivityFeedIcon.icon(for: event.kind))
.foregroundStyle(ActivityFeedIcon.tint(for: event.kind))
.font(.subheadline)
.frame(width: 20)
.padding(.top, 2)
VStack(alignment: .leading, spacing: 2) {
HStack(spacing: 6) {
Text(event.sessionTitle.isEmpty ? "Untitled chat" : event.sessionTitle)
.font(.subheadline.weight(.medium))
.lineLimit(1)
Spacer(minLength: 4)
Text(event.at, format: .relative(presentation: .named))
.font(.caption2).foregroundStyle(.tertiary)
}
Text(subtitle)
.font(.caption).foregroundStyle(.secondary)
.lineLimit(2)
}
}
.padding(.vertical, 2)
}
private var subtitle: String {
var parts: [String] = [ActivityFeedIcon.headline(for: event)]
if !event.projectName.isEmpty { parts.append(event.projectName) }
if !event.hostName.isEmpty { parts.append(event.hostName) }
return parts.joined(separator: " · ")
}
}
@@ -71,6 +71,13 @@ struct SettingsView: View {
let pairedHosts = IdentityStore.pairedHosts()
if !pairedHosts.isEmpty {
Section("Mesh") {
// A read-only diagnostics view of the mesh as this phone sees it — the
// iOS analogue of the Mac's Carbon Mesh Viewer.
NavigationLink {
MeshInfoView()
} label: {
Label("Mesh info", systemImage: "point.3.connected.trianglepath.dotted")
}
ForEach(pairedHosts, id: \.fingerprint) { host in
HStack(spacing: 10) {
Image(systemName: "desktopcomputer").foregroundStyle(.secondary)