From 0b990443de4294490d05d296bd00b63c28d58748 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 13 Dec 2025 19:23:41 +0000 Subject: [PATCH] style(macos): tidy settings and CLI --- .../Sources/Clawdis/ClawdisConfigFile.swift | 1 - .../Sources/Clawdis/ConfigSettings.swift | 12 ++--- apps/macos/Sources/Clawdis/CronSettings.swift | 23 +++++++--- .../Sources/Clawdis/MenuContentView.swift | 22 +++++---- .../PeekabooBridgeHostCoordinator.swift | 3 +- .../Sources/Clawdis/PointingHandCursor.swift | 1 - .../Sources/Clawdis/ScreenshotSize.swift | 1 - .../macos/Sources/Clawdis/ToolsSettings.swift | 34 +++++++------- apps/macos/Sources/Clawdis/Utilities.swift | 4 +- .../Sources/Clawdis/WebChatSwiftUI.swift | 19 +++++--- .../macos/Sources/ClawdisCLI/BrowserCLI.swift | 45 +++++++++++-------- apps/macos/Sources/ClawdisCLI/UICLI.swift | 38 ++++++++-------- package.json | 2 + 13 files changed, 116 insertions(+), 89 deletions(-) diff --git a/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift b/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift index 69b988151..cc161b580 100644 --- a/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift +++ b/apps/macos/Sources/Clawdis/ClawdisConfigFile.swift @@ -38,4 +38,3 @@ enum ClawdisConfigFile { self.saveDict(root) } } - diff --git a/apps/macos/Sources/Clawdis/ConfigSettings.swift b/apps/macos/Sources/Clawdis/ConfigSettings.swift index a5e1ce64b..5c8730f77 100644 --- a/apps/macos/Sources/Clawdis/ConfigSettings.swift +++ b/apps/macos/Sources/Clawdis/ConfigSettings.swift @@ -203,17 +203,17 @@ struct ConfigSettings: View { .toggleStyle(.checkbox) .disabled(!self.browserEnabled) .onChange(of: self.browserAttachOnly) { _, _ in self.autosaveConfig() } - .help("When enabled, the browser server will only connect if the clawd browser is already running.") + .help( + "When enabled, the browser server will only connect if the clawd browser is already running.") } GridRow { Color.clear .frame(width: self.labelColumnWidth, height: 1) Text( - "Clawd uses a separate Chrome profile and ports (default 18791/18792) so it won’t interfere with your daily browser." - ) - .font(.footnote) - .foregroundStyle(.secondary) - .frame(maxWidth: .infinity, alignment: .leading) + "Clawd uses a separate Chrome profile and ports (default 18791/18792) so it won’t interfere with your daily browser.") + .font(.footnote) + .foregroundStyle(.secondary) + .frame(maxWidth: .infinity, alignment: .leading) } } } diff --git a/apps/macos/Sources/Clawdis/CronSettings.swift b/apps/macos/Sources/Clawdis/CronSettings.swift index d5feaffb6..3384eda64 100644 --- a/apps/macos/Sources/Clawdis/CronSettings.swift +++ b/apps/macos/Sources/Clawdis/CronSettings.swift @@ -525,7 +525,8 @@ private struct CronJobEditor: View { VStack(alignment: .leading, spacing: 6) { Text(self.job == nil ? "New cron job" : "Edit cron job") .font(.title3.weight(.semibold)) - Text("Create a schedule that wakes clawd via the Gateway. Use an isolated session for agent turns so your main chat stays clean.") + Text( + "Create a schedule that wakes clawd via the Gateway. Use an isolated session for agent turns so your main chat stays clean.") .font(.callout) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) @@ -570,7 +571,8 @@ private struct CronJobEditor: View { GridRow { Color.clear .frame(width: self.labelColumnWidth, height: 1) - Text("Main jobs post a system event into the current main session. Isolated jobs run clawd in a dedicated session and can deliver results (WhatsApp/Telegram/etc).") + Text( + "Main jobs post a system event into the current main session. Isolated jobs run clawd in a dedicated session and can deliver results (WhatsApp/Telegram/etc).") .font(.footnote) .foregroundStyle(.secondary) .frame(maxWidth: .infinity, alignment: .leading) @@ -594,7 +596,8 @@ private struct CronJobEditor: View { GridRow { Color.clear .frame(width: self.labelColumnWidth, height: 1) - Text("“At” runs once, “Every” repeats with a duration, “Cron” uses a 5-field Unix expression.") + Text( + "“At” runs once, “Every” repeats with a duration, “Cron” uses a 5-field Unix expression.") .font(.footnote) .foregroundStyle(.secondary) .frame(maxWidth: .infinity, alignment: .leading) @@ -604,7 +607,10 @@ private struct CronJobEditor: View { case .at: GridRow { self.gridLabel("At") - DatePicker("", selection: self.$atDate, displayedComponents: [.date, .hourAndMinute]) + DatePicker( + "", + selection: self.$atDate, + displayedComponents: [.date, .hourAndMinute]) .labelsHidden() .frame(maxWidth: .infinity, alignment: .leading) } @@ -635,7 +641,8 @@ private struct CronJobEditor: View { GroupBox("Payload") { VStack(alignment: .leading, spacing: 10) { if self.sessionTarget == .isolated { - Text("Isolated jobs always run an agent turn. The result can be delivered to a surface, and a short summary is posted back to your main chat.") + Text( + "Isolated jobs always run an agent turn. The result can be delivered to a surface, and a short summary is posted back to your main chat.") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) @@ -655,7 +662,8 @@ private struct CronJobEditor: View { GridRow { Color.clear .frame(width: self.labelColumnWidth, height: 1) - Text("System events are injected into the current main session. Agent turns require an isolated session target.") + Text( + "System events are injected into the current main session. Agent turns require an isolated session target.") .font(.footnote) .foregroundStyle(.secondary) .frame(maxWidth: .infinity, alignment: .leading) @@ -687,7 +695,8 @@ private struct CronJobEditor: View { GridRow { Color.clear .frame(width: self.labelColumnWidth, height: 1) - Text("Controls the label used when posting the completion summary back to the main session.") + Text( + "Controls the label used when posting the completion summary back to the main session.") .font(.footnote) .foregroundStyle(.secondary) .frame(maxWidth: .infinity, alignment: .leading) diff --git a/apps/macos/Sources/Clawdis/MenuContentView.swift b/apps/macos/Sources/Clawdis/MenuContentView.swift index 5c7af9314..3d894491f 100644 --- a/apps/macos/Sources/Clawdis/MenuContentView.swift +++ b/apps/macos/Sources/Clawdis/MenuContentView.swift @@ -45,13 +45,13 @@ struct MenuContent: View { WebChatManager.shared.show(sessionKey: WebChatManager.shared.preferredSessionKey()) } } - Toggle(isOn: Binding( - get: { self.browserControlEnabled }, - set: { enabled in - self.browserControlEnabled = enabled - ClawdisConfigFile.setBrowserControlEnabled(enabled) - }) - ) { + Toggle( + isOn: Binding( + get: { self.browserControlEnabled }, + set: { enabled in + self.browserControlEnabled = enabled + ClawdisConfigFile.setBrowserControlEnabled(enabled) + })) { Text("Browser Control") } Toggle(isOn: Binding(get: { self.state.canvasEnabled }, set: { self.state.canvasEnabled = $0 })) { @@ -110,7 +110,9 @@ struct MenuContent: View { await self.reloadSessionMenu() } } label: { - Label(level.capitalized, systemImage: row.thinkingLevel == normalized ? "checkmark" : "") + Label( + level.capitalized, + systemImage: row.thinkingLevel == normalized ? "checkmark" : "") } } } @@ -126,7 +128,9 @@ struct MenuContent: View { await self.reloadSessionMenu() } } label: { - Label(level.capitalized, systemImage: row.verboseLevel == normalized ? "checkmark" : "") + Label( + level.capitalized, + systemImage: row.verboseLevel == normalized ? "checkmark" : "") } } } diff --git a/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift b/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift index 35286ed25..2ed7a2826 100644 --- a/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift +++ b/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift @@ -53,7 +53,8 @@ final class PeekabooBridgeHostCoordinator { self.host = host await host.start() - self.logger.info("PeekabooBridge host started at \(PeekabooBridgeConstants.clawdisSocketPath, privacy: .public)") + self.logger + .info("PeekabooBridge host started at \(PeekabooBridgeConstants.clawdisSocketPath, privacy: .public)") } } diff --git a/apps/macos/Sources/Clawdis/PointingHandCursor.swift b/apps/macos/Sources/Clawdis/PointingHandCursor.swift index 2f1828e05..ceb6fb6f8 100644 --- a/apps/macos/Sources/Clawdis/PointingHandCursor.swift +++ b/apps/macos/Sources/Clawdis/PointingHandCursor.swift @@ -28,4 +28,3 @@ extension View { self.modifier(PointingHandCursorModifier()) } } - diff --git a/apps/macos/Sources/Clawdis/ScreenshotSize.swift b/apps/macos/Sources/Clawdis/ScreenshotSize.swift index 8ffeba779..e1ad915f5 100644 --- a/apps/macos/Sources/Clawdis/ScreenshotSize.swift +++ b/apps/macos/Sources/Clawdis/ScreenshotSize.swift @@ -15,4 +15,3 @@ enum ScreenshotSize { return Size(width: width, height: height) } } - diff --git a/apps/macos/Sources/Clawdis/ToolsSettings.swift b/apps/macos/Sources/Clawdis/ToolsSettings.swift index 939dd5316..51463315b 100644 --- a/apps/macos/Sources/Clawdis/ToolsSettings.swift +++ b/apps/macos/Sources/Clawdis/ToolsSettings.swift @@ -224,26 +224,26 @@ struct ToolsSettings: View { EmptyView() } else { VStack(alignment: .leading, spacing: 10) { - Text(title) - .font(.callout.weight(.semibold)) - .padding(.top, 6) + Text(title) + .font(.callout.weight(.semibold)) + .padding(.top, 6) - VStack(spacing: 8) { - ForEach(filtered) { tool in - ToolRow( - tool: tool, - state: self.binding(for: tool), - packageManager: self.packageManager, - refreshState: { await self.refresh(tool: tool) }) - .padding(10) - .background(Color(nsColor: .controlBackgroundColor)) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(Color.secondary.opacity(0.15), lineWidth: 1)) + VStack(spacing: 8) { + ForEach(filtered) { tool in + ToolRow( + tool: tool, + state: self.binding(for: tool), + packageManager: self.packageManager, + refreshState: { await self.refresh(tool: tool) }) + .padding(10) + .background(Color(nsColor: .controlBackgroundColor)) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color.secondary.opacity(0.15), lineWidth: 1)) + } } } - } } } diff --git a/apps/macos/Sources/Clawdis/Utilities.swift b/apps/macos/Sources/Clawdis/Utilities.swift index 315afa38d..ab724b9f8 100644 --- a/apps/macos/Sources/Clawdis/Utilities.swift +++ b/apps/macos/Sources/Clawdis/Utilities.swift @@ -639,9 +639,9 @@ enum CommandResolver { return URL(fileURLWithPath: expanded) } -#if SWIFT_PACKAGE + #if SWIFT_PACKAGE static func _testNodeManagerBinPaths(home: URL) -> [String] { self.nodeManagerBinPaths(home: home) } -#endif + #endif } diff --git a/apps/macos/Sources/Clawdis/WebChatSwiftUI.swift b/apps/macos/Sources/Clawdis/WebChatSwiftUI.swift index b42f01316..27cc465df 100644 --- a/apps/macos/Sources/Clawdis/WebChatSwiftUI.swift +++ b/apps/macos/Sources/Clawdis/WebChatSwiftUI.swift @@ -36,8 +36,8 @@ struct GatewayChatMessage: Codable, Identifiable { id: UUID = .init(), role: String, content: [GatewayChatMessageContent], - timestamp: Double? - ) { + timestamp: Double?) + { self.id = id self.role = role self.content = content @@ -124,6 +124,7 @@ final class WebChatViewModel: ObservableObject { private var pendingRuns = Set() { didSet { self.pendingRunCount = self.pendingRuns.count } } + private var lastHealthPollAt: Date? init(sessionKey: String) { @@ -162,7 +163,8 @@ final class WebChatViewModel: ObservableObject { do { let data = try await Task.detached { try Data(contentsOf: url) }.value guard data.count <= 5_000_000 else { - await MainActor.run { self.errorText = "Attachment \(url.lastPathComponent) exceeds 5 MB limit" } + await MainActor + .run { self.errorText = "Attachment \(url.lastPathComponent) exceeds 5 MB limit" } continue } let uti = UTType(filenameExtension: url.pathExtension) ?? .data @@ -445,7 +447,8 @@ struct WebChatView: View { .foregroundStyle(Color.accentColor.opacity(0.9)) Text("Say hi to Clawd") .font(.headline) - Text(self.viewModel.healthOK ? "This is the native SwiftUI debug chat." : "Connecting to the gateway…") + Text(self.viewModel + .healthOK ? "This is the native SwiftUI debug chat." : "Connecting to the gateway…") .font(.subheadline) .foregroundStyle(.secondary) } @@ -458,7 +461,9 @@ struct WebChatView: View { } else { ForEach(self.viewModel.messages) { msg in MessageBubble(message: msg) - .frame(maxWidth: .infinity, alignment: msg.role.lowercased() == "user" ? .trailing : .leading) + .frame( + maxWidth: .infinity, + alignment: msg.role.lowercased() == "user" ? .trailing : .leading) } } @@ -673,7 +678,7 @@ private struct ChatMessageBody: View { switch block.kind { case .text: MarkdownTextView(text: block.text) - case .code(let language): + case let .code(language): CodeBlockView(code: block.text, language: language) } } @@ -747,7 +752,7 @@ private struct AttachmentRow: View { var body: some View { HStack(spacing: 8) { Image(systemName: "paperclip") - Text(att.fileName ?? "Attachment") + Text(self.att.fileName ?? "Attachment") .font(.footnote) .lineLimit(1) Spacer() diff --git a/apps/macos/Sources/ClawdisCLI/BrowserCLI.swift b/apps/macos/Sources/ClawdisCLI/BrowserCLI.swift index 00f25354a..38e292027 100644 --- a/apps/macos/Sources/ClawdisCLI/BrowserCLI.swift +++ b/apps/macos/Sources/ClawdisCLI/BrowserCLI.swift @@ -83,25 +83,34 @@ enum BrowserCLI { do { switch sub { case "status": - self.printResult( + try await self.printResult( jsonOutput: jsonOutput, - res: try await self.httpJSON(method: "GET", url: baseURL.appendingPathComponent("/"))) + res: self.httpJSON(method: "GET", url: baseURL.appendingPathComponent("/"))) return 0 case "start": - self.printResult( + try await self.printResult( jsonOutput: jsonOutput, - res: try await self.httpJSON(method: "POST", url: baseURL.appendingPathComponent("/start"), timeoutInterval: 15.0)) + res: self.httpJSON( + method: "POST", + url: baseURL.appendingPathComponent("/start"), + timeoutInterval: 15.0)) return 0 case "stop": - self.printResult( + try await self.printResult( jsonOutput: jsonOutput, - res: try await self.httpJSON(method: "POST", url: baseURL.appendingPathComponent("/stop"), timeoutInterval: 15.0)) + res: self.httpJSON( + method: "POST", + url: baseURL.appendingPathComponent("/stop"), + timeoutInterval: 15.0)) return 0 case "tabs": - let res = try await self.httpJSON(method: "GET", url: baseURL.appendingPathComponent("/tabs"), timeoutInterval: 3.0) + let res = try await self.httpJSON( + method: "GET", + url: baseURL.appendingPathComponent("/tabs"), + timeoutInterval: 3.0) if jsonOutput { self.printJSON(ok: true, result: res) } else { @@ -114,9 +123,9 @@ enum BrowserCLI { self.printHelp() return 2 } - self.printResult( + try await self.printResult( jsonOutput: jsonOutput, - res: try await self.httpJSON( + res: self.httpJSON( method: "POST", url: baseURL.appendingPathComponent("/tabs/open"), body: ["url": url], @@ -128,9 +137,9 @@ enum BrowserCLI { self.printHelp() return 2 } - self.printResult( + try await self.printResult( jsonOutput: jsonOutput, - res: try await self.httpJSON( + res: self.httpJSON( method: "POST", url: baseURL.appendingPathComponent("/tabs/focus"), body: ["targetId": id], @@ -142,9 +151,9 @@ enum BrowserCLI { self.printHelp() return 2 } - self.printResult( + try await self.printResult( jsonOutput: jsonOutput, - res: try await self.httpJSON( + res: self.httpJSON( method: "DELETE", url: baseURL.appendingPathComponent("/tabs/\(id)"), timeoutInterval: 5.0)) @@ -345,8 +354,8 @@ enum BrowserCLI { method: String, url: URL, body: [String: Any]? = nil, - timeoutInterval: TimeInterval = 2.0 - ) async throws -> [String: Any] { + timeoutInterval: TimeInterval = 2.0) async throws -> [String: Any] + { var req = URLRequest(url: url, timeoutInterval: timeoutInterval) req.httpMethod = method if let body { @@ -369,7 +378,7 @@ enum BrowserCLI { ]) } - if status >= 200 && status < 300 { + if status >= 200, status < 300 { return obj } @@ -517,11 +526,11 @@ enum BrowserCLI { } } -#if SWIFT_PACKAGE + #if SWIFT_PACKAGE static func _testFormatTabs(res: [String: Any]) -> [String] { self.formatTabs(res: res) } -#endif + #endif private static func printJSON(ok: Bool, result: Any) { let obj: [String: Any] = ["ok": ok, "result": result] diff --git a/apps/macos/Sources/ClawdisCLI/UICLI.swift b/apps/macos/Sources/ClawdisCLI/UICLI.swift index 0c987d0c3..0e135ae95 100644 --- a/apps/macos/Sources/ClawdisCLI/UICLI.swift +++ b/apps/macos/Sources/ClawdisCLI/UICLI.swift @@ -1,5 +1,5 @@ -import Foundation import Darwin +import Foundation import PeekabooAutomationKit import PeekabooBridge import PeekabooFoundation @@ -94,7 +94,7 @@ enum UICLI { private static func runPermissions(args: [String], jsonOutput: Bool, context: Context) async throws -> Int32 { let sub = args.first ?? "status" - if sub != "status" && sub != "--help" && sub != "-h" && sub != "help" { + if sub != "status", sub != "--help", sub != "-h", sub != "help" { self.printHelp() return 1 } @@ -103,7 +103,7 @@ enum UICLI { try self.writeJSON([ "ok": true, "host": context.hostDescription, - "result": try self.toJSONObject(status), + "result": self.toJSONObject(status), ]) } else { FileHandle.standardOutput.write(Data((self.formatPermissions(status) + "\n").utf8)) @@ -123,7 +123,7 @@ enum UICLI { try self.writeJSON([ "ok": true, "host": context.hostDescription, - "app": try self.toJSONObject(app), + "app": self.toJSONObject(app), "window": windowObject, ]) } else { @@ -131,7 +131,7 @@ enum UICLI { let line = "\(bundle) (pid \(app.processIdentifier))" FileHandle.standardOutput.write(Data((line + "\n").utf8)) if let window { - FileHandle.standardOutput.write(Data(("window \(window.windowID): \(window.title)\n").utf8)) + FileHandle.standardOutput.write(Data("window \(window.windowID): \(window.title)\n".utf8)) } } return 0 @@ -143,12 +143,12 @@ enum UICLI { try self.writeJSON([ "ok": true, "host": context.hostDescription, - "result": try self.toJSONObject(apps), + "result": self.toJSONObject(apps), ]) } else { for app in apps { let bundle = app.bundleIdentifier ?? "" - FileHandle.standardOutput.write(Data(("\(bundle)\t\(app.name)\n").utf8)) + FileHandle.standardOutput.write(Data("\(bundle)\t\(app.name)\n".utf8)) } } return 0 @@ -176,11 +176,11 @@ enum UICLI { try self.writeJSON([ "ok": true, "host": context.hostDescription, - "result": try self.toJSONObject(windows), + "result": self.toJSONObject(windows), ]) } else { for window in windows { - FileHandle.standardOutput.write(Data(("\(window.windowID)\t\(window.title)\n").utf8)) + FileHandle.standardOutput.write(Data("\(window.windowID)\t\(window.title)\n".utf8)) } } return 0 @@ -217,20 +217,19 @@ enum UICLI { } } - let capture: CaptureResult - if let bundleId, !bundleId.isEmpty { - capture = try await context.client.captureWindow( + let capture: CaptureResult = if let bundleId, !bundleId.isEmpty { + try await context.client.captureWindow( appIdentifier: bundleId, windowIndex: windowIndex, visualizerMode: mode, scale: scale) } else if displayIndex != nil { - capture = try await context.client.captureScreen( + try await context.client.captureScreen( displayIndex: displayIndex, visualizerMode: mode, scale: scale) } else { - capture = try await context.client.captureFrontmost(visualizerMode: mode, scale: scale) + try await context.client.captureFrontmost(visualizerMode: mode, scale: scale) } let path = try self.writeTempPNG(capture.imageData) @@ -240,7 +239,7 @@ enum UICLI { "ok": true, "host": context.hostDescription, "path": path, - "metadata": try self.toJSONObject(capture.metadata), + "metadata": self.toJSONObject(capture.metadata), "warning": capture.warning ?? "", ]) } else { @@ -287,7 +286,8 @@ enum UICLI { let resolvedSnapshotId: String = if let snapshotId, !snapshotId.isEmpty { snapshotId } else if let bundleId, !bundleId.isEmpty, let existing = try? await context.client - .getMostRecentSnapshot(applicationBundleId: bundleId) { + .getMostRecentSnapshot(applicationBundleId: bundleId) + { existing } else { try await context.client.createSnapshot() @@ -321,7 +321,7 @@ enum UICLI { "host": context.hostDescription, "snapshotId": resolvedSnapshotId, "screenshotPath": screenshotPath, - "result": try self.toJSONObject(detection), + "result": self.toJSONObject(detection), ]) } else { FileHandle.standardOutput.write(Data((screenshotPath + "\n").utf8)) @@ -494,7 +494,7 @@ enum UICLI { try self.writeJSON([ "ok": true, "host": context.hostDescription, - "result": try self.toJSONObject(result), + "result": self.toJSONObject(result), ]) } else { FileHandle.standardOutput.write(Data((result.found ? "found\n" : "not found\n").utf8)) @@ -549,7 +549,7 @@ enum UICLI { return "\(sr) \(ax) \(ascr)" } - private static func toJSONObject(_ value: T) throws -> Any { + private static func toJSONObject(_ value: some Encodable) throws -> Any { let encoder = JSONEncoder() encoder.dateEncodingStrategy = .iso8601 let data = try encoder.encode(value) diff --git a/package.json b/package.json index da17ddfff..877e2e490 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,10 @@ "clawdis": "tsx src/index.ts", "clawdis:rpc": "tsx src/index.ts agent --mode rpc --json", "lint": "biome check src", + "lint:swift": "swiftlint lint --config .swiftlint.yml && (cd apps/ios && swiftlint lint --config .swiftlint.yml)", "lint:fix": "biome check --write --unsafe src && biome format --write src", "format": "biome format src", + "format:swift": "swiftformat --lint --config .swiftformat apps/macos/Sources apps/ios/Sources apps/shared/ClawdisKit/Sources", "format:fix": "biome format src --write", "test": "vitest", "test:force": "tsx scripts/test-force.ts",