import React, { useEffect, useRef, useCallback, useState } from 'react'; import { DeviceType } from './device-detector'; import { ExtendedDeviceInfo } from './real-time-device-monitor'; // Edge case detection and handling export interface EdgeCaseConfig { enableOrientationLock: boolean; enableRapidResizeProtection: boolean; enableMultipleInstancePrevention: boolean; enableMemoryLeakPrevention: boolean; enableGracefulDegradation: boolean; debounceMs: number; maxResizeFrequency: number; memoryCheckInterval: number; } const DEFAULT_EDGE_CASE_CONFIG: EdgeCaseConfig = { enableOrientationLock: true, enableRapidResizeProtection: true, enableMultipleInstancePrevention: true, enableMemoryLeakPrevention: true, enableGracefulDegradation: true, debounceMs: 300, maxResizeFrequency: 10, // Max resizes per second memoryCheckInterval: 30000 // 30 seconds }; // Edge case event types export enum EdgeCaseEvent { RAPID_RESIZE = 'rapid_resize', ORIENTATION_CHANGE = 'orientation_change', MEMORY_WARNING = 'memory_warning', MULTIPLE_INSTANCE = 'multiple_instance', DEVICE_TRANSITION = 'device_transition', NETWORK_CHANGE = 'network_change', VISIBILITY_CHANGE = 'visibility_change' } export interface EdgeCaseEventData { type: EdgeCaseEvent; timestamp: number; data: any; severity: 'low' | 'medium' | 'high' | 'critical'; } // Custom hook for edge case handling export const useEdgeCaseHandler = ( config: Partial = {}, onEdgeCase?: (event: EdgeCaseEventData) => void ) => { const finalConfig = { ...DEFAULT_EDGE_CASE_CONFIG, ...config }; const [edgeCases, setEdgeCases] = useState([]); // Refs for tracking const resizeCountRef = useRef(0); const lastResizeTimeRef = useRef(0); const orientationLockRef = useRef(false); const instanceIdRef = useRef(`instance_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`); const memoryWarningRef = useRef(false); // Edge case handlers const handleEdgeCase = useCallback((event: EdgeCaseEventData) => { setEdgeCases(prev => [...prev.slice(-9), event]); // Keep last 10 events onEdgeCase?.(event); // Log in development if (process.env.NODE_ENV === 'development') { console.warn(`[EdgeCase] ${event.type}:`, event); } }, [onEdgeCase]); // Rapid resize protection const handleRapidResize = useCallback(() => { const now = Date.now(); const timeDiff = now - lastResizeTimeRef.current; if (timeDiff < 1000) { // Within 1 second resizeCountRef.current++; } else { resizeCountRef.current = 1; } lastResizeTimeRef.current = now; if (resizeCountRef.current > finalConfig.maxResizeFrequency) { handleEdgeCase({ type: EdgeCaseEvent.RAPID_RESIZE, timestamp: now, data: { count: resizeCountRef.current, frequency: resizeCountRef.current / (timeDiff / 1000) }, severity: 'medium' }); // Reset counter after warning resizeCountRef.current = 0; } }, [finalConfig.maxResizeFrequency, handleEdgeCase]); // Orientation change handler const handleOrientationChange = useCallback(() => { if (orientationLockRef.current) return; orientationLockRef.current = true; handleEdgeCase({ type: EdgeCaseEvent.ORIENTATION_CHANGE, timestamp: Date.now(), data: { orientation: screen.orientation?.angle || 0, type: screen.orientation?.type || 'unknown' }, severity: 'low' }); // Release lock after delay setTimeout(() => { orientationLockRef.current = false; }, finalConfig.debounceMs); }, [finalConfig.debounceMs, handleEdgeCase]); // Memory monitoring const checkMemoryUsage = useCallback(() => { if (!('memory' in performance)) return; const memory = (performance as any).memory; const usedMemory = memory.usedJSHeapSize; const totalMemory = memory.totalJSHeapSize; const memoryRatio = usedMemory / totalMemory; if (memoryRatio > 0.8 && !memoryWarningRef.current) { memoryWarningRef.current = true; handleEdgeCase({ type: EdgeCaseEvent.MEMORY_WARNING, timestamp: Date.now(), data: { used: usedMemory, total: totalMemory, ratio: memoryRatio }, severity: memoryRatio > 0.9 ? 'critical' : 'high' }); // Reset warning after delay setTimeout(() => { memoryWarningRef.current = false; }, finalConfig.memoryCheckInterval); } }, [finalConfig.memoryCheckInterval, handleEdgeCase]); // Multiple instance prevention const preventMultipleInstances = useCallback(() => { const storageKey = 'agentc_chat_active_instance'; const currentInstance = localStorage.getItem(storageKey); if (currentInstance && currentInstance !== instanceIdRef.current) { handleEdgeCase({ type: EdgeCaseEvent.MULTIPLE_INSTANCE, timestamp: Date.now(), data: { currentInstance: instanceIdRef.current, existingInstance: currentInstance }, severity: 'medium' }); return false; // Prevent this instance from running } localStorage.setItem(storageKey, instanceIdRef.current); return true; }, [handleEdgeCase]); // Network change handler const handleNetworkChange = useCallback(() => { if (!('connection' in navigator)) return; const connection = (navigator as any).connection; handleEdgeCase({ type: EdgeCaseEvent.NETWORK_CHANGE, timestamp: Date.now(), data: { effectiveType: connection.effectiveType, downlink: connection.downlink, rtt: connection.rtt, saveData: connection.saveData }, severity: connection.effectiveType === 'slow-2g' ? 'high' : 'low' }); }, [handleEdgeCase]); // Visibility change handler const handleVisibilityChange = useCallback(() => { handleEdgeCase({ type: EdgeCaseEvent.VISIBILITY_CHANGE, timestamp: Date.now(), data: { hidden: document.hidden, visibilityState: document.visibilityState }, severity: 'low' }); }, [handleEdgeCase]); // Setup event listeners useEffect(() => { const cleanup: (() => void)[] = []; // Initialize instance prevention if (finalConfig.enableMultipleInstancePrevention) { if (!preventMultipleInstances()) { return; // Exit if another instance is active } } // Resize protection if (finalConfig.enableRapidResizeProtection) { window.addEventListener('resize', handleRapidResize); cleanup.push(() => window.removeEventListener('resize', handleRapidResize)); } // Orientation change if (finalConfig.enableOrientationLock) { window.addEventListener('orientationchange', handleOrientationChange); screen.orientation?.addEventListener('change', handleOrientationChange); cleanup.push(() => { window.removeEventListener('orientationchange', handleOrientationChange); screen.orientation?.removeEventListener('change', handleOrientationChange); }); } // Memory monitoring if (finalConfig.enableMemoryLeakPrevention) { const memoryInterval = setInterval(checkMemoryUsage, finalConfig.memoryCheckInterval); cleanup.push(() => clearInterval(memoryInterval)); } // Network monitoring if ('connection' in navigator) { (navigator as any).connection.addEventListener('change', handleNetworkChange); cleanup.push(() => (navigator as any).connection.removeEventListener('change', handleNetworkChange) ); } // Visibility monitoring document.addEventListener('visibilitychange', handleVisibilityChange); cleanup.push(() => document.removeEventListener('visibilitychange', handleVisibilityChange)); // Cleanup on unmount return () => { cleanup.forEach(fn => fn()); // Clear instance lock if (finalConfig.enableMultipleInstancePrevention) { const currentInstance = localStorage.getItem('agentc_chat_active_instance'); if (currentInstance === instanceIdRef.current) { localStorage.removeItem('agentc_chat_active_instance'); } } }; }, [ finalConfig, preventMultipleInstances, handleRapidResize, handleOrientationChange, checkMemoryUsage, handleNetworkChange, handleVisibilityChange ]); return { edgeCases, instanceId: instanceIdRef.current, isHealthy: edgeCases.filter(e => e.severity === 'critical').length === 0 }; };// Edge case monitor component interface EdgeCaseMonitorProps { config?: Partial; onEdgeCase?: (event: EdgeCaseEventData) => void; children: React.ReactNode; } export const EdgeCaseMonitor: React.FC = ({ config, onEdgeCase, children }) => { const { edgeCases, instanceId, isHealthy } = useEdgeCaseHandler(config, onEdgeCase); // Show warning if system is unhealthy if (!isHealthy && process.env.NODE_ENV === 'development') { console.warn('[EdgeCaseMonitor] System is experiencing critical edge cases:', edgeCases); } return ( <> {children} {process.env.NODE_ENV === 'development' && ( )} ); }; // Debug panel for edge cases interface EdgeCaseDebugPanelProps { edgeCases: EdgeCaseEventData[]; instanceId: string; isHealthy: boolean; } const EdgeCaseDebugPanel: React.FC = ({ edgeCases, instanceId, isHealthy }) => { const [isExpanded, setIsExpanded] = useState(false); const getSeverityColor = (severity: string) => { switch (severity) { case 'critical': return '#ff4444'; case 'high': return '#ff8800'; case 'medium': return '#ffbb00'; case 'low': return '#00aa00'; default: return '#666666'; } }; return (
setIsExpanded(!isExpanded)} > Edge Case Monitor {isHealthy ? '✅' : '⚠️'} {isExpanded ? '▼' : '▶'}
{isExpanded && ( <>
Instance: {instanceId.slice(-8)}
Status: {isHealthy ? 'Healthy' : 'Degraded'}
Events: {edgeCases.length}/10
{edgeCases.length > 0 && (
Recent Events:
{edgeCases.slice(-5).map((event, index) => (
{event.type} ({event.severity})
{new Date(event.timestamp).toLocaleTimeString()}
))}
)} )}
); }; // Graceful degradation hook export const useGracefulDegradation = (deviceInfo: ExtendedDeviceInfo) => { const [degradationLevel, setDegradationLevel] = useState<'none' | 'low' | 'medium' | 'high'>('none'); useEffect(() => { let level: 'none' | 'low' | 'medium' | 'high' = 'none'; // Check various degradation triggers if (deviceInfo.connectionType === 'slow-2g') level = 'high'; else if (deviceInfo.connectionType === '2g') level = 'medium'; else if (deviceInfo.hardwareConcurrency <= 2) level = 'medium'; else if (deviceInfo.deviceMemory <= 2) level = 'low'; setDegradationLevel(level); }, [deviceInfo]); const shouldDisableFeature = useCallback((feature: string) => { const featureMap = { 'animations': degradationLevel === 'high', 'transitions': degradationLevel === 'high', 'debug': degradationLevel === 'medium' || degradationLevel === 'high', 'performance-monitoring': degradationLevel === 'high', 'lazy-loading': false, // Always enabled 'memory-management': false // Always enabled }; return featureMap[feature as keyof typeof featureMap] || false; }, [degradationLevel]); return { degradationLevel, shouldDisableFeature, isLowPerformance: degradationLevel === 'medium' || degradationLevel === 'high' }; }; export default EdgeCaseMonitor;