This commit is contained in:
Max Goedjen 2025-08-31 13:24:37 -07:00
parent cd76bb95ec
commit fa658646d7
No known key found for this signature in database
9 changed files with 173 additions and 155 deletions

View File

@ -1,7 +1,10 @@
{ {
"sourceLanguage" : "en", "sourceLanguage" : "en",
"strings" : { "strings" : {
"Add Automatically" : { "" : {
},
"Add This:" : {
}, },
"agent_not_running_notice_detail_description" : { "agent_not_running_notice_detail_description" : {
@ -1174,11 +1177,14 @@
} }
} }
} }
},
"Configuration File" : {
}, },
"Configure" : { "Configure" : {
}, },
"Copy" : { "Configuring" : {
}, },
"copyable_click_to_copy_button" : { "copyable_click_to_copy_button" : {
@ -3858,6 +3864,9 @@
} }
} }
} }
},
"Secretive was unable to get SecretAgent to launch. Please try restarting your Mac, and if that doesn't work, file an issue on GitHub." : {
}, },
"secure_enclave" : { "secure_enclave" : {
"extractionState" : "manual", "extractionState" : "manual",
@ -5233,6 +5242,9 @@
}, },
"Start Agent" : { "Start Agent" : {
},
"Starting Agent" : {
}, },
"unnamed_secret" : { "unnamed_secret" : {
"extractionState" : "manual", "extractionState" : "manual",

View File

@ -53,6 +53,7 @@
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; }; 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
50BDCB722E63BAF20072D2E7 /* AgentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB712E63BAF20072D2E7 /* AgentStatusView.swift */; }; 50BDCB722E63BAF20072D2E7 /* AgentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB712E63BAF20072D2E7 /* AgentStatusView.swift */; };
50BDCB742E6436CA0072D2E7 /* ErrorStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB732E6436C60072D2E7 /* ErrorStyle.swift */; }; 50BDCB742E6436CA0072D2E7 /* ErrorStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB732E6436C60072D2E7 /* ErrorStyle.swift */; };
50BDCB762E6450950072D2E7 /* ConfigurationItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB752E6450950072D2E7 /* ConfigurationItemView.swift */; };
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; }; 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; };
50CF4ABC2E601B0F005588DC /* ActionButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CF4ABB2E601B0F005588DC /* ActionButtonStyle.swift */; }; 50CF4ABC2E601B0F005588DC /* ActionButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CF4ABB2E601B0F005588DC /* ActionButtonStyle.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -144,6 +145,7 @@
50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStoreView.swift; sourceTree = "<group>"; }; 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStoreView.swift; sourceTree = "<group>"; };
50BDCB712E63BAF20072D2E7 /* AgentStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusView.swift; sourceTree = "<group>"; }; 50BDCB712E63BAF20072D2E7 /* AgentStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusView.swift; sourceTree = "<group>"; };
50BDCB732E6436C60072D2E7 /* ErrorStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorStyle.swift; sourceTree = "<group>"; }; 50BDCB732E6436C60072D2E7 /* ErrorStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorStyle.swift; sourceTree = "<group>"; };
50BDCB752E6450950072D2E7 /* ConfigurationItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationItemView.swift; sourceTree = "<group>"; };
50C385A42407A76D00AF2719 /* SecretDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretDetailView.swift; sourceTree = "<group>"; }; 50C385A42407A76D00AF2719 /* SecretDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretDetailView.swift; sourceTree = "<group>"; };
50CF4ABB2E601B0F005588DC /* ActionButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtonStyle.swift; sourceTree = "<group>"; }; 50CF4ABB2E601B0F005588DC /* ActionButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtonStyle.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -262,6 +264,7 @@
50AE96FF2E5C1A420018C710 /* ConfigurationView.swift */, 50AE96FF2E5C1A420018C710 /* ConfigurationView.swift */,
5066A6C72516FE6E004B5A36 /* CopyableView.swift */, 5066A6C72516FE6E004B5A36 /* CopyableView.swift */,
50BDCB732E6436C60072D2E7 /* ErrorStyle.swift */, 50BDCB732E6436C60072D2E7 /* ErrorStyle.swift */,
50BDCB752E6450950072D2E7 /* ConfigurationItemView.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -460,6 +463,7 @@
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */, 5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */, 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */, 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
50BDCB762E6450950072D2E7 /* ConfigurationItemView.swift in Sources */,
50617D8323FCE48E0099B055 /* App.swift in Sources */, 50617D8323FCE48E0099B055 /* App.swift in Sources */,
506772C92425BB8500034DED /* NoStoresView.swift in Sources */, 506772C92425BB8500034DED /* NoStoresView.swift in Sources */,
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */, 50153E22250DECA300525160 /* SecretListItemView.swift in Sources */,

