Merge remote-tracking branch 'origin/main'

This commit is contained in:
Peter Steinberger
2025-12-13 04:01:20 +00:00
4 changed files with 80 additions and 18 deletions

View File

@@ -2,7 +2,7 @@ import Foundation
let launchdLabel = "com.steipete.clawdis" let launchdLabel = "com.steipete.clawdis"
let onboardingVersionKey = "clawdis.onboardingVersion" let onboardingVersionKey = "clawdis.onboardingVersion"
let currentOnboardingVersion = 3 let currentOnboardingVersion = 4
let pauseDefaultsKey = "clawdis.pauseEnabled" let pauseDefaultsKey = "clawdis.pauseEnabled"
let iconAnimationsEnabledKey = "clawdis.iconAnimationsEnabled" let iconAnimationsEnabledKey = "clawdis.iconAnimationsEnabled"
let swabbleEnabledKey = "clawdis.swabbleEnabled" let swabbleEnabledKey = "clawdis.swabbleEnabled"

View File

@@ -125,18 +125,9 @@ struct GeneralSettings: View {
TextField("user@host[:22]", text: self.$state.remoteTarget) TextField("user@host[:22]", text: self.$state.remoteTarget)
.textFieldStyle(.roundedBorder) .textFieldStyle(.roundedBorder)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
Menu { MasterDiscoveryMenu(discovery: self.masterDiscovery) { master in
if self.masterDiscovery.masters.isEmpty { self.applyDiscoveredMaster(master)
Button(self.masterDiscovery.statusText) {}.disabled(true)
} else {
ForEach(self.masterDiscovery.masters) { master in
Button(master.displayName) { self.applyDiscoveredMaster(master) }
}
}
} label: {
Image(systemName: "dot.radiowaves.left.and.right")
} }
.help("Discover Clawdis masters on your LAN")
Button { Button {
Task { await self.testRemote() } Task { await self.testRemote() }
} label: { } label: {

View File

@@ -0,0 +1,23 @@
import SwiftUI
struct MasterDiscoveryMenu: View {
@ObservedObject var discovery: MasterDiscoveryModel
var onSelect: (MasterDiscoveryModel.DiscoveredMaster) -> Void
var body: some View {
Menu {
if self.discovery.masters.isEmpty {
Button(self.discovery.statusText) {}
.disabled(true)
} else {
ForEach(self.discovery.masters) { master in
Button(master.displayName) { self.onSelect(master) }
}
}
} label: {
Image(systemName: "dot.radiowaves.left.and.right")
}
.help("Discover Clawdis masters on your LAN")
}
}

View File

@@ -44,16 +44,19 @@ struct OnboardingView: View {
@State private var cliStatus: String? @State private var cliStatus: String?
@State private var copied = false @State private var copied = false
@State private var monitoringPermissions = false @State private var monitoringPermissions = false
@State private var monitoringDiscovery = false
@State private var cliInstalled = false @State private var cliInstalled = false
@State private var cliInstallLocation: String? @State private var cliInstallLocation: String?
@State private var gatewayStatus: GatewayEnvironmentStatus = .checking @State private var gatewayStatus: GatewayEnvironmentStatus = .checking
@State private var gatewayInstalling = false @State private var gatewayInstalling = false
@State private var gatewayInstallMessage: String? @State private var gatewayInstallMessage: String?
@StateObject private var masterDiscovery = MasterDiscoveryModel()
@ObservedObject private var state = AppStateStore.shared @ObservedObject private var state = AppStateStore.shared
@ObservedObject private var permissionMonitor = PermissionMonitor.shared @ObservedObject private var permissionMonitor = PermissionMonitor.shared
private let pageWidth: CGFloat = 680 private let pageWidth: CGFloat = 680
private let contentHeight: CGFloat = 520 private let contentHeight: CGFloat = 520
private let connectionPageIndex = 1
private let permissionsPageIndex = 3 private let permissionsPageIndex = 3
private var pageCount: Int { 7 } private var pageCount: Int { 7 }
private var buttonTitle: String { self.currentPage == self.pageCount - 1 ? "Finish" : "Next" } private var buttonTitle: String { self.currentPage == self.pageCount - 1 ? "Finish" : "Next" }
@@ -91,12 +94,18 @@ struct OnboardingView: View {
.background(Color(NSColor.windowBackgroundColor)) .background(Color(NSColor.windowBackgroundColor))
.onAppear { .onAppear {
self.currentPage = 0 self.currentPage = 0
self.updatePermissionMonitoring(for: 0) self.updateMonitoring(for: 0)
} }
.onChange(of: self.currentPage) { _, newValue in .onChange(of: self.currentPage) { _, newValue in
self.updatePermissionMonitoring(for: newValue) self.updateMonitoring(for: newValue)
}
.onChange(of: self.state.connectionMode) { _, _ in
self.updateDiscoveryMonitoring(for: self.currentPage)
}
.onDisappear {
self.stopPermissionMonitoring()
self.stopDiscovery()
} }
.onDisappear { self.stopPermissionMonitoring() }
.task { .task {
await self.refreshPerms() await self.refreshPerms()
self.refreshCLIStatus() self.refreshCLIStatus()
@@ -145,9 +154,14 @@ struct OnboardingView: View {
if self.state.connectionMode == .remote { if self.state.connectionMode == .remote {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
LabeledContent("SSH target") { LabeledContent("SSH target") {
TextField("user@host[:22]", text: self.$state.remoteTarget) HStack(spacing: 8) {
.textFieldStyle(.roundedBorder) TextField("user@host[:22]", text: self.$state.remoteTarget)
.frame(width: 300) .textFieldStyle(.roundedBorder)
.frame(width: 300)
MasterDiscoveryMenu(discovery: self.masterDiscovery) { master in
self.applyDiscoveredMaster(master)
}
}
} }
DisclosureGroup("Advanced") { DisclosureGroup("Advanced") {
@@ -256,6 +270,17 @@ struct OnboardingView: View {
} }
} }
private func applyDiscoveredMaster(_ master: MasterDiscoveryModel.DiscoveredMaster) {
let host = master.tailnetDns ?? master.lanHost
guard let host else { return }
let user = NSUserName()
var target = "\(user)@\(host)"
if master.sshPort != 22 {
target += ":\(master.sshPort)"
}
self.state.remoteTarget = target
}
private func permissionsPage() -> some View { private func permissionsPage() -> some View {
self.onboardingPage { self.onboardingPage {
Text("Grant permissions") Text("Grant permissions")
@@ -549,12 +574,35 @@ struct OnboardingView: View {
} }
} }
private func updateDiscoveryMonitoring(for pageIndex: Int) {
let isConnectionPage = pageIndex == self.connectionPageIndex
let shouldMonitor = isConnectionPage && self.state.connectionMode == .remote
if shouldMonitor, !self.monitoringDiscovery {
self.monitoringDiscovery = true
self.masterDiscovery.start()
} else if !shouldMonitor, self.monitoringDiscovery {
self.monitoringDiscovery = false
self.masterDiscovery.stop()
}
}
private func updateMonitoring(for pageIndex: Int) {
self.updatePermissionMonitoring(for: pageIndex)
self.updateDiscoveryMonitoring(for: pageIndex)
}
private func stopPermissionMonitoring() { private func stopPermissionMonitoring() {
guard self.monitoringPermissions else { return } guard self.monitoringPermissions else { return }
self.monitoringPermissions = false self.monitoringPermissions = false
PermissionMonitor.shared.unregister() PermissionMonitor.shared.unregister()
} }
private func stopDiscovery() {
guard self.monitoringDiscovery else { return }
self.monitoringDiscovery = false
self.masterDiscovery.stop()
}
private func installCLI() async { private func installCLI() async {
guard !self.installingCLI else { return } guard !self.installingCLI else { return }
self.installingCLI = true self.installingCLI = true