Session Cache Implementation

Nucleic-Session: B2346642-3353-4BD5-9DE2-A8B77BD66C98
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-05 22:32:28 -07:00
co-authored by Nucleic
parent 1a870c0436
commit b9eff62f81
@@ -222,8 +222,40 @@ struct SessionDetailView: View {
/// The floating action layer: one Liquid Glass bar carrying the queued pills, the working
/// indicator, the session controls, and the composer — or the approval card when a gate is
/// pending. Content scrolls beneath it; nothing renders when there's nothing to act on.
@ViewBuilder
private var actionArea: some View {
VStack(spacing: 8) {
// Interacting with a chat while the owning Mac is unreachable surfaces this first, so a
// disabled composer reads as "offline / read-only history" rather than broken.
if !store.connectivity.isLive { disconnectedBanner }
actionContent
}
.padding(.horizontal, 12)
.padding(.bottom, 8)
}
/// The offline notice over the transcript: the chat's history is cached and readable, but the
/// composer + controls below stay disabled until the Mac that owns it is reachable again.
private var disconnectedBanner: some View {
HStack(spacing: 10) {
Image(systemName: "wifi.slash").foregroundStyle(.secondary)
VStack(alignment: .leading, spacing: 1) {
Text("Disconnected").font(.footnote.weight(.semibold))
Text("Showing saved history · reconnect to send")
.font(.caption2).foregroundStyle(.secondary)
}
Spacer(minLength: 8)
Button("Reconnect") { store.reconnect() }
.font(.caption.weight(.semibold))
.buttonStyle(.borderless)
.disabled(!store.isPaired)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 14).padding(.vertical, 10)
.glassSurface(cornerRadius: 20)
}
@ViewBuilder
private var actionContent: some View {
Group {
if let approval = store.openApprovals.first {
// AskUserQuestion isn't a permission gate — it collects the user's answers and