import React, { useEffect, useState, useRef, useCallback } from 'react'; import { classNames } from '~/utils/classNames'; import { Line } from 'react-chartjs-2'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, } from 'chart.js'; import { toast } from 'react-toastify'; // Import toast // Register ChartJS components ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend); interface BatteryManager extends EventTarget { charging: boolean; chargingTime: number; dischargingTime: number; level: number; } interface ProcessInfo { name: string; type: 'API' | 'Animation' | 'Background' | 'Render' | 'Network' | 'Storage'; cpuUsage: number; memoryUsage: number; status: 'active' | 'idle' | 'suspended'; lastUpdate: string; impact: 'high' | 'medium' | 'low'; } interface SystemMetrics { cpu: number; memory: { used: number; total: number; percentage: number; }; activeProcesses: number; uptime: number; battery?: { level: number; charging: boolean; timeRemaining?: number; }; network: { downlink: number; latency: number; type: string; }; } interface MetricsHistory { timestamps: string[]; cpu: number[]; memory: number[]; battery: number[]; network: number[]; } interface EnergySavings { updatesReduced: number; timeInSaverMode: number; estimatedEnergySaved: number; // in mWh (milliwatt-hours) } declare global { interface Navigator { getBattery(): Promise; } interface Performance { memory?: { jsHeapSizeLimit: number; totalJSHeapSize: number; usedJSHeapSize: number; }; } } const MAX_HISTORY_POINTS = 60; // 1 minute of history at 1s intervals const BATTERY_THRESHOLD = 20; // Enable energy saver when battery below 20% const UPDATE_INTERVALS = { normal: { metrics: 1000, // 1s processes: 2000, // 2s }, energySaver: { metrics: 5000, // 5s processes: 10000, // 10s }, }; // Energy consumption estimates (milliwatts) const ENERGY_COSTS = { update: 2, // mW per update apiCall: 5, // mW per API call rendering: 1, // mW per render }; export default function TaskManagerTab() { const [processes, setProcesses] = useState([]); const [metrics, setMetrics] = useState({ cpu: 0, memory: { used: 0, total: 0, percentage: 0 }, activeProcesses: 0, uptime: 0, network: { downlink: 0, latency: 0, type: 'unknown' }, }); const [metricsHistory, setMetricsHistory] = useState({ timestamps: [], cpu: [], memory: [], battery: [], network: [], }); const [loading, setLoading] = useState({ metrics: false, processes: false, }); const [energySaverMode, setEnergySaverMode] = useState(() => { // Initialize from localStorage, default to false const saved = localStorage.getItem('energySaverMode'); return saved ? JSON.parse(saved) : false; }); const [autoEnergySaver, setAutoEnergySaver] = useState(() => { // Initialize from localStorage, default to false const saved = localStorage.getItem('autoEnergySaver'); return saved ? JSON.parse(saved) : false; }); const [energySavings, setEnergySavings] = useState({ updatesReduced: 0, timeInSaverMode: 0, estimatedEnergySaved: 0, }); const saverModeStartTime = useRef(null); // Handle energy saver mode changes const handleEnergySaverChange = (checked: boolean) => { setEnergySaverMode(checked); localStorage.setItem('energySaverMode', JSON.stringify(checked)); toast.success(checked ? 'Energy Saver mode enabled' : 'Energy Saver mode disabled'); }; // Handle auto energy saver changes const handleAutoEnergySaverChange = (checked: boolean) => { setAutoEnergySaver(checked); localStorage.setItem('autoEnergySaver', JSON.stringify(checked)); toast.success(checked ? 'Auto Energy Saver enabled' : 'Auto Energy Saver disabled'); if (!checked) { // When disabling auto mode, also disable energy saver mode setEnergySaverMode(false); localStorage.setItem('energySaverMode', 'false'); } }; // Calculate energy savings const updateEnergySavings = useCallback(() => { if (!energySaverMode) { saverModeStartTime.current = null; return; } if (!saverModeStartTime.current) { saverModeStartTime.current = Date.now(); } const timeInSaverMode = (Date.now() - saverModeStartTime.current) / 1000; // in seconds const normalUpdatesPerMinute = 60 / (UPDATE_INTERVALS.normal.metrics / 1000) + 60 / (UPDATE_INTERVALS.normal.processes / 1000); const saverUpdatesPerMinute = 60 / (UPDATE_INTERVALS.energySaver.metrics / 1000) + 60 / (UPDATE_INTERVALS.energySaver.processes / 1000); const updatesReduced = Math.floor((normalUpdatesPerMinute - saverUpdatesPerMinute) * (timeInSaverMode / 60)); // Calculate energy saved (mWh) const energySaved = (updatesReduced * ENERGY_COSTS.update + // Energy saved from reduced updates updatesReduced * ENERGY_COSTS.apiCall + // Energy saved from fewer API calls updatesReduced * ENERGY_COSTS.rendering) / // Energy saved from fewer renders 3600; // Convert to watt-hours (divide by 3600 seconds) setEnergySavings({ updatesReduced, timeInSaverMode, estimatedEnergySaved: energySaved, }); }, [energySaverMode]); useEffect((): (() => void) | undefined => { if (!energySaverMode) { // Clear any existing intervals and reset savings when disabled setEnergySavings({ updatesReduced: 0, timeInSaverMode: 0, estimatedEnergySaved: 0, }); return undefined; } const savingsInterval = setInterval(updateEnergySavings, 1000); return () => clearInterval(savingsInterval); }, [energySaverMode, updateEnergySavings]); useEffect((): (() => void) | undefined => { if (!autoEnergySaver) { // If auto mode is disabled, clear any forced energy saver state setEnergySaverMode(false); return undefined; } const checkBatteryStatus = async () => { try { const battery = await navigator.getBattery(); const shouldEnableSaver = !battery.charging && battery.level * 100 <= BATTERY_THRESHOLD; setEnergySaverMode(shouldEnableSaver); } catch { console.log('Battery API not available'); } }; checkBatteryStatus(); const batteryCheckInterval = setInterval(checkBatteryStatus, 60000); return () => clearInterval(batteryCheckInterval); }, [autoEnergySaver]); const getUsageColor = (usage: number): string => { if (usage > 80) { return 'text-red-500'; } if (usage > 50) { return 'text-yellow-500'; } return 'text-gray-500'; }; const getImpactColor = (impact: 'high' | 'medium' | 'low'): string => { if (impact === 'high') { return 'text-red-500'; } if (impact === 'medium') { return 'text-yellow-500'; } return 'text-gray-500'; }; const renderUsageGraph = (data: number[], label: string, color: string) => { const chartData = { labels: metricsHistory.timestamps, datasets: [ { label, data, borderColor: color, fill: false, tension: 0.4, }, ], }; const options = { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, max: 100, grid: { color: 'rgba(255, 255, 255, 0.1)', }, }, x: { grid: { display: false, }, }, }, plugins: { legend: { display: false, }, }, animation: { duration: 0, } as const, }; return (
); }; const updateMetrics = async () => { try { setLoading((prev) => ({ ...prev, metrics: true })); // Get memory info using Performance API const memory = performance.memory || { jsHeapSizeLimit: 0, totalJSHeapSize: 0, usedJSHeapSize: 0, }; const totalMem = memory.totalJSHeapSize / (1024 * 1024); const usedMem = memory.usedJSHeapSize / (1024 * 1024); const memPercentage = (usedMem / totalMem) * 100; // Get CPU usage using Performance API const cpuUsage = await getCPUUsage(); // Get battery info let batteryInfo: SystemMetrics['battery'] | undefined; try { const battery = await navigator.getBattery(); batteryInfo = { level: battery.level * 100, charging: battery.charging, timeRemaining: battery.charging ? battery.chargingTime : battery.dischargingTime, }; } catch { console.log('Battery API not available'); } // Get network info using Network Information API const connection = (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection; const networkInfo = { downlink: connection?.downlink || 0, latency: connection?.rtt || 0, type: connection?.type || 'unknown', }; const newMetrics = { cpu: cpuUsage, memory: { used: Math.round(usedMem), total: Math.round(totalMem), percentage: Math.round(memPercentage), }, activeProcesses: await getActiveProcessCount(), uptime: performance.now() / 1000, battery: batteryInfo, network: networkInfo, }; setMetrics(newMetrics); // Update metrics history const now = new Date().toLocaleTimeString(); setMetricsHistory((prev) => { const timestamps = [...prev.timestamps, now].slice(-MAX_HISTORY_POINTS); const cpu = [...prev.cpu, newMetrics.cpu].slice(-MAX_HISTORY_POINTS); const memory = [...prev.memory, newMetrics.memory.percentage].slice(-MAX_HISTORY_POINTS); const battery = [...prev.battery, batteryInfo?.level || 0].slice(-MAX_HISTORY_POINTS); const network = [...prev.network, networkInfo.downlink].slice(-MAX_HISTORY_POINTS); return { timestamps, cpu, memory, battery, network }; }); } catch (error: unknown) { console.error('Failed to update system metrics:', error); } finally { setLoading((prev) => ({ ...prev, metrics: false })); } }; // Get real CPU usage using Performance API const getCPUUsage = async (): Promise => { try { const t0 = performance.now(); const startEntries = performance.getEntriesByType('measure'); // Wait a short time to measure CPU usage await new Promise((resolve) => setTimeout(resolve, 100)); const t1 = performance.now(); const endEntries = performance.getEntriesByType('measure'); // Calculate CPU usage based on the number of performance entries const entriesPerMs = (endEntries.length - startEntries.length) / (t1 - t0); // Normalize to percentage (0-100) return Math.min(100, entriesPerMs * 1000); } catch (error) { console.error('Failed to get CPU usage:', error); return 0; } }; // Get real active process count const getActiveProcessCount = async (): Promise => { try { // Count active network connections const networkCount = (navigator as any)?.connections?.length || 0; // Count active service workers const swCount = (await navigator.serviceWorker?.getRegistrations().then((regs) => regs.length)) || 0; // Count active animations const animationCount = document.getAnimations().length; // Count active fetch requests const fetchCount = performance .getEntriesByType('resource') .filter( (entry) => (entry as PerformanceResourceTiming).initiatorType === 'fetch' && entry.duration === 0, ).length; return networkCount + swCount + animationCount + fetchCount; } catch (error) { console.error('Failed to get active process count:', error); return 0; } }; const updateProcesses = async () => { try { setLoading((prev) => ({ ...prev, processes: true })); // Get real process information const processes: ProcessInfo[] = []; // Add network processes const networkEntries = performance .getEntriesByType('resource') .filter((entry) => (entry as PerformanceResourceTiming).initiatorType === 'fetch' && entry.duration === 0) .slice(-5); // Get last 5 active requests networkEntries.forEach((entry) => { processes.push({ name: `Network Request: ${new URL((entry as PerformanceResourceTiming).name).pathname}`, type: 'Network', cpuUsage: entry.duration > 0 ? entry.duration / 100 : 0, memoryUsage: (entry as PerformanceResourceTiming).encodedBodySize / (1024 * 1024), // Convert to MB status: entry.duration === 0 ? 'active' : 'idle', lastUpdate: new Date().toISOString(), impact: entry.duration > 1000 ? 'high' : entry.duration > 500 ? 'medium' : 'low', }); }); // Add animation processes document .getAnimations() .slice(0, 5) .forEach((animation) => { processes.push({ name: `Animation: ${animation.id || 'Unnamed'}`, type: 'Animation', cpuUsage: animation.playState === 'running' ? 2 : 0, memoryUsage: 1, // Approximate memory usage status: animation.playState === 'running' ? 'active' : 'idle', lastUpdate: new Date().toISOString(), impact: 'low', }); }); // Add service worker processes const serviceWorkers = (await navigator.serviceWorker?.getRegistrations()) || []; serviceWorkers.forEach((sw) => { processes.push({ name: `Service Worker: ${sw.scope}`, type: 'Background', cpuUsage: sw.active ? 1 : 0, memoryUsage: 5, // Approximate memory usage status: sw.active ? 'active' : 'idle', lastUpdate: new Date().toISOString(), impact: 'low', }); }); setProcesses(processes); } catch (error) { console.error('Failed to update process list:', error); } finally { setLoading((prev) => ({ ...prev, processes: false })); } }; // Initial update effect useEffect((): (() => void) => { // Initial update updateMetrics(); updateProcesses(); // Set up intervals for live updates const metricsInterval = setInterval( updateMetrics, energySaverMode ? UPDATE_INTERVALS.energySaver.metrics : UPDATE_INTERVALS.normal.metrics, ); const processesInterval = setInterval( updateProcesses, energySaverMode ? UPDATE_INTERVALS.energySaver.processes : UPDATE_INTERVALS.normal.processes, ); // Cleanup on unmount return () => { clearInterval(metricsInterval); clearInterval(processesInterval); }; }, [energySaverMode]); // Re-create intervals when energy saver mode changes return (
{/* System Overview */}

System Overview

handleAutoEnergySaverChange(e.target.checked)} className="form-checkbox h-4 w-4 text-purple-600 rounded border-gray-300 dark:border-gray-700" />
!autoEnergySaver && handleEnergySaverChange(e.target.checked)} disabled={autoEnergySaver} className="form-checkbox h-4 w-4 text-purple-600 rounded border-gray-300 dark:border-gray-700 disabled:opacity-50" />
CPU Usage

