Read App Intents Documentation

Nucleic-Session: 56C5F781-4444-466B-91CE-A7734A8E117F
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-06 14:49:42 -07:00
co-authored by Nucleic
parent 853bb27486
commit d37263145b
@@ -131,15 +131,42 @@ private struct LockScreenView: View {
private struct SessionRow: View {
let line: NucleicSessionAttributes.SessionLine
/// An approval this row can resolve inline: it's blocked on an approval, carries that approval's
/// id, and isn't high-risk (§3.3 — high-risk keeps the plain tap-to-open row and is answered on
/// the app's guarded card). `nil` until the producer populates `approvalID` — an older host, or
/// today's summary that doesn't yet carry it, so the row stays a deep-link exactly as before.
private var inlineApprovalID: String? {
guard line.kind == .approval, (line.approvalIsHighRisk ?? false) == false else { return nil }
return line.approvalID
}
var body: some View {
if let url = NucleicDeepLink.session(line.id) {
if let approvalID = inlineApprovalID {
actionableRow(approvalID: approvalID)
} else if let url = NucleicDeepLink.session(line.id) {
Link(destination: url) { rowContent }
} else {
rowContent
}
}
private var rowContent: some View {
/// An approval row with inline Allow/Deny. The identity still deep-links to the session; the
/// buttons fire `ApproveFromActivityIntent`, which the system performs in the app's background
/// process — allow/deny without unlocking (§4.1).
private func actionableRow(approvalID: String) -> some View {
HStack(spacing: 8) {
if let url = NucleicDeepLink.session(line.id) {
Link(destination: url) { rowIdentity }
} else {
rowIdentity
}
Spacer(minLength: 6)
ApprovalButtons(sessionID: line.id, approvalID: approvalID)
}
}
/// The glyph + title + "project · Backend" — the row's identity, minus the trailing status.
private var rowIdentity: some View {
HStack(spacing: 8) {
Image(systemName: ActivityPalette.glyph(line.kind))
.font(.footnote)
@@ -159,6 +186,12 @@ private struct SessionRow: View {
.foregroundStyle(.secondary)
.lineLimit(1)
}
}
}
private var rowContent: some View {
HStack(spacing: 8) {
rowIdentity
Spacer(minLength: 6)
Text(line.detail)
.font(.caption2.weight(.medium))
@@ -169,6 +202,33 @@ private struct SessionRow: View {
}
}
/// The compact inline Allow/Deny pair for an actionable approval row. Each button fires
/// `ApproveFromActivityIntent` (a low/medium-risk decision — high-risk never reaches here).
private struct ApprovalButtons: View {
let sessionID: String
let approvalID: String
var body: some View {
HStack(spacing: 6) {
Button(intent: intent(allow: false)) {
Image(systemName: "xmark").font(.caption2.bold())
}
.tint(ActivityPalette.danger)
Button(intent: intent(allow: true)) {
Image(systemName: "checkmark").font(.caption2.bold())
}
.tint(ActivityPalette.success)
}
.buttonStyle(.borderedProminent)
.controlSize(.small)
}
private func intent(allow: Bool) -> ApproveFromActivityIntent {
ApproveFromActivityIntent(
approvalID: approvalID, sessionID: sessionID, isHighRisk: false, allow: allow)
}
}
/// Headline count chips: running (blue), waiting (teal), approvals (amber). Only nonzero show.
private struct CountChips: View {
let state: NucleicSessionAttributes.ContentState