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 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 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 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: Math.random() * 100, memory: { used: Math.round(usedMem), total: Math.round(totalMem), percentage: Math.round(memPercentage), }, activeProcesses: document.querySelectorAll('[data-process]').length, 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 })); } }; const updateProcesses = async () => { try { setLoading((prev) => ({ ...prev, processes: true })); // Enhanced process monitoring const mockProcesses: ProcessInfo[] = [ { name: 'Ollama Model Updates', type: 'Network', cpuUsage: Math.random() * 5, memoryUsage: Math.random() * 50, status: 'idle', lastUpdate: new Date().toISOString(), impact: 'high', }, { name: 'UI Animations', type: 'Animation', cpuUsage: Math.random() * 3, memoryUsage: Math.random() * 30, status: 'idle', lastUpdate: new Date().toISOString(), impact: 'medium', }, { name: 'Background Sync', type: 'Background', cpuUsage: Math.random() * 2, memoryUsage: Math.random() * 20, status: 'idle', lastUpdate: new Date().toISOString(), impact: 'low', }, { name: 'IndexedDB Operations', type: 'Storage', cpuUsage: Math.random() * 1, memoryUsage: Math.random() * 15, status: 'idle', lastUpdate: new Date().toISOString(), impact: 'low', }, { name: 'WebSocket Connection', type: 'Network', cpuUsage: Math.random() * 2, memoryUsage: Math.random() * 10, status: 'idle', lastUpdate: new Date().toISOString(), impact: 'medium', }, ]; setProcesses(mockProcesses); } 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

); }