feat(macos): detect installed CLI helper
parent
1a10569f6d
commit
21dfbd0103
|
|
@ -5,6 +5,8 @@ struct GeneralSettings: View {
|
||||||
@ObservedObject var state: AppState
|
@ObservedObject var state: AppState
|
||||||
@State private var isInstallingCLI = false
|
@State private var isInstallingCLI = false
|
||||||
@State private var cliStatus: String?
|
@State private var cliStatus: String?
|
||||||
|
@State private var cliInstalled = false
|
||||||
|
@State private var cliInstallLocation: String?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 18) {
|
VStack(alignment: .leading, spacing: 18) {
|
||||||
|
|
@ -63,6 +65,7 @@ struct GeneralSettings: View {
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(.horizontal, 22)
|
.padding(.horizontal, 22)
|
||||||
|
.onAppear { self.refreshCLIStatus() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private var activeBinding: Binding<Bool> {
|
private var activeBinding: Binding<Bool> {
|
||||||
|
|
@ -80,18 +83,37 @@ struct GeneralSettings: View {
|
||||||
if self.isInstallingCLI {
|
if self.isInstallingCLI {
|
||||||
ProgressView().controlSize(.small)
|
ProgressView().controlSize(.small)
|
||||||
} else {
|
} else {
|
||||||
Text("Install CLI helper")
|
Text(self.cliInstalled ? "Reinstall CLI helper" : "Install CLI helper")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.disabled(self.isInstallingCLI)
|
.disabled(self.isInstallingCLI)
|
||||||
|
|
||||||
if let status = cliStatus {
|
if self.isInstallingCLI {
|
||||||
Text(status)
|
Text("Working...")
|
||||||
.font(.caption)
|
.font(.callout)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
} else if self.cliInstalled {
|
||||||
|
Label("Installed", systemImage: "checkmark.circle.fill")
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
} else {
|
||||||
|
Text("Not installed")
|
||||||
|
.font(.callout)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.lineLimit(2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let status = cliStatus {
|
||||||
|
Text(status)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.lineLimit(2)
|
||||||
|
} else if let installLocation = self.cliInstallLocation {
|
||||||
|
Text("Found at \(installLocation)")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.lineLimit(2)
|
||||||
|
}
|
||||||
Text("Symlink \"clawdis-mac\" into /usr/local/bin and /opt/homebrew/bin for scripts.")
|
Text("Symlink \"clawdis-mac\" into /usr/local/bin and /opt/homebrew/bin for scripts.")
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
|
|
@ -104,7 +126,16 @@ struct GeneralSettings: View {
|
||||||
self.isInstallingCLI = true
|
self.isInstallingCLI = true
|
||||||
defer { isInstallingCLI = false }
|
defer { isInstallingCLI = false }
|
||||||
await CLIInstaller.install { status in
|
await CLIInstaller.install { status in
|
||||||
await MainActor.run { self.cliStatus = status }
|
await MainActor.run {
|
||||||
|
self.cliStatus = status
|
||||||
|
self.refreshCLIStatus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func refreshCLIStatus() {
|
||||||
|
let installLocation = CLIInstaller.installedLocation()
|
||||||
|
self.cliInstallLocation = installLocation
|
||||||
|
self.cliInstalled = installLocation != nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,29 @@ enum LaunchAgentManager {
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
enum CLIInstaller {
|
enum CLIInstaller {
|
||||||
|
static func installedLocation() -> String? {
|
||||||
|
let fm = FileManager.default
|
||||||
|
|
||||||
|
for basePath in cliHelperSearchPaths {
|
||||||
|
let candidate = URL(fileURLWithPath: basePath).appendingPathComponent("clawdis-mac").path
|
||||||
|
var isDirectory: ObjCBool = false
|
||||||
|
|
||||||
|
guard fm.fileExists(atPath: candidate, isDirectory: &isDirectory), !isDirectory.boolValue else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if fm.isExecutableFile(atPath: candidate) {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
static func isInstalled() -> Bool {
|
||||||
|
self.installedLocation() != nil
|
||||||
|
}
|
||||||
|
|
||||||
static func install(statusHandler: @escaping @Sendable (String) async -> Void) async {
|
static func install(statusHandler: @escaping @Sendable (String) async -> Void) async {
|
||||||
let helper = Bundle.main.bundleURL.appendingPathComponent("Contents/MacOS/ClawdisCLI")
|
let helper = Bundle.main.bundleURL.appendingPathComponent("Contents/MacOS/ClawdisCLI")
|
||||||
guard FileManager.default.isExecutableFile(atPath: helper.path) else {
|
guard FileManager.default.isExecutableFile(atPath: helper.path) else {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue