mirror of
https://github.com/carey314/mio-plugin-clipper.git
synced 2026-08-10 07:44:32 +00:00
109 lines
3.9 KiB
Swift
109 lines
3.9 KiB
Swift
|
|
//
|
||
|
|
// 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)
|
||
|
|
}
|
||
|
|
}
|