This commit is contained in:
Max Goedjen 2025-09-01 17:43:33 -07:00
parent ea96dd88eb
commit f3ce6b9d0f
No known key found for this signature in database
9 changed files with 138 additions and 50 deletions

View File

@ -1186,6 +1186,9 @@
}, },
"Configure" : { "Configure" : {
},
"Configure Integrations" : {
}, },
"Configuring Tools for Secretive" : { "Configuring Tools for Secretive" : {
@ -3974,6 +3977,9 @@
} }
} }
} }
},
"Setup" : {
}, },
"setup_agent_activity_monitor_description" : { "setup_agent_activity_monitor_description" : {
"extractionState" : "manual", "extractionState" : "manual",
@ -5287,6 +5293,9 @@
}, },
"System" : { "System" : {
},
"Tell the tools you use how to talk to Secretive." : {
}, },
"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." : { "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." : {

View File

@ -61,7 +61,6 @@ struct Secretive: App {
} }
.sheet(isPresented: $showingIntegrations) { .sheet(isPresented: $showingIntegrations) {
IntegrationsView() IntegrationsView()
.frame(minHeight: 400)
} }
} }
.commands { .commands {
@ -81,6 +80,11 @@ struct Secretive: App {
NSWorkspace.shared.open(Constants.helpURL) NSWorkspace.shared.open(Constants.helpURL)
} }
} }
CommandGroup(after: .help) {
Button("Setup") {
showingSetup = true
}
}
SidebarCommands() SidebarCommands()
} }
} }

View File

@ -3,10 +3,11 @@ import SwiftUI
struct PrimaryButtonModifier: ViewModifier { struct PrimaryButtonModifier: ViewModifier {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@Environment(\.isEnabled) var isEnabled
func body(content: Content) -> some View { func body(content: Content) -> some View {
// Tinted glass prominent is really hard to read on 26.0. // Tinted glass prominent is really hard to read on 26.0.
if #available(macOS 26.0, *), colorScheme == .dark { if #available(macOS 26.0, *), colorScheme == .dark, isEnabled {
content.buttonStyle(.glassProminent) content.buttonStyle(.glassProminent)
} else { } else {
content.buttonStyle(.borderedProminent) content.buttonStyle(.borderedProminent)
@ -17,13 +18,13 @@ struct PrimaryButtonModifier: ViewModifier {
extension View { extension View {
func primary() -> some View { func primaryButton() -> some View {
modifier(PrimaryButtonModifier()) modifier(PrimaryButtonModifier())
} }
} }
struct NormalButtonModifier: ViewModifier { struct MenuButtonModifier: ViewModifier {
func body(content: Content) -> some View { func body(content: Content) -> some View {
if #available(macOS 26.0, *) { if #available(macOS 26.0, *) {
@ -39,7 +40,27 @@ struct NormalButtonModifier: ViewModifier {
extension View { extension View {
func normal() -> some View { func menuButton() -> some View {
modifier(MenuButtonModifier())
}
}
struct NormalButtonModifier: ViewModifier {
func body(content: Content) -> some View {
if #available(macOS 26.0, *) {
content.buttonStyle(.glass)
} else {
content.buttonStyle(.bordered)
}
}
}
extension View {
func normalButton() -> some View {
modifier(NormalButtonModifier()) modifier(NormalButtonModifier())
} }

View File

@ -127,7 +127,7 @@ struct AgentNotRunningView: View {
} }
} }
} }
.primary() .primaryButton()
} else { } else {
Text("Secretive was unable to get SecretAgent to launch. Please try restarting your Mac, and if that doesn't work, file an issue on GitHub.") Text("Secretive was unable to get SecretAgent to launch. Please try restarting your Mac, and if that doesn't work, file an issue on GitHub.")
.bold() .bold()

View File

@ -106,7 +106,7 @@ extension ContentView {
Button(.appMenuNewSecretButton, systemImage: "plus") { Button(.appMenuNewSecretButton, systemImage: "plus") {
showingCreation = true showingCreation = true
} }
.normal() .menuButton()
.sheet(isPresented: $showingCreation) { .sheet(isPresented: $showingCreation) {
if let modifiable = storeList.modifiableStore { if let modifiable = storeList.modifiableStore {
CreateSecretView(store: modifiable) { created in CreateSecretView(store: modifiable) { created in

View File

@ -113,7 +113,7 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
} }
Button(.createSecretCreateButton, action: save) Button(.createSecretCreateButton, action: save)
.keyboardShortcut(.return) .keyboardShortcut(.return)
.primary() .primaryButton()
.disabled(name.isEmpty) .disabled(name.isEmpty)
} }
.padding() .padding()

View File

@ -45,7 +45,7 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
Button(.editSaveButton, action: rename) Button(.editSaveButton, action: rename)
.disabled(name.isEmpty) .disabled(name.isEmpty)
.keyboardShortcut(.return) .keyboardShortcut(.return)
.primary() .primaryButton()
} }
.padding() .padding()
} }

View File

@ -21,19 +21,47 @@ struct IntegrationsView: View {
} }
} }
} detail: { } detail: {
IntegrationsDetailView(selectedInstruction: $selectedInstruction) IntegrationsDetailView(selectedInstruction: $selectedInstruction)
.fauxToolbar {
Button("Done") {
dismiss()
}
.normalButton()
}
} }
.onAppear { .onAppear {
selectedInstruction = instructions.gettingStarted selectedInstruction = instructions.gettingStarted
} }
.toolbar { .frame(minHeight: 500)
ToolbarItem(placement: .primaryAction) { }
Button("Done") {
dismiss() }
}
.styled extension View {
func fauxToolbar<Content: View>(content: () -> Content) -> some View {
modifier(FauxToolbarModifier(toolbarContent: content()))
}
}
struct FauxToolbarModifier<ToolbarContent: View>: ViewModifier {
var toolbarContent: ToolbarContent
func body(content: Content) -> some View {
VStack(alignment: .leading) {
content
Divider()
HStack {
Spacer()
toolbarContent
.padding(.top, 8)
.padding(.trailing, 16)
.padding(.bottom, 16)
} }
} }
} }
} }

