- );
-}
diff --git a/app/components/@settings/tabs/task-manager/TaskManagerTab.tsx b/app/components/@settings/tabs/task-manager/TaskManagerTab.tsx
deleted file mode 100644
index e753bd15..00000000
--- a/app/components/@settings/tabs/task-manager/TaskManagerTab.tsx
+++ /dev/null
@@ -1,1604 +0,0 @@
-import * as React from 'react';
-import { useEffect, useState, 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,
- type Chart,
-} from 'chart.js';
-import { toast } from 'react-toastify'; // Import toast
-import { useUpdateCheck } from '~/lib/hooks/useUpdateCheck';
-import { tabConfigurationStore, type TabConfig } from '~/lib/stores/tabConfigurationStore';
-import { useStore } from 'zustand';
-
-// 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 SystemMemoryInfo {
- total: number;
- free: number;
- used: number;
- percentage: number;
- swap?: {
- total: number;
- free: number;
- used: number;
- percentage: number;
- };
- timestamp: string;
- error?: string;
-}
-
-interface ProcessInfo {
- pid: number;
- name: string;
- cpu: number;
- memory: number;
- command?: string;
- timestamp: string;
- error?: string;
-}
-
-interface DiskInfo {
- filesystem: string;
- size: number;
- used: number;
- available: number;
- percentage: number;
- mountpoint: string;
- timestamp: string;
- error?: string;
-}
-
-interface SystemMetrics {
- memory: {
- used: number;
- total: number;
- percentage: number;
- process?: {
- heapUsed: number;
- heapTotal: number;
- external: number;
- rss: number;
- };
- };
- systemMemory?: SystemMemoryInfo;
- processes?: ProcessInfo[];
- disks?: DiskInfo[];
- battery?: {
- level: number;
- charging: boolean;
- timeRemaining?: number;
- };
- network: {
- downlink: number;
- uplink?: number;
- latency: {
- current: number;
- average: number;
- min: number;
- max: number;
- history: number[];
- lastUpdate: number;
- };
- type: string;
- effectiveType?: string;
- };
- performance: {
- pageLoad: number;
- domReady: number;
- resources: {
- total: number;
- size: number;
- loadTime: number;
- };
- timing: {
- ttfb: number;
- fcp: number;
- lcp: number;
- };
- };
-}
-
-type SortField = 'name' | 'pid' | 'cpu' | 'memory';
-type SortDirection = 'asc' | 'desc';
-
-interface MetricsHistory {
- timestamps: string[];
- memory: number[];
- battery: number[];
- network: number[];
- cpu: number[];
- disk: number[];
-}
-
-interface PerformanceAlert {
- type: 'warning' | 'error' | 'info';
- message: string;
- timestamp: number;
- metric: string;
- threshold: number;
- value: number;
-}
-
-declare global {
- interface Navigator {
- getBattery(): Promise;
- }
- interface Performance {
- memory?: {
- jsHeapSizeLimit: number;
- totalJSHeapSize: number;
- usedJSHeapSize: number;
- };
- }
-}
-
-// Constants for performance thresholds
-const PERFORMANCE_THRESHOLDS = {
- memory: {
- warning: 75,
- critical: 90,
- },
- network: {
- latency: {
- warning: 200,
- critical: 500,
- },
- },
- battery: {
- warning: 20,
- critical: 10,
- },
-};
-
-// Default metrics state
-const DEFAULT_METRICS_STATE: SystemMetrics = {
- memory: {
- used: 0,
- total: 0,
- percentage: 0,
- },
- network: {
- downlink: 0,
- latency: {
- current: 0,
- average: 0,
- min: 0,
- max: 0,
- history: [],
- lastUpdate: 0,
- },
- type: 'unknown',
- },
- performance: {
- pageLoad: 0,
- domReady: 0,
- resources: {
- total: 0,
- size: 0,
- loadTime: 0,
- },
- timing: {
- ttfb: 0,
- fcp: 0,
- lcp: 0,
- },
- },
-};
-
-// Default metrics history
-const DEFAULT_METRICS_HISTORY: MetricsHistory = {
- timestamps: Array(8).fill(new Date().toLocaleTimeString()),
- memory: Array(8).fill(0),
- battery: Array(8).fill(0),
- network: Array(8).fill(0),
- cpu: Array(8).fill(0),
- disk: Array(8).fill(0),
-};
-
-// Maximum number of history points to keep
-const MAX_HISTORY_POINTS = 8;
-
-// Used for environment detection in updateMetrics function
-const isLocalDevelopment =
- typeof window !== 'undefined' &&
- window.location &&
- (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
-
-// For development environments, we'll always provide mock data if real data isn't available
-const isDevelopment =
- typeof window !== 'undefined' &&
- (window.location.hostname === 'localhost' ||
- window.location.hostname === '127.0.0.1' ||
- window.location.hostname.includes('192.168.') ||
- window.location.hostname.includes('.local'));
-
-// Function to detect Cloudflare and similar serverless environments where TaskManager is not useful
-const isServerlessHosting = (): boolean => {
- if (typeof window === 'undefined') {
- return false;
- }
-
- // For testing: Allow forcing serverless mode via URL param for easy testing
- if (typeof window !== 'undefined' && window.location.search.includes('simulate-serverless=true')) {
- console.log('Simulating serverless environment for testing');
- return true;
- }
-
- // Check for common serverless hosting domains
- const hostname = window.location.hostname;
-
- return (
- hostname.includes('.cloudflare.') ||
- hostname.includes('.netlify.app') ||
- hostname.includes('.vercel.app') ||
- hostname.endsWith('.workers.dev')
- );
-};
-
-const TaskManagerTab: React.FC = () => {
- const [metrics, setMetrics] = useState(() => DEFAULT_METRICS_STATE);
- const [metricsHistory, setMetricsHistory] = useState(() => DEFAULT_METRICS_HISTORY);
- const [alerts, setAlerts] = useState([]);
- const [lastAlertState, setLastAlertState] = useState('normal');
- const [sortField, setSortField] = useState('memory');
- const [sortDirection, setSortDirection] = useState('desc');
- const [isNotSupported, setIsNotSupported] = useState(false);
-
- // Chart refs for cleanup
- const memoryChartRef = React.useRef | null>(null);
- const batteryChartRef = React.useRef | null>(null);
- const networkChartRef = React.useRef | null>(null);
- const cpuChartRef = React.useRef | null>(null);
- const diskChartRef = React.useRef | null>(null);
-
- // Cleanup chart instances on unmount
- React.useEffect(() => {
- const cleanupCharts = () => {
- if (memoryChartRef.current) {
- memoryChartRef.current.destroy();
- }
-
- if (batteryChartRef.current) {
- batteryChartRef.current.destroy();
- }
-
- if (networkChartRef.current) {
- networkChartRef.current.destroy();
- }
-
- if (cpuChartRef.current) {
- cpuChartRef.current.destroy();
- }
-
- if (diskChartRef.current) {
- diskChartRef.current.destroy();
- }
- };
-
- return cleanupCharts;
- }, []);
-
- // Get update status and tab configuration
- const { hasUpdate } = useUpdateCheck();
- const tabConfig = useStore(tabConfigurationStore);
-
- const resetTabConfiguration = useCallback(() => {
- tabConfig.reset();
- return tabConfig.get();
- }, [tabConfig]);
-
- // Effect to handle tab visibility
- useEffect(() => {
- const handleTabVisibility = () => {
- const currentConfig = tabConfig.get();
- const controlledTabs = ['debug', 'update'];
-
- // Update visibility based on conditions
- const updatedTabs = currentConfig.userTabs.map((tab: TabConfig) => {
- if (controlledTabs.includes(tab.id)) {
- return {
- ...tab,
- visible: tab.id === 'debug' ? metrics.memory.percentage > 80 : hasUpdate,
- };
- }
-
- return tab;
- });
-
- tabConfig.set({
- ...currentConfig,
- userTabs: updatedTabs,
- });
- };
-
- const checkInterval = setInterval(handleTabVisibility, 5000);
-
- return () => {
- clearInterval(checkInterval);
- };
- }, [metrics.memory.percentage, hasUpdate, tabConfig]);
-
- // Effect to handle reset and initialization
- useEffect(() => {
- const resetToDefaults = () => {
- console.log('TaskManagerTab: Resetting to defaults');
-
- // Reset metrics and local state
- setMetrics(DEFAULT_METRICS_STATE);
- setMetricsHistory(DEFAULT_METRICS_HISTORY);
- setAlerts([]);
-
- // Reset tab configuration to ensure proper visibility
- const defaultConfig = resetTabConfiguration();
- console.log('TaskManagerTab: Reset tab configuration:', defaultConfig);
- };
-
- // Listen for both storage changes and custom reset event
- const handleReset = (event: Event | StorageEvent) => {
- if (event instanceof StorageEvent) {
- if (event.key === 'tabConfiguration' && event.newValue === null) {
- resetToDefaults();
- }
- } else if (event instanceof CustomEvent && event.type === 'tabConfigReset') {
- resetToDefaults();
- }
- };
-
- // Initial setup
- const initializeTab = async () => {
- try {
- await updateMetrics();
- } catch (error) {
- console.error('Failed to initialize TaskManagerTab:', error);
- resetToDefaults();
- }
- };
-
- window.addEventListener('storage', handleReset);
- window.addEventListener('tabConfigReset', handleReset);
- initializeTab();
-
- return () => {
- window.removeEventListener('storage', handleReset);
- window.removeEventListener('tabConfigReset', handleReset);
- };
- }, []);
-
- // Effect to update metrics periodically
- useEffect(() => {
- const updateInterval = 5000; // Update every 5 seconds instead of 2.5 seconds
- let metricsInterval: NodeJS.Timeout;
-
- // Only run updates when tab is visible
- const handleVisibilityChange = () => {
- if (document.hidden) {
- clearInterval(metricsInterval);
- } else {
- updateMetrics();
- metricsInterval = setInterval(updateMetrics, updateInterval);
- }
- };
-
- // Initial setup
- handleVisibilityChange();
- document.addEventListener('visibilitychange', handleVisibilityChange);
-
- return () => {
- clearInterval(metricsInterval);
- document.removeEventListener('visibilitychange', handleVisibilityChange);
- };
- }, []);
-
- // Effect to disable taskmanager on serverless environments
- useEffect(() => {
- const checkEnvironment = async () => {
- // If we're on Cloudflare/Netlify/etc., set not supported
- if (isServerlessHosting()) {
- setIsNotSupported(true);
- return;
- }
-
- // For testing: Allow forcing API failures via URL param
- if (typeof window !== 'undefined' && window.location.search.includes('simulate-api-failure=true')) {
- console.log('Simulating API failures for testing');
- setIsNotSupported(true);
-
- return;
- }
-
- // Try to fetch system metrics once as detection
- try {
- const response = await fetch('/api/system/memory-info');
- const diskResponse = await fetch('/api/system/disk-info');
- const processResponse = await fetch('/api/system/process-info');
-
- // If all these return errors or not found, system monitoring is not supported
- if (!response.ok && !diskResponse.ok && !processResponse.ok) {
- setIsNotSupported(true);
- }
- } catch (error) {
- console.warn('Failed to fetch system metrics. TaskManager features may be limited:', error);
-
- // Don't automatically disable - we'll show partial data based on what's available
- }
- };
-
- checkEnvironment();
- }, []);
-
- // Get detailed performance metrics
- const getPerformanceMetrics = async (): Promise> => {
- try {
- // Get page load metrics
- const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
- const pageLoad = navigation.loadEventEnd - navigation.startTime;
- const domReady = navigation.domContentLoadedEventEnd - navigation.startTime;
-
- // Get resource metrics
- const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[];
- const resourceMetrics = {
- total: resources.length,
- size: resources.reduce((total, r) => total + (r.transferSize || 0), 0),
- loadTime: Math.max(0, ...resources.map((r) => r.duration)),
- };
-
- // Get Web Vitals
- const ttfb = navigation.responseStart - navigation.requestStart;
- const paintEntries = performance.getEntriesByType('paint');
- const fcp = paintEntries.find((entry) => entry.name === 'first-contentful-paint')?.startTime || 0;
-
- // Get LCP using PerformanceObserver
- const lcp = await new Promise((resolve) => {
- new PerformanceObserver((list) => {
- const entries = list.getEntries();
- const lastEntry = entries[entries.length - 1];
- resolve(lastEntry?.startTime || 0);
- }).observe({ entryTypes: ['largest-contentful-paint'] });
-
- // Resolve after 3s if no LCP
- setTimeout(() => resolve(0), 3000);
- });
-
- return {
- pageLoad,
- domReady,
- resources: resourceMetrics,
- timing: {
- ttfb,
- fcp,
- lcp,
- },
- };
- } catch (error) {
- console.error('Failed to get performance metrics:', error);
- return {};
- }
- };
-
- // Function to measure endpoint latency
- const measureLatency = async (): Promise => {
- try {
- const headers = new Headers();
- headers.append('Cache-Control', 'no-cache, no-store, must-revalidate');
- headers.append('Pragma', 'no-cache');
- headers.append('Expires', '0');
-
- const attemptMeasurement = async (): Promise => {
- const start = performance.now();
- const response = await fetch('/api/health', {
- method: 'HEAD',
- headers,
- });
- const end = performance.now();
-
- if (!response.ok) {
- throw new Error(`Health check failed with status: ${response.status}`);
- }
-
- return Math.round(end - start);
- };
-
- try {
- const latency = await attemptMeasurement();
- console.log(`Measured latency: ${latency}ms`);
-
- return latency;
- } catch (error) {
- console.warn(`Latency measurement failed, retrying: ${error}`);
-
- try {
- // Retry once
- const latency = await attemptMeasurement();
- console.log(`Measured latency on retry: ${latency}ms`);
-
- return latency;
- } catch (retryError) {
- console.error(`Latency measurement failed after retry: ${retryError}`);
-
- // Return a realistic random latency value for development
- const mockLatency = 30 + Math.floor(Math.random() * 120); // 30-150ms
- console.log(`Using mock latency: ${mockLatency}ms`);
-
- return mockLatency;
- }
- }
- } catch (error) {
- console.error(`Error in latency measurement: ${error}`);
-
- // Return a realistic random latency value
- const mockLatency = 30 + Math.floor(Math.random() * 120); // 30-150ms
- console.log(`Using mock latency due to error: ${mockLatency}ms`);
-
- return mockLatency;
- }
- };
-
- // Update metrics with real data only
- const updateMetrics = async () => {
- try {
- // If we already determined this environment doesn't support system metrics, don't try fetching
- if (isNotSupported) {
- console.log('TaskManager: System metrics not supported in this environment');
- return;
- }
-
- // Get system memory info first as it's most important
- let systemMemoryInfo: SystemMemoryInfo | undefined;
- let memoryMetrics = {
- used: 0,
- total: 0,
- percentage: 0,
- };
-
- try {
- const response = await fetch('/api/system/memory-info');
-
- if (response.ok) {
- systemMemoryInfo = await response.json();
- console.log('Memory info response:', systemMemoryInfo);
-
- // Use system memory as primary memory metrics if available
- if (systemMemoryInfo && 'used' in systemMemoryInfo) {
- memoryMetrics = {
- used: systemMemoryInfo.used || 0,
- total: systemMemoryInfo.total || 1,
- percentage: systemMemoryInfo.percentage || 0,
- };
- }
- }
- } catch (error) {
- console.error('Failed to fetch system memory info:', error);
- }
-
- // Get process information
- let processInfo: ProcessInfo[] | undefined;
-
- try {
- const response = await fetch('/api/system/process-info');
-
- if (response.ok) {
- processInfo = await response.json();
- console.log('Process info response:', processInfo);
- }
- } catch (error) {
- console.error('Failed to fetch process info:', error);
- }
-
- // Get disk information
- let diskInfo: DiskInfo[] | undefined;
-
- try {
- const response = await fetch('/api/system/disk-info');
-
- if (response.ok) {
- diskInfo = await response.json();
- console.log('Disk info response:', diskInfo);
- }
- } catch (error) {
- console.error('Failed to fetch disk info:', error);
- }
-
- // Get battery info
- let batteryInfo: SystemMetrics['battery'] | undefined;
-
- try {
- if ('getBattery' in navigator) {
- const battery = await (navigator as any).getBattery();
- batteryInfo = {
- level: battery.level * 100,
- charging: battery.charging,
- timeRemaining: battery.charging ? battery.chargingTime : battery.dischargingTime,
- };
- } else {
- // Mock battery data if API not available
- batteryInfo = {
- level: 75 + Math.floor(Math.random() * 20),
- charging: Math.random() > 0.3,
- timeRemaining: 7200 + Math.floor(Math.random() * 3600),
- };
- console.log('Battery API not available, using mock data');
- }
- } catch (error) {
- console.log('Battery API error, using mock data:', error);
- batteryInfo = {
- level: 75 + Math.floor(Math.random() * 20),
- charging: Math.random() > 0.3,
- timeRemaining: 7200 + Math.floor(Math.random() * 3600),
- };
- }
-
- // Enhanced network metrics
- const connection =
- (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection;
-
- // Measure real latency
- const measuredLatency = await measureLatency();
- const connectionRtt = connection?.rtt || 0;
-
- // Use measured latency if available, fall back to connection.rtt
- const currentLatency = measuredLatency || connectionRtt || Math.floor(Math.random() * 100);
-
- // Update network metrics with historical data
- const networkInfo = {
- downlink: connection?.downlink || 1.5 + Math.random(),
- uplink: connection?.uplink || 0.5 + Math.random(),
- latency: {
- current: currentLatency,
- average:
- metrics.network.latency.history.length > 0
- ? [...metrics.network.latency.history, currentLatency].reduce((a, b) => a + b, 0) /
- (metrics.network.latency.history.length + 1)
- : currentLatency,
- min:
- metrics.network.latency.history.length > 0
- ? Math.min(...metrics.network.latency.history, currentLatency)
- : currentLatency,
- max:
- metrics.network.latency.history.length > 0
- ? Math.max(...metrics.network.latency.history, currentLatency)
- : currentLatency,
- history: [...metrics.network.latency.history, currentLatency].slice(-30), // Keep last 30 measurements
- lastUpdate: Date.now(),
- },
- type: connection?.type || 'unknown',
- effectiveType: connection?.effectiveType || '4g',
- };
-
- // Get performance metrics
- const performanceMetrics = await getPerformanceMetrics();
-
- const updatedMetrics: SystemMetrics = {
- memory: memoryMetrics,
- systemMemory: systemMemoryInfo,
- processes: processInfo || [],
- disks: diskInfo || [],
- battery: batteryInfo,
- network: networkInfo,
- performance: performanceMetrics as SystemMetrics['performance'],
- };
-
- setMetrics(updatedMetrics);
-
- // Update history with real data
- const now = new Date().toLocaleTimeString();
- setMetricsHistory((prev) => {
- // Ensure we have valid data or use zeros
- const memoryPercentage = systemMemoryInfo?.percentage || 0;
- const batteryLevel = batteryInfo?.level || 0;
- const networkDownlink = networkInfo.downlink || 0;
-
- // Calculate CPU usage more accurately
- let cpuUsage = 0;
-
- if (processInfo && processInfo.length > 0) {
- // Get the average of the top 3 CPU-intensive processes
- const topProcesses = [...processInfo].sort((a, b) => b.cpu - a.cpu).slice(0, 3);
- const topCpuUsage = topProcesses.reduce((total, proc) => total + proc.cpu, 0);
-
- // Get the sum of all processes
- const totalCpuUsage = processInfo.reduce((total, proc) => total + proc.cpu, 0);
-
- // Use the higher of the two values, but cap at 100%
- cpuUsage = Math.min(Math.max(topCpuUsage, (totalCpuUsage / processInfo.length) * 3), 100);
- } else {
- // If no process info, generate random CPU usage between 5-30%
- cpuUsage = 5 + Math.floor(Math.random() * 25);
- }
-
- // Calculate disk usage (average of all disks)
- let diskUsage = 0;
-
- if (diskInfo && diskInfo.length > 0) {
- diskUsage = diskInfo.reduce((total, disk) => total + disk.percentage, 0) / diskInfo.length;
- } else {
- // If no disk info, generate random disk usage between 30-70%
- diskUsage = 30 + Math.floor(Math.random() * 40);
- }
-
- // Create new arrays with the latest data
- const timestamps = [...prev.timestamps, now].slice(-MAX_HISTORY_POINTS);
- const memory = [...prev.memory, memoryPercentage].slice(-MAX_HISTORY_POINTS);
- const battery = [...prev.battery, batteryLevel].slice(-MAX_HISTORY_POINTS);
- const network = [...prev.network, networkDownlink].slice(-MAX_HISTORY_POINTS);
- const cpu = [...prev.cpu, cpuUsage].slice(-MAX_HISTORY_POINTS);
- const disk = [...prev.disk, diskUsage].slice(-MAX_HISTORY_POINTS);
-
- console.log('Updated metrics history:', {
- timestamps,
- memory,
- battery,
- network,
- cpu,
- disk,
- });
-
- return { timestamps, memory, battery, network, cpu, disk };
- });
-
- // Check for memory alerts - only show toast when state changes
- const currentState =
- systemMemoryInfo && systemMemoryInfo.percentage > PERFORMANCE_THRESHOLDS.memory.critical
- ? 'critical-memory'
- : networkInfo.latency.current > PERFORMANCE_THRESHOLDS.network.latency.critical
- ? 'critical-network'
- : batteryInfo && !batteryInfo.charging && batteryInfo.level < PERFORMANCE_THRESHOLDS.battery.critical
- ? 'critical-battery'
- : 'normal';
-
- if (currentState === 'critical-memory' && lastAlertState !== 'critical-memory') {
- const alert: PerformanceAlert = {
- type: 'error',
- message: 'Critical system memory usage detected',
- timestamp: Date.now(),
- metric: 'memory',
- threshold: PERFORMANCE_THRESHOLDS.memory.critical,
- value: systemMemoryInfo?.percentage || 0,
- };
- setAlerts((prev) => {
- const newAlerts = [...prev, alert];
- return newAlerts.slice(-10);
- });
- toast.warning(alert.message, {
- toastId: 'memory-critical',
- autoClose: 5000,
- });
- } else if (currentState === 'critical-network' && lastAlertState !== 'critical-network') {
- const alert: PerformanceAlert = {
- type: 'warning',
- message: 'High network latency detected',
- timestamp: Date.now(),
- metric: 'network',
- threshold: PERFORMANCE_THRESHOLDS.network.latency.critical,
- value: networkInfo.latency.current,
- };
- setAlerts((prev) => {
- const newAlerts = [...prev, alert];
- return newAlerts.slice(-10);
- });
- toast.warning(alert.message, {
- toastId: 'network-critical',
- autoClose: 5000,
- });
- } else if (currentState === 'critical-battery' && lastAlertState !== 'critical-battery') {
- const alert: PerformanceAlert = {
- type: 'error',
- message: 'Critical battery level detected',
- timestamp: Date.now(),
- metric: 'battery',
- threshold: PERFORMANCE_THRESHOLDS.battery.critical,
- value: batteryInfo?.level || 0,
- };
- setAlerts((prev) => {
- const newAlerts = [...prev, alert];
- return newAlerts.slice(-10);
- });
- toast.error(alert.message, {
- toastId: 'battery-critical',
- autoClose: 5000,
- });
- }
-
- setLastAlertState(currentState);
-
- // Then update the environment detection
- const isCloudflare =
- !isDevelopment && // Not in development mode
- ((systemMemoryInfo?.error && systemMemoryInfo.error.includes('not available')) ||
- (processInfo?.[0]?.error && processInfo[0].error.includes('not available')) ||
- (diskInfo?.[0]?.error && diskInfo[0].error.includes('not available')));
-
- // If we detect that we're in a serverless environment, set the flag
- if (isCloudflare || isServerlessHosting()) {
- setIsNotSupported(true);
- }
-
- if (isCloudflare) {
- console.log('Running in Cloudflare environment. System metrics not available.');
- } else if (isLocalDevelopment) {
- console.log('Running in local development environment. Using real or mock system metrics as available.');
- } else if (isDevelopment) {
- console.log('Running in development environment. Using real or mock system metrics as available.');
- } else {
- console.log('Running in production environment. Using real system metrics.');
- }
- } catch (error) {
- console.error('Failed to update metrics:', error);
- }
- };
-
- const getUsageColor = (usage: number): string => {
- if (usage > 80) {
- return 'text-red-500';
- }
-
- if (usage > 50) {
- return 'text-yellow-500';
- }
-
- return 'text-gray-500';
- };
-
- // Chart rendering function
- const renderUsageGraph = React.useMemo(
- () =>
- (data: number[], label: string, color: string, chartRef: React.RefObject>) => {
- // Ensure we have valid data
- const validData = data.map((value) => (isNaN(value) ? 0 : value));
-
- // Ensure we have at least 2 data points
- if (validData.length < 2) {
- // Add a second point if we only have one
- if (validData.length === 1) {
- validData.push(validData[0]);
- } else {
- // Add two points if we have none
- validData.push(0, 0);
- }
- }
-
- const chartData = {
- labels:
- metricsHistory.timestamps.length > 0
- ? metricsHistory.timestamps
- : Array(validData.length)
- .fill('')
- .map((_, _i) => new Date().toLocaleTimeString()),
- datasets: [
- {
- label,
- data: validData.slice(-MAX_HISTORY_POINTS),
- borderColor: color,
- backgroundColor: `${color}33`, // Add slight transparency for fill
- fill: true,
- tension: 0.4,
- pointRadius: 2, // Small points for better UX
- borderWidth: 2,
- },
- ],
- };
-
- const options = {
- responsive: true,
- maintainAspectRatio: false,
- scales: {
- y: {
- beginAtZero: true,
- max: label === 'Network' ? undefined : 100, // Auto-scale for network, 0-100 for others
- grid: {
- color: 'rgba(200, 200, 200, 0.1)',
- drawBorder: false,
- },
- ticks: {
- maxTicksLimit: 5,
- callback: (value: any) => {
- if (label === 'Network') {
- return `${value} Mbps`;
- }
-
- return `${value}%`;
- },
- },
- },
- x: {
- grid: {
- display: false,
- },
- ticks: {
- maxTicksLimit: 4,
- maxRotation: 0,
- },
- },
- },
- plugins: {
- legend: {
- display: false,
- },
- tooltip: {
- enabled: true,
- mode: 'index' as const,
- intersect: false,
- backgroundColor: 'rgba(0, 0, 0, 0.8)',
- titleColor: 'white',
- bodyColor: 'white',
- borderColor: color,
- borderWidth: 1,
- padding: 10,
- cornerRadius: 4,
- displayColors: false,
- callbacks: {
- title: (tooltipItems: any) => {
- return tooltipItems[0].label; // Show timestamp
- },
- label: (context: any) => {
- const value = context.raw;
-
- if (label === 'Memory') {
- return `Memory: ${value.toFixed(1)}%`;
- } else if (label === 'CPU') {
- return `CPU: ${value.toFixed(1)}%`;
- } else if (label === 'Battery') {
- return `Battery: ${value.toFixed(1)}%`;
- } else if (label === 'Network') {
- return `Network: ${value.toFixed(1)} Mbps`;
- } else if (label === 'Disk') {
- return `Disk: ${value.toFixed(1)}%`;
- }
-
- return `${label}: ${value.toFixed(1)}`;
- },
- },
- },
- },
- animation: {
- duration: 300, // Short animation for better UX
- } as const,
- elements: {
- line: {
- tension: 0.3,
- },
- },
- };
-
- return (
-
-
-
- );
- },
- [metricsHistory.timestamps],
- );
-
- // Function to handle sorting
- const handleSort = (field: SortField) => {
- if (sortField === field) {
- // Toggle direction if clicking the same field
- setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
- } else {
- // Set new field and default to descending
- setSortField(field);
- setSortDirection('desc');
- }
- };
-
- // Function to sort processes
- const getSortedProcesses = () => {
- if (!metrics.processes) {
- return [];
- }
-
- return [...metrics.processes].sort((a, b) => {
- let comparison = 0;
-
- switch (sortField) {
- case 'name':
- comparison = a.name.localeCompare(b.name);
- break;
- case 'pid':
- comparison = a.pid - b.pid;
- break;
- case 'cpu':
- comparison = a.cpu - b.cpu;
- break;
- case 'memory':
- comparison = a.memory - b.memory;
- break;
- }
-
- return sortDirection === 'asc' ? comparison : -comparison;
- });
- };
-
- // If we're in an environment where the task manager won't work, show a message
- if (isNotSupported) {
- return (
-
-
-
System Monitoring Not Available
-
- System monitoring is not available in serverless environments like Cloudflare Pages, Netlify, or Vercel. These
- platforms don't provide access to the underlying system resources.
-
-
-
- Why is this disabled?
-
- Serverless platforms execute your code in isolated environments without access to the server's operating
- system metrics like CPU, memory, and disk usage.
-
-
- System monitoring features will be available when running in:
-
-
Local development environment
-
Virtual Machines (VMs)
-
Dedicated servers
-
Docker containers (with proper permissions)
-
-
-
-
- {/* Testing controls - only shown in development */}
- {isDevelopment && (
-
-
Testing Controls
-
- These controls are only visible in development mode
-
- System Disk
-
- {(metricsHistory.disk[metricsHistory.disk.length - 1] || 0).toFixed(1)}%
-
-
- {renderUsageGraph(metricsHistory.disk, 'Disk', '#8b5cf6', diskChartRef)}
-
- {/* Show only the main system disk (usually the first one) */}
- {metrics.disks[0] && (
- <>
-
-
-
-
-
Used: {formatBytes(metrics.disks[0].used)}
-
Free: {formatBytes(metrics.disks[0].available)}
-
Total: {formatBytes(metrics.disks[0].size)}
-
- >
- )}
-
- ) : (
-
-
-
Disk information is not available
-
- This feature may not be supported in your environment
-
- {/* Calculate approximate MB based on percentage and total system memory */}
- {metrics.systemMemory
- ? `${formatBytes(metrics.systemMemory.total * (process.memory / 100))}`
- : `${process.memory.toFixed(1)}%`}
-
-
-
- ))}
-
-
-
-
- {metrics.processes[0].error ? (
-
-
- Error retrieving process information: {metrics.processes[0].error}
-
- ) : metrics.processes[0].name === 'Browser' ? (
-
-
- Showing browser process information. System process information is not available in this
- environment.
-
- ) : (
- Showing top {metrics.processes.length} processes by memory usage
- )}
-
- >
- ) : (
-
-
-
Process information is not available
-
- This feature may not be supported in your environment
-
-
-
- )}
-
-
-
- {/* CPU Usage Graph */}
-
-
CPU Usage History
-
-
- System CPU
-
- {(metricsHistory.cpu[metricsHistory.cpu.length - 1] || 0).toFixed(1)}%
-
-