Wrap approval detail and size the box to its content

The first pass put the detail in a ScrollView with fixedSize(horizontal:)
and a fixed maxHeight, so the command was forced onto one long line that
scrolled horizontally and the box always claimed full height — a tall box
with a lot of empty space.

Replace it with an ApprovalDetailBox (macOS + iOS) that wraps the text and
measures its content height via a PreferenceKey, sizing the box to fit and
only scrolling vertically once the content exceeds the max height. No more
horizontal scroll; short commands sit in a snug box.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-06-16 02:00:05 -07:00
co-authored by Claude Opus 4.8
parent e31b6a8799
commit 3954ff13b7
@@ -33,18 +33,11 @@ struct ApprovalCardView: View {
.background(approval.risk.color.opacity(0.2), in: Capsule()) .background(approval.risk.color.opacity(0.2), in: Capsule())
.foregroundStyle(approval.risk.color) .foregroundStyle(approval.risk.color)
} }
// Full, untruncated content in a bounded scroll view — the user must be // Full, untruncated content — the command wraps and the box fits its
// able to read exactly what they are granting, so long commands/paths/inputs // content, only scrolling vertically once it's genuinely tall, so the user
// scroll instead of being clipped to a few lines. // can read exactly what they are granting without a giant half-empty box.
if !approval.input.approvalDetail.isEmpty { if !approval.input.approvalDetail.isEmpty {
ScrollView([.horizontal, .vertical]) { ApprovalDetailBox(text: approval.input.approvalDetail)
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))
} }
if requiresBiometric && !biometricPassed { 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())
}
}