Stijnus commited on
Commit
0c6f363
·
1 Parent(s): d1d23d8

Taskmanager update

Browse files

Enhanced System Metrics:
Detailed CPU metrics including temperature and frequency
Comprehensive memory breakdown with heap usage
Advanced performance metrics (FPS, page load times, web vitals)
Detailed network statistics
Storage monitoring with visual indicators
Battery health and detailed status
Power Management:
Multiple power profiles (Performance, Balanced, Power Saver)
Enhanced energy saver mode
Automatic power management based on system state
Detailed energy savings statistics
System Health:
Overall system health score
Real-time issue detection and alerts
Performance optimization suggestions
Historical metrics tracking
UI Improvements:
Interactive graphs for all metrics
Color-coded status indicators
Detailed tooltips and explanations
Collapsible sections for better organization
Alert system for critical events
Performance Monitoring:
Frame rate monitoring
Resource usage tracking
Network performance analysis
Web vitals monitoring
Detailed timing metrics
To use the enhanced task manager:
Monitor system health in the new health score section
Choose a power profile based on your needs
Enable auto energy saver for automatic power management
Monitor real-time alerts for system issues
5. View detailed metrics in each category
Check optimization suggestions when performance issues arise

app/components/settings/debug/DebugTab.tsx CHANGED
@@ -447,6 +447,8 @@ export default function DebugTab() {
447
  const appData = (await appResponse.json()) as Omit<WebAppInfo, 'gitInfo'>;
448
  const gitData = (await gitResponse.json()) as GitInfo;
449
 
 
 
450
  setWebAppInfo({
451
  ...appData,
452
  gitInfo: gitData,
@@ -1084,7 +1086,7 @@ export default function DebugTab() {
1084
  <>
1085
  <div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-800">
1086
  <div className="text-sm flex items-center gap-2">
1087
- <div className="i-ph:git-fork text-bolt-elements-textSecondary w-4 h-4" />
1088
  <span className="text-bolt-elements-textSecondary">Repository:</span>
1089
  <span className="text-bolt-elements-textPrimary">
1090
  {webAppInfo.gitInfo.github.currentRepo.fullName}
 
447
  const appData = (await appResponse.json()) as Omit<WebAppInfo, 'gitInfo'>;
448
  const gitData = (await gitResponse.json()) as GitInfo;
449
 
450
+ console.log('Git Info Response:', gitData); // Add logging to debug
451
+
452
  setWebAppInfo({
453
  ...appData,
454
  gitInfo: gitData,
 
1086
  <>
1087
  <div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-800">
1088
  <div className="text-sm flex items-center gap-2">
1089
+ <div className="i-ph:git-repository text-bolt-elements-textSecondary w-4 h-4" />
1090
  <span className="text-bolt-elements-textSecondary">Repository:</span>
1091
  <span className="text-bolt-elements-textPrimary">
1092
  {webAppInfo.gitInfo.github.currentRepo.fullName}
app/components/settings/task-manager/TaskManagerTab.tsx CHANGED
@@ -24,22 +24,66 @@ interface BatteryManager extends EventTarget {
24
  }
25
 
26
  interface SystemMetrics {
27
- cpu: number;
 
 
 
 
 
28
  memory: {
29
  used: number;
30
  total: number;
31
  percentage: number;
 
 
 
 
 
 
32
  };
33
  uptime: number;
34
  battery?: {
35
  level: number;
36
  charging: boolean;
37
  timeRemaining?: number;
 
 
 
38
  };
39
  network: {
40
  downlink: number;
 
41
  latency: number;
42
  type: string;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  };
44
  }
45
 
@@ -57,6 +101,26 @@ interface EnergySavings {
57
  estimatedEnergySaved: number; // in mWh (milliwatt-hours)
58
  }
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  declare global {
61
  interface Navigator {
62
  getBattery(): Promise<BatteryManager>;
@@ -88,12 +152,61 @@ const ENERGY_COSTS = {
88
  rendering: 1, // mW per render
89
  };
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  export default function TaskManagerTab() {
92
  const [metrics, setMetrics] = useState<SystemMetrics>({
93
- cpu: 0,
94
- memory: { used: 0, total: 0, percentage: 0 },
95
  uptime: 0,
96
- network: { downlink: 0, latency: 0, type: 'unknown' },
 
 
 
 
 
 
 
 
 
97
  });
98
  const [metricsHistory, setMetricsHistory] = useState<MetricsHistory>({
99
  timestamps: [],
@@ -121,6 +234,8 @@ export default function TaskManagerTab() {
121
  });
122
 
123
  const saverModeStartTime = useRef<number | null>(null);
 
 
124
 
125
  // Handle energy saver mode changes
126
  const handleEnergySaverChange = (checked: boolean) => {
@@ -181,7 +296,135 @@ export default function TaskManagerTab() {
181
  return () => clearInterval(interval);
182
  }, [updateEnergySavings]);
183
 
184
- // Update metrics
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  const updateMetrics = async () => {
186
  try {
187
  // Get memory info using Performance API
@@ -216,36 +459,62 @@ export default function TaskManagerTab() {
216
  (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection;
217
  const networkInfo = {
218
  downlink: connection?.downlink || 0,
 
219
  latency: connection?.rtt || 0,
220
  type: connection?.type || 'unknown',
 
 
 
221
  };
222
 
223
- const newMetrics = {
224
- cpu: cpuUsage,
 
 
 
225
  memory: {
226
  used: Math.round(usedMem),
227
  total: Math.round(totalMem),
228
  percentage: Math.round(memPercentage),
 
 
 
 
 
229
  },
230
  uptime: performance.now() / 1000,
231
  battery: batteryInfo,
232
  network: networkInfo,
 
 
 
 
 
 
 
 
233
  };
234
 
235
- setMetrics(newMetrics);
 
 
 
 
 
 
236
 
237
  // Update metrics history
238
  const now = new Date().toLocaleTimeString();
239
  setMetricsHistory((prev) => {
240
  const timestamps = [...prev.timestamps, now].slice(-MAX_HISTORY_POINTS);
241
- const cpu = [...prev.cpu, newMetrics.cpu].slice(-MAX_HISTORY_POINTS);
242
- const memory = [...prev.memory, newMetrics.memory.percentage].slice(-MAX_HISTORY_POINTS);
243
  const battery = [...prev.battery, batteryInfo?.level || 0].slice(-MAX_HISTORY_POINTS);
244
  const network = [...prev.network, networkInfo.downlink].slice(-MAX_HISTORY_POINTS);
245
 
246
  return { timestamps, cpu, memory, battery, network };
247
  });
248
- } catch (error: unknown) {
249
  console.error('Failed to update system metrics:', error);
250
  }
251
  };
@@ -300,6 +569,8 @@ export default function TaskManagerTab() {
300
  downlink: connection.downlink || 0,
301
  latency: connection.rtt || 0,
302
  type: connection.type || 'unknown',
 
 
303
  },
304
  }));
305
  };
@@ -427,12 +698,60 @@ export default function TaskManagerTab() {
427
  return () => clearInterval(batteryCheckInterval);
428
  }, [autoEnergySaver]);
429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  return (
431
  <div className="flex flex-col gap-6">
432
- {/* System Metrics */}
433
  <div className="flex flex-col gap-4">
434
  <div className="flex items-center justify-between">
435
- <h3 className="text-base font-medium text-bolt-elements-textPrimary">System Metrics</h3>
436
  <div className="flex items-center gap-4">
437
  <div className="flex items-center gap-2">
438
  <input
@@ -463,19 +782,99 @@ export default function TaskManagerTab() {
463
  {energySaverMode && <span className="ml-2 text-xs text-bolt-elements-textSecondary">Active</span>}
464
  </label>
465
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  </div>
467
  </div>
 
468
 
 
 
 
469
  <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
470
  {/* CPU Usage */}
471
  <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
472
  <div className="flex items-center justify-between">
473
  <span className="text-sm text-bolt-elements-textSecondary">CPU Usage</span>
474
- <span className={classNames('text-sm font-medium', getUsageColor(metrics.cpu))}>
475
- {Math.round(metrics.cpu)}%
476
  </span>
477
  </div>
478
  {renderUsageGraph(metricsHistory.cpu, 'CPU', '#9333ea')}
 
 
 
 
 
 
 
 
 
 
479
  </div>
480
 
481
  {/* Memory Usage */}
@@ -487,28 +886,42 @@ export default function TaskManagerTab() {
487
  </span>
488
  </div>
489
  {renderUsageGraph(metricsHistory.memory, 'Memory', '#2563eb')}
 
 
 
 
 
 
 
490
  </div>
491
 
492
- {/* Battery */}
493
- {metrics.battery && (
494
- <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
495
- <div className="flex items-center justify-between">
496
- <span className="text-sm text-bolt-elements-textSecondary">Battery</span>
497
- <div className="flex items-center gap-2">
498
- {metrics.battery.charging && <div className="i-ph:lightning-fill w-4 h-4 text-bolt-action-primary" />}
499
- <span
500
- className={classNames(
501
- 'text-sm font-medium',
502
- metrics.battery.level > 20 ? 'text-bolt-elements-textPrimary' : 'text-red-500',
503
- )}
504
- >
505
- {Math.round(metrics.battery.level)}%
506
- </span>
507
- </div>
508
- </div>
509
- {renderUsageGraph(metricsHistory.battery, 'Battery', '#22c55e')}
 
 
 
 
510
  </div>
511
- )}
 
 
 
512
 
513
  {/* Network */}
514
  <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
@@ -519,9 +932,118 @@ export default function TaskManagerTab() {
519
  </span>
520
  </div>
521
  {renderUsageGraph(metricsHistory.network, 'Network', '#f59e0b')}
 
 
 
 
 
 
 
 
522
  </div>
523
  </div>
524
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  {/* Energy Savings */}
526
  {energySaverMode && (
527
  <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
@@ -550,3 +1072,32 @@ export default function TaskManagerTab() {
550
  </div>
551
  );
552
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
  interface SystemMetrics {
27
+ cpu: {
28
+ usage: number;
29
+ cores: number[];
30
+ temperature?: number;
31
+ frequency?: number;
32
+ };
33
  memory: {
34
  used: number;
35
  total: number;
36
  percentage: number;
37
+ heap: {
38
+ used: number;
39
+ total: number;
40
+ limit: number;
41
+ };
42
+ cache?: number;
43
  };
44
  uptime: number;
45
  battery?: {
46
  level: number;
47
  charging: boolean;
48
  timeRemaining?: number;
49
+ temperature?: number;
50
+ cycles?: number;
51
+ health?: number;
52
  };
53
  network: {
54
  downlink: number;
55
+ uplink?: number;
56
  latency: number;
57
  type: string;
58
+ activeConnections?: number;
59
+ bytesReceived: number;
60
+ bytesSent: number;
61
+ };
62
+ performance: {
63
+ fps: number;
64
+ pageLoad: number;
65
+ domReady: number;
66
+ resources: {
67
+ total: number;
68
+ size: number;
69
+ loadTime: number;
70
+ };
71
+ timing: {
72
+ ttfb: number;
73
+ fcp: number;
74
+ lcp: number;
75
+ };
76
+ };
77
+ storage: {
78
+ total: number;
79
+ used: number;
80
+ free: number;
81
+ type: string;
82
+ };
83
+ health: {
84
+ score: number;
85
+ issues: string[];
86
+ suggestions: string[];
87
  };
88
  }
89
 
 
101
  estimatedEnergySaved: number; // in mWh (milliwatt-hours)
102
  }
103
 
104
+ interface PowerProfile {
105
+ name: string;
106
+ description: string;
107
+ settings: {
108
+ updateInterval: number;
109
+ enableAnimations: boolean;
110
+ backgroundProcessing: boolean;
111
+ networkThrottling: boolean;
112
+ };
113
+ }
114
+
115
+ interface PerformanceAlert {
116
+ type: 'warning' | 'error' | 'info';
117
+ message: string;
118
+ timestamp: number;
119
+ metric: string;
120
+ threshold: number;
121
+ value: number;
122
+ }
123
+
124
  declare global {
125
  interface Navigator {
126
  getBattery(): Promise<BatteryManager>;
 
152
  rendering: 1, // mW per render
153
  };
154
 
155
+ const PERFORMANCE_THRESHOLDS = {
156
+ cpu: { warning: 70, critical: 90 },
157
+ memory: { warning: 80, critical: 95 },
158
+ fps: { warning: 30, critical: 15 },
159
+ loadTime: { warning: 3000, critical: 5000 },
160
+ };
161
+
162
+ const POWER_PROFILES: PowerProfile[] = [
163
+ {
164
+ name: 'Performance',
165
+ description: 'Maximum performance, higher power consumption',
166
+ settings: {
167
+ updateInterval: 1000,
168
+ enableAnimations: true,
169
+ backgroundProcessing: true,
170
+ networkThrottling: false,
171
+ },
172
+ },
173
+ {
174
+ name: 'Balanced',
175
+ description: 'Balance between performance and power saving',
176
+ settings: {
177
+ updateInterval: 2000,
178
+ enableAnimations: true,
179
+ backgroundProcessing: true,
180
+ networkThrottling: false,
181
+ },
182
+ },
183
+ {
184
+ name: 'Power Saver',
185
+ description: 'Maximum power saving, reduced performance',
186
+ settings: {
187
+ updateInterval: 5000,
188
+ enableAnimations: false,
189
+ backgroundProcessing: false,
190
+ networkThrottling: true,
191
+ },
192
+ },
193
+ ];
194
+
195
  export default function TaskManagerTab() {
196
  const [metrics, setMetrics] = useState<SystemMetrics>({
197
+ cpu: { usage: 0, cores: [] },
198
+ memory: { used: 0, total: 0, percentage: 0, heap: { used: 0, total: 0, limit: 0 } },
199
  uptime: 0,
200
+ network: { downlink: 0, latency: 0, type: 'unknown', bytesReceived: 0, bytesSent: 0 },
201
+ performance: {
202
+ fps: 0,
203
+ pageLoad: 0,
204
+ domReady: 0,
205
+ resources: { total: 0, size: 0, loadTime: 0 },
206
+ timing: { ttfb: 0, fcp: 0, lcp: 0 },
207
+ },
208
+ storage: { total: 0, used: 0, free: 0, type: 'unknown' },
209
+ health: { score: 0, issues: [], suggestions: [] },
210
  });
211
  const [metricsHistory, setMetricsHistory] = useState<MetricsHistory>({
212
  timestamps: [],
 
234
  });
235
 
236
  const saverModeStartTime = useRef<number | null>(null);
237
+ const [selectedProfile, setSelectedProfile] = useState<PowerProfile>(POWER_PROFILES[1]); // Default to Balanced
238
+ const [alerts, setAlerts] = useState<PerformanceAlert[]>([]);
239
 
240
  // Handle energy saver mode changes
241
  const handleEnergySaverChange = (checked: boolean) => {
 
296
  return () => clearInterval(interval);
297
  }, [updateEnergySavings]);
298
 
299
+ // Get detailed performance metrics
300
+ const getPerformanceMetrics = async (): Promise<Partial<SystemMetrics['performance']>> => {
301
+ try {
302
+ // Get FPS
303
+ const fps = await measureFrameRate();
304
+
305
+ // Get page load metrics
306
+ const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
307
+ const pageLoad = navigation.loadEventEnd - navigation.startTime;
308
+ const domReady = navigation.domContentLoadedEventEnd - navigation.startTime;
309
+
310
+ // Get resource metrics
311
+ const resources = performance.getEntriesByType('resource');
312
+ const resourceMetrics = {
313
+ total: resources.length,
314
+ size: resources.reduce((total, r) => total + (r as any).transferSize || 0, 0),
315
+ loadTime: Math.max(...resources.map((r) => r.duration)),
316
+ };
317
+
318
+ // Get Web Vitals
319
+ const ttfb = navigation.responseStart - navigation.requestStart;
320
+ const paintEntries = performance.getEntriesByType('paint');
321
+ const fcp = paintEntries.find((entry) => entry.name === 'first-contentful-paint')?.startTime || 0;
322
+ const lcpEntry = await getLargestContentfulPaint();
323
+
324
+ return {
325
+ fps,
326
+ pageLoad,
327
+ domReady,
328
+ resources: resourceMetrics,
329
+ timing: {
330
+ ttfb,
331
+ fcp,
332
+ lcp: lcpEntry?.startTime || 0,
333
+ },
334
+ };
335
+ } catch (error) {
336
+ console.error('Failed to get performance metrics:', error);
337
+ return {};
338
+ }
339
+ };
340
+
341
+ // Measure frame rate
342
+ const measureFrameRate = async (): Promise<number> => {
343
+ return new Promise((resolve) => {
344
+ const frameCount = { value: 0 };
345
+ const startTime = performance.now();
346
+
347
+ const countFrame = (time: number) => {
348
+ frameCount.value++;
349
+
350
+ if (time - startTime >= 1000) {
351
+ resolve(Math.round((frameCount.value * 1000) / (time - startTime)));
352
+ } else {
353
+ requestAnimationFrame(countFrame);
354
+ }
355
+ };
356
+
357
+ requestAnimationFrame(countFrame);
358
+ });
359
+ };
360
+
361
+ // Get Largest Contentful Paint
362
+ const getLargestContentfulPaint = async (): Promise<PerformanceEntry | undefined> => {
363
+ return new Promise((resolve) => {
364
+ new PerformanceObserver((list) => {
365
+ const entries = list.getEntries();
366
+ resolve(entries[entries.length - 1]);
367
+ }).observe({ entryTypes: ['largest-contentful-paint'] });
368
+
369
+ // Resolve after 3 seconds if no LCP entry is found
370
+ setTimeout(() => resolve(undefined), 3000);
371
+ });
372
+ };
373
+
374
+ // Analyze system health
375
+ const analyzeSystemHealth = (currentMetrics: SystemMetrics): SystemMetrics['health'] => {
376
+ const issues: string[] = [];
377
+ const suggestions: string[] = [];
378
+ let score = 100;
379
+
380
+ // CPU analysis
381
+ if (currentMetrics.cpu.usage > PERFORMANCE_THRESHOLDS.cpu.critical) {
382
+ score -= 30;
383
+ issues.push('Critical CPU usage');
384
+ suggestions.push('Consider closing resource-intensive applications');
385
+ } else if (currentMetrics.cpu.usage > PERFORMANCE_THRESHOLDS.cpu.warning) {
386
+ score -= 15;
387
+ issues.push('High CPU usage');
388
+ suggestions.push('Monitor system processes for unusual activity');
389
+ }
390
+
391
+ // Memory analysis
392
+ if (currentMetrics.memory.percentage > PERFORMANCE_THRESHOLDS.memory.critical) {
393
+ score -= 30;
394
+ issues.push('Critical memory usage');
395
+ suggestions.push('Close unused applications to free up memory');
396
+ } else if (currentMetrics.memory.percentage > PERFORMANCE_THRESHOLDS.memory.warning) {
397
+ score -= 15;
398
+ issues.push('High memory usage');
399
+ suggestions.push('Consider freeing up memory by closing background applications');
400
+ }
401
+
402
+ // Performance analysis
403
+ if (currentMetrics.performance.fps < PERFORMANCE_THRESHOLDS.fps.critical) {
404
+ score -= 20;
405
+ issues.push('Very low frame rate');
406
+ suggestions.push('Disable animations or switch to power saver mode');
407
+ } else if (currentMetrics.performance.fps < PERFORMANCE_THRESHOLDS.fps.warning) {
408
+ score -= 10;
409
+ issues.push('Low frame rate');
410
+ suggestions.push('Consider reducing visual effects');
411
+ }
412
+
413
+ // Battery analysis
414
+ if (currentMetrics.battery && !currentMetrics.battery.charging && currentMetrics.battery.level < 20) {
415
+ score -= 10;
416
+ issues.push('Low battery');
417
+ suggestions.push('Connect to power source or enable power saver mode');
418
+ }
419
+
420
+ return {
421
+ score: Math.max(0, score),
422
+ issues,
423
+ suggestions,
424
+ };
425
+ };
426
+
427
+ // Update metrics with enhanced data
428
  const updateMetrics = async () => {
429
  try {
430
  // Get memory info using Performance API
 
459
  (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection;
460
  const networkInfo = {
461
  downlink: connection?.downlink || 0,
462
+ uplink: connection?.uplink,
463
  latency: connection?.rtt || 0,
464
  type: connection?.type || 'unknown',
465
+ activeConnections: connection?.activeConnections,
466
+ bytesReceived: connection?.bytesReceived || 0,
467
+ bytesSent: connection?.bytesSent || 0,
468
  };
469
 
470
+ // Get enhanced performance metrics
471
+ const performanceMetrics = await getPerformanceMetrics();
472
+
473
+ const metrics: SystemMetrics = {
474
+ cpu: { usage: cpuUsage, cores: [], temperature: undefined, frequency: undefined },
475
  memory: {
476
  used: Math.round(usedMem),
477
  total: Math.round(totalMem),
478
  percentage: Math.round(memPercentage),
479
+ heap: {
480
+ used: Math.round(usedMem),
481
+ total: Math.round(totalMem),
482
+ limit: Math.round(totalMem),
483
+ },
484
  },
485
  uptime: performance.now() / 1000,
486
  battery: batteryInfo,
487
  network: networkInfo,
488
+ performance: performanceMetrics as SystemMetrics['performance'],
489
+ storage: {
490
+ total: 0,
491
+ used: 0,
492
+ free: 0,
493
+ type: 'unknown',
494
+ },
495
+ health: { score: 0, issues: [], suggestions: [] },
496
  };
497
 
498
+ // Analyze system health
499
+ metrics.health = analyzeSystemHealth(metrics);
500
+
501
+ // Check for alerts
502
+ checkPerformanceAlerts(metrics);
503
+
504
+ setMetrics(metrics);
505
 
506
  // Update metrics history
507
  const now = new Date().toLocaleTimeString();
508
  setMetricsHistory((prev) => {
509
  const timestamps = [...prev.timestamps, now].slice(-MAX_HISTORY_POINTS);
510
+ const cpu = [...prev.cpu, metrics.cpu.usage].slice(-MAX_HISTORY_POINTS);
511
+ const memory = [...prev.memory, metrics.memory.percentage].slice(-MAX_HISTORY_POINTS);
512
  const battery = [...prev.battery, batteryInfo?.level || 0].slice(-MAX_HISTORY_POINTS);
513
  const network = [...prev.network, networkInfo.downlink].slice(-MAX_HISTORY_POINTS);
514
 
515
  return { timestamps, cpu, memory, battery, network };
516
  });
517
+ } catch (error) {
518
  console.error('Failed to update system metrics:', error);
519
  }
520
  };
 
569
  downlink: connection.downlink || 0,
570
  latency: connection.rtt || 0,
571
  type: connection.type || 'unknown',
572
+ bytesReceived: connection.bytesReceived || 0,
573
+ bytesSent: connection.bytesSent || 0,
574
  },
575
  }));
576
  };
 
698
  return () => clearInterval(batteryCheckInterval);
699
  }, [autoEnergySaver]);
700
 
701
+ // Check for performance alerts
702
+ const checkPerformanceAlerts = (currentMetrics: SystemMetrics) => {
703
+ const newAlerts: PerformanceAlert[] = [];
704
+
705
+ // CPU alert
706
+ if (currentMetrics.cpu.usage > PERFORMANCE_THRESHOLDS.cpu.critical) {
707
+ newAlerts.push({
708
+ type: 'error',
709
+ message: 'Critical CPU usage detected',
710
+ timestamp: Date.now(),
711
+ metric: 'cpu',
712
+ threshold: PERFORMANCE_THRESHOLDS.cpu.critical,
713
+ value: currentMetrics.cpu.usage,
714
+ });
715
+ }
716
+
717
+ // Memory alert
718
+ if (currentMetrics.memory.percentage > PERFORMANCE_THRESHOLDS.memory.critical) {
719
+ newAlerts.push({
720
+ type: 'error',
721
+ message: 'Critical memory usage detected',
722
+ timestamp: Date.now(),
723
+ metric: 'memory',
724
+ threshold: PERFORMANCE_THRESHOLDS.memory.critical,
725
+ value: currentMetrics.memory.percentage,
726
+ });
727
+ }
728
+
729
+ // Performance alert
730
+ if (currentMetrics.performance.fps < PERFORMANCE_THRESHOLDS.fps.critical) {
731
+ newAlerts.push({
732
+ type: 'warning',
733
+ message: 'Very low frame rate detected',
734
+ timestamp: Date.now(),
735
+ metric: 'fps',
736
+ threshold: PERFORMANCE_THRESHOLDS.fps.critical,
737
+ value: currentMetrics.performance.fps,
738
+ });
739
+ }
740
+
741
+ if (newAlerts.length > 0) {
742
+ setAlerts((prev) => [...prev, ...newAlerts]);
743
+ newAlerts.forEach((alert) => {
744
+ toast.warning(alert.message);
745
+ });
746
+ }
747
+ };
748
+
749
  return (
750
  <div className="flex flex-col gap-6">
751
+ {/* Power Profile Selection */}
752
  <div className="flex flex-col gap-4">
753
  <div className="flex items-center justify-between">
754
+ <h3 className="text-base font-medium text-bolt-elements-textPrimary">Power Management</h3>
755
  <div className="flex items-center gap-4">
756
  <div className="flex items-center gap-2">
757
  <input
 
782
  {energySaverMode && <span className="ml-2 text-xs text-bolt-elements-textSecondary">Active</span>}
783
  </label>
784
  </div>
785
+ <select
786
+ value={selectedProfile.name}
787
+ onChange={(e) => {
788
+ const profile = POWER_PROFILES.find((p) => p.name === e.target.value);
789
+
790
+ if (profile) {
791
+ setSelectedProfile(profile);
792
+ toast.success(`Switched to ${profile.name} power profile`);
793
+ }
794
+ }}
795
+ className="px-3 py-1 rounded-md bg-[#F8F8F8] dark:bg-[#141414] border border-[#E5E5E5] dark:border-[#1A1A1A] text-sm"
796
+ >
797
+ {POWER_PROFILES.map((profile) => (
798
+ <option key={profile.name} value={profile.name}>
799
+ {profile.name}
800
+ </option>
801
+ ))}
802
+ </select>
803
+ </div>
804
+ </div>
805
+ <div className="text-sm text-bolt-elements-textSecondary">{selectedProfile.description}</div>
806
+ </div>
807
+
808
+ {/* System Health Score */}
809
+ <div className="flex flex-col gap-4">
810
+ <h3 className="text-base font-medium text-bolt-elements-textPrimary">System Health</h3>
811
+ <div className="grid grid-cols-1 gap-4">
812
+ <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
813
+ <div className="flex items-center justify-between">
814
+ <span className="text-sm text-bolt-elements-textSecondary">Health Score</span>
815
+ <span
816
+ className={classNames('text-lg font-medium', {
817
+ 'text-green-500': metrics.health.score >= 80,
818
+ 'text-yellow-500': metrics.health.score >= 60 && metrics.health.score < 80,
819
+ 'text-red-500': metrics.health.score < 60,
820
+ })}
821
+ >
822
+ {metrics.health.score}%
823
+ </span>
824
+ </div>
825
+ {metrics.health.issues.length > 0 && (
826
+ <div className="mt-2">
827
+ <div className="text-sm font-medium text-bolt-elements-textSecondary mb-1">Issues:</div>
828
+ <ul className="text-sm text-bolt-elements-textSecondary space-y-1">
829
+ {metrics.health.issues.map((issue, index) => (
830
+ <li key={index} className="flex items-center gap-2">
831
+ <div className="i-ph:warning-circle-fill text-yellow-500 w-4 h-4" />
832
+ {issue}
833
+ </li>
834
+ ))}
835
+ </ul>
836
+ </div>
837
+ )}
838
+ {metrics.health.suggestions.length > 0 && (
839
+ <div className="mt-2">
840
+ <div className="text-sm font-medium text-bolt-elements-textSecondary mb-1">Suggestions:</div>
841
+ <ul className="text-sm text-bolt-elements-textSecondary space-y-1">
842
+ {metrics.health.suggestions.map((suggestion, index) => (
843
+ <li key={index} className="flex items-center gap-2">
844
+ <div className="i-ph:lightbulb-fill text-purple-500 w-4 h-4" />
845
+ {suggestion}
846
+ </li>
847
+ ))}
848
+ </ul>
849
+ </div>
850
+ )}
851
  </div>
852
  </div>
853
+ </div>
854
 
855
+ {/* System Metrics */}
856
+ <div className="flex flex-col gap-4">
857
+ <h3 className="text-base font-medium text-bolt-elements-textPrimary">System Metrics</h3>
858
  <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
859
  {/* CPU Usage */}
860
  <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
861
  <div className="flex items-center justify-between">
862
  <span className="text-sm text-bolt-elements-textSecondary">CPU Usage</span>
863
+ <span className={classNames('text-sm font-medium', getUsageColor(metrics.cpu.usage))}>
864
+ {Math.round(metrics.cpu.usage)}%
865
  </span>
866
  </div>
867
  {renderUsageGraph(metricsHistory.cpu, 'CPU', '#9333ea')}
868
+ {metrics.cpu.temperature && (
869
+ <div className="text-xs text-bolt-elements-textSecondary mt-2">
870
+ Temperature: {metrics.cpu.temperature}°C
871
+ </div>
872
+ )}
873
+ {metrics.cpu.frequency && (
874
+ <div className="text-xs text-bolt-elements-textSecondary">
875
+ Frequency: {(metrics.cpu.frequency / 1000).toFixed(1)} GHz
876
+ </div>
877
+ )}
878
  </div>
879
 
880
  {/* Memory Usage */}
 
886
  </span>
887
  </div>
888
  {renderUsageGraph(metricsHistory.memory, 'Memory', '#2563eb')}
889
+ <div className="text-xs text-bolt-elements-textSecondary mt-2">
890
+ Used: {formatBytes(metrics.memory.used)}
891
+ </div>
892
+ <div className="text-xs text-bolt-elements-textSecondary">Total: {formatBytes(metrics.memory.total)}</div>
893
+ <div className="text-xs text-bolt-elements-textSecondary">
894
+ Heap: {formatBytes(metrics.memory.heap.used)} / {formatBytes(metrics.memory.heap.total)}
895
+ </div>
896
  </div>
897
 
898
+ {/* Performance */}
899
+ <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
900
+ <div className="flex items-center justify-between">
901
+ <span className="text-sm text-bolt-elements-textSecondary">Performance</span>
902
+ <span
903
+ className={classNames('text-sm font-medium', {
904
+ 'text-red-500': metrics.performance.fps < PERFORMANCE_THRESHOLDS.fps.critical,
905
+ 'text-yellow-500': metrics.performance.fps < PERFORMANCE_THRESHOLDS.fps.warning,
906
+ 'text-green-500': metrics.performance.fps >= PERFORMANCE_THRESHOLDS.fps.warning,
907
+ })}
908
+ >
909
+ {Math.round(metrics.performance.fps)} FPS
910
+ </span>
911
+ </div>
912
+ <div className="text-xs text-bolt-elements-textSecondary mt-2">
913
+ Page Load: {(metrics.performance.pageLoad / 1000).toFixed(2)}s
914
+ </div>
915
+ <div className="text-xs text-bolt-elements-textSecondary">
916
+ DOM Ready: {(metrics.performance.domReady / 1000).toFixed(2)}s
917
+ </div>
918
+ <div className="text-xs text-bolt-elements-textSecondary">
919
+ TTFB: {(metrics.performance.timing.ttfb / 1000).toFixed(2)}s
920
  </div>
921
+ <div className="text-xs text-bolt-elements-textSecondary">
922
+ Resources: {metrics.performance.resources.total} ({formatBytes(metrics.performance.resources.size)})
923
+ </div>
924
+ </div>
925
 
926
  {/* Network */}
927
  <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
 
932
  </span>
933
  </div>
934
  {renderUsageGraph(metricsHistory.network, 'Network', '#f59e0b')}
935
+ <div className="text-xs text-bolt-elements-textSecondary mt-2">Type: {metrics.network.type}</div>
936
+ <div className="text-xs text-bolt-elements-textSecondary">Latency: {metrics.network.latency}ms</div>
937
+ <div className="text-xs text-bolt-elements-textSecondary">
938
+ Received: {formatBytes(metrics.network.bytesReceived)}
939
+ </div>
940
+ <div className="text-xs text-bolt-elements-textSecondary">
941
+ Sent: {formatBytes(metrics.network.bytesSent)}
942
+ </div>
943
  </div>
944
  </div>
945
 
946
+ {/* Battery Section */}
947
+ {metrics.battery && (
948
+ <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
949
+ <div className="flex items-center justify-between">
950
+ <span className="text-sm text-bolt-elements-textSecondary">Battery</span>
951
+ <div className="flex items-center gap-2">
952
+ {metrics.battery.charging && <div className="i-ph:lightning-fill w-4 h-4 text-bolt-action-primary" />}
953
+ <span
954
+ className={classNames(
955
+ 'text-sm font-medium',
956
+ metrics.battery.level > 20 ? 'text-bolt-elements-textPrimary' : 'text-red-500',
957
+ )}
958
+ >
959
+ {Math.round(metrics.battery.level)}%
960
+ </span>
961
+ </div>
962
+ </div>
963
+ {renderUsageGraph(metricsHistory.battery, 'Battery', '#22c55e')}
964
+ {metrics.battery.timeRemaining && (
965
+ <div className="text-xs text-bolt-elements-textSecondary mt-2">
966
+ {metrics.battery.charging ? 'Time to full: ' : 'Time remaining: '}
967
+ {formatTime(metrics.battery.timeRemaining)}
968
+ </div>
969
+ )}
970
+ {metrics.battery.temperature && (
971
+ <div className="text-xs text-bolt-elements-textSecondary">
972
+ Temperature: {metrics.battery.temperature}°C
973
+ </div>
974
+ )}
975
+ {metrics.battery.cycles && (
976
+ <div className="text-xs text-bolt-elements-textSecondary">Charge cycles: {metrics.battery.cycles}</div>
977
+ )}
978
+ {metrics.battery.health && (
979
+ <div className="text-xs text-bolt-elements-textSecondary">Battery health: {metrics.battery.health}%</div>
980
+ )}
981
+ </div>
982
+ )}
983
+
984
+ {/* Storage Section */}
985
+ <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
986
+ <div className="flex items-center justify-between">
987
+ <span className="text-sm text-bolt-elements-textSecondary">Storage</span>
988
+ <span className="text-sm font-medium text-bolt-elements-textPrimary">
989
+ {formatBytes(metrics.storage.used)} / {formatBytes(metrics.storage.total)}
990
+ </span>
991
+ </div>
992
+ <div className="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
993
+ <div
994
+ className={classNames('h-full transition-all duration-300', {
995
+ 'bg-green-500': metrics.storage.used / metrics.storage.total < 0.7,
996
+ 'bg-yellow-500':
997
+ metrics.storage.used / metrics.storage.total >= 0.7 &&
998
+ metrics.storage.used / metrics.storage.total < 0.9,
999
+ 'bg-red-500': metrics.storage.used / metrics.storage.total >= 0.9,
1000
+ })}
1001
+ style={{ width: `${(metrics.storage.used / metrics.storage.total) * 100}%` }}
1002
+ />
1003
+ </div>
1004
+ <div className="text-xs text-bolt-elements-textSecondary mt-2">Free: {formatBytes(metrics.storage.free)}</div>
1005
+ <div className="text-xs text-bolt-elements-textSecondary">Type: {metrics.storage.type}</div>
1006
+ </div>
1007
+
1008
+ {/* Performance Alerts */}
1009
+ {alerts.length > 0 && (
1010
+ <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
1011
+ <div className="flex items-center justify-between">
1012
+ <span className="text-sm font-medium text-bolt-elements-textPrimary">Recent Alerts</span>
1013
+ <button
1014
+ onClick={() => setAlerts([])}
1015
+ className="text-xs text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary"
1016
+ >
1017
+ Clear All
1018
+ </button>
1019
+ </div>
1020
+ <div className="space-y-2">
1021
+ {alerts.slice(-5).map((alert, index) => (
1022
+ <div
1023
+ key={index}
1024
+ className={classNames('flex items-center gap-2 text-sm', {
1025
+ 'text-red-500': alert.type === 'error',
1026
+ 'text-yellow-500': alert.type === 'warning',
1027
+ 'text-blue-500': alert.type === 'info',
1028
+ })}
1029
+ >
1030
+ <div
1031
+ className={classNames('w-4 h-4', {
1032
+ 'i-ph:warning-circle-fill': alert.type === 'warning',
1033
+ 'i-ph:x-circle-fill': alert.type === 'error',
1034
+ 'i-ph:info-fill': alert.type === 'info',
1035
+ })}
1036
+ />
1037
+ <span>{alert.message}</span>
1038
+ <span className="text-xs text-bolt-elements-textSecondary ml-auto">
1039
+ {new Date(alert.timestamp).toLocaleTimeString()}
1040
+ </span>
1041
+ </div>
1042
+ ))}
1043
+ </div>
1044
+ </div>
1045
+ )}
1046
+
1047
  {/* Energy Savings */}
1048
  {energySaverMode && (
1049
  <div className="flex flex-col gap-2 rounded-lg bg-[#F8F8F8] dark:bg-[#141414] p-4">
 
1072
  </div>
1073
  );
1074
  }
1075
+
1076
+ // Helper function to format bytes
1077
+ const formatBytes = (bytes: number): string => {
1078
+ if (bytes === 0) {
1079
+ return '0 B';
1080
+ }
1081
+
1082
+ const k = 1024;
1083
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
1084
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1085
+
1086
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
1087
+ };
1088
+
1089
+ // Helper function to format time
1090
+ const formatTime = (seconds: number): string => {
1091
+ if (!isFinite(seconds) || seconds === 0) {
1092
+ return 'Unknown';
1093
+ }
1094
+
1095
+ const hours = Math.floor(seconds / 3600);
1096
+ const minutes = Math.floor((seconds % 3600) / 60);
1097
+
1098
+ if (hours > 0) {
1099
+ return `${hours}h ${minutes}m`;
1100
+ }
1101
+
1102
+ return `${minutes}m`;
1103
+ };
app/routes/api.system.git-info.ts CHANGED
@@ -20,7 +20,7 @@ interface GitHubRepoInfo {
20
  const getLocalGitInfo = () => {
21
  try {
22
  return {
23
- commitHash: execSync('git rev-parse --short HEAD').toString().trim(),
24
  branch: execSync('git rev-parse --abbrev-ref HEAD').toString().trim(),
25
  commitTime: execSync('git log -1 --format=%cd').toString().trim(),
26
  author: execSync('git log -1 --format=%an').toString().trim(),
@@ -40,13 +40,42 @@ const getLocalGitInfo = () => {
40
 
41
  const getGitHubInfo = async (repoFullName: string) => {
42
  try {
43
- const response = await fetch(`https://api.github.com/repos/${repoFullName}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  if (!response.ok) {
 
 
 
 
 
 
 
 
 
 
 
46
  throw new Error(`GitHub API error: ${response.statusText}`);
47
  }
48
 
49
- return (await response.json()) as GitHubRepoInfo;
 
 
 
50
  } catch (error) {
51
  console.error('Failed to get GitHub info:', error);
52
  return null;
@@ -55,6 +84,7 @@ const getGitHubInfo = async (repoFullName: string) => {
55
 
56
  export const loader: LoaderFunction = async ({ request: _request }) => {
57
  const localInfo = getLocalGitInfo();
 
58
 
59
  // If we have local info, try to get GitHub info for both our fork and upstream
60
  let githubInfo = null;
@@ -68,7 +98,7 @@ export const loader: LoaderFunction = async ({ request: _request }) => {
68
  githubInfo = await getGitHubInfo('stackblitz-labs/bolt.diy');
69
  }
70
 
71
- return json({
72
  local: localInfo || {
73
  commitHash: 'unknown',
74
  branch: 'unknown',
@@ -99,5 +129,10 @@ export const loader: LoaderFunction = async ({ request: _request }) => {
99
  : null,
100
  isForked: Boolean(githubInfo?.parent),
101
  timestamp: new Date().toISOString(),
102
- });
 
 
 
 
 
103
  };
 
20
  const getLocalGitInfo = () => {
21
  try {
22
  return {
23
+ commitHash: execSync('git rev-parse HEAD').toString().trim(),
24
  branch: execSync('git rev-parse --abbrev-ref HEAD').toString().trim(),
25
  commitTime: execSync('git log -1 --format=%cd').toString().trim(),
26
  author: execSync('git log -1 --format=%an').toString().trim(),
 
40
 
41
  const getGitHubInfo = async (repoFullName: string) => {
42
  try {
43
+ // Add GitHub token if available
44
+ const headers: Record<string, string> = {
45
+ Accept: 'application/vnd.github.v3+json',
46
+ };
47
+
48
+ const githubToken = process.env.GITHUB_TOKEN;
49
+
50
+ if (githubToken) {
51
+ headers.Authorization = `token ${githubToken}`;
52
+ }
53
+
54
+ console.log('Fetching GitHub info for:', repoFullName); // Debug log
55
+
56
+ const response = await fetch(`https://api.github.com/repos/${repoFullName}`, {
57
+ headers,
58
+ });
59
 
60
  if (!response.ok) {
61
+ console.error('GitHub API error:', {
62
+ status: response.status,
63
+ statusText: response.statusText,
64
+ repoFullName,
65
+ });
66
+
67
+ // If we get a 404, try the main repo as fallback
68
+ if (response.status === 404 && repoFullName !== 'stackblitz-labs/bolt.diy') {
69
+ return getGitHubInfo('stackblitz-labs/bolt.diy');
70
+ }
71
+
72
  throw new Error(`GitHub API error: ${response.statusText}`);
73
  }
74
 
75
+ const data = await response.json();
76
+ console.log('GitHub API response:', data); // Debug log
77
+
78
+ return data as GitHubRepoInfo;
79
  } catch (error) {
80
  console.error('Failed to get GitHub info:', error);
81
  return null;
 
84
 
85
  export const loader: LoaderFunction = async ({ request: _request }) => {
86
  const localInfo = getLocalGitInfo();
87
+ console.log('Local git info:', localInfo); // Debug log
88
 
89
  // If we have local info, try to get GitHub info for both our fork and upstream
90
  let githubInfo = null;
 
98
  githubInfo = await getGitHubInfo('stackblitz-labs/bolt.diy');
99
  }
100
 
101
+ const response = {
102
  local: localInfo || {
103
  commitHash: 'unknown',
104
  branch: 'unknown',
 
129
  : null,
130
  isForked: Boolean(githubInfo?.parent),
131
  timestamp: new Date().toISOString(),
132
+ };
133
+
134
+ console.log('Final response:', response);
135
+
136
+ // Debug log
137
+ return json(response);
138
  };
pages/api/system/git-info.ts DELETED
@@ -1,38 +0,0 @@
1
- import type { NextApiRequest, NextApiResponse } from 'next';
2
- import { execSync } from 'child_process';
3
-
4
- export default async function handler(req: NextApiRequest, res: NextApiResponse) {
5
- if (req.method !== 'GET') {
6
- return res.status(405).json({ message: 'Method not allowed' });
7
- }
8
-
9
- try {
10
- // Get git information using git commands
11
- const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
12
- const commitHash = execSync('git rev-parse HEAD').toString().trim();
13
- const commitTime = execSync('git log -1 --format=%cd').toString().trim();
14
- const author = execSync('git log -1 --format=%an').toString().trim();
15
- const email = execSync('git log -1 --format=%ae').toString().trim();
16
- const remoteUrl = execSync('git config --get remote.origin.url').toString().trim();
17
-
18
- // Extract repo name from remote URL
19
- const repoName = remoteUrl.split('/').pop()?.replace('.git', '') || '';
20
-
21
- const gitInfo = {
22
- local: {
23
- commitHash,
24
- branch,
25
- commitTime,
26
- author,
27
- email,
28
- remoteUrl,
29
- repoName,
30
- },
31
- };
32
-
33
- return res.status(200).json(gitInfo);
34
- } catch (error) {
35
- console.error('Failed to get git information:', error);
36
- return res.status(500).json({ message: 'Failed to get git information' });
37
- }
38
- }