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

166 lines
6.8 KiB
Swift
Raw Permalink Normal View History

//
// PermissionBanner.swift
// plugin
//
// Two banners share this file:
// Accessibility-permission warning (when sourceMode = .selection)
// Missing-config (state E) full-card with copy-path helper
//
import SwiftUI
struct AccessibilityBanner: View {
var onOpenSettings: () -> Void
var body: some View {
HStack(alignment: .top, spacing: 8) {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(ClipperTheme.amber)
.font(.system(size: 12))
.padding(.top, 1)
VStack(alignment: .leading, spacing: 4) {
Text("选中文本需要辅助功能权限")
.font(.system(size: 11.5, weight: .semibold))
.foregroundColor(ClipperTheme.fg85)
Text("前往 系统设置 > 隐私与安全性 > 辅助功能,勾选 Mio Island 后回来重试。当前已自动回退到粘贴板。")
.font(.system(size: 10.5))
.foregroundColor(ClipperTheme.fg55)
.fixedSize(horizontal: false, vertical: true)
Button(action: onOpenSettings) {
Text("打开 系统设置")
.font(.system(size: 11, weight: .semibold))
.foregroundColor(ClipperTheme.amber)
}
.buttonStyle(.plain)
.padding(.top, 2)
}
Spacer(minLength: 0)
}
.padding(10)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(ClipperTheme.amber.opacity(0.08))
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(ClipperTheme.amber.opacity(0.25), lineWidth: 0.5)
)
)
}
}
struct MissingConfigCard: View {
@ObservedObject var store: ClipperStore
@State private var copied = false
var body: some View {
VStack(alignment: .leading, spacing: 12) {
HStack(spacing: 8) {
Image(systemName: "key.horizontal")
.font(.system(size: 14, weight: .semibold))
.foregroundColor(ClipperTheme.amber)
Text("先填一个 DeepSeek API Key")
.font(.system(size: 13, weight: .semibold))
.foregroundColor(ClipperTheme.fgPrimary)
}
VStack(alignment: .leading, spacing: 6) {
Text("打开下面的配置文件,把 deepseek_api_key 改成你的真实 key然后点 重新加载。")
.font(.system(size: 11.5))
.foregroundColor(ClipperTheme.fg70)
.fixedSize(horizontal: false, vertical: true)
Text(ClipperConfig.configPath)
.font(.system(size: 10.5, design: .monospaced))
.foregroundColor(ClipperTheme.fg55)
.padding(.horizontal, 10)
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(ClipperTheme.overlay04)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(ClipperTheme.overlay08, lineWidth: 0.5)
)
)
.textSelection(.enabled)
}
HStack(spacing: 8) {
Button {
store.copyToPasteboard(ClipperConfig.configPath)
copied = true
Task {
try? await Task.sleep(nanoseconds: 1_200_000_000)
await MainActor.run { copied = false }
}
} label: {
HStack(spacing: 6) {
Image(systemName: copied ? "checkmark" : "doc.on.doc")
.font(.system(size: 11, weight: .semibold))
Text(copied ? "路径已复制" : "复制路径")
.font(.system(size: 12, weight: .semibold))
}
.foregroundColor(copied ? ClipperTheme.lime : ClipperTheme.fg70)
.frame(maxWidth: .infinity)
.frame(height: 30)
.background(
Capsule().fill(ClipperTheme.overlay06)
.overlay(Capsule().stroke(ClipperTheme.overlay08, lineWidth: 0.5))
)
}
.buttonStyle(.plain)
Button {
NSWorkspace.shared.open(URL(fileURLWithPath: ClipperConfig.configDir))
} label: {
HStack(spacing: 6) {
Image(systemName: "folder")
.font(.system(size: 11, weight: .semibold))
Text("打开文件夹")
.font(.system(size: 12, weight: .semibold))
}
.foregroundColor(ClipperTheme.fg70)
.frame(maxWidth: .infinity)
.frame(height: 30)
.background(
Capsule().fill(ClipperTheme.overlay06)
.overlay(Capsule().stroke(ClipperTheme.overlay08, lineWidth: 0.5))
)
}
.buttonStyle(.plain)
Button {
store.reloadConfig()
} label: {
HStack(spacing: 6) {
Image(systemName: "arrow.clockwise")
.font(.system(size: 11, weight: .semibold))
Text("重新加载")
.font(.system(size: 12, weight: .semibold))
}
.foregroundColor(Color(red: 0x0B/255, green: 0x0B/255, blue: 0x0B/255))
.frame(maxWidth: .infinity)
.frame(height: 30)
.background(Capsule().fill(ClipperTheme.lime))
}
.buttonStyle(.plain)
}
Text("没有 key去 platform.deepseek.com 注册一个,新用户有免费额度。")
.font(.system(size: 10.5))
.foregroundColor(ClipperTheme.fg45)
}
.padding(14)
.frame(maxWidth: .infinity, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 14)
.fill(ClipperTheme.overlay04)
.overlay(
RoundedRectangle(cornerRadius: 14)
.stroke(ClipperTheme.overlay08, lineWidth: 0.5)
)
)
}
}