Files
scarf/scarf/Scarf iOS/Kanban/DiagnosticDetailSheet.swift
T
Alan Wizemann a8cdb3e663 feat(ios): v0.13 read-only catch-up — goal pill, queue chip, Kanban diagnostics, Curator archived, Platforms (WS-9)
Mirrors the v0.13 surfaces from WS-2 (Persistent Goals + ACP /queue),
WS-3 (Kanban diagnostics + hallucination gate), WS-4 (Curator archive),
and WS-5 (Google Chat platform + cross-platform allowlists + behavior
toggles) onto ScarfGo. Per Phase H precedent, every iOS surface is
strictly read-only — write verbs (Verify / Reject, /goal --clear, queue
send, allowlist editing, archive Restore / Prune) live on Mac in v2.8.0
and are deferred to v2.8.x.

Five iOS additions, all capability-gated so pre-v0.13 hosts see the
v2.7.5 layout unchanged:

1. Chat — goal pill ("Goal: <text>") and queue chip ("N queued") render
   inside `projectContextBar` whenever a project, goal, or queue is
   present. The bar is no longer project-only; goal/queue chips render
   even outside a project chat. Goal text scales with Dynamic Type
   (semantic `.subheadline`); the full untruncated text rides VoiceOver
   via the chip's accessibility label.
2. Kanban — `ScarfGoKanbanDetailSheet` gains a `retries: N` chip in the
   header `FlowLayout`, a yellow "Worker-created — verify on Mac" badge
   for `pending` hallucination state, a red "Auto-blocked" banner with
   the server-supplied `auto_blocked_reason`, and tappable diagnostics
   chip-lists (task-level + per-run) that present a new
   `DiagnosticDetailSheet` with kind / severity / message / timestamp.
   No Verify or Reject buttons; the badge copy points users to the Mac
   app.
3. Curator — `CuratorView` appends a read-only "Archived" section that
   loads via `viewModel.loadArchive()` on appear and pull-to-refresh.
   Per-row name + category badge + reason + archived-at + size; footer
   signposts users to the Mac app for Restore / Prune.
4. Settings → Platforms — adds a Google Chat status row (configured /
   not configured), busy-ack and restart-notification rows summarized
   across `gatewayPlatforms` (yes / no / mixed (N platforms)), and
   collapsed DisclosureGroups for allowed channels / chats / rooms with
   monospaced "platform: id" entries when expanded. No editor.
5. Settings — green "v0.13 features active" `ScarfBadge` above the
   quick-edits section when `caps.isV013OrLater`. Tap presents a new
   `V013FeaturesSheet` listing the six v0.13 surfaces with one-sentence
   summaries; the section footer is explicit that editing lives on Mac.

Implements WS-9 of Scarf v2.8.0 (Hermes v0.13.0 catch-up).
Plan: scarf/docs/v2.8/WS-9-ios-v0.13-plan.md (on coordination/v2.8.0-plans).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 19:25:16 +02:00

87 lines
3.1 KiB
Swift

import SwiftUI
import ScarfCore
import ScarfDesign
/// iOS substitute for the Mac inspector's `.help()` tooltip on a Kanban
/// diagnostic chip. iOS doesn't have hover, so each diagnostic chip in
/// the detail sheet is tappable; tap presents this sheet with the kind,
/// severity, server-supplied message, and detection timestamp.
///
/// Read-only there are no recovery actions on iOS in v2.8.0. The
/// surface is deliberately small (one screen, no scroll padding) so it
/// reads as a fast peek rather than a full editor.
struct DiagnosticDetailSheet: View {
let diagnostic: HermesKanbanDiagnostic
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
List {
Section {
LabeledContent("Kind") {
Text(diagnostic.kind)
.font(.body.monospaced())
.foregroundStyle(.primary)
}
LabeledContent("Severity") {
ScarfBadge(severityLabel, kind: severityBadgeKind)
}
if let detectedAt = diagnostic.detectedAt, !detectedAt.isEmpty {
LabeledContent("Detected at") {
Text(detectedAt)
.font(.caption.monospaced())
.foregroundStyle(.secondary)
}
}
} header: {
Text("Diagnostic")
}
if let message = diagnostic.message, !message.isEmpty {
Section {
Text(message)
.font(.body)
.textSelection(.enabled)
} header: {
Text("Message")
}
}
Section {
Label("Recovery actions live on the Mac app — open this task there to verify, reject, or unblock.", systemImage: "info.circle")
.font(.caption)
.foregroundStyle(.secondary)
}
}
.scrollContentBackground(.hidden)
.background(ScarfColor.backgroundPrimary)
.navigationTitle("Diagnostic")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") { dismiss() }
}
}
}
}
private var severityLabel: String {
let kind = KanbanDiagnosticKind.from(diagnostic.kind)
switch kind.severity {
case .danger: return "danger"
case .warning: return "warning"
case .neutral: return "neutral"
}
}
private var severityBadgeKind: ScarfBadgeKind {
let kind = KanbanDiagnosticKind.from(diagnostic.kind)
switch kind.severity {
case .danger: return .danger
case .warning: return .warning
case .neutral: return .neutral
}
}
}