feat: refine skills install actions

main
Peter Steinberger 2025-12-21 01:07:28 +01:00
parent 98891103d0
commit e94aa296e2
1 changed files with 23 additions and 41 deletions

View File

@ -82,8 +82,7 @@ struct SkillsSettings: View {
SkillRow( SkillRow(
skill: skill, skill: skill,
isBusy: self.model.isBusy(skill: skill), isBusy: self.model.isBusy(skill: skill),
canInstallLocally: self.state.connectionMode == .local, connectionMode: self.state.connectionMode,
defaultInstallTarget: self.defaultInstallTarget(for: skill),
onToggleEnabled: { enabled in onToggleEnabled: { enabled in
Task { await self.model.setEnabled(skillKey: skill.skillKey, enabled: enabled) } Task { await self.model.setEnabled(skillKey: skill.skillKey, enabled: enabled) }
}, },
@ -134,11 +133,6 @@ struct SkillsSettings: View {
} }
} }
} }
private func defaultInstallTarget(for skill: SkillStatus) -> InstallTarget {
let localPreferred = ["imsg", "peekaboo", "spotify-player"]
return localPreferred.contains(skill.skillKey) ? .local : .gateway
}
} }
private enum SkillsFilter: String, CaseIterable, Identifiable { private enum SkillsFilter: String, CaseIterable, Identifiable {
@ -171,11 +165,10 @@ private enum InstallTarget: String, CaseIterable {
private struct SkillRow: View { private struct SkillRow: View {
let skill: SkillStatus let skill: SkillStatus
let isBusy: Bool let isBusy: Bool
let canInstallLocally: Bool let connectionMode: AppState.ConnectionMode
let onToggleEnabled: (Bool) -> Void let onToggleEnabled: (Bool) -> Void
let onInstall: (SkillInstallOption, InstallTarget) -> Void let onInstall: (SkillInstallOption, InstallTarget) -> Void
let onSetEnv: (String, Bool) -> Void let onSetEnv: (String, Bool) -> Void
@State private var installTarget: InstallTarget
private var missingBins: [String] { self.skill.missing.bins } private var missingBins: [String] { self.skill.missing.bins }
private var missingEnv: [String] { self.skill.missing.env } private var missingEnv: [String] { self.skill.missing.env }
@ -184,22 +177,17 @@ private struct SkillRow: View {
init( init(
skill: SkillStatus, skill: SkillStatus,
isBusy: Bool, isBusy: Bool,
canInstallLocally: Bool, connectionMode: AppState.ConnectionMode,
defaultInstallTarget: InstallTarget,
onToggleEnabled: @escaping (Bool) -> Void, onToggleEnabled: @escaping (Bool) -> Void,
onInstall: @escaping (SkillInstallOption, InstallTarget) -> Void, onInstall: @escaping (SkillInstallOption, InstallTarget) -> Void,
onSetEnv: @escaping (String, Bool) -> Void) onSetEnv: @escaping (String, Bool) -> Void)
{ {
self.skill = skill self.skill = skill
self.isBusy = isBusy self.isBusy = isBusy
self.canInstallLocally = canInstallLocally self.connectionMode = connectionMode
self.onToggleEnabled = onToggleEnabled self.onToggleEnabled = onToggleEnabled
self.onInstall = onInstall self.onInstall = onInstall
self.onSetEnv = onSetEnv self.onSetEnv = onSetEnv
let initialTarget: InstallTarget = (defaultInstallTarget == .local && !canInstallLocally)
? .gateway
: defaultInstallTarget
self._installTarget = State(initialValue: initialTarget)
} }
var body: some View { var body: some View {
@ -339,28 +327,18 @@ private struct SkillRow: View {
private var trailingActions: some View { private var trailingActions: some View {
VStack(alignment: .trailing, spacing: 8) { VStack(alignment: .trailing, spacing: 8) {
if !self.installOptions.isEmpty { if !self.installOptions.isEmpty {
HStack(spacing: 6) {
Text("Install on")
.font(.caption)
.foregroundStyle(.secondary)
Picker("Install on", selection: self.$installTarget) {
Text("Gateway")
.tag(InstallTarget.gateway)
Text("This Mac")
.tag(InstallTarget.local)
.disabled(!self.canInstallLocally)
}
.labelsHidden()
.pickerStyle(.segmented)
.frame(width: 160)
.controlSize(.small)
.help(self.canInstallLocally ? "" : "Local install requires a local gateway connection.")
}
ForEach(self.installOptions) { option in ForEach(self.installOptions) { option in
Button("Install") { self.onInstall(option, self.installTarget) } HStack(spacing: 6) {
.buttonStyle(.borderedProminent) if self.showGatewayInstall {
.disabled(self.isBusy || self.installBlocked) Button("Install on Gateway") { self.onInstall(option, .gateway) }
.help(self.installBlocked ? "Local install requires a local gateway connection." : "") .buttonStyle(.borderedProminent)
.disabled(self.isBusy)
}
Button("Install on This Mac") { self.onInstall(option, .local) }
.buttonStyle(self.showGatewayInstall ? .bordered : .borderedProminent)
.disabled(self.isBusy)
.help(self.localInstallNeedsSwitch ? "Switches to Local mode to install on this Mac." : "")
}
} }
} else { } else {
Toggle("", isOn: self.enabledBinding) Toggle("", isOn: self.enabledBinding)
@ -399,8 +377,12 @@ private struct SkillRow: View {
!self.missingConfig.isEmpty !self.missingConfig.isEmpty
} }
private var installBlocked: Bool { private var showGatewayInstall: Bool {
self.installTarget == .local && !self.canInstallLocally self.connectionMode == .remote
}
private var localInstallNeedsSwitch: Bool {
self.connectionMode != .local
} }
private func formatConfigValue(_ value: AnyCodable?) -> String { private func formatConfigValue(_ value: AnyCodable?) -> String {
@ -512,8 +494,8 @@ final class SkillsSettingsModel {
await self.withBusy(skill.skillKey) { await self.withBusy(skill.skillKey) {
do { do {
if target == .local, AppStateStore.shared.connectionMode != .local { if target == .local, AppStateStore.shared.connectionMode != .local {
self.statusMessage = "Local install requires a local gateway connection" AppStateStore.shared.connectionMode = .local
return self.statusMessage = "Switched to Local mode to install on this Mac"
} }
let result = try await GatewayConnection.shared.skillsInstall( let result = try await GatewayConnection.shared.skillsInstall(
name: skill.name, name: skill.name,