View File

@ -23,6 +23,28 @@ extension View {
} }
struct NormalButtonModifier: ViewModifier {
func body(content: Content) -> some View {
if #available(macOS 26.0, *) {
content
.glassEffect(.regular.tint(.white.opacity(0.1)), in: .circle)
} else {
content
.buttonStyle(.borderless)
}
}
}
extension View {
func normal() -> some View {
modifier(NormalButtonModifier())
}
}
struct DangerButtonModifier: ViewModifier { struct DangerButtonModifier: ViewModifier {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme

View File

@ -21,22 +21,22 @@ struct AgentRunningView: View {
Form { Form {
Section { Section {
if let process = agentStatusChecker.process { if let process = agentStatusChecker.process {
AgentInformationView( ConfigurationItemView(
title: "Secret Agent Location", title: "Secret Agent Location",
value: process.bundleURL!.path(), value: process.bundleURL!.path(),
actions: [.revealInFinder], action: .revealInFinder(process.bundleURL!.path()),
) )
AgentInformationView( ConfigurationItemView(
title: "Socket Path", title: "Socket Path",
value: socketPath, value: socketPath,
actions: [.copy], action: .copy(socketPath),
) )
AgentInformationView( ConfigurationItemView(
title: "Version", title: "Version",
value: Bundle(url: process.bundleURL!)!.infoDictionary!["CFBundleShortVersionString"] as! String value: Bundle(url: process.bundleURL!)!.infoDictionary!["CFBundleShortVersionString"] as! String
) )
if let launchDate = process.launchDate { if let launchDate = process.launchDate {
AgentInformationView( ConfigurationItemView(
title: "Running Since", title: "Running Since",
value: launchDate.formatted() value: launchDate.formatted()
) )
@ -144,50 +144,6 @@ struct AgentNotRunningView: View {
} }
struct AgentInformationView: View {
enum Action {
case copy
case revealInFinder
}
let title: LocalizedStringResource
let value: String
let actions: Set<Action>
@State var tapping = false
init(title: LocalizedStringResource, value: String, actions: Set<Action> = []) {
self.title = title
self.value = value
self.actions = actions
}
var body: some View {
VStack(alignment: .leading) {
HStack {
Text(title)
Spacer()
if actions.contains(.revealInFinder) {
Button("Reveal in Finder", systemImage: "folder") {
NSWorkspace.shared.selectFile(value, inFileViewerRootedAtPath: value)
}
.labelStyle(.iconOnly)
.buttonStyle(.borderless)
}
if actions.contains(.copy) {
Button("Copy", systemImage: "document.on.document") {
}
.labelStyle(.iconOnly)
.buttonStyle(.borderless)
}
}
Text(value)
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
}
#Preview { #Preview {
AgentStatusView() AgentStatusView()
.environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: false)) .environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: false))

View File

@ -0,0 +1,54 @@
import SwiftUI
struct ConfigurationItemView<Content: View>: View {
enum Action: Hashable {
case copy(String)
case revealInFinder(String)
}
let title: LocalizedStringResource
let content: Content
let action: Action?
init(title: LocalizedStringResource, value: String, action: Action? = nil) where Content == Text {
self.title = title
self.content = Text(value)
.font(.subheadline)
.foregroundStyle(.secondary)
self.action = action
}
init(title: LocalizedStringResource, action: Action? = nil, content: () -> Content) {
self.title = title
self.content = content()
self.action = action
}
var body: some View {
VStack(alignment: .leading) {
HStack {
Text(title)
Spacer()
switch action {
case .copy(let string):
Button("Reveal in Finder", systemImage: "folder") {
NSWorkspace.shared.selectFile(string, inFileViewerRootedAtPath: string)
}
.labelStyle(.iconOnly)
.buttonStyle(.borderless)
case .revealInFinder(let string):
Button("Reveal in Finder", systemImage: "folder") {
NSWorkspace.shared.selectFile(string, inFileViewerRootedAtPath: string)
}
.labelStyle(.iconOnly)
.buttonStyle(.borderless)
case nil:
EmptyView()
}
}
content
}
}
}

View File

@ -4,52 +4,79 @@ struct ConfigurationView: View {
@Binding var visible: Bool @Binding var visible: Bool
@State var running = true
@State var sshConfig = false
@Environment(\.agentStatusChecker) var agentStatusChecker let buttonAction: () -> Void
@State private var selectedShellInstruction: ShellConfigInstruction?
private let socketPath = (NSHomeDirectory().replacingOccurrences(of: Bundle.hostBundleID, with: Bundle.agentBundleID) as NSString).appendingPathComponent("socket.ssh") as String
private var shellInstructions: [ShellConfigInstruction] {
[
ShellConfigInstruction(shell: "SSH",
shellConfigDirectory: "~/.ssh/",
shellConfigFilename: "config",
text: "Host *\n\tIdentityAgent \(socketPath)"),
ShellConfigInstruction(shell: "zsh",
shellConfigDirectory: "~/",
shellConfigFilename: ".zshrc",
text: "export SSH_AUTH_SOCK=\(socketPath)"),
ShellConfigInstruction(shell: "bash",
shellConfigDirectory: "~/",
shellConfigFilename: ".bashrc",
text: "export SSH_AUTH_SOCK=\(socketPath)"),
ShellConfigInstruction(shell: "fish",
shellConfigDirectory: "~/.config/fish",
shellConfigFilename: "config.fish",
text: "set -x SSH_AUTH_SOCK \(socketPath)"),
]
}
var body: some View { var body: some View {
VStack(spacing: 0) { Form {
NewStepView( Section {
title: "setup_agent_title", Picker("Configuring", selection: $selectedShellInstruction) {
description: "setup_agent_description", ForEach(shellInstructions) { instruction in
systemImage: "network.badge.shield.half.filled", Text(instruction.shell)
) { .tag(instruction)
OnboardingButton("setup_agent_install_button", running) { .padding()
Task {
_ = await LaunchAgentController().forceLaunch()
agentStatusChecker.check()
running = agentStatusChecker.running
} }
} }
} if let selectedShellInstruction {
Divider() ConfigurationItemView(title: "Configuration File", value: selectedShellInstruction.shellConfigPath, action: .revealInFinder(selectedShellInstruction.shellConfigPath))
Divider() ConfigurationItemView(title: "Add This:", action: .copy(selectedShellInstruction.text)) {
NewStepView( HStack {
title: "setup_ssh_title", Text(selectedShellInstruction.text)
description: "setup_ssh_description", .padding(8)
systemImage: "network.badge.shield.half.filled", .font(.system(.subheadline, design: .monospaced))
) { Spacer()
HStack { }
OnboardingButton("setup_ssh_added_manually_button", false) { .frame(maxWidth: .infinity)
sshConfig = true .background {
RoundedRectangle(cornerRadius: 6)
.fill(.black.opacity(0.05))
.stroke(.separator, lineWidth: 1)
}
} }
OnboardingButton("Add Automatically", false) { Button("setup_ssh_add_for_me_button") {
// let controller = ShellConfigurationController()
// if controller.addToShell(shellInstructions: selectedShellInstruction) {
// }
sshConfig = true
} }
} }
} footer: {
Link("setup_third_party_faq_link", destination: URL(string: "https://github.com/maxgoedjen/secretive/blob/main/APP_CONFIG.md")!)
} }
} }
.background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) .formStyle(.grouped)
.frame(minWidth: 500, idealWidth: 500, minHeight: 500, idealHeight: 500) .onAppear {
.padding() selectedShellInstruction = shellInstructions.first
.task {
running = agentStatusChecker.running
} }
// }
} }
} }
#Preview {
ConfigurationView(visible: .constant(true)) {}
.frame(width: 400, height: 300)
}

View File

@ -103,11 +103,10 @@ extension ContentView {
@ViewBuilder @ViewBuilder
var newItemView: some View { var newItemView: some View {
if storeList.modifiableStore?.isAvailable ?? false { if storeList.modifiableStore?.isAvailable ?? false {
Button(action: { Button(.appMenuNewSecretButton, systemImage: "plus") {
showingCreation = true showingCreation = true
}, label: { }
Image(systemName: "plus") .normal()
})
.sheet(isPresented: $showingCreation) { .sheet(isPresented: $showingCreation) {
if let modifiable = storeList.modifiableStore { if let modifiable = storeList.modifiableStore {
CreateSecretView(store: modifiable, showing: $showingCreation) { created in CreateSecretView(store: modifiable, showing: $showingCreation) { created in

View File

@ -23,6 +23,12 @@ struct SecretDetailView<SecretType: Secret>: View {
.frame(height: 20) .frame(height: 20)
CopyableView(title: .secretDetailPublicKeyPathLabel, image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.publicKeyPath(for: secret)) CopyableView(title: .secretDetailPublicKeyPathLabel, image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.publicKeyPath(for: secret))
Spacer() Spacer()
} header: {
Text(verbatim: secret.name)
.font(.system(size: 16, weight: .bold, design: .default))
.foregroundStyle(.secondary)
.padding(.leading)
.padding(.bottom)
} }
} }
.padding() .padding()
@ -45,12 +51,6 @@ extension URL {
} }
#if DEBUG #Preview {
SecretDetailView(secret: Preview.Secret(name: "Demonstration Secret"))
struct SecretDetailView_Previews: PreviewProvider {
static var previews: some View {
SecretDetailView(secret: Preview.Store(numberOfRandomSecrets: 1).secrets[0])
}
} }
#endif

View File

@ -133,62 +133,6 @@ struct NewStepView<Content: View>: View {
} }
//struct SSHAgentSetupView: View {
//
// let buttonAction: () -> Void
//
// @State private var selectedShellInstruction: ShellConfigInstruction?
//
// private let socketPath = (NSHomeDirectory().replacingOccurrences(of: Bundle.hostBundleID, with: Bundle.agentBundleID) as NSString).appendingPathComponent("socket.ssh") as String
//
// private var shellInstructions: [ShellConfigInstruction] {
// [
// ShellConfigInstruction(shell: "global",
// shellConfigDirectory: "~/.ssh/",
// shellConfigFilename: "config",
// text: "Host *\n\tIdentityAgent \(socketPath)"),
// ShellConfigInstruction(shell: "zsh",
// shellConfigDirectory: "~/",
// shellConfigFilename: ".zshrc",
// text: "export SSH_AUTH_SOCK=\(socketPath)"),
// ShellConfigInstruction(shell: "bash",
// shellConfigDirectory: "~/",
// shellConfigFilename: ".bashrc",
// text: "export SSH_AUTH_SOCK=\(socketPath)"),
// ShellConfigInstruction(shell: "fish",
// shellConfigDirectory: "~/.config/fish",
// shellConfigFilename: "config.fish",
// text: "set -x SSH_AUTH_SOCK \(socketPath)"),
// ]
//
// }
//
// var body: some View {
// SetupStepView(title: "setup_ssh_title",
// image: Image(systemName: "terminal"),
// bodyText: "setup_ssh_description",
// buttonTitle: "setup_ssh_added_manually_button",
// buttonAction: buttonAction) {
// Link("setup_third_party_faq_link", destination: URL(string: "https://github.com/maxgoedjen/secretive/blob/main/APP_CONFIG.md")!)
// Picker(selection: $selectedShellInstruction, label: EmptyView()) {
// ForEach(SSHAgentSetupView.controller.shellInstructions) { instruction in
// Text(instruction.shell)
// .tag(instruction)
// .padding()
// }
// }.pickerStyle(SegmentedPickerStyle())
// CopyableView(title: "setup_ssh_add_to_config_button_\(selectedShellInstruction.shellConfigPath)", image: Image(systemName: "greaterthan.square"), text: selectedShellInstruction.text)
// Button("setup_ssh_add_for_me_button") {
// let controller = ShellConfigurationController()
// if controller.addToShell(shellInstructions: selectedShellInstruction) {
// buttonAction()
// }
// }
// }
// }
//
//}
extension SetupView { extension SetupView {
enum Constants { enum Constants {