From 215803b4d4e29e50f82ba1b2f47b88d19ad61086 Mon Sep 17 00:00:00 2001 From: mannyrepos Date: Mon, 26 Jan 2026 01:40:21 -0800 Subject: [PATCH] Add native macOS control panel for non-technical users Adds a SwiftUI-based GUI application that provides: - Start/Stop/Restart controls for the gateway - Real-time status monitoring (CPU, memory, uptime) - Log viewer with search - Health check integration (clawdbot doctor) - Channel status display - Menu bar quick access - Auto-start on login option - System notifications Built for users who prefer a graphical interface over terminal commands. --- contrib/macos-control-panel/LICENSE | 21 + contrib/macos-control-panel/Makefile | 13 + contrib/macos-control-panel/README.md | 129 +++ contrib/macos-control-panel/build.sh | 41 + contrib/macos-control-panel/install.sh | 23 + .../resources/AppIcon.icns | Bin 0 -> 120048 bytes .../resources/icon_1024.png | Bin 0 -> 10583 bytes contrib/macos-control-panel/src/Info.plist | 38 + contrib/macos-control-panel/src/main.swift | 965 ++++++++++++++++++ 9 files changed, 1230 insertions(+) create mode 100644 contrib/macos-control-panel/LICENSE create mode 100644 contrib/macos-control-panel/Makefile create mode 100644 contrib/macos-control-panel/README.md create mode 100755 contrib/macos-control-panel/build.sh create mode 100755 contrib/macos-control-panel/install.sh create mode 100644 contrib/macos-control-panel/resources/AppIcon.icns create mode 100644 contrib/macos-control-panel/resources/icon_1024.png create mode 100644 contrib/macos-control-panel/src/Info.plist create mode 100644 contrib/macos-control-panel/src/main.swift diff --git a/contrib/macos-control-panel/LICENSE b/contrib/macos-control-panel/LICENSE new file mode 100644 index 000000000..b77bf2ab7 --- /dev/null +++ b/contrib/macos-control-panel/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/contrib/macos-control-panel/Makefile b/contrib/macos-control-panel/Makefile new file mode 100644 index 000000000..015d648b0 --- /dev/null +++ b/contrib/macos-control-panel/Makefile @@ -0,0 +1,13 @@ +.PHONY: build install clean + +build: + @./build.sh + +install: + @./install.sh + +clean: + rm -rf build/ + +run: build + open "build/Clawdbot Control.app" diff --git a/contrib/macos-control-panel/README.md b/contrib/macos-control-panel/README.md new file mode 100644 index 000000000..6130d27a3 --- /dev/null +++ b/contrib/macos-control-panel/README.md @@ -0,0 +1,129 @@ +# Clawdbot Control + +A native macOS application for managing Clawdbot. Provides a graphical interface for users who prefer not to use the command line. + +## Requirements + +- macOS 13.0 (Ventura) or later +- Apple Silicon (ARM64) Mac +- [Clawdbot](https://github.com/clawdbot/clawdbot) installed via Homebrew at `/opt/homebrew/bin/clawdbot` + +## Installation + +### Quick Install + +```bash +./install.sh +``` + +### Manual Build + +```bash +./build.sh +cp -R build/Clawdbot\ Control.app /Applications/ +``` + +## Features + +### Main Dashboard + +- Start, stop, and restart the Clawdbot gateway +- View running status with uptime display +- Monitor CPU and memory usage +- View and search logs in real-time +- Run health checks (`clawdbot doctor`) +- View channel status (WhatsApp, Signal) + +### Menu Bar + +- Quick access from the menu bar icon +- Start/Stop/Restart controls +- Open dashboard +- Run health check +- Toggle auto-start + +### Settings + +- Auto-start on login (via LaunchAgent) +- System notifications for status changes +- Configurable refresh interval +- Log display options + +## Usage + +### Opening the App + +- **Spotlight**: Press `Cmd+Space`, type "Clawdbot", press Enter +- **Menu Bar**: Click the antenna icon in the top-right corner +- **Applications**: Double-click in `/Applications/Clawdbot Control.app` + +### Basic Controls + +| Action | Description | +|--------|-------------| +| Start | Starts the Clawdbot gateway | +| Stop | Stops the Clawdbot gateway | +| Restart | Stops and starts the gateway | +| Dashboard | Opens the web dashboard at http://127.0.0.1:18789 | +| Health Check | Runs `clawdbot doctor` diagnostics | + +### Auto-Start on Login + +Enable auto-start from the Settings sidebar. This creates a LaunchAgent at: +``` +~/Library/LaunchAgents/com.clawdbot.autostart.plist +``` + +## File Structure + +``` +clawdbot-control-app/ +├── src/ +│ ├── main.swift # SwiftUI application source +│ └── Info.plist # Application metadata +├── resources/ +│ ├── AppIcon.icns # Application icon +│ └── icon_1024.png # Icon source image +├── build.sh # Build script +├── install.sh # Build and install script +└── README.md +``` + +## Configuration + +The app reads Clawdbot configuration from: +``` +~/.clawdbot/clawdbot.json +``` + +Logs are read from: +``` +/tmp/clawdbot/clawdbot-YYYY-MM-DD.log +``` + +## Technical Details + +- **Language**: Swift 5 with SwiftUI +- **Frameworks**: AppKit, SwiftUI, UserNotifications +- **Architecture**: Native ARM64 +- **Bundle ID**: com.clawdbot.control + +## Troubleshooting + +### App does not detect Clawdbot + +Ensure Clawdbot is installed at `/opt/homebrew/bin/clawdbot`. The app expects this path for Homebrew installations on Apple Silicon Macs. + +### Menu bar icon not appearing + +The app must be running for the menu bar icon to appear. Check Activity Monitor for "ClawdbotControl" process. + +### Auto-start not working + +1. Check that the LaunchAgent file exists at `~/Library/LaunchAgents/com.clawdbot.autostart.plist` +2. Run `launchctl list | grep clawdbot` to verify it is loaded +3. Check Console.app for any error messages + +## License + +MIT License diff --git a/contrib/macos-control-panel/build.sh b/contrib/macos-control-panel/build.sh new file mode 100755 index 000000000..b34f273ac --- /dev/null +++ b/contrib/macos-control-panel/build.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +APP_NAME="Clawdbot Control" +BUNDLE_NAME="Clawdbot Control.app" +BUILD_DIR="$SCRIPT_DIR/build" + +echo "Building $APP_NAME..." + +# Create build directory +rm -rf "$BUILD_DIR" +mkdir -p "$BUILD_DIR" + +# Compile Swift source +echo "Compiling Swift source..." +swiftc -parse-as-library \ + -o "$BUILD_DIR/ClawdbotControl" \ + "$SCRIPT_DIR/src/main.swift" \ + -framework AppKit \ + -framework SwiftUI \ + -framework UserNotifications + +# Create app bundle structure +echo "Creating app bundle..." +mkdir -p "$BUILD_DIR/$BUNDLE_NAME/Contents/MacOS" +mkdir -p "$BUILD_DIR/$BUNDLE_NAME/Contents/Resources" + +# Copy files +cp "$BUILD_DIR/ClawdbotControl" "$BUILD_DIR/$BUNDLE_NAME/Contents/MacOS/" +cp "$SCRIPT_DIR/src/Info.plist" "$BUILD_DIR/$BUNDLE_NAME/Contents/" +cp "$SCRIPT_DIR/resources/AppIcon.icns" "$BUILD_DIR/$BUNDLE_NAME/Contents/Resources/" + +# Set executable permission +chmod +x "$BUILD_DIR/$BUNDLE_NAME/Contents/MacOS/ClawdbotControl" + +echo "" +echo "Build complete: $BUILD_DIR/$BUNDLE_NAME" +echo "" +echo "To install, run:" +echo " cp -R \"$BUILD_DIR/$BUNDLE_NAME\" /Applications/" diff --git a/contrib/macos-control-panel/install.sh b/contrib/macos-control-panel/install.sh new file mode 100755 index 000000000..f12fcfc27 --- /dev/null +++ b/contrib/macos-control-panel/install.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# Build first +"$SCRIPT_DIR/build.sh" + +# Install to Applications +echo "Installing to /Applications..." +rm -rf "/Applications/Clawdbot Control.app" +cp -R "$SCRIPT_DIR/build/Clawdbot Control.app" /Applications/ + +# Register with Launch Services +/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f "/Applications/Clawdbot Control.app" + +echo "" +echo "Installation complete." +echo "" +echo "You can now:" +echo " - Open from Spotlight (Cmd+Space, type 'Clawdbot')" +echo " - Open from Applications folder" +echo " - Use the menu bar icon" diff --git a/contrib/macos-control-panel/resources/AppIcon.icns b/contrib/macos-control-panel/resources/AppIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..60f421636a5f82f7fc2069cedaad87c2041e115d GIT binary patch literal 120048 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
  • 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 + + + + CFBundleExecutable + ClawdbotControl + CFBundleIconFile + AppIcon + CFBundleIdentifier + com.clawdbot.control + CFBundleName + Clawdbot Control + CFBundleDisplayName + Clawdbot Control + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + 1 + CFBundlePackageType + APPL + CFBundleInfoDictionaryVersion + 6.0 + LSMinimumSystemVersion + 13.0 + LSUIElement + + NSHighResolutionCapable + + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + LSApplicationCategoryType + public.app-category.utilities + NSAppleEventsUsageDescription + Clawdbot Control needs to run shell commands to manage the Clawdbot gateway. + + diff --git a/contrib/macos-control-panel/src/main.swift b/contrib/macos-control-panel/src/main.swift new file mode 100644 index 000000000..eaa92b114 --- /dev/null +++ b/contrib/macos-control-panel/src/main.swift @@ -0,0 +1,965 @@ +import SwiftUI +import AppKit +import UserNotifications + +@main +struct ClawdbotControlApp: App { + @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + @StateObject private var manager = ClawdbotManager() + + var body: some Scene { + WindowGroup { + DashboardView() + .environmentObject(manager) + .frame(minWidth: 680, minHeight: 700) + } + .windowStyle(.titleBar) + .windowToolbarStyle(.unified(showsTitle: true)) + + MenuBarExtra("Clawdbot", systemImage: manager.isRunning ? "antenna.radiowaves.left.and.right.circle.fill" : "antenna.radiowaves.left.and.right.circle") { + MenuBarView() + .environmentObject(manager) + } + .menuBarExtraStyle(.menu) + } +} + +class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + NSApp.setActivationPolicy(.regular) + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { _, _ in } + } + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { false } +} + +// MARK: - Settings Store +class SettingsStore: ObservableObject { + @AppStorage("autoStart") var autoStart = false + @AppStorage("showMenuBar") var showMenuBar = true + @AppStorage("notificationsEnabled") var notificationsEnabled = true + @AppStorage("refreshInterval") var refreshInterval = 4.0 + @AppStorage("showResourcesInFooter") var showResourcesInFooter = true + @AppStorage("compactLogs") var compactLogs = true + @AppStorage("logLines") var logLines = 30 +} + +// MARK: - Manager +class ClawdbotManager: ObservableObject { + @Published var isRunning = false + @Published var version = "" + @Published var logs: [String] = [] + @Published var channels: [(name: String, enabled: Bool)] = [] + @Published var startedAt: Date? + @Published var autoStartEnabled = false + @Published var cpuUsage: Double = 0 + @Published var memoryUsage: Double = 0 + @Published var isRestarting = false + @Published var lastChecked = Date() + @Published var healthStatus: String = "Unknown" + @Published var healthDetails: [String] = [] + @Published var isCheckingHealth = false + @Published var showSettings = false + + let settings = SettingsStore() + private var timer: Timer? + private let path = "/opt/homebrew/bin/clawdbot" + private var pid: Int32? + private var wasRunning = false + + init() { + checkAutoStart() + refresh() + startTimer() + } + + func startTimer() { + timer?.invalidate() + timer = Timer.scheduledTimer(withTimeInterval: settings.refreshInterval, repeats: true) { [weak self] _ in + self?.refresh() + } + } + + func refresh() { + DispatchQueue.global(qos: .utility).async { [weak self] in + self?.checkStatus() + self?.loadVersion() + self?.loadChannels() + self?.loadLogs() + self?.loadResources() + DispatchQueue.main.async { self?.lastChecked = Date() } + } + } + + private func checkStatus() { + let result = shell("pgrep -f 'clawdbot-gateway' 2>/dev/null").trimmingCharacters(in: .whitespacesAndNewlines) + let running = !result.isEmpty + if let p = Int32(result.components(separatedBy: "\n").first ?? "") { pid = p } + + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + if running && !self.isRunning { self.startedAt = Date() } + if !running { self.startedAt = nil; self.pid = nil } + if self.wasRunning != running && self.settings.notificationsEnabled { self.notify(running: running) } + self.wasRunning = running + self.isRunning = running + } + } + + private func loadVersion() { + let v = shell("\(path) --version 2>&1 | head -1").trimmingCharacters(in: .whitespacesAndNewlines) + DispatchQueue.main.async { [weak self] in self?.version = v } + } + + private func loadChannels() { + let configPath = NSHomeDirectory() + "/.clawdbot/clawdbot.json" + guard let data = FileManager.default.contents(atPath: configPath), + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let plugins = json["plugins"] as? [String: Any], + let entries = plugins["entries"] as? [String: Any] else { return } + + var ch: [(String, Bool)] = [] + if let w = entries["whatsapp"] as? [String: Any] { ch.append(("WhatsApp", w["enabled"] as? Bool ?? false)) } + if let s = entries["signal"] as? [String: Any] { ch.append(("Signal", s["enabled"] as? Bool ?? false)) } + DispatchQueue.main.async { [weak self] in self?.channels = ch } + } + + private func loadLogs() { + let df = DateFormatter(); df.dateFormat = "yyyy-MM-dd" + let logPath = "/tmp/clawdbot/clawdbot-\(df.string(from: Date())).log" + let lines = Int(settings.logLines) + let result = shell("tail -\(lines) '\(logPath)' 2>/dev/null") + let logLines = result.components(separatedBy: "\n").filter { !$0.isEmpty } + DispatchQueue.main.async { [weak self] in self?.logs = Array(logLines.suffix(lines)) } + } + + private func loadResources() { + guard let p = pid else { + DispatchQueue.main.async { [weak self] in self?.cpuUsage = 0; self?.memoryUsage = 0 } + return + } + let result = shell("ps -p \(p) -o %cpu,%mem 2>/dev/null | tail -1") + let parts = result.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: .whitespaces).filter { !$0.isEmpty } + DispatchQueue.main.async { [weak self] in + self?.cpuUsage = Double(parts.first ?? "0") ?? 0 + self?.memoryUsage = Double(parts.dropFirst().first ?? "0") ?? 0 + } + } + + func start() { + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + _ = self.shell("\(self.path) gateway start >/dev/null 2>&1 &") + sleep(2) + self.refresh() + } + } + + func stop() { + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + _ = self.shell("\(self.path) gateway stop >/dev/null 2>&1") + sleep(1) + self.refresh() + } + } + + func restart() { + DispatchQueue.main.async { self.isRestarting = true } + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + _ = self.shell("\(self.path) gateway stop >/dev/null 2>&1") + sleep(2) + _ = self.shell("\(self.path) gateway start >/dev/null 2>&1 &") + sleep(2) + DispatchQueue.main.async { self.isRestarting = false } + self.refresh() + } + } + + func runHealthCheck() { + DispatchQueue.main.async { self.isCheckingHealth = true } + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + let result = self.shell("\(self.path) doctor 2>&1") + let lines = result.components(separatedBy: "\n").filter { !$0.isEmpty } + DispatchQueue.main.async { + self.healthDetails = Array(lines.prefix(10)) + if result.lowercased().contains("error") || result.lowercased().contains("fail") { + self.healthStatus = "Issues Found" + } else if result.lowercased().contains("warn") { + self.healthStatus = "Warnings" + } else { + self.healthStatus = "Healthy" + } + self.isCheckingHealth = false + } + } + } + + func openDashboard() { NSWorkspace.shared.open(URL(string: "http://127.0.0.1:18789/")!) } + func openConfig() { NSWorkspace.shared.selectFile(NSHomeDirectory() + "/.clawdbot/clawdbot.json", inFileViewerRootedAtPath: "") } + func openLogs() { + let df = DateFormatter(); df.dateFormat = "yyyy-MM-dd" + let logPath = "/tmp/clawdbot/clawdbot-\(df.string(from: Date())).log" + NSWorkspace.shared.selectFile(logPath, inFileViewerRootedAtPath: "") + } + + func checkAutoStart() { + let exists = FileManager.default.fileExists(atPath: NSHomeDirectory() + "/Library/LaunchAgents/com.clawdbot.autostart.plist") + DispatchQueue.main.async { [weak self] in + self?.autoStartEnabled = exists + self?.settings.autoStart = exists + } + } + + func setAutoStart(_ on: Bool) { + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + let dir = NSHomeDirectory() + "/Library/LaunchAgents" + let plist = dir + "/com.clawdbot.autostart.plist" + if on { + try? FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true) + let content = """ + + + +Labelcom.clawdbot.autostart +ProgramArguments\(self.path)gatewaystart +RunAtLoad + +""" + try? content.write(toFile: plist, atomically: true, encoding: .utf8) + _ = self.shell("launchctl load '\(plist)' 2>/dev/null") + } else { + _ = self.shell("launchctl unload '\(plist)' 2>/dev/null") + try? FileManager.default.removeItem(atPath: plist) + } + self.checkAutoStart() + } + } + + private func notify(running: Bool) { + let c = UNMutableNotificationContent() + c.title = running ? "Clawdbot Started" : "Clawdbot Stopped" + c.sound = running ? .default : .defaultCritical + UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: UUID().uuidString, content: c, trigger: nil)) + } + + func shell(_ cmd: String) -> String { + let task = Process(); let pipe = Pipe() + task.standardOutput = pipe; task.standardError = pipe + task.launchPath = "/bin/zsh"; task.arguments = ["-c", cmd] + try? task.run(); task.waitUntilExit() + return String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" + } +} + +// MARK: - Menu Bar +struct MenuBarView: View { + @EnvironmentObject var m: ClawdbotManager + var body: some View { + Group { + Label(m.isRunning ? "Running" : "Stopped", systemImage: m.isRunning ? "circle.fill" : "circle") + Divider() + if m.isRunning { + Button("Stop") { m.stop() } + Button("Restart") { m.restart() }.disabled(m.isRestarting) + } else { + Button("Start") { m.start() } + } + Button("Dashboard") { m.openDashboard() }.disabled(!m.isRunning) + Divider() + Button("Run Health Check") { m.runHealthCheck() } + Button(m.autoStartEnabled ? "Disable Auto-start" : "Enable Auto-start") { m.setAutoStart(!m.autoStartEnabled) } + Divider() + Button("Show Window") { NSApp.activate(ignoringOtherApps: true); NSApp.windows.first?.makeKeyAndOrderFront(nil) } + Button("Quit") { NSApp.terminate(nil) }.keyboardShortcut("q") + } + } +} + +// MARK: - Dashboard View +struct DashboardView: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + HStack(spacing: 0) { + // Main Content + ScrollView { + VStack(spacing: 16) { + HeaderCard().environmentObject(m) + + HStack(spacing: 12) { + StatusCard(title: "Status", value: m.isRunning ? "Running" : "Stopped", icon: "power", color: m.isRunning ? .green : .red) + StatusCard(title: "Port", value: "18789", icon: "network", color: .blue) + StatusCard(title: "Channels", value: "\(m.channels.filter { $0.enabled }.count) active", icon: "message.fill", color: .purple) + StatusCard(title: "Health", value: m.healthStatus, icon: healthIcon, color: healthColor) + } + + InfoCard().environmentObject(m) + QuickActionsCard().environmentObject(m) + ResourceCard().environmentObject(m) + HealthCard().environmentObject(m) + ChannelsCard().environmentObject(m) + LogsCard().environmentObject(m) + FooterView().environmentObject(m) + } + .padding(20) + } + .background(Color(nsColor: .windowBackgroundColor)) + + // Settings Sidebar + if m.showSettings { + Divider() + SettingsSidebar() + .environmentObject(m) + .frame(width: 220) + .transition(.move(edge: .trailing)) + } + } + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button(action: { withAnimation(.easeInOut(duration: 0.2)) { m.showSettings.toggle() } }) { + Image(systemName: "gear") + } + .help("Settings") + } + } + } + + var healthIcon: String { + switch m.healthStatus { + case "Healthy": return "checkmark.circle.fill" + case "Warnings": return "exclamationmark.triangle.fill" + case "Issues Found": return "xmark.circle.fill" + default: return "questionmark.circle" + } + } + + var healthColor: Color { + switch m.healthStatus { + case "Healthy": return .green + case "Warnings": return .orange + case "Issues Found": return .red + default: return .gray + } + } +} + +// MARK: - Settings Sidebar +struct SettingsSidebar: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + Text("Settings") + .font(.headline) + .padding(.bottom, 4) + + // Startup + VStack(alignment: .leading, spacing: 10) { + Label("Startup", systemImage: "power") + .font(.subheadline.weight(.medium)) + .foregroundStyle(.secondary) + + Toggle("Auto-start on login", isOn: Binding( + get: { m.autoStartEnabled }, + set: { m.setAutoStart($0) } + )) + .toggleStyle(.switch) + .controlSize(.small) + } + + Divider() + + // Notifications + VStack(alignment: .leading, spacing: 10) { + Label("Notifications", systemImage: "bell") + .font(.subheadline.weight(.medium)) + .foregroundStyle(.secondary) + + Toggle("Enable notifications", isOn: m.settings.$notificationsEnabled) + .toggleStyle(.switch) + .controlSize(.small) + } + + Divider() + + // Display + VStack(alignment: .leading, spacing: 10) { + Label("Display", systemImage: "rectangle.3.group") + .font(.subheadline.weight(.medium)) + .foregroundStyle(.secondary) + + Toggle("Show resources in footer", isOn: m.settings.$showResourcesInFooter) + .toggleStyle(.switch) + .controlSize(.small) + + Toggle("Compact log view", isOn: m.settings.$compactLogs) + .toggleStyle(.switch) + .controlSize(.small) + } + + Divider() + + // Refresh + VStack(alignment: .leading, spacing: 10) { + Label("Refresh", systemImage: "arrow.clockwise") + .font(.subheadline.weight(.medium)) + .foregroundStyle(.secondary) + + HStack { + Text("Interval") + .font(.caption) + Spacer() + Picker("", selection: m.settings.$refreshInterval) { + Text("2s").tag(2.0) + Text("4s").tag(4.0) + Text("10s").tag(10.0) + Text("30s").tag(30.0) + } + .pickerStyle(.menu) + .frame(width: 70) + .onChange(of: m.settings.refreshInterval) { _ in + m.startTimer() + } + } + + HStack { + Text("Log lines") + .font(.caption) + Spacer() + Picker("", selection: m.settings.$logLines) { + Text("10").tag(10) + Text("30").tag(30) + Text("50").tag(50) + Text("100").tag(100) + } + .pickerStyle(.menu) + .frame(width: 70) + } + } + + Divider() + + // Actions + VStack(alignment: .leading, spacing: 10) { + Label("Actions", systemImage: "folder") + .font(.subheadline.weight(.medium)) + .foregroundStyle(.secondary) + + Button(action: { m.openConfig() }) { + Label("Open Config File", systemImage: "doc.text") + .frame(maxWidth: .infinity, alignment: .leading) + } + .buttonStyle(.bordered) + .controlSize(.small) + + Button(action: { m.openLogs() }) { + Label("Open Log File", systemImage: "doc.plaintext") + .frame(maxWidth: .infinity, alignment: .leading) + } + .buttonStyle(.bordered) + .controlSize(.small) + + Button(action: { openTerminal() }) { + Label("Open Terminal", systemImage: "terminal") + .frame(maxWidth: .infinity, alignment: .leading) + } + .buttonStyle(.bordered) + .controlSize(.small) + } + + Divider() + + // About + VStack(alignment: .leading, spacing: 6) { + Label("About", systemImage: "info.circle") + .font(.subheadline.weight(.medium)) + .foregroundStyle(.secondary) + + Text(m.version.isEmpty ? "Clawdbot" : m.version) + .font(.caption) + .foregroundStyle(.secondary) + + Text("Control App v1.0") + .font(.caption) + .foregroundStyle(.tertiary) + } + + Spacer() + } + .padding(16) + } + .background(.regularMaterial) + } + + func openTerminal() { + NSAppleScript(source: "tell application \"Terminal\" to do script \"cd ~/.clawdbot && clawdbot\"")?.executeAndReturnError(nil) + } +} + +// MARK: - Header Card +struct HeaderCard: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + HStack(spacing: 16) { + ZStack { + Circle() + .fill(m.isRunning ? Color.green.opacity(0.2) : Color.red.opacity(0.2)) + .frame(width: 60, height: 60) + Circle() + .fill(m.isRunning ? Color.green : Color.red) + .frame(width: 20, height: 20) + .shadow(color: m.isRunning ? .green.opacity(0.5) : .red.opacity(0.5), radius: 8) + } + + VStack(alignment: .leading, spacing: 4) { + Text("Clawdbot") + .font(.title.weight(.bold)) + HStack(spacing: 6) { + Circle() + .fill(m.isRunning ? Color.green : Color.red) + .frame(width: 8, height: 8) + Text(m.isRunning ? "Running" : "Stopped") + .font(.subheadline) + .foregroundStyle(.secondary) + if let started = m.startedAt { + Text("•").foregroundStyle(.tertiary) + Text("Uptime: \(formatUptime(started))") + .font(.subheadline) + .foregroundStyle(.secondary) + } + } + } + + Spacer() + + HStack(spacing: 8) { + Button(action: { m.isRunning ? m.stop() : m.start() }) { + Label(m.isRunning ? "Stop" : "Start", systemImage: m.isRunning ? "stop.fill" : "play.fill") + .frame(width: 70) + } + .buttonStyle(.borderedProminent) + .tint(m.isRunning ? .red : .green) + + Button(action: { m.restart() }) { + Label("Restart", systemImage: "arrow.clockwise") + } + .buttonStyle(.bordered) + .disabled(!m.isRunning || m.isRestarting) + } + } + .padding(16) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } + + func formatUptime(_ date: Date) -> String { + let s = Int(Date().timeIntervalSince(date)) + if s >= 3600 { return "\(s/3600)h \((s%3600)/60)m" } + if s >= 60 { return "\(s/60)m" } + return "\(s)s" + } +} + +// MARK: - Status Card +struct StatusCard: View { + let title: String + let value: String + let icon: String + let color: Color + + var body: some View { + VStack(spacing: 8) { + Image(systemName: icon) + .font(.title2) + .foregroundStyle(color) + Text(value) + .font(.system(.subheadline, weight: .semibold)) + .lineLimit(1) + Text(title) + .font(.caption) + .foregroundStyle(.secondary) + } + .frame(maxWidth: .infinity) + .padding(.vertical, 14) + .background(color.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) + } +} + +// MARK: - Info Card +struct InfoCard: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + Label("Information", systemImage: "info.circle") + .font(.headline) + .foregroundStyle(.secondary) + + Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 8) { + GridRow { + Text("Version").foregroundStyle(.secondary) + Text(m.version.isEmpty ? "Unknown" : m.version) + } + GridRow { + Text("Config Path").foregroundStyle(.secondary) + Text("~/.clawdbot/clawdbot.json").textSelection(.enabled) + } + GridRow { + Text("Dashboard").foregroundStyle(.secondary) + Link("http://127.0.0.1:18789/", destination: URL(string: "http://127.0.0.1:18789/")!) + } + GridRow { + Text("Auto-start").foregroundStyle(.secondary) + Text(m.autoStartEnabled ? "Enabled" : "Disabled") + .foregroundStyle(m.autoStartEnabled ? .green : .secondary) + } + GridRow { + Text("Last Checked").foregroundStyle(.secondary) + Text(formatTime(m.lastChecked)) + } + } + .font(.system(.body, design: .monospaced)) + } + .padding(16) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } + + func formatTime(_ date: Date) -> String { + let f = DateFormatter(); f.timeStyle = .medium + return f.string(from: date) + } +} + +// MARK: - Quick Actions Card +struct QuickActionsCard: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + Label("Quick Actions", systemImage: "bolt.fill") + .font(.headline) + .foregroundStyle(.secondary) + + HStack(spacing: 12) { + ActionButton(title: "Dashboard", icon: "globe", color: .blue) { m.openDashboard() } + .opacity(m.isRunning ? 1 : 0.5) + ActionButton(title: "Config", icon: "folder", color: .orange) { m.openConfig() } + ActionButton(title: "Health", icon: "stethoscope", color: .pink) { m.runHealthCheck() } + ActionButton(title: "Refresh", icon: "arrow.clockwise", color: .teal) { m.refresh() } + } + } + .padding(16) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } +} + +struct ActionButton: View { + let title: String + let icon: String + let color: Color + let action: () -> Void + + var body: some View { + Button(action: action) { + VStack(spacing: 8) { + Image(systemName: icon) + .font(.title2) + .foregroundStyle(color) + Text(title) + .font(.caption) + .foregroundStyle(.primary) + } + .frame(maxWidth: .infinity) + .padding(.vertical, 14) + .background(color.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) + } + .buttonStyle(.plain) + } +} + +// MARK: - Resource Card +struct ResourceCard: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + Label("Resource Usage", systemImage: "cpu") + .font(.headline) + .foregroundStyle(.secondary) + + HStack(spacing: 20) { + VStack(alignment: .leading, spacing: 6) { + HStack { + Text("CPU") + Spacer() + Text(String(format: "%.1f%%", m.cpuUsage)) + .font(.system(.body, design: .monospaced)) + .foregroundStyle(.secondary) + } + ProgressView(value: min(m.cpuUsage / 100, 1.0)) + .tint(m.cpuUsage > 80 ? .red : (m.cpuUsage > 50 ? .orange : .green)) + } + + VStack(alignment: .leading, spacing: 6) { + HStack { + Text("Memory") + Spacer() + Text(String(format: "%.1f%%", m.memoryUsage)) + .font(.system(.body, design: .monospaced)) + .foregroundStyle(.secondary) + } + ProgressView(value: min(m.memoryUsage / 100, 1.0)) + .tint(m.memoryUsage > 80 ? .red : (m.memoryUsage > 50 ? .orange : .blue)) + } + } + + if let started = m.startedAt { + HStack { + Text("Uptime") + Spacer() + Text(formatUptime(started)) + .font(.system(size: 24, weight: .medium, design: .rounded)) + .foregroundStyle(.green) + } + .padding(.top, 4) + } + } + .padding(16) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } + + func formatUptime(_ date: Date) -> String { + let s = Int(Date().timeIntervalSince(date)) + let d = s / 86400, h = (s % 86400) / 3600, m = (s % 3600) / 60, sec = s % 60 + if d > 0 { return String(format: "%dd %dh %dm", d, h, m) } + if h > 0 { return String(format: "%dh %dm %ds", h, m, sec) } + return String(format: "%dm %ds", m, sec) + } +} + +// MARK: - Health Card +struct HealthCard: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack { + Label("Health Check", systemImage: "stethoscope") + .font(.headline) + .foregroundStyle(.secondary) + Spacer() + Button(action: { m.runHealthCheck() }) { + if m.isCheckingHealth { + ProgressView().controlSize(.small) + } else { + Label("Run", systemImage: "arrow.clockwise") + } + } + .buttonStyle(.bordered) + .controlSize(.small) + .disabled(m.isCheckingHealth) + } + + HStack(spacing: 8) { + Image(systemName: healthIcon) + .foregroundStyle(healthColor) + Text(m.healthStatus) + .font(.subheadline.weight(.medium)) + } + + if !m.healthDetails.isEmpty { + Divider() + VStack(alignment: .leading, spacing: 4) { + ForEach(Array(m.healthDetails.prefix(5).enumerated()), id: \.offset) { _, line in + Text(line) + .font(.system(.caption, design: .monospaced)) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + } + } + .padding(16) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } + + var healthIcon: String { + switch m.healthStatus { + case "Healthy": return "checkmark.circle.fill" + case "Warnings": return "exclamationmark.triangle.fill" + case "Issues Found": return "xmark.circle.fill" + default: return "questionmark.circle" + } + } + + var healthColor: Color { + switch m.healthStatus { + case "Healthy": return .green + case "Warnings": return .orange + case "Issues Found": return .red + default: return .gray + } + } +} + +// MARK: - Channels Card +struct ChannelsCard: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + Label("Channels", systemImage: "antenna.radiowaves.left.and.right") + .font(.headline) + .foregroundStyle(.secondary) + + if m.channels.isEmpty { + Text("No channels configured") + .font(.subheadline) + .foregroundStyle(.tertiary) + .frame(maxWidth: .infinity, alignment: .center) + .padding(.vertical, 8) + } else { + HStack(spacing: 12) { + ForEach(m.channels, id: \.name) { ch in + HStack(spacing: 10) { + Image(systemName: ch.name == "WhatsApp" ? "message.fill" : "lock.shield.fill") + .font(.title3) + .foregroundStyle(ch.enabled ? .green : .secondary) + VStack(alignment: .leading, spacing: 2) { + Text(ch.name) + .font(.subheadline.weight(.medium)) + HStack(spacing: 4) { + Circle() + .fill(ch.enabled && m.isRunning ? Color.green : Color.gray.opacity(0.4)) + .frame(width: 6, height: 6) + Text(ch.enabled ? (m.isRunning ? "Connected" : "Enabled") : "Disabled") + .font(.caption) + .foregroundStyle(.secondary) + } + } + Spacer() + if ch.enabled { + Image(systemName: "checkmark.circle.fill") + .foregroundStyle(.green) + } + } + .padding(12) + .background(.quaternary.opacity(0.5), in: RoundedRectangle(cornerRadius: 10)) + } + } + } + } + .padding(16) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } +} + +// MARK: - Logs Card +struct LogsCard: View { + @EnvironmentObject var m: ClawdbotManager + @State private var expanded = false + @State private var searchText = "" + + var filteredLogs: [String] { + let logs = expanded ? m.logs : Array(m.logs.suffix(5)) + if searchText.isEmpty { return logs } + return logs.filter { $0.localizedCaseInsensitiveContains(searchText) } + } + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack { + Label("Logs", systemImage: "doc.text") + .font(.headline) + .foregroundStyle(.secondary) + Spacer() + Button(action: { withAnimation { expanded.toggle() } }) { + Image(systemName: expanded ? "chevron.up" : "chevron.down") + .foregroundStyle(.secondary) + } + .buttonStyle(.plain) + } + + if expanded { + HStack { + Image(systemName: "magnifyingglass") + .foregroundStyle(.tertiary) + TextField("Search logs...", text: $searchText) + .textFieldStyle(.plain) + } + .padding(8) + .background(.quaternary.opacity(0.5), in: RoundedRectangle(cornerRadius: 8)) + } + + if filteredLogs.isEmpty { + Text("No logs available") + .font(.caption) + .foregroundStyle(.tertiary) + .frame(maxWidth: .infinity, alignment: .center) + .padding(.vertical, 8) + } else { + VStack(alignment: .leading, spacing: m.settings.compactLogs ? 2 : 4) { + ForEach(Array(filteredLogs.enumerated()), id: \.offset) { _, log in + HStack(alignment: .top, spacing: 6) { + Circle() + .fill(logColor(log)) + .frame(width: 6, height: 6) + .padding(.top, 5) + Text(log) + .font(.system(m.settings.compactLogs ? .caption2 : .caption, design: .monospaced)) + .foregroundStyle(.secondary) + .lineLimit(expanded ? nil : 1) + } + } + } + } + } + .padding(16) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } + + func logColor(_ log: String) -> Color { + let l = log.lowercased() + if l.contains("error") || l.contains("fail") { return .red } + if l.contains("warn") { return .orange } + if l.contains("success") || l.contains("started") || l.contains("connected") { return .green } + return .gray + } +} + +// MARK: - Footer +struct FooterView: View { + @EnvironmentObject var m: ClawdbotManager + + var body: some View { + HStack { + if m.isRunning && m.settings.showResourcesInFooter { + HStack(spacing: 12) { + Label(String(format: "%.1f%%", m.cpuUsage), systemImage: "cpu") + Label(String(format: "%.1f%%", m.memoryUsage), systemImage: "memorychip") + } + .font(.caption) + .foregroundStyle(.secondary) + } + Spacer() + Text("Last updated: \(formatTime(m.lastChecked))") + .font(.caption) + .foregroundStyle(.tertiary) + Button(action: { m.refresh() }) { + Image(systemName: "arrow.clockwise") + } + .buttonStyle(.plain) + .foregroundStyle(.secondary) + } + .padding(.horizontal, 4) + } + + func formatTime(_ date: Date) -> String { + let f = DateFormatter(); f.timeStyle = .medium + return f.string(from: date) + } +}