This commit is contained in:
Max Goedjen
2025-09-01 14:52:17 -07:00
parent fa658646d7
commit 9299bf343f
10 changed files with 321 additions and 137 deletions

View File

@@ -32,14 +32,19 @@ struct ConfigurationItemView<Content: View>: View {
Spacer()
switch action {
case .copy(let string):
Button("Reveal in Finder", systemImage: "folder") {
NSWorkspace.shared.selectFile(string, inFileViewerRootedAtPath: string)
Button("Copy", systemImage: "document.on.document") {
NSPasteboard.general.declareTypes([.string], owner: nil)
NSPasteboard.general.setString(string, forType: .string)
}
.labelStyle(.iconOnly)
.buttonStyle(.borderless)
case .revealInFinder(let string):
case .revealInFinder(let rawPath):
Button("Reveal in Finder", systemImage: "folder") {
NSWorkspace.shared.selectFile(string, inFileViewerRootedAtPath: string)
// All foundation-based normalization methods replace this with the container directly.
let processedPath = rawPath.replacingOccurrences(of: "~", with: "/Users/\(NSUserName())")
let url = URL(filePath: processedPath)
let folder = url.deletingLastPathComponent().path()
NSWorkspace.shared.selectFile(processedPath, inFileViewerRootedAtPath: folder)
}
.labelStyle(.iconOnly)
.buttonStyle(.borderless)

View File

@@ -1,82 +0,0 @@
import SwiftUI
struct ConfigurationView: View {
@Binding var visible: Bool
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 {
Form {
Section {
Picker("Configuring", selection: $selectedShellInstruction) {
ForEach(shellInstructions) { instruction in
Text(instruction.shell)
.tag(instruction)
.padding()
}
}
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

@@ -36,7 +36,7 @@ struct ContentView: View {
toolbarItem(newItemView, id: "new")
}
.sheet(isPresented: $runningSetup) {
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
SetupView(setupComplete: $hasRunSetup)
}
}
@@ -109,7 +109,7 @@ extension ContentView {
.normal()
.sheet(isPresented: $showingCreation) {
if let modifiable = storeList.modifiableStore {
CreateSecretView(store: modifiable, showing: $showingCreation) { created in
CreateSecretView(store: modifiable) { created in
if let created {
activeSecret = created
}

View File

@@ -4,7 +4,7 @@ import SecretKit
struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
@State var store: StoreType
@Binding var showing: Bool
@Environment(\.dismiss) private var dismiss
var createdSecret: (AnySecret?) -> Void
@State private var name = ""
@@ -109,7 +109,7 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
.toggleStyle(.button)
Spacer()
Button(.createSecretCancelButton, role: .cancel) {
showing = false
dismiss()
}
Button(.createSecretCreateButton, action: save)
.keyboardShortcut(.return)
@@ -137,7 +137,7 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
)
)
createdSecret(AnySecret(new))
showing = false
dismiss()
} catch {
errorText = error.localizedDescription
}
@@ -147,5 +147,5 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
}
#Preview {
CreateSecretView(store: Preview.StoreModifiable(), showing: .constant(true)) { _ in }
CreateSecretView(store: Preview.StoreModifiable()) { _ in }
}

View File

@@ -0,0 +1,192 @@
import SwiftUI
struct IntegrationsView: View {
@Environment(\.dismiss) private var dismiss
@State private var selectedInstruction: ConfigurationFileInstructions?
private let socketPath = (NSHomeDirectory().replacingOccurrences(of: Bundle.hostBundleID, with: Bundle.agentBundleID) as NSString).appendingPathComponent("socket.ssh") as String
private var instructions: [ConfigurationGroup] {
[
ConfigurationGroup(name:"Integrations", instructions: [
ConfigurationFileInstructions("Getting Started", id: .gettingStarted),
]),
ConfigurationGroup(name: "System", instructions: [
ConfigurationFileInstructions(
tool: "ssh",
configPath: "~/.ssh/config",
configText: "Host *\n\tIdentityAgent \(socketPath)",
website: URL(string: "https://man.openbsd.org/ssh_config.5")!,
),
ConfigurationFileInstructions(
tool: "git",
configPath: "~/.gitconfig",
configText: "PLACEHOLDER",
website: URL(string: "https://git-scm.com/docs/git-config")!
)
]),
ConfigurationGroup(name: "Shell", instructions: [
ConfigurationFileInstructions(
tool: "zsh",
configPath: "~/.zshrc",
configText: "export SSH_AUTH_SOCK=\(socketPath)"
),
ConfigurationFileInstructions(
tool: "bash",
configPath: "~/.bashrc",
configText: "export SSH_AUTH_SOCK=\(socketPath)"
),
ConfigurationFileInstructions(
tool: "fish",
configPath: "~/.config/fish/config.fish",
configText: "set -x SSH_AUTH_SOCK \(socketPath)"
),
ConfigurationFileInstructions("other", id: .otherShell),
]),
ConfigurationGroup(name:"Apps", instructions: [
ConfigurationFileInstructions("Other", id: .otherApp),
]),
]
}
var body: some View {
NavigationSplitView {
List(selection: $selectedInstruction) {
ForEach(instructions) { group in
Section(group.name) {
ForEach(group.instructions) { instruction in
Text(instruction.tool)
.padding(.vertical, 8)
.tag(instruction)
}
}
}
}
} detail: {
if let selectedInstruction {
Form {
switch selectedInstruction.id {
case .gettingStarted:
Text("TBD")
case .tool:
Section(selectedInstruction.tool) {
ConfigurationItemView(title: "Configuration File", value: selectedInstruction.configPath, action: .revealInFinder( selectedInstruction.configPath))
ConfigurationItemView(title: "Add This:", action: .copy(selectedInstruction.configText)) {
HStack {
Text(selectedInstruction.configText)
.padding(8)
.font(.system(.subheadline, design: .monospaced))
Spacer()
}
.frame(maxWidth: .infinity)
.background {
RoundedRectangle(cornerRadius: 6)
.fill(.black.opacity(0.05))
.stroke(.separator, lineWidth: 1)
}
}
}
if let url = selectedInstruction.website {
Section {
Link(destination: url) {
VStack(alignment: .leading, spacing: 5) {
Text("View Documentation on Web")
.font(.headline)
Text(url.absoluteString)
.font(.caption2)
}
}
}
}
case .otherShell:
Section {
Link("View on GitHub", destination: URL(string: "https://github.com/maxgoedjen/secretive-config-instructions/tree/main/shells")!)
} header: {
Text("There's a community-maintained list of shell instructions on GitHub. If the shell you're looking for isn't supported, create an issue and the community may be able to help.")
.font(.body)
}
case .otherApp:
Section {
Link("View on GitHub", destination: URL(string: "https://github.com/maxgoedjen/secretive-config-instructions/tree/main/apps")!)
} header: {
Text("There's a community-maintained list of instructions for apps on GitHub. If the app you're looking for isn't supported, create an issue and the community may be able to help.")
.font(.body)
}
}
}
.formStyle(.grouped)
}
}
.onAppear {
selectedInstruction = instructions.first?.instructions.first
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Done") {
dismiss()
}
.styled
}
}
}
}
struct ConfigurationGroup: Identifiable {
let id = UUID()
var name: LocalizedStringResource
var instructions: [ConfigurationFileInstructions] = []
}
struct ConfigurationFileInstructions: Identifiable, Hashable {
var id: ID
var tool: String
var configPath: String
var configText: String
var website: URL?
init(tool: String, configPath: String, configText: String, website: URL? = nil) {
self.id = .tool(tool)
self.tool = tool
self.configPath = configPath
self.configText = configText
self.website = website
}
init(_ name: LocalizedStringResource, id: ID) {
self.id = id
tool = String(localized: name)
configPath = ""
configText = ""
}
enum ID: Identifiable, Hashable {
case gettingStarted
case tool(String)
case otherShell
case otherApp
var id: String {
switch self {
case .gettingStarted:
"getting_started"
case .tool(let name):
name
case .otherShell:
"other_shell"
case .otherApp:
"other_app"
}
}
}
}
#Preview {
IntegrationsView()
.frame(height: 500)
}

View File

@@ -23,12 +23,6 @@ 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()

View File

@@ -2,7 +2,7 @@ import SwiftUI
struct SetupView: View {
@Binding var visible: Bool
@Environment(\.dismiss) private var dismiss
@Binding var setupComplete: Bool
@State var installed = false
@@ -12,7 +12,7 @@ struct SetupView: View {
var body: some View {
VStack {
VStack(spacing: 0) {
NewStepView(
StepView(
title: "setup_agent_title",
description: "setup_agent_description",
systemImage: "lock.laptopcomputer",
@@ -24,7 +24,7 @@ struct SetupView: View {
}
}
Divider()
NewStepView(
StepView(
title: "setup_updates_title",
description: "setup_updates_description",
systemImage: "network.badge.shield.half.filled",
@@ -36,7 +36,7 @@ struct SetupView: View {
}
}
Divider()
NewStepView(
StepView(
title: "setup_ssh_title",
description: "setup_ssh_description",
systemImage: "network.badge.shield.half.filled",
@@ -101,7 +101,7 @@ extension View {
}
struct NewStepView<Content: View>: View {
struct StepView<Content: View>: View {
let title: LocalizedStringResource
let icon: Image
@@ -141,27 +141,7 @@ extension SetupView {
}
struct ShellConfigInstruction: Identifiable, Hashable {
var shell: String
var shellConfigDirectory: String
var shellConfigFilename: String
var text: String
var id: String {
shell
}
var shellConfigPath: String {
return (shellConfigDirectory as NSString).appendingPathComponent(shellConfigFilename)
}
}
#Preview {
SetupView(visible: .constant(true), setupComplete: .constant(false))
SetupView(setupComplete: .constant(false))
}
//#Preview {
// SSHAgentSetupView(buttonAction: {})
//}