View File

@ -4,20 +4,26 @@ struct SetupView: View {
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@Binding var setupComplete: Bool @Binding var setupComplete: Bool
@State var showingIntegrations = false
@State var buttonWidth: CGFloat?
@State var installed = false @State var installed = false
@State var updates = false @State var updates = false
@State var sshConfig = false @State var integrations = false
var allDone: Bool {
installed && updates && integrations
}
var body: some View { var body: some View {
VStack { VStack {
VStack(spacing: 0) { VStack(alignment: .leading, spacing: 0) {
StepView( StepView(
title: "setup_agent_title", title: "setup_agent_title",
description: "setup_agent_description", description: "setup_agent_description",
systemImage: "lock.laptopcomputer", systemImage: "lock.laptopcomputer",
) { ) {
OnboardingButton("setup_agent_install_button", installed) { OnboardingButton("setup_agent_install_button", installed, width: buttonWidth) {
Task { Task {
installed = await LaunchAgentController().install() installed = await LaunchAgentController().install()
} }
@ -29,78 +35,97 @@ struct SetupView: View {
description: "setup_updates_description", description: "setup_updates_description",
systemImage: "network.badge.shield.half.filled", systemImage: "network.badge.shield.half.filled",
) { ) {
OnboardingButton("setup_updates_ok", updates) { OnboardingButton("setup_updates_ok", updates, width: buttonWidth) {
Task { updates = true
updates = true
}
} }
} }
Divider() Divider()
StepView( StepView(
title: "setup_ssh_title", title: "Configure Integrations",
description: "setup_ssh_description", description: "Tell the tools you use how to talk to Secretive.",
systemImage: "network.badge.shield.half.filled", systemImage: "firewall",
) { ) {
HStack { OnboardingButton("Configure", integrations, width: buttonWidth) {
OnboardingButton("Configure", false) { showingIntegrations = true
// sshConfig = true
}
} }
} }
} }
.onPreferenceChange(OnboardingButton.WidthKey.self) { width in
buttonWidth = width
}
.background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10))
.frame(minWidth: 700, maxWidth: .infinity) .frame(minWidth: 700, maxWidth: .infinity)
HStack { HStack {
Spacer() Spacer()
Button("Done") {} Button("Done") {
.styled setupComplete = true
dismiss()
}
.disabled(!allDone)
.primaryButton()
} }
} }
.interactiveDismissDisabled()
.padding() .padding()
.sheet(isPresented: $showingIntegrations, onDismiss: {
integrations = true
}, content: {
IntegrationsView()
})
} }
} }
struct OnboardingButton: View { struct OnboardingButton: View {
struct WidthKey: @MainActor PreferenceKey {
@MainActor static var defaultValue: CGFloat? = nil
static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
if let next = nextValue(), next > (value ?? -1) {
value = next
}
}
}
let label: LocalizedStringResource let label: LocalizedStringResource
let complete: Bool let complete: Bool
let action: () -> Void let action: () -> Void
let width: CGFloat?
init(_ label: LocalizedStringResource, _ complete: Bool, action: @escaping () -> Void) { @State var currentWidth: CGFloat?
init(_ label: LocalizedStringResource, _ complete: Bool, width: CGFloat? = nil, action: @escaping () -> Void) {
self.label = label self.label = label
self.complete = complete self.complete = complete
self.action = action self.action = action
self.width = width
} }
var body: some View { var body: some View {
Button(action: action) { Button(action: action) {
HStack(spacing: 6) { HStack(spacing: 6) {
Text(label)
if complete { if complete {
Text("Done")
Image(systemName: "checkmark.circle.fill") Image(systemName: "checkmark.circle.fill")
} else {
Text(label)
} }
} }
.frame(width: width)
.padding(.vertical, 2) .padding(.vertical, 2)
.onGeometryChange(for: CGFloat.self) { proxy in
proxy.size.width
} action: { newValue in
currentWidth = newValue
}
} }
.preference(key: WidthKey.self, value: currentWidth)
.primaryButton()
.disabled(complete) .disabled(complete)
.styled .tint(complete ? .green : nil)
} }
} }
extension View {
@ViewBuilder
var styled: some View {
if #available(macOS 26.0, *) {
buttonStyle(.glassProminent)
} else {
buttonStyle(.borderedProminent)
}
}
}
struct StepView<Content: View>: View { struct StepView<Content: View>: View {
let title: LocalizedStringResource let title: LocalizedStringResource
@ -126,6 +151,7 @@ struct StepView<Content: View>: View {
.bold() .bold()
Text(description) Text(description)
} }
Spacer()
actions actions
} }
.padding(20) .padding(20)