Add usage commands, a vendor-neutral usage format and a knowledge base
This commit is contained in:
@@ -3,6 +3,7 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
typealias Permission = Components.Schemas.Permission
|
||||
typealias UsageCommands = Components.Schemas.UsageCommands
|
||||
|
||||
let serverHelper = Bundle.main.bundleURL.appending(path: "Contents/Helpers/byre").path
|
||||
private let service = "gui/\(getuid())/run.fc.byre"
|
||||
@@ -215,6 +216,71 @@ struct PermissionsCheck: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct UsageCommandsForm: View {
|
||||
let api: API
|
||||
@ViewState private var saved: UsageCommands?
|
||||
@ViewState private var draft: UsageCommands?
|
||||
@ViewState private var error = ""
|
||||
@ViewState private var busy = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
if draft != nil {
|
||||
Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 6) {
|
||||
row("claude", \.claude)
|
||||
row("codex", \.codex)
|
||||
row("antigravity", \.antigravity)
|
||||
}
|
||||
Button(busy ? "Saving…" : "Save", action: save)
|
||||
.buttonStyle(WebButtonStyle())
|
||||
.disabled(busy || draft == saved)
|
||||
}
|
||||
if !error.isEmpty {
|
||||
Text(error)
|
||||
.foregroundStyle(Palette.danger)
|
||||
.textSelection(.enabled)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
.padding(.top, 4)
|
||||
.task {
|
||||
do {
|
||||
saved = try await api.client.getUsageCommands().ok.body.json
|
||||
draft = saved
|
||||
error = ""
|
||||
} catch {
|
||||
self.error = "Couldn't load usage commands: \(errorText(error))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func row(_ vendor: String, _ key: WritableKeyPath<UsageCommands, String>) -> some View {
|
||||
GridRow {
|
||||
Text(vendor)
|
||||
TextField("Built in", text: Binding(get: { draft?[keyPath: key] ?? "" }, set: { draft?[keyPath: key] = $0 }))
|
||||
.modifier(FieldStyle())
|
||||
.onSubmit(save)
|
||||
}
|
||||
}
|
||||
|
||||
private func save() {
|
||||
guard let draft, draft != saved, !busy else {
|
||||
return
|
||||
}
|
||||
busy = true
|
||||
Task {
|
||||
do {
|
||||
saved = try await api.client.updateUsageCommands(body: .json(draft)).ok.body.json
|
||||
self.draft = saved
|
||||
error = ""
|
||||
} catch {
|
||||
self.error = "Couldn't save usage commands: \(errorText(error))"
|
||||
}
|
||||
busy = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcessFailure: LocalizedError {
|
||||
let errorDescription: String?
|
||||
}
|
||||
@@ -336,6 +402,13 @@ struct ServerView: View {
|
||||
PermissionsCheck {
|
||||
try await API(origin: origin).client.checkPermissions().ok.body.json
|
||||
}
|
||||
Text("Usage commands")
|
||||
.font(Fonts.font(13, weight: 700))
|
||||
.padding(.top, 16)
|
||||
Text("A shell command per vendor that prints its usage as JSON for the Status tab, run after each of that vendor's turns. Empty uses what the vendor itself reports.")
|
||||
.foregroundStyle(Palette.muted)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
UsageCommandsForm(api: API(origin: origin))
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
Reference in New Issue
Block a user