mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-07-01 17:53:36 +00:00
Compare commits
4 Commits
proxystore
...
newcreatio
Author | SHA1 | Date | |
---|---|---|---|
13f30f0343 | |||
964ef1e201 | |||
ee850e58d1 | |||
e94346583e |
@ -18,9 +18,6 @@ let package = Package(
|
|||||||
.library(
|
.library(
|
||||||
name: "SmartCardSecretKit",
|
name: "SmartCardSecretKit",
|
||||||
targets: ["SmartCardSecretKit"]),
|
targets: ["SmartCardSecretKit"]),
|
||||||
.library(
|
|
||||||
name: "ProxyAgentSecretKit",
|
|
||||||
targets: ["ProxyAgentSecretKit"]),
|
|
||||||
.library(
|
.library(
|
||||||
name: "SecretAgentKit",
|
name: "SecretAgentKit",
|
||||||
targets: ["SecretAgentKit"]),
|
targets: ["SecretAgentKit"]),
|
||||||
@ -50,10 +47,6 @@ let package = Package(
|
|||||||
name: "SmartCardSecretKit",
|
name: "SmartCardSecretKit",
|
||||||
dependencies: ["SecretKit"]
|
dependencies: ["SecretKit"]
|
||||||
),
|
),
|
||||||
.target(
|
|
||||||
name: "ProxyAgentSecretKit",
|
|
||||||
dependencies: ["SecretKit", "SecretAgentKit"]
|
|
||||||
),
|
|
||||||
.target(
|
.target(
|
||||||
name: "SecretAgentKit",
|
name: "SecretAgentKit",
|
||||||
dependencies: ["SecretKit", "SecretAgentKitHeaders"]
|
dependencies: ["SecretKit", "SecretAgentKitHeaders"]
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
/// Namespace for the Proxy Agent implementations.
|
|
||||||
public enum ProxyAgent {}
|
|
@ -1,19 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import Combine
|
|
||||||
import SecretKit
|
|
||||||
|
|
||||||
extension ProxyAgent {
|
|
||||||
|
|
||||||
/// An implementation of Secret backed by a Smart Card.
|
|
||||||
public struct Secret: SecretKit.Secret {
|
|
||||||
|
|
||||||
public let id: Data
|
|
||||||
public let name: String
|
|
||||||
public let algorithm: Algorithm
|
|
||||||
public let keySize: Int
|
|
||||||
public let requiresAuthentication: Bool = false
|
|
||||||
public let publicKey: Data
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import Security
|
|
||||||
import CryptoTokenKit
|
|
||||||
import LocalAuthentication
|
|
||||||
import SecretKit
|
|
||||||
|
|
||||||
extension ProxyAgent {
|
|
||||||
|
|
||||||
/// An implementation of Store backed by a Proxy Agent.
|
|
||||||
public class Store: SecretStore {
|
|
||||||
|
|
||||||
@Published public var isAvailable: Bool = true
|
|
||||||
public let id = UUID()
|
|
||||||
public private(set) var name = NSLocalizedString("Proxy SSH Agent", comment: "Proxy SSH Agent")
|
|
||||||
@Published public private(set) var secrets: [Secret] = []
|
|
||||||
private let agentPath: String
|
|
||||||
|
|
||||||
/// Initializes a Store.
|
|
||||||
public init(path: String) {
|
|
||||||
agentPath = path
|
|
||||||
secrets.append(Secret(id: "hello".data(using: .utf8)!, name: "Test", algorithm: .ellipticCurve, keySize: 256, publicKey: Data(base64Encoded: "AAAAC3NzaC1lZDI1NTE5AAAAIINQz8WohBS46ICEUtkJ/vdxJPM63T5Dy4bQC35JVgGR")!))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Public API
|
|
||||||
|
|
||||||
public func create(name: String) throws {
|
|
||||||
fatalError("Keys must be created on the smart card.")
|
|
||||||
}
|
|
||||||
|
|
||||||
public func delete(secret: Secret) throws {
|
|
||||||
fatalError("Keys must be deleted on the smart card.")
|
|
||||||
}
|
|
||||||
|
|
||||||
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func existingPersistedAuthenticationContext(secret: ProxyAgent.Secret) -> PersistedAuthenticationContext? {
|
|
||||||
nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public func persistAuthentication(secret: ProxyAgent.Secret, forDuration: TimeInterval) throws {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ProxyAgent.Store {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ProxyAgent {
|
|
||||||
|
|
||||||
/// A signing-related error.
|
|
||||||
public struct SigningError: Error {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -64,10 +64,6 @@ extension OpenSSHKeyWriter {
|
|||||||
switch algorithm {
|
switch algorithm {
|
||||||
case .ellipticCurve:
|
case .ellipticCurve:
|
||||||
return "ecdsa-sha2-nistp" + String(describing: length)
|
return "ecdsa-sha2-nistp" + String(describing: length)
|
||||||
case .rsa:
|
|
||||||
return "ssh-rsa"
|
|
||||||
case .ed25519:
|
|
||||||
return "ssh-ed25519"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,11 +76,6 @@ extension OpenSSHKeyWriter {
|
|||||||
switch algorithm {
|
switch algorithm {
|
||||||
case .ellipticCurve:
|
case .ellipticCurve:
|
||||||
return "nistp" + String(describing: length)
|
return "nistp" + String(describing: length)
|
||||||
// TODO: VERIFY
|
|
||||||
case .rsa:
|
|
||||||
return "rsa"
|
|
||||||
case .ed25519:
|
|
||||||
return "ed25519"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,6 @@ public protocol Secret: Identifiable, Hashable {
|
|||||||
/// The type of algorithm the Secret uses. Currently, only elliptic curve algorithms are supported.
|
/// The type of algorithm the Secret uses. Currently, only elliptic curve algorithms are supported.
|
||||||
public enum Algorithm: Hashable {
|
public enum Algorithm: Hashable {
|
||||||
|
|
||||||
case rsa
|
|
||||||
case ed25519
|
|
||||||
case ellipticCurve
|
case ellipticCurve
|
||||||
|
|
||||||
/// Initializes the Algorithm with a secAttr representation of an algorithm.
|
/// Initializes the Algorithm with a secAttr representation of an algorithm.
|
||||||
|
@ -47,8 +47,6 @@
|
|||||||
50A3B79124026B7600D209EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79024026B7600D209EA /* Assets.xcassets */; };
|
50A3B79124026B7600D209EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79024026B7600D209EA /* Assets.xcassets */; };
|
||||||
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
||||||
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
||||||
50A63F6B27E7DC5700085D7B /* ProxyAgentSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 50A63F6A27E7DC5700085D7B /* ProxyAgentSecretKit */; };
|
|
||||||
50A63F6D27E7E04800085D7B /* ProxyAgentSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 50A63F6C27E7E04800085D7B /* ProxyAgentSecretKit */; };
|
|
||||||
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; };
|
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; };
|
||||||
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
|
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
|
||||||
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; };
|
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; };
|
||||||
@ -156,7 +154,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
50A63F6D27E7E04800085D7B /* ProxyAgentSecretKit in Frameworks */,
|
|
||||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
|
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
|
||||||
501421622781262300BBAA70 /* Brief in Frameworks */,
|
501421622781262300BBAA70 /* Brief in Frameworks */,
|
||||||
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
||||||
@ -177,7 +174,6 @@
|
|||||||
files = (
|
files = (
|
||||||
5003EF3D278005F300DF2006 /* Brief in Frameworks */,
|
5003EF3D278005F300DF2006 /* Brief in Frameworks */,
|
||||||
5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
||||||
50A63F6B27E7DC5700085D7B /* ProxyAgentSecretKit in Frameworks */,
|
|
||||||
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */,
|
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */,
|
||||||
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */,
|
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */,
|
||||||
5003EF41278005FA00DF2006 /* SecretKit in Frameworks */,
|
5003EF41278005FA00DF2006 /* SecretKit in Frameworks */,
|
||||||
@ -349,7 +345,6 @@
|
|||||||
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
|
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
|
||||||
5003EF602780081600DF2006 /* SmartCardSecretKit */,
|
5003EF602780081600DF2006 /* SmartCardSecretKit */,
|
||||||
501421612781262300BBAA70 /* Brief */,
|
501421612781262300BBAA70 /* Brief */,
|
||||||
50A63F6C27E7E04800085D7B /* ProxyAgentSecretKit */,
|
|
||||||
);
|
);
|
||||||
productName = Secretive;
|
productName = Secretive;
|
||||||
productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */;
|
productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */;
|
||||||
@ -393,7 +388,6 @@
|
|||||||
5003EF40278005FA00DF2006 /* SecretKit */,
|
5003EF40278005FA00DF2006 /* SecretKit */,
|
||||||
5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */,
|
5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */,
|
||||||
5003EF642780081B00DF2006 /* SmartCardSecretKit */,
|
5003EF642780081B00DF2006 /* SmartCardSecretKit */,
|
||||||
50A63F6A27E7DC5700085D7B /* ProxyAgentSecretKit */,
|
|
||||||
);
|
);
|
||||||
productName = SecretAgent;
|
productName = SecretAgent;
|
||||||
productReference = 50A3B78A24026B7500D209EA /* SecretAgent.app */;
|
productReference = 50A3B78A24026B7500D209EA /* SecretAgent.app */;
|
||||||
@ -1027,14 +1021,6 @@
|
|||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Brief;
|
productName = Brief;
|
||||||
};
|
};
|
||||||
50A63F6A27E7DC5700085D7B /* ProxyAgentSecretKit */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
productName = ProxyAgentSecretKit;
|
|
||||||
};
|
|
||||||
50A63F6C27E7E04800085D7B /* ProxyAgentSecretKit */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
productName = ProxyAgentSecretKit;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
|
@ -88,7 +88,7 @@ extension ContentView {
|
|||||||
}, label: {
|
}, label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
})
|
})
|
||||||
.popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
.sheet(isPresented: $showingCreation) {
|
||||||
if let modifiable = storeList.modifiableStore {
|
if let modifiable = storeList.modifiableStore {
|
||||||
CreateSecretView(store: modifiable, showing: $showingCreation)
|
CreateSecretView(store: modifiable, showing: $showingCreation)
|
||||||
}
|
}
|
||||||
|
@ -8,32 +8,40 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
|||||||
|
|
||||||
@State private var name = ""
|
@State private var name = ""
|
||||||
@State private var requiresAuthentication = true
|
@State private var requiresAuthentication = true
|
||||||
|
@State private var test: ThumbnailPickerView.Item = ThumbnailPickerView.Item(name: "Test", description: "Hello", thumbnail: Text("Hello"))
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
Image(nsImage: NSApplication.shared.applicationIconImage)
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 64, height: 64)
|
|
||||||
.padding()
|
|
||||||
VStack {
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Create a New Secret").bold()
|
Text("Create a New Secret")
|
||||||
|
.font(.largeTitle)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
HStack {
|
HStack {
|
||||||
Text("Name:")
|
Text("Name:")
|
||||||
TextField("Shhhhh", text: $name).focusable()
|
TextField("Shhhhh", text: $name)
|
||||||
|
.focusable()
|
||||||
}
|
}
|
||||||
HStack {
|
if #available(macOS 12.0, *) {
|
||||||
VStack(spacing: 20) {
|
ThumbnailPickerView(items: [
|
||||||
Picker("", selection: $requiresAuthentication) {
|
ThumbnailPickerView.Item(name: "Requires Authentication Before Use", description: "You will be required to authenticate using Touch ID, Apple Watch, or password before each use.", thumbnail: AuthenticationView()),
|
||||||
Text("Requires Authentication (Biometrics or Password) before each use").tag(true)
|
ThumbnailPickerView.Item(name: "Notify on Use",
|
||||||
Text("Authentication not required when Mac is unlocked").tag(false)
|
description: "No authentication is required while your Mac is unlocked.",
|
||||||
}
|
thumbnail: NotificationView())
|
||||||
.pickerStyle(RadioGroupPickerStyle())
|
], selection: $test)
|
||||||
}
|
} else {
|
||||||
Spacer()
|
// HStack {
|
||||||
|
// VStack(spacing: 20) {
|
||||||
|
// Picker("", selection: $requiresAuthentication) {
|
||||||
|
// Text("Requires Authentication (Biometrics or Password) before each use").tag(true)
|
||||||
|
// Text("Authentication not required when Mac is unlocked").tag(false)
|
||||||
|
// }
|
||||||
|
// .pickerStyle(SegmentedPickerStyle())
|
||||||
|
// }
|
||||||
|
// Spacer()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,3 +63,193 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
|||||||
showing = false
|
showing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ThumbnailPickerView: View {
|
||||||
|
|
||||||
|
let items: [Item]
|
||||||
|
@Binding var selection: Item
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
ForEach(items) { item in
|
||||||
|
VStack {
|
||||||
|
item.thumbnail
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 10)
|
||||||
|
.stroke(lineWidth: item.id == selection.id ? 5 : 0))
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
Text(item.name)
|
||||||
|
.bold()
|
||||||
|
Text(item.description)
|
||||||
|
}.onTapGesture {
|
||||||
|
selection = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.onAppear {
|
||||||
|
selection = items.first!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ThumbnailPickerView {
|
||||||
|
|
||||||
|
struct Item: Identifiable {
|
||||||
|
let id = UUID()
|
||||||
|
let name: String
|
||||||
|
let description: String
|
||||||
|
let thumbnail: AnyView
|
||||||
|
|
||||||
|
init<ViewType: View>(name: String, description: String, thumbnail: ViewType) {
|
||||||
|
self.name = name
|
||||||
|
self.description = description
|
||||||
|
self.thumbnail = AnyView(thumbnail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 12.0, *)
|
||||||
|
struct SystemBackgroundView: View {
|
||||||
|
|
||||||
|
let anchor: UnitPoint
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if let mainScreen = NSScreen.main, let imageURL = NSWorkspace.shared.desktopImageURL(for: mainScreen) {
|
||||||
|
AsyncImage(url: imageURL) { phase in
|
||||||
|
switch phase {
|
||||||
|
case .empty, .failure:
|
||||||
|
Rectangle()
|
||||||
|
.foregroundColor(Color(.systemPurple))
|
||||||
|
case .success(let image):
|
||||||
|
image
|
||||||
|
.resizable()
|
||||||
|
.scaleEffect(3, anchor: anchor)
|
||||||
|
.clipped()
|
||||||
|
@unknown default:
|
||||||
|
Rectangle()
|
||||||
|
.foregroundColor(Color(.systemPurple))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Rectangle()
|
||||||
|
.foregroundColor(Color(.systemPurple))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 12.0, *)
|
||||||
|
struct AuthenticationView: View {
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
SystemBackgroundView(anchor: .center)
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "touchid")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: 100)
|
||||||
|
.foregroundColor(Color(.systemRed))
|
||||||
|
Spacer()
|
||||||
|
Text("Touch ID Prompt")
|
||||||
|
.font(.largeTitle)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.redacted(reason: .placeholder)
|
||||||
|
Spacer()
|
||||||
|
VStack {
|
||||||
|
Text("Touch ID Detail prompt.Detail two.")
|
||||||
|
.font(.title3)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
Text("Touch ID Detail prompt.Detail two.")
|
||||||
|
.font(.title3)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
}
|
||||||
|
.redacted(reason: .placeholder)
|
||||||
|
Spacer()
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.frame(width: 275, height: 40, alignment: .center)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.frame(width: 275, height: 40, alignment: .center)
|
||||||
|
.foregroundColor(Color(.unemphasizedSelectedContentBackgroundColor))
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 15)
|
||||||
|
.foregroundStyle(.ultraThickMaterial)
|
||||||
|
)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 12.0, *)
|
||||||
|
struct NotificationView: View {
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
SystemBackgroundView(anchor: .topTrailing)
|
||||||
|
VStack {
|
||||||
|
Rectangle()
|
||||||
|
.background(Color.clear)
|
||||||
|
.foregroundStyle(.thinMaterial)
|
||||||
|
.frame(height: 35)
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
HStack {
|
||||||
|
Image(nsImage: NSApplication.shared.applicationIconImage)
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 64, height: 64)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.padding()
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Secretive")
|
||||||
|
.font(.largeTitle)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
Text("Secretive wants to sign some request")
|
||||||
|
.font(.title3)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
Text("Secretive wants to sign some request")
|
||||||
|
.font(.title3)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.redacted(reason: .placeholder)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 15)
|
||||||
|
.foregroundStyle(.ultraThickMaterial)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
|
||||||
|
struct CreateSecretView_Previews: PreviewProvider {
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
Group {
|
||||||
|
CreateSecretView(store: Preview.StoreModifiable(), showing: .constant(true))
|
||||||
|
if #available(macOS 12.0, *) {
|
||||||
|
AuthenticationView().environment(\.colorScheme, .dark)
|
||||||
|
AuthenticationView().environment(\.colorScheme, .light)
|
||||||
|
NotificationView().environment(\.colorScheme, .dark)
|
||||||
|
NotificationView().environment(\.colorScheme, .light)
|
||||||
|
} else {
|
||||||
|
// Fallback on earlier versions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Reference in New Issue
Block a user