chore(macos): make debug settings scrollable

This commit is contained in:
Peter Steinberger
2025-12-07 14:48:12 +01:00
parent f97415755b
commit 4d2f4f1be3

View File

@@ -14,130 +14,133 @@ struct DebugSettings: View {
@State private var sessionStoreSaveError: String? @State private var sessionStoreSaveError: String?
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 10) { ScrollView(.vertical) {
LabeledContent("PID") { Text("\(ProcessInfo.processInfo.processIdentifier)") } VStack(alignment: .leading, spacing: 10) {
LabeledContent("Log file") { LabeledContent("PID") { Text("\(ProcessInfo.processInfo.processIdentifier)") }
Button("Open pino log") { self.openLog() } LabeledContent("Log file") {
.help(self.pinoLogPath) Button("Open pino log") { self.openLog() }
Text(self.pinoLogPath) .help(self.pinoLogPath)
.font(.caption2.monospaced()) Text(self.pinoLogPath)
.foregroundStyle(.secondary) .font(.caption2.monospaced())
.textSelection(.enabled) .foregroundStyle(.secondary)
} .textSelection(.enabled)
LabeledContent("Binary path") { Text(Bundle.main.bundlePath).font(.footnote) } }
LabeledContent("Relay status") { LabeledContent("Binary path") { Text(Bundle.main.bundlePath).font(.footnote) }
VStack(alignment: .leading, spacing: 2) { LabeledContent("Relay status") {
Text(self.relayManager.status.label) VStack(alignment: .leading, spacing: 2) {
Text("Restarts: \(self.relayManager.restartCount)") Text(self.relayManager.status.label)
Text("Restarts: \(self.relayManager.restartCount)")
.font(.caption2)
.foregroundStyle(.secondary)
}
}
VStack(alignment: .leading, spacing: 4) {
Text("Relay stdout/stderr")
.font(.caption.weight(.semibold))
ScrollView {
Text(self.relayManager.log.isEmpty ? "" : self.relayManager.log)
.font(.caption.monospaced())
.frame(maxWidth: .infinity, alignment: .leading)
.textSelection(.enabled)
}
.frame(height: 180)
.overlay(RoundedRectangle(cornerRadius: 6).stroke(Color.secondary.opacity(0.2)))
}
VStack(alignment: .leading, spacing: 6) {
Text("Clawdis project root")
.font(.caption.weight(.semibold))
HStack(spacing: 8) {
TextField("Path to clawdis repo", text: self.$relayRootInput)
.textFieldStyle(.roundedBorder)
.font(.caption.monospaced())
.onSubmit { self.saveRelayRoot() }
Button("Save") { self.saveRelayRoot() }
.buttonStyle(.borderedProminent)
Button("Reset") {
let def = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Projects/clawdis").path
self.relayRootInput = def
self.saveRelayRoot()
}
.buttonStyle(.bordered)
}
Text("Used for pnpm/node fallback and PATH population when launching the relay.")
.font(.caption2) .font(.caption2)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
} LabeledContent("Session store") {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 6) {
Text("Relay stdout/stderr") HStack(spacing: 8) {
.font(.caption.weight(.semibold)) TextField("Path", text: self.$sessionStorePath)
ScrollView { .textFieldStyle(.roundedBorder)
Text(self.relayManager.log.isEmpty ? "" : self.relayManager.log) .font(.caption.monospaced())
.font(.caption.monospaced()) .frame(width: 340)
.frame(maxWidth: .infinity, alignment: .leading) Button("Save") { self.saveSessionStorePath() }
.textSelection(.enabled) .buttonStyle(.borderedProminent)
} }
.frame(height: 180) if let sessionStoreSaveError {
.overlay(RoundedRectangle(cornerRadius: 6).stroke(Color.secondary.opacity(0.2))) Text(sessionStoreSaveError)
} .font(.footnote)
VStack(alignment: .leading, spacing: 6) { .foregroundStyle(.secondary)
Text("Clawdis project root") } else {
.font(.caption.weight(.semibold)) Text("Used by the CLI session loader; stored in ~/.clawdis/clawdis.json.")
HStack(spacing: 8) { .font(.footnote)
TextField("Path to clawdis repo", text: self.$relayRootInput) .foregroundStyle(.secondary)
.textFieldStyle(.roundedBorder) }
.font(.caption.monospaced())
.onSubmit { self.saveRelayRoot() }
Button("Save") { self.saveRelayRoot() }
.buttonStyle(.borderedProminent)
Button("Reset") {
let def = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Projects/clawdis").path
self.relayRootInput = def
self.saveRelayRoot()
} }
.buttonStyle(.bordered)
} }
Text("Used for pnpm/node fallback and PATH population when launching the relay.") LabeledContent("Model catalog") {
.font(.caption2) VStack(alignment: .leading, spacing: 6) {
.foregroundStyle(.secondary) Text(self.modelCatalogPath)
}
LabeledContent("Session store") {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 8) {
TextField("Path", text: self.$sessionStorePath)
.textFieldStyle(.roundedBorder)
.font(.caption.monospaced()) .font(.caption.monospaced())
.frame(width: 340)
Button("Save") { self.saveSessionStorePath() }
.buttonStyle(.borderedProminent)
}
if let sessionStoreSaveError {
Text(sessionStoreSaveError)
.font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} else { .lineLimit(2)
Text("Used by the CLI session loader; stored in ~/.clawdis/clawdis.json.") HStack(spacing: 8) {
.font(.footnote) Button {
.foregroundStyle(.secondary) self.chooseCatalogFile()
} } label: {
} Label("Choose models.generated.ts…", systemImage: "folder")
} }
LabeledContent("Model catalog") { .buttonStyle(.bordered)
VStack(alignment: .leading, spacing: 6) {
Text(self.modelCatalogPath)
.font(.caption.monospaced())
.foregroundStyle(.secondary)
.lineLimit(2)
HStack(spacing: 8) {
Button {
self.chooseCatalogFile()
} label: {
Label("Choose models.generated.ts…", systemImage: "folder")
}
.buttonStyle(.bordered)
Button { Button {
Task { await self.reloadModels() } Task { await self.reloadModels() }
} label: { } label: {
Label(self.modelsLoading ? "Reloading…" : "Reload models", systemImage: "arrow.clockwise") Label(self.modelsLoading ? "Reloading…" : "Reload models", systemImage: "arrow.clockwise")
}
.buttonStyle(.bordered)
.disabled(self.modelsLoading)
} }
.buttonStyle(.bordered) if let modelsError {
.disabled(self.modelsLoading) Text(modelsError)
} .font(.footnote)
if let modelsError { .foregroundStyle(.secondary)
Text(modelsError) } else if let modelsCount {
Text("Loaded \(modelsCount) models")
.font(.footnote)
.foregroundStyle(.secondary)
}
Text("Used by the Config tab model picker; point at a different build when debugging.")
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.tertiary)
} else if let modelsCount {
Text("Loaded \(modelsCount) models")
.font(.footnote)
.foregroundStyle(.secondary)
} }
Text("Used by the Config tab model picker; point at a different build when debugging.")
.font(.footnote)
.foregroundStyle(.tertiary)
} }
Button("Send Test Notification") {
Task { _ = await NotificationManager().send(title: "Clawdis", body: "Test notification", sound: nil) }
}
.buttonStyle(.bordered)
HStack {
Button("Restart app") { self.relaunch() }
Button("Reveal app in Finder") { self.revealApp() }
Button("Restart relay") { self.restartRelay() }
}
.buttonStyle(.bordered)
Spacer(minLength: 8)
} }
Button("Send Test Notification") { .frame(maxWidth: .infinity, alignment: .leading)
Task { _ = await NotificationManager().send(title: "Clawdis", body: "Test notification", sound: nil) } .padding(.horizontal, 12)
} .padding(.vertical, 8)
.buttonStyle(.bordered)
HStack {
Button("Restart app") { self.relaunch() }
Button("Reveal app in Finder") { self.revealApp() }
Button("Restart relay") { self.restartRelay() }
}
.buttonStyle(.bordered)
Spacer()
} }
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 12)
.task { .task {
await self.reloadModels() await self.reloadModels()
self.loadSessionStorePath() self.loadSessionStorePath()