{Math.round(metrics.cpu)}%

{renderUsageGraph(metricsHistory.cpu, 'CPU', '#9333ea')}
Memory Usage

{metrics.memory.used}MB / {metrics.memory.total}MB

{renderUsageGraph(metricsHistory.memory, 'Memory', '#2563eb')}
Battery
{metrics.battery ? (

{Math.round(metrics.battery.level)}% {metrics.battery.charging && (

)}

{metrics.battery.timeRemaining && metrics.battery.timeRemaining !== Infinity && (

{metrics.battery.charging ? 'Full in: ' : 'Remaining: '} {Math.round(metrics.battery.timeRemaining / 60)}m

)} {renderUsageGraph(metricsHistory.battery, 'Battery', '#22c55e')}
) : (

Not available

)}
Network

{metrics.network.downlink} Mbps

Latency: {metrics.network.latency}ms

{renderUsageGraph(metricsHistory.network, 'Network', '#f59e0b')}
{/* Process List */}

Active Processes

{processes.map((process, index) => ( ))}
Process Type CPU Memory Status Impact Last Update
{process.name}
{process.type} {process.cpuUsage.toFixed(1)}% {process.memoryUsage.toFixed(1)} MB {process.status} {process.impact} {new Date(process.lastUpdate).toLocaleTimeString()}
{/* Energy Savings */}

Energy Savings

Time in Saver Mode

{Math.floor(energySavings.timeInSaverMode / 60)}m {Math.floor(energySavings.timeInSaverMode % 60)}s

Updates Reduced

{energySavings.updatesReduced}

Estimated Energy Saved

{energySavings.estimatedEnergySaved.toFixed(2)} mWh

); }