Spaces:
Paused
Paused
| import OpenClawProtocol | |
| import SwiftUI | |
| struct AgentEventsWindow: View { | |
| private let store = AgentEventStore.shared | |
| var body: some View { | |
| VStack(alignment: .leading, spacing: 6) { | |
| HStack { | |
| Text("Agent Events") | |
| .font(.title3.weight(.semibold)) | |
| Spacer() | |
| Button("Clear") { self.store.clear() } | |
| .buttonStyle(.bordered) | |
| } | |
| .padding(.bottom, 4) | |
| ScrollView { | |
| LazyVStack(alignment: .leading, spacing: 8) { | |
| ForEach(self.store.events.reversed(), id: \.seq) { evt in | |
| EventRow(event: evt) | |
| } | |
| } | |
| } | |
| } | |
| .padding(12) | |
| .frame(minWidth: 520, minHeight: 360) | |
| } | |
| } | |
| private struct EventRow: View { | |
| let event: ControlAgentEvent | |
| var body: some View { | |
| VStack(alignment: .leading, spacing: 2) { | |
| HStack(spacing: 6) { | |
| Text(self.event.stream.uppercased()) | |
| .font(.caption2.weight(.bold)) | |
| .padding(.horizontal, 6) | |
| .padding(.vertical, 2) | |
| .background(self.tint) | |
| .foregroundStyle(Color.white) | |
| .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) | |
| Text("run " + self.event.runId) | |
| .font(.caption.monospaced()) | |
| .foregroundStyle(.secondary) | |
| Spacer() | |
| Text(self.formattedTs) | |
| .font(.caption2) | |
| .foregroundStyle(.secondary) | |
| } | |
| if let json = self.prettyJSON(event.data) { | |
| Text(json) | |
| .font(.caption.monospaced()) | |
| .foregroundStyle(.primary) | |
| .textSelection(.enabled) | |
| .frame(maxWidth: .infinity, alignment: .leading) | |
| .padding(.top, 2) | |
| } | |
| } | |
| .padding(8) | |
| .background( | |
| RoundedRectangle(cornerRadius: 8, style: .continuous) | |
| .fill(Color.primary.opacity(0.04))) | |
| } | |
| private var tint: Color { | |
| switch self.event.stream { | |
| case "job": .blue | |
| case "tool": .orange | |
| case "assistant": .green | |
| default: .gray | |
| } | |
| } | |
| private var formattedTs: String { | |
| let date = Date(timeIntervalSince1970: event.ts / 1000) | |
| let f = DateFormatter() | |
| f.dateFormat = "HH:mm:ss.SSS" | |
| return f.string(from: date) | |
| } | |
| private func prettyJSON(_ dict: [String: OpenClawProtocol.AnyCodable]) -> String? { | |
| let normalized = dict.mapValues { $0.value } | |
| guard JSONSerialization.isValidJSONObject(normalized), | |
| let data = try? JSONSerialization.data(withJSONObject: normalized, options: [.prettyPrinted]), | |
| let str = String(data: data, encoding: .utf8) | |
| else { return nil } | |
| return str | |
| } | |
| } | |
| struct AgentEventsWindow_Previews: PreviewProvider { | |
| static var previews: some View { | |
| let sample = ControlAgentEvent( | |
| runId: "abc", | |
| seq: 1, | |
| stream: "tool", | |
| ts: Date().timeIntervalSince1970 * 1000, | |
| data: [ | |
| "phase": OpenClawProtocol.AnyCodable("start"), | |
| "name": OpenClawProtocol.AnyCodable("bash"), | |
| ], | |
| summary: nil) | |
| AgentEventStore.shared.append(sample) | |
| return AgentEventsWindow() | |
| } | |
| } | |