This commit is contained in:
Max Goedjen 2025-08-30 13:55:19 -07:00
parent 260e63341d
commit f60a44c599
No known key found for this signature in database
7 changed files with 151 additions and 62 deletions

View File

@ -1,6 +1,9 @@
{ {
"sourceLanguage" : "en", "sourceLanguage" : "en",
"strings" : { "strings" : {
".zshrc" : {
},
"Add Automatically" : { "Add Automatically" : {
}, },
@ -1163,6 +1166,9 @@
} }
} }
} }
},
"Configure" : {
}, },
"copyable_click_to_copy_button" : { "copyable_click_to_copy_button" : {
"extractionState" : "manual", "extractionState" : "manual",
@ -1500,7 +1506,7 @@
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "This shows at the end of your public key." "value" : "This shows at the end of your public key. Its usually an email address."
} }
} }
} }
@ -2483,6 +2489,9 @@
} }
} }
} }
},
"Done" : {
}, },
"edit_cancel_button" : { "edit_cancel_button" : {
"extractionState" : "manual", "extractionState" : "manual",
@ -5195,6 +5204,9 @@
} }
} }
} }
},
"TfileDialogMessageest" : {
}, },
"unnamed_secret" : { "unnamed_secret" : {
"extractionState" : "manual", "extractionState" : "manual",

View File

@ -11,7 +11,11 @@ struct ConfigurationView: View {
var body: some View { var body: some View {
VStack(spacing: 0) { 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) { OnboardingButton("setup_agent_install_button", running) {
Task { Task {
_ = await LaunchAgentController().forceLaunch() _ = await LaunchAgentController().forceLaunch()
@ -22,7 +26,11 @@ struct ConfigurationView: View {
} }
Divider() Divider()
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 { HStack {
OnboardingButton("setup_ssh_added_manually_button", false) { OnboardingButton("setup_ssh_added_manually_button", false) {
sshConfig = true sshConfig = true

View File

@ -94,7 +94,7 @@ extension ContentView {
.foregroundColor(.white) .foregroundColor(.white)
}) })
.buttonStyle(ToolbarButtonStyle(color: color)) .buttonStyle(ToolbarButtonStyle(color: color))
.popover(item: $selectedUpdate, attachmentAnchor: attachmentAnchor, arrowEdge: .bottom) { update in .sheet(item: $selectedUpdate) { update in
UpdateDetailView(update: update) UpdateDetailView(update: update)
} }
} }

View File

@ -103,6 +103,7 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
showing = false showing = false
} }
Button(.createSecretCreateButton, action: save) Button(.createSecretCreateButton, action: save)
.keyboardShortcut(.return)
.primary() .primary()
.disabled(name.isEmpty) .disabled(name.isEmpty)
} }

View File

@ -38,13 +38,14 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
} }
} }
HStack { HStack {
Button(.editSaveButton, action: rename)
.disabled(name.isEmpty)
.keyboardShortcut(.return)
Button(.editCancelButton) { Button(.editCancelButton) {
dismissalBlock(false) dismissalBlock(false)
} }
.keyboardShortcut(.cancelAction) .keyboardShortcut(.cancelAction)
Button(.editSaveButton, action: rename)
.disabled(name.isEmpty)
.keyboardShortcut(.return)
.primary()
} }
.padding() .padding()
} }

View File

