From f60a44c599f1e76c9064202a3ceda9ba776d39b8 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sat, 30 Aug 2025 13:55:19 -0700 Subject: [PATCH] WIP --- Sources/Packages/Localizable.xcstrings | 14 ++- .../Secretive/Views/ConfigurationView.swift | 12 +- Sources/Secretive/Views/ContentView.swift | 2 +- .../Secretive/Views/CreateSecretView.swift | 1 + Sources/Secretive/Views/EditSecretView.swift | 7 +- Sources/Secretive/Views/SetupView.swift | 104 ++++++++++++------ Sources/Secretive/Views/UpdateView.swift | 73 ++++++++---- 7 files changed, 151 insertions(+), 62 deletions(-) diff --git a/Sources/Packages/Localizable.xcstrings b/Sources/Packages/Localizable.xcstrings index fd856c6..403b721 100644 --- a/Sources/Packages/Localizable.xcstrings +++ b/Sources/Packages/Localizable.xcstrings @@ -1,6 +1,9 @@ { "sourceLanguage" : "en", "strings" : { + ".zshrc" : { + + }, "Add Automatically" : { }, @@ -1163,6 +1166,9 @@ } } } + }, + "Configure" : { + }, "copyable_click_to_copy_button" : { "extractionState" : "manual", @@ -1500,7 +1506,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "This shows at the end of your public key." + "value" : "This shows at the end of your public key. It’s usually an email address." } } } @@ -2483,6 +2489,9 @@ } } } + }, + "Done" : { + }, "edit_cancel_button" : { "extractionState" : "manual", @@ -5195,6 +5204,9 @@ } } } + }, + "TfileDialogMessageest" : { + }, "unnamed_secret" : { "extractionState" : "manual", diff --git a/Sources/Secretive/Views/ConfigurationView.swift b/Sources/Secretive/Views/ConfigurationView.swift index c69f0b1..6d9eef7 100644 --- a/Sources/Secretive/Views/ConfigurationView.swift +++ b/Sources/Secretive/Views/ConfigurationView.swift @@ -11,7 +11,11 @@ struct ConfigurationView: View { var body: some View { VStack(spacing: 0) { - NewStepView(title: "setup_agent_title", description: "setup_agent_description") { + 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() @@ -22,7 +26,11 @@ struct ConfigurationView: View { } Divider() Divider() - NewStepView(title: "setup_ssh_title", description: "setup_ssh_description") { + 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 diff --git a/Sources/Secretive/Views/ContentView.swift b/Sources/Secretive/Views/ContentView.swift index dfc6dff..899d12b 100644 --- a/Sources/Secretive/Views/ContentView.swift +++ b/Sources/Secretive/Views/ContentView.swift @@ -94,7 +94,7 @@ extension ContentView { .foregroundColor(.white) }) .buttonStyle(ToolbarButtonStyle(color: color)) - .popover(item: $selectedUpdate, attachmentAnchor: attachmentAnchor, arrowEdge: .bottom) { update in + .sheet(item: $selectedUpdate) { update in UpdateDetailView(update: update) } } diff --git a/Sources/Secretive/Views/CreateSecretView.swift b/Sources/Secretive/Views/CreateSecretView.swift index b5f17b5..7173042 100644 --- a/Sources/Secretive/Views/CreateSecretView.swift +++ b/Sources/Secretive/Views/CreateSecretView.swift @@ -103,6 +103,7 @@ struct CreateSecretView: View { showing = false } Button(.createSecretCreateButton, action: save) + .keyboardShortcut(.return) .primary() .disabled(name.isEmpty) } diff --git a/Sources/Secretive/Views/EditSecretView.swift b/Sources/Secretive/Views/EditSecretView.swift index 606b130..12be6ad 100644 --- a/Sources/Secretive/Views/EditSecretView.swift +++ b/Sources/Secretive/Views/EditSecretView.swift @@ -38,13 +38,14 @@ struct EditSecretView: View { } } HStack { - Button(.editSaveButton, action: rename) - .disabled(name.isEmpty) - .keyboardShortcut(.return) Button(.editCancelButton) { dismissalBlock(false) } .keyboardShortcut(.cancelAction) + Button(.editSaveButton, action: rename) + .disabled(name.isEmpty) + .keyboardShortcut(.return) + .primary() } .padding() } diff --git a/Sources/Secretive/Views/SetupView.swift b/Sources/Secretive/Views/SetupView.swift index 0338244..030caf3 100644 --- a/Sources/Secretive/Views/SetupView.swift +++ b/Sources/Secretive/Views/SetupView.swift @@ -10,44 +10,73 @@ struct SetupView: View { @State var sshConfig = false var body: some View { - VStack(spacing: 0) { - NewStepView(title: "setup_agent_title", description: "setup_agent_description") { - OnboardingButton("setup_agent_install_button", installed) { - Task { - await LaunchAgentController().install() - installed = true + VStack { + VStack(spacing: 0) { + NewStepView( + title: "setup_agent_title", + description: "setup_agent_description", + systemImage: "lock.laptopcomputer", + ) { + OnboardingButton("setup_agent_install_button", installed) { + Task { + await LaunchAgentController().install() + installed = true + } + } + } + Divider() + NewStepView( + title: "setup_updates_title", + description: "setup_updates_description", + systemImage: "network.badge.shield.half.filled", + ) { + OnboardingButton("setup_updates_ok", false) { + Task { + updates = true + } + } + } + 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 + } + .fileImporter(isPresented: $sshConfig, allowedContentTypes: [.utf8PlainText, .symbolicLink, .data]) { result in + print(result) + } + // FIXME: + .fileDialogDefaultDirectory(URL(fileURLWithPath: "/Users/max/")) + .fileDialogBrowserOptions([.displayFileExtensions, .includeHiddenFiles]) + .fileExporterFilenameLabel(Text(".zshrc")) + .fileDialogMessage("TfileDialogMessageest") + .fileDialogConfirmationLabel("Configure") + .fileDialogURLEnabled(#Predicate { + return $0.lastPathComponent == ".zshrc" || $0.hasDirectoryPath + }) } } } - Divider() - NewStepView(title: "setup_updates_title", description: "setup_updates_description") { - OnboardingButton("setup_updates_ok", false) { - Task { - updates = true - } - } - } - Divider() - NewStepView(title: "setup_ssh_title", description: "setup_ssh_description") { - 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: 700, maxWidth: .infinity) + HStack { + Spacer() + Button("Done") {} + .styled } } - .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) - .frame(minWidth: 500, idealWidth: 500, minHeight: 500, idealHeight: 500) - .padding() - + .padding() } - } struct OnboardingButton: View { @@ -94,23 +123,28 @@ extension View { struct NewStepView: View { let title: LocalizedStringResource + let icon: Image let description: LocalizedStringResource let actions: Content - init(title: LocalizedStringResource, description: LocalizedStringResource, actions: () -> Content) { + init(title: LocalizedStringResource, description: LocalizedStringResource, systemImage: String, actions: () -> Content) { self.title = title + self.icon = Image(systemName: systemImage) self.description = description self.actions = actions() } var body: some View { - HStack { + HStack(spacing: 20) { + icon + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 24) VStack(alignment: .leading, spacing: 6) { Text(title) .bold() Text(description) } - Spacer(minLength: 20) actions } .padding(20) diff --git a/Sources/Secretive/Views/UpdateView.swift b/Sources/Secretive/Views/UpdateView.swift index 810e0e8..2540bda 100644 --- a/Sources/Secretive/Views/UpdateView.swift +++ b/Sources/Secretive/Views/UpdateView.swift @@ -12,7 +12,7 @@ struct UpdateDetailView: View { Text(.updateVersionName(updateName: update.name)).font(.title) GroupBox(label: Text(.updateReleaseNotesTitle)) { ScrollView { - attributedBody + Text(attributedBody) } } HStack { @@ -35,29 +35,62 @@ struct UpdateDetailView: View { .frame(maxWidth: 500) } - var attributedBody: Text { - var text = Text(verbatim: "") - for line in update.body.split(whereSeparator: \.isNewline) { - let attributed: Text - let split = line.split(separator: " ") - let unprefixed = split.dropFirst().joined(separator: " ") - if let prefix = split.first { - switch prefix { - case "#": - attributed = Text(unprefixed).font(.title) + Text(verbatim: "\n") - case "##": - attributed = Text(unprefixed).font(.title2) + Text(verbatim: "\n") - case "###": - attributed = Text(unprefixed).font(.title3) + Text(verbatim: "\n") + var attributedBody: AttributedString { + do { + var text = try AttributedString( + markdown: update.body, + options: .init( + allowsExtendedAttributes: true, + interpretedSyntax: .full, + ), + baseURL: URL(string: "https://github.com/maxgoedjen/secretive")! + ) + .transformingAttributes(AttributeScopes.FoundationAttributes.PresentationIntentAttribute.self) { key in + let font: Font? = switch key.value?.components.first?.kind { + case .header(level: 1): + Font.title + case .header(level: 2): + Font.title2 + case .header(level: 3): + Font.title3 default: - attributed = Text(line) + Text(verbatim: "\n\n") + nil + } + if let font { + key.replace(with: AttributeScopes.SwiftUIAttributes.FontAttribute.self, value: font) } - } else { - attributed = Text(line) + Text(verbatim: "\n\n") } - text = text + attributed + let lineBreak = AttributedString("\n\n") + for run in text.runs.reversed() { + text.insert(lineBreak, at: run.range.lowerBound) + } + return text + } catch { + var text = AttributedString() + for line in update.body.split(whereSeparator: \.isNewline) { + let attributed: AttributedString + let split = line.split(separator: " ") + let unprefixed = split.dropFirst().joined(separator: " ") + if let prefix = split.first { + var container = AttributeContainer() + switch prefix { + case "#": + container.font = .title + case "##": + container.font = .title2 + case "###": + container.font = .title3 + default: + continue + } + attributed = AttributedString(unprefixed, attributes: container) + } else { + attributed = AttributedString(line + "\n\n") + } + text = text + attributed + } + return text } - return text } }