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",
"strings" : {
"Add Automatically" : {
"" : {
},
"Add This:" : {
},
"agent_not_running_notice_detail_description" : {
@ -1174,11 +1177,14 @@
}
}
}
},
"Configuration File" : {
},
"Configure" : {
},
"Copy" : {
"Configuring" : {
},
"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" : {
"extractionState" : "manual",
@ -5233,6 +5242,9 @@
},
"Start Agent" : {
},
"Starting Agent" : {
},
"unnamed_secret" : {
"extractionState" : "manual",

View File

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

View File

@ -21,22 +21,22 @@ struct AgentRunningView: View {
Form {
Section {
if let process = agentStatusChecker.process {
AgentInformationView(
ConfigurationItemView(
title: "Secret Agent Location",
value: process.bundleURL!.path(),
actions: [.revealInFinder],
action: .revealInFinder(process.bundleURL!.path()),
)
AgentInformationView(
ConfigurationItemView(
title: "Socket Path",
value: socketPath,
actions: [.copy],
action: .copy(socketPath),
)
AgentInformationView(
ConfigurationItemView(
title: "Version",
value: Bundle(url: process.bundleURL!)!.infoDictionary!["CFBundleShortVersionString"] as! String
)
if let launchDate = process.launchDate {
AgentInformationView(
ConfigurationItemView(
title: "Running Since",
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 {
AgentStatusView()
.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
@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 {
VStack(spacing: 0) {
NewStepView(
title: "setup_agent_title",
description: "setup_agent_description",
systemImage: "network.badge.shield.half.filled",
) {
OnboardingButton("setup_agent_install_button", running) {
Task {
_ = await LaunchAgentController().forceLaunch()
agentStatusChecker.check()
running = agentStatusChecker.running
}
}
}
Divider()
Divider()
NewStepView(
title: "setup_ssh_title",
description: "setup_ssh_description",
systemImage: "network.badge.shield.half.filled",
) {
HStack {
OnboardingButton("setup_ssh_added_manually_button", false) {
sshConfig = true
}
OnboardingButton("Add Automatically", false) {
// let controller = ShellConfigurationController()
// if controller.addToShell(shellInstructions: selectedShellInstruction) {
// }
sshConfig = true
}
}
}
}
.background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10))
.frame(minWidth: 500, idealWidth: 500, minHeight: 500, idealHeight: 500)
Form {
Section {
Picker("Configuring", selection: $selectedShellInstruction) {
ForEach(shellInstructions) { instruction in
Text(instruction.shell)
.tag(instruction)
.padding()
.task {
running = agentStatusChecker.running
}
}
if let selectedShellInstruction {
ConfigurationItemView(title: "Configuration File", value: selectedShellInstruction.shellConfigPath, action: .revealInFinder(selectedShellInstruction.shellConfigPath))
ConfigurationItemView(title: "Add This:", action: .copy(selectedShellInstruction.text)) {
HStack {
Text(selectedShellInstruction.text)
.padding(8)
.font(.system(.subheadline, design: .monospaced))
Spacer()
}
.frame(maxWidth: .infinity)
.background {
RoundedRectangle(cornerRadius: 6)
.fill(.black.opacity(0.05))
.stroke(.separator, lineWidth: 1)
}
}
Button("setup_ssh_add_for_me_button") {
}
}
} footer: {
Link("setup_third_party_faq_link", destination: URL(string: "https://github.com/maxgoedjen/secretive/blob/main/APP_CONFIG.md")!)
}
}
.formStyle(.grouped)
.onAppear {
selectedShellInstruction = shellInstructions.first
}
// }
}
}
#Preview {
ConfigurationView(visible: .constant(true)) {}
.frame(width: 400, height: 300)
}

View File

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

View File

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