@ -10,8 +10,13 @@ struct SetupView: View {
@State var sshConfig = false @State var sshConfig = false
var body: some View { var body: some View {
VStack {
VStack(spacing: 0) { VStack(spacing: 0) {
NewStepView(title: "setup_agent_title", description: "setup_agent_description") { NewStepView(
title: "setup_agent_title",
description: "setup_agent_description",
systemImage: "lock.laptopcomputer",
) {
OnboardingButton("setup_agent_install_button", installed) { OnboardingButton("setup_agent_install_button", installed) {
Task { Task {
await LaunchAgentController().install() await LaunchAgentController().install()
@ -20,7 +25,11 @@ struct SetupView: View {
} }
} }
Divider() Divider()
NewStepView(title: "setup_updates_title", description: "setup_updates_description") { NewStepView(
title: "setup_updates_title",
description: "setup_updates_description",
systemImage: "network.badge.shield.half.filled",
) {
OnboardingButton("setup_updates_ok", false) { OnboardingButton("setup_updates_ok", false) {
Task { Task {
updates = true updates = true
@ -28,7 +37,11 @@ struct SetupView: 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 { HStack {
OnboardingButton("setup_ssh_added_manually_button", false) { OnboardingButton("setup_ssh_added_manually_button", false) {
sshConfig = true sshConfig = true
@ -39,15 +52,31 @@ struct SetupView: View {
// } // }
sshConfig = true 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
})
} }
} }
} }
.background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10))
.frame(minWidth: 500, idealWidth: 500, minHeight: 500, idealHeight: 500) .frame(minWidth: 700, maxWidth: .infinity)
.padding() HStack {
Spacer()
Button("Done") {}
.styled
}
}
.padding()
} }
} }
struct OnboardingButton: View { struct OnboardingButton: View {
@ -94,23 +123,28 @@ extension View {
struct NewStepView<Content: View>: View { struct NewStepView<Content: View>: View {
let title: LocalizedStringResource let title: LocalizedStringResource
let icon: Image
let description: LocalizedStringResource let description: LocalizedStringResource
let actions: Content let actions: Content
init(title: LocalizedStringResource, description: LocalizedStringResource, actions: () -> Content) { init(title: LocalizedStringResource, description: LocalizedStringResource, systemImage: String, actions: () -> Content) {
self.title = title self.title = title
self.icon = Image(systemName: systemImage)
self.description = description self.description = description
self.actions = actions() self.actions = actions()
} }
var body: some View { var body: some View {
HStack { HStack(spacing: 20) {
icon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 24)
VStack(alignment: .leading, spacing: 6) { VStack(alignment: .leading, spacing: 6) {
Text(title) Text(title)
.bold() .bold()
Text(description) Text(description)
} }
Spacer(minLength: 20)
actions actions
} }
.padding(20) .padding(20)

View File

@ -12,7 +12,7 @@ struct UpdateDetailView: View {
Text(.updateVersionName(updateName: update.name)).font(.title) Text(.updateVersionName(updateName: update.name)).font(.title)
GroupBox(label: Text(.updateReleaseNotesTitle)) { GroupBox(label: Text(.updateReleaseNotesTitle)) {
ScrollView { ScrollView {
attributedBody Text(attributedBody)
} }
} }
HStack { HStack {
@ -35,29 +35,62 @@ struct UpdateDetailView: View {
.frame(maxWidth: 500) .frame(maxWidth: 500)
} }
var attributedBody: Text { var attributedBody: AttributedString {
var text = Text(verbatim: "") 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:
nil
}
if let font {
key.replace(with: AttributeScopes.SwiftUIAttributes.FontAttribute.self, value: font)
}
}
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) { for line in update.body.split(whereSeparator: \.isNewline) {
let attributed: Text let attributed: AttributedString
let split = line.split(separator: " ") let split = line.split(separator: " ")
let unprefixed = split.dropFirst().joined(separator: " ") let unprefixed = split.dropFirst().joined(separator: " ")
if let prefix = split.first { if let prefix = split.first {
var container = AttributeContainer()
switch prefix { switch prefix {
case "#": case "#":
attributed = Text(unprefixed).font(.title) + Text(verbatim: "\n") container.font = .title
case "##": case "##":
attributed = Text(unprefixed).font(.title2) + Text(verbatim: "\n") container.font = .title2
case "###": case "###":
attributed = Text(unprefixed).font(.title3) + Text(verbatim: "\n") container.font = .title3
default: default:
attributed = Text(line) + Text(verbatim: "\n\n") continue
} }
attributed = AttributedString(unprefixed, attributes: container)
} else { } else {
attributed = Text(line) + Text(verbatim: "\n\n") attributed = AttributedString(line + "\n\n")
} }
text = text + attributed text = text + attributed
} }
return text return text
} }
}
} }