mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-09-16 09:20:56 +00:00
WIP
This commit is contained in:
parent
ea96dd88eb
commit
f3ce6b9d0f
@ -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." : {
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user