1→import SwiftUI 2→import Charts 3→ 4→// MARK: - Live Consumption Widget (Compact) 5→ 6→struct LiveConsumptionWidget: View { 7→ @StateObject private var tracker = ConsumptionTracker.shared 8→ @State private var showDetailSheet = false 9→ 10→ var body: some View { 11→ GroupBox { 12→ VStack(spacing: 12) { 13→ // Terminal Status (prominent at top) 14→ if !tracker.activeProcesses.isEmpty { 15→ HStack(spacing: 10) { 16→ // Terminal icon with pulse 17→ ZStack { 18→ Circle() 19→ .fill(Color.orange.opacity(0.2)) 20→ .frame(width: 36, height: 36) 21→ Image(systemName: "terminal.fill") 22→ .font(.body) 23→ .foregroundStyle(.orange) 24→ } 25→ 26→ VStack(alignment: .leading, spacing: 2) { 27→ Text("\(tracker.activeProcesses.count) Terminal\(tracker.activeProcesses.count > 1 ? "s" : "") Running") 28→ .font(.subheadline.bold()) 29→ .foregroundStyle(.orange) 30→ Text("Session: \(tracker.sessionDuration)") 31→ .font(.caption.monospacedDigit()) 32→ .foregroundStyle(.secondary) 33→ } 34→ 35→ Spacer() 36→ 37→ // Live indicator 38→ VStack(spacing: 4) { 39→ PulsingDot(isActive: true) 40→ Text("LIVE") 41→ .font(.caption2.bold()) 42→ .foregroundStyle(.green) 43→ } 44→ } 45→ .padding(10) 46→ .background(Color.orange.opacity(0.1)) 47→ .cornerRadius(10) 48→ } else { 49→ // No terminals - show waiting state 50→ HStack { 51→ HStack(spacing: 6) { 52→ PulsingDot(isActive: !tracker.isPaused) 53→ Text("Live Consumption") 54→ .font(.subheadline.bold()) 55→ } 56→ 57→ Spacer() 58→ 59→ Text("No terminals") 60→ .font(.caption) 61→ .foregroundStyle(.tertiary) 62→ 63→ Button { 64→ showDetailSheet = true 65→ } label: { 66→ Image(systemName: "arrow.up.right.square") 67→ } 68→ .buttonStyle(.plain) 69→ .help("Open detailed view") 70→ } 71→ } 72→ 73→ // Quick metrics 74→ HStack(spacing: 16) { 75→ VStack(alignment: .leading, spacing: 2) { 76→ Text("Session Cost") 77→ .font(.caption) 78→ .foregroundStyle(.secondary) 79→ Text(String(format: "$%.4f", tracker.sessionCost)) 80→ .font(.title3.bold().monospacedDigit()) 81→ .foregroundStyle(.green) 82→ } 83→ 84→ Divider() 85→ .frame(height: 40) 86→ 87→ VStack(alignment: .leading, spacing: 2) { 88→ Text("Rate") 89→ .font(.caption) 90→ .foregroundStyle(.secondary) 91→ Text(String(format: "$%.2f/hr", tracker.costPerHour)) 92→ .font(.title3.bold().monospacedDigit()) 93→ .foregroundStyle(rateColor) 94→ } 95→ 96→ Spacer() 97→ 98→ // Mini chart or expand button 99→ if !tracker.consumptionHistory.isEmpty { 100→ MiniConsumptionChart(dataPoints: tracker.consumptionHistory) 101→ .frame(width: 80, height: 40) 102→ } 103→ 104→ if !tracker.activeProcesses.isEmpty { 105→ Button { 106→ showDetailSheet = true 107→ } label: { 108→ Image(systemName: "arrow.up.right.square") 109→ } 110→ .buttonStyle(.plain) 111→ .help("Open detailed view") 112→ } 113→ } 114→ 115→ // Token summary bar 116→ HStack(spacing: 4) { 117→ TokenPill(label: "In", count: tracker.sessionInputTokens, color: .blue) 118→ TokenPill(label: "Out", count: tracker.sessionOutputTokens, color: .purple) 119→ TokenPill(label: "Cache", count: tracker.sessionCacheTokens, color: .cyan) 120→ } 121→ } 122→ } label: { 123→ Label("Real-time Monitor", systemImage: "waveform.path.ecg") 124→ } 125→ .sheet(isPresented: $showDetailSheet) { 126→ NavigationStack { 127→ RealtimeConsumptionView() 128→ .frame(minWidth: 700, minHeight: 600) 129→ .toolbar { 130→ ToolbarItem(placement: .cancellationAction) { 131→ Button("Close") { 132→ showDetailSheet = false 133→ } 134→ } 135→ } 136→ } 137→ } 138→ } 139→ 140→ private var rateColor: Color { 141→ if tracker.costPerHour > 5.0 { return .red } 142→ if tracker.costPerHour > 2.0 { return .orange } 143→ return .green 144→ } 145→} 146→ 147→// MARK: - Pulsing Dot 148→ 149→struct PulsingDot: View { 150→ let isActive: Bool 151→ @State private var isPulsing = false 152→ 153→ var body: some View { 154→ Circle() 155→ .fill(isActive ? .green : .orange) 156→ .frame(width: 8, height: 8) 157→ .scaleEffect(isPulsing && isActive ? 1.3 : 1.0) 158→ .animation( 159→ isActive ? .easeInOut(duration: 1).repeatForever(autoreverses: true) : .default, 160→ value: isPulsing 161→ ) 162→ .onAppear { 163→ isPulsing = true 164→ } 165→ } 166→} 167→ 168→// MARK: - Token Pill 169→ 170→struct TokenPill: View { 171→ let label: String 172→ let count: Int 173→ let color: Color 174→ 175→ var body: some View { 176→ HStack(spacing: 4) { 177→ Circle() 178→ .fill(color) 179→ .frame(width: 6, height: 6) 180→ Text(label) 181→ .font(.caption2) 182→ Text(formatCount(count)) 183→ .font(.caption2.monospacedDigit().bold()) 184→ } 185→ .padding(.horizontal, 8) 186→ .padding(.vertical, 4) 187→ .background(color.opacity(0.15)) 188→ .clipShape(Capsule()) 189→ } 190→ 191→ private func formatCount(_ count: Int) -> String { 192→ if count >= 1_000_000 { 193→ return String(format: "%.1fM", Double(count) / 1_000_000) 194→ } else if count >= 1_000 { 195→ return String(format: "%.1fK", Double(count) / 1_000) 196→ } 197→ return "\(count)" 198→ } 199→} 200→ 201→// MARK: - Mini Consumption Chart 202→ 203→struct MiniConsumptionChart: View { 204→ let dataPoints: [ConsumptionDataPoint] 205→ 206→ var body: some View { 207→ Chart { 208→ ForEach(dataPoints.suffix(20)) { point in 209→ LineMark( 210→ x: .value("Time", point.timestamp), 211→ y: .value("Cost", point.cumulativeCost) 212→ ) 213→ .foregroundStyle(.green) 214→ .interpolationMethod(.catmullRom) 215→ } 216→ } 217→ .chartXAxis(.hidden) 218→ .chartYAxis(.hidden) 219→ .chartLegend(.hidden) 220→ } 221→} 222→ 223→// MARK: - Menu Bar Consumption View 224→ 225→struct MenuBarConsumptionView: View { 226→ @StateObject private var tracker = ConsumptionTracker.shared 227→ 228→ var body: some View { 229→ VStack(alignment: .leading, spacing: 8) { 230→ // Terminal Status (prominent when active) 231→ if !tracker.activeProcesses.isEmpty { 232→ HStack(spacing: 6) { 233→ ZStack { 234→ Circle() 235→ .fill(Color.orange.opacity(0.2)) 236→ .frame(width: 24, height: 24) 237→ Image(systemName: "terminal.fill") 238→ .font(.caption) 239→ .foregroundStyle(.orange) 240→ } 241→ 242→ VStack(alignment: .leading, spacing: 1) { 243→ Text("\(tracker.activeProcesses.count) Terminal\(tracker.activeProcesses.count > 1 ? "s" : "") Active") 244→ .font(.caption.bold()) 245→ .foregroundStyle(.orange) 246→ Text(tracker.sessionDuration) 247→ .font(.caption2.monospacedDigit()) 248→ .foregroundStyle(.secondary) 249→ } 250→ 251→ Spacer() 252→ 253→ // Live pulse 254→ PulsingDot(isActive: true) 255→ } 256→ .padding(8) 257→ .background(Color.orange.opacity(0.1)) 258→ .cornerRadius(8) 259→ } else { 260→ HStack { 261→ PulsingDot(isActive: !tracker.isPaused) 262→ Text("Live Tracking") 263→ .font(.caption.bold()) 264→ Spacer() 265→ Text("No terminals") 266→ .font(.caption2) 267→ .foregroundStyle(.tertiary) 268→ } 269→ } 270→ 271→ Divider() 272→ 273→ HStack { 274→ VStack(alignment: .leading, spacing: 2) { 275→ Text("Session") 276→ .font(.caption2) 277→ .foregroundStyle(.secondary) 278→ Text(String(format: "$%.4f", tracker.sessionCost)) 279→ .font(.caption.bold().monospacedDigit()) 280→ } 281→ 282→ Spacer() 283→ 284→ VStack(alignment: .trailing, spacing: 2) { 285→ Text("Rate") 286→ .font(.caption2) 287→ .foregroundStyle(.secondary) 288→ Text(String(format: "$%.2f/hr", tracker.costPerHour)) 289→ .font(.caption.bold().monospacedDigit()) 290→ .foregroundStyle(tracker.costPerHour > 2.0 ? .orange : .green) 291→ } 292→ } 293→ 294→ // Compact token display 295→ HStack(spacing: 8) { 296→ Label(formatTokens(tracker.sessionInputTokens), systemImage: "arrow.down") 297→ .foregroundStyle(.blue) 298→ Label(formatTokens(tracker.sessionOutputTokens), systemImage: "arrow.up") 299→ .foregroundStyle(.purple) 300→ } 301→ .font(.caption2.monospacedDigit()) 302→ } 303→ .padding(8) 304→ } 305→ 306→ private func formatTokens(_ count: Int) -> String { 307→ if count >= 1_000_000 { 308→ return String(format: "%.1fM", Double(count) / 1_000_000) 309→ } else if count >= 1_000 { 310→ return String(format: "%.0fK", Double(count) / 1_000) 311→ } 312→ return "\(count)" 313→ } 314→} 315→ 316→// MARK: - Preview 317→ 318→#Preview("Widget") { 319→ LiveConsumptionWidget() 320→ .frame(width: 400) 321→ .padding() 322→} 323→ 324→#Preview("Menu Bar") { 325→ MenuBarConsumptionView() 326→ .frame(width: 200) 327→} 328→ Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.