276d2bb6-619d-4888-ba11-8a6.../Sources/ui/ResultView.swift

109 lines
3.9 KiB
Swift
Raw Normal View History

//
// ResultView.swift
// plugin
//
// Scrollable result text + / / buttons.
//
import SwiftUI
struct ResultView: View {
@ObservedObject var store: ClipperStore
let actionLabel: String
let output: String
@State private var copyConfirmed = false
var body: some View {
VStack(spacing: 10) {
// Action header
HStack(spacing: 6) {
Image(systemName: "sparkles")
.font(.system(size: 11, weight: .semibold))
.foregroundColor(ClipperTheme.lime)
Text(actionLabel)
.font(.system(size: 12, weight: .semibold))
.foregroundColor(ClipperTheme.fgPrimary)
Spacer()
Text("\(output.count)")
.font(.system(size: 10.5))
.foregroundColor(ClipperTheme.fg40)
.monospacedDigit()
}
// Result body scrollable.
ScrollView {
Text(output)
.font(.system(size: 13))
.foregroundColor(ClipperTheme.fg85)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
.textSelection(.enabled)
.padding(.horizontal, 12)
.padding(.vertical, 10)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(ClipperTheme.overlay04)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(ClipperTheme.overlay08, lineWidth: 0.5)
)
)
// Action buttons
HStack(spacing: 8) {
resultButton(label: copyConfirmed ? "已复制" : "复制",
icon: copyConfirmed ? "checkmark" : "doc.on.doc",
tint: copyConfirmed ? ClipperTheme.lime : ClipperTheme.fg70) {
store.copyToPasteboard(output)
copyConfirmed = true
Task {
try? await Task.sleep(nanoseconds: 1_200_000_000)
await MainActor.run { copyConfirmed = false }
}
}
resultButton(label: "重新生成",
icon: "arrow.clockwise",
tint: ClipperTheme.fg70) {
store.regenerate()
}
resultButton(label: "完成",
icon: "checkmark.circle.fill",
tint: Color(red: 0x0B/255, green: 0x0B/255, blue: 0x0B/255),
filled: true) {
store.finishResult()
}
}
}
}
private func resultButton(label: String,
icon: String,
tint: Color,
filled: Bool = false,
action: @escaping () -> Void) -> some View {
Button(action: action) {
HStack(spacing: 6) {
Image(systemName: icon)
.font(.system(size: 11, weight: .semibold))
Text(label)
.font(.system(size: 12, weight: .semibold))
}
.foregroundColor(tint)
.frame(maxWidth: .infinity)
.frame(height: 32)
.background(
Capsule()
.fill(filled ? ClipperTheme.lime : ClipperTheme.overlay06)
.overlay(
Capsule()
.stroke(filled ? Color.clear : ClipperTheme.overlay08, lineWidth: 0.5)
)
)
}
.buttonStyle(.plain)
}
}