Merge nucleic/rustic-north-koala into dev

# Conflicts:
#	Sources/NucleicApp/SessionDetailView.swift
#	ios/NucleicRemote/NucleicRemote/Views/ApprovalCardView.swift
This commit is contained in:
2026-06-16 02:03:44 -07:00
@@ -33,18 +33,11 @@ struct ApprovalCardView: View {
.background(approval.risk.color.opacity(0.2), in: Capsule())
.foregroundStyle(approval.risk.color)
}
// Full, untruncated content in a bounded scroll view — the user must be
// able to read exactly what they are granting, so long commands/paths/inputs
// scroll instead of being clipped to a few lines.
// Full, untruncated content — the command wraps and the box fits its
// content, only scrolling vertically once it's genuinely tall, so the user
// can read exactly what they are granting without a giant half-empty box.
if !approval.input.approvalDetail.isEmpty {
ScrollView([.horizontal, .vertical]) {
Text(approval.input.approvalDetail)
.font(.caption.monospaced())
.textSelection(.enabled)
.padding(8)
}
.frame(maxWidth: .infinity, maxHeight: 200, alignment: .leading)
.background(Color(.secondarySystemBackground), in: RoundedRectangle(cornerRadius: 8))
ApprovalDetailBox(text: approval.input.approvalDetail)
}
if requiresBiometric && !biometricPassed {
@@ -118,3 +111,36 @@ struct ApprovalCardView: View {
}
}
}
/// The full content of an approval, wrapped and selectable. The box sizes to its
/// content and only begins scrolling once the content exceeds `maxHeight`, so a short
/// command sits in a snug box while a large paste stays bounded. The text wraps; it
/// never scrolls horizontally.
private struct ApprovalDetailBox: View {
let text: String
private let maxHeight: CGFloat = 200
@State private var contentHeight: CGFloat = 0
var body: some View {
ScrollView(.vertical) {
Text(text)
.font(.caption.monospaced())
.textSelection(.enabled)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(8)
.background(GeometryReader { geo in
Color.clear.preference(key: ApprovalDetailHeightKey.self, value: geo.size.height)
})
}
.frame(height: contentHeight == 0 ? nil : min(contentHeight, maxHeight))
.onPreferenceChange(ApprovalDetailHeightKey.self) { contentHeight = $0 }
.background(Color(.secondarySystemBackground), in: RoundedRectangle(cornerRadius: 8))
}
}
private struct ApprovalDetailHeightKey: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}