Files
wehub-resource-sync 869b84f27c
Build / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

79 lines
3.2 KiB
Swift

import AppKit
import SwiftUI
/// Draggable PureMac.app affordance. The user grabs the icon out of our sheet
/// and drops it into the Full Disk Access list inside System Settings, which
/// auto-adds the bundle without making them hunt for it in Finder.
///
/// macOS's Privacy & Security panes accept file-URL drops in their service
/// lists. We expose the running app's bundle URL via `NSItemProvider` so the
/// drop registers as a legitimate filesystem drag — identical to dragging
/// from Finder, just sourced from inside the app.
struct AppBundleDragHandle: View {
@State private var hovering = false
private var bundleURL: URL {
Bundle.main.bundleURL
}
var body: some View {
VStack(spacing: 8) {
ZStack {
RoundedRectangle(cornerRadius: 14, style: .continuous)
.fill(Color.primary.opacity(hovering ? 0.10 : 0.06))
.frame(width: 84, height: 84)
.overlay(
RoundedRectangle(cornerRadius: 14, style: .continuous)
.strokeBorder(
hovering ? Tint.blue.opacity(0.6) : Color.primary.opacity(0.10),
style: StrokeStyle(lineWidth: hovering ? 1.5 : 0.5,
dash: hovering ? [] : [3, 3])
)
)
appIcon
.frame(width: 56, height: 56)
.scaleEffect(hovering ? 1.04 : 1.0)
.shadow(color: .black.opacity(hovering ? 0.18 : 0.08),
radius: hovering ? 8 : 4, y: hovering ? 3 : 1)
}
.animation(.easeOut(duration: 0.18), value: hovering)
VStack(spacing: 1) {
Text("PureMac.app")
.font(.system(size: 11.5, weight: .semibold))
Text("Drag to the Settings list")
.font(.system(size: 10.5))
.foregroundStyle(.secondary)
}
}
.padding(12)
.onHover { hovering = $0 }
.help("Drag this icon into the Full Disk Access list in System Settings.")
// NSItemProvider(object: NSURL) is exactly what Finder emits when
// you drag an .app — it exposes both the file representation and
// the URL string under public.file-url, which is the shape System
// Settings' Privacy panes are built to receive. `contentsOf:` works
// too but registers as a generic file copy operation; Settings'
// FDA list reads the URL representation directly.
.onDrag {
let provider = NSItemProvider(object: bundleURL as NSURL)
provider.suggestedName = bundleURL.lastPathComponent
return provider
}
}
@ViewBuilder
private var appIcon: some View {
if let nsImage = NSWorkspace.shared.icon(forFile: bundleURL.path) as NSImage? {
Image(nsImage: nsImage)
.resizable()
.interpolation(.high)
} else {
Image(systemName: "app.fill")
.font(.system(size: 36))
.foregroundStyle(Tint.blue)
}
}
}