import SwiftUI // MARK: - Brand Colors (Watch) extension Color { static let brHotPink = Color(red: 1.0, green: 0.114, blue: 0.424) static let brAmber = Color(red: 0.961, green: 0.651, blue: 0.137) static let brElectricBlue = Color(red: 0.161, green: 0.475, blue: 1.0) static let brViolet = Color(red: 0.612, green: 0.153, blue: 0.690) } // MARK: - Main Watch View struct WatchContentView: View { @EnvironmentObject var store: WatchDataStore @State private var currentPage = 0 var body: some View { TabView(selection: $currentPage) { WatchFaceView() .tag(0) FleetDashboardView() .tag(1) SensorView() .tag(2) AIView() .tag(3) } .tabViewStyle(.page) } } // MARK: - Page 1: Watch Face struct WatchFaceView: View { @EnvironmentObject var store: WatchDataStore @State private var currentTime = Date() let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { ZStack { LinearGradient( colors: [.black, Color(white: 0.05)], startPoint: .top, endPoint: .bottom ) VStack(spacing: 4) { Text(timeString) .font(.system(size: 48, weight: .thin, design: .rounded)) .foregroundStyle( LinearGradient( colors: [.brAmber, .brHotPink], startPoint: .leading, endPoint: .trailing ) ) Text(dateString) .font(.system(size: 13, weight: .medium)) .foregroundColor(.gray) Spacer().frame(height: 8) HStack(spacing: 12) { VStack(spacing: 2) { Image(systemName: "server.rack") .font(.system(size: 12)) .foregroundColor(.brElectricBlue) Text("\(store.health?.fleetOnline ?? 0)/\(store.health?.fleetTotal ?? 0)") .font(.system(size: 11, weight: .semibold, design: .monospaced)) .foregroundColor(.white) } VStack(spacing: 2) { Circle() .fill(.green) .frame(width: 10, height: 10) Text("\(store.health?.trafficGreen ?? 0)") .font(.system(size: 11, weight: .semibold, design: .monospaced)) .foregroundColor(.white) } VStack(spacing: 2) { Image(systemName: "brain") .font(.system(size: 12)) .foregroundColor(.brViolet) Text("\(store.health?.agentsActive ?? 0)") .font(.system(size: 11, weight: .semibold, design: .monospaced)) .foregroundColor(.white) } VStack(spacing: 2) { Image(systemName: "thermometer.medium") .font(.system(size: 12)) .foregroundColor(.brAmber) Text(String(format: "%.0f\u{00B0}", store.sensor?.temperature ?? 0)) .font(.system(size: 11, weight: .semibold, design: .monospaced)) .foregroundColor(.white) } } HStack(spacing: 4) { Circle() .fill(store.isConnected ? .brHotPink : .gray) .frame(width: 6, height: 6) Text(store.isConnected ? "BLACKROAD" : "OFFLINE") .font(.system(size: 9, weight: .bold, design: .monospaced)) .foregroundColor(store.isConnected ? .brHotPink : .gray) } .padding(.top, 4) } .padding() } .ignoresSafeArea() .onReceive(timer) { input in currentTime = input } } private var timeString: String { let formatter = DateFormatter() formatter.dateFormat = "h:mm" return formatter.string(from: currentTime) } private var dateString: String { let formatter = DateFormatter() formatter.dateFormat = "EEEE, MMM d" return formatter.string(from: currentTime).uppercased() } } // MARK: - Page 2: Fleet Dashboard struct FleetDashboardView: View { @EnvironmentObject var store: WatchDataStore var body: some View { ScrollView { VStack(alignment: .leading, spacing: 8) { Text("FLEET") .font(.system(size: 11, weight: .bold, design: .monospaced)) .foregroundColor(.brHotPink) if let health = store.health { StatRow(icon: "server.rack", label: "Devices", value: "\(health.fleetOnline)/\(health.fleetTotal)", color: .brElectricBlue) StatRow(icon: "brain", label: "Agents", value: "\(health.agentsActive)", color: .brViolet) StatRow(icon: "circle.fill", label: "Green", value: "\(health.trafficGreen)", color: .green) StatRow(icon: "doc.text", label: "Repos", value: "\(health.reposCount)", color: .brAmber) StatRow(icon: "cloud", label: "CF Projects", value: "\(health.cfProjects)", color: .brElectricBlue) StatRow(icon: "checkmark.circle", label: "Tasks Done", value: "\(health.tasksDone)", color: .green) StatRow(icon: "memorychip", label: "Memory", value: "\(health.memoryEntries)", color: .brViolet) } else { Text("Waiting for data...") .font(.system(size: 12)) .foregroundColor(.gray) } } .padding(.horizontal) } } } // MARK: - Page 3: Sensors struct SensorView: View { @EnvironmentObject var store: WatchDataStore var body: some View { ScrollView { VStack(alignment: .leading, spacing: 8) { Text("SENSORS") .font(.system(size: 11, weight: .bold, design: .monospaced)) .foregroundColor(.brAmber) if let sensor = store.sensor { StatRow(icon: "thermometer", label: "Temp", value: String(format: "%.1f\u{00B0}C", sensor.temperature), color: .brAmber) StatRow(icon: "humidity", label: "Humidity", value: String(format: "%.1f%%", sensor.humidity), color: .brElectricBlue) StatRow(icon: "battery.100", label: "Battery", value: "\(sensor.batteryMV)mV", color: sensor.batteryMV > 3500 ? .green : .red) StatRow(icon: "clock", label: "Uptime", value: formatUptime(sensor.uptimeSec), color: .gray) } else { Text("No sensor data") .font(.system(size: 12)) .foregroundColor(.gray) } } .padding(.horizontal) } } private func formatUptime(_ seconds: UInt32) -> String { let h = seconds / 3600 let m = (seconds % 3600) / 60 let s = seconds % 60 return String(format: "%02d:%02d:%02d", h, m, s) } } // MARK: - Page 4: AI Status struct AIView: View { @EnvironmentObject var store: WatchDataStore var body: some View { ScrollView { VStack(alignment: .leading, spacing: 8) { Text("NPU") .font(.system(size: 11, weight: .bold, design: .monospaced)) .foregroundColor(.brViolet) if let ai = store.ai { // Confidence bar VStack(alignment: .leading, spacing: 4) { HStack { Text("Confidence") .font(.system(size: 11)) .foregroundColor(.gray) Spacer() Text("\(ai.confidence)%") .font(.system(size: 11, weight: .bold, design: .monospaced)) .foregroundColor(.brHotPink) } GeometryReader { geo in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 3) .fill(Color.white.opacity(0.1)) RoundedRectangle(cornerRadius: 3) .fill( LinearGradient( colors: [.brAmber, .brHotPink], startPoint: .leading, endPoint: .trailing ) ) .frame(width: geo.size.width * CGFloat(ai.confidence) / 100.0) } } .frame(height: 6) } StatRow(icon: "number", label: "Total", value: "\(ai.totalInferences)", color: .brAmber) // NPU load bar VStack(alignment: .leading, spacing: 4) { HStack { Text("NPU Load") .font(.system(size: 11)) .foregroundColor(.gray) Spacer() Text("\(ai.npuLoad)%") .font(.system(size: 11, weight: .bold, design: .monospaced)) .foregroundColor(.brElectricBlue) } GeometryReader { geo in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 3) .fill(Color.white.opacity(0.1)) RoundedRectangle(cornerRadius: 3) .fill( LinearGradient( colors: [.brElectricBlue, .brViolet], startPoint: .leading, endPoint: .trailing ) ) .frame(width: geo.size.width * CGFloat(ai.npuLoad) / 100.0) } } .frame(height: 6) } StatRow(icon: "thermometer", label: "NPU Temp", value: "\(ai.npuTemp)\u{00B0}C", color: ai.npuTemp > 70 ? .red : .brAmber) } else { Text("No NPU data") .font(.system(size: 12)) .foregroundColor(.gray) } } .padding(.horizontal) } } } // MARK: - Reusable Components struct StatRow: View { let icon: String let label: String let value: String let color: Color var body: some View { HStack { Image(systemName: icon) .font(.system(size: 12)) .foregroundColor(color) .frame(width: 20) Text(label) .font(.system(size: 12)) .foregroundColor(.gray) Spacer() Text(value) .font(.system(size: 12, weight: .semibold, design: .monospaced)) .foregroundColor(.white) } } }