import { useEffect, useRef, useState } from 'react'; import { Box, IconButton } from '@mui/material'; import { Close } from '@mui/icons-material'; import { useThemeStore } from '../stores/themeStore'; interface ScreensaverProps { onClose: () => void; } // Color palette from ColorPicker for complementary color selection const colorPalette = [ '#673ab7', '#9c27b0', '#e1bee7', // Purples '#1976d2', '#03dac6', '#3f51b5', // Blues '#2e7d32', '#4caf50', '#009688', // Greens '#d32f2f', '#ff5722', '#ff9800', // Reds/Oranges '#424242', '#607d8b', '#795548', // Grays ]; // Get complementary colors based on primary color function getComplementaryColors(primaryColor: string): string[] { const primaryIndex = colorPalette.findIndex(color => color === primaryColor); if (primaryIndex === -1) { // If primary color is not in palette, use default complementary colors return ['#ff5722', '#4caf50']; // Orange and Green } // Choose colors that are visually distinct from primary const complementaryIndices = [ (primaryIndex + 6) % colorPalette.length, // Opposite side (primaryIndex + 9) % colorPalette.length, // Further opposite ]; return complementaryIndices.map(index => colorPalette[index]); } // Create gradient pattern function createGradientPattern( ctx: CanvasRenderingContext2D, width: number, height: number, colors: string[], time: number ) { const gradient1 = ctx.createRadialGradient( width * 0.3 + Math.sin(time * 0.001) * 100, height * 0.3 + Math.cos(time * 0.001) * 100, 0, width * 0.3 + Math.sin(time * 0.001) * 100, height * 0.3 + Math.cos(time * 0.001) * 100, width * 0.8 ); gradient1.addColorStop(0, `${colors[0]}80`); gradient1.addColorStop(0.5, `${colors[1]}40`); gradient1.addColorStop(1, `${colors[2]}20`); ctx.fillStyle = gradient1; ctx.fillRect(0, 0, width, height); const gradient2 = ctx.createRadialGradient( width * 0.7 + Math.cos(time * 0.002) * 150, height * 0.7 + Math.sin(time * 0.002) * 150, 0, width * 0.7 + Math.cos(time * 0.002) * 150, height * 0.7 + Math.sin(time * 0.002) * 150, width * 0.6 ); gradient2.addColorStop(0, `${colors[2]}60`); gradient2.addColorStop(0.7, `${colors[0]}30`); gradient2.addColorStop(1, 'transparent'); ctx.fillStyle = gradient2; ctx.fillRect(0, 0, width, height); } // Draw animated shapes function drawShapes( ctx: CanvasRenderingContext2D, width: number, height: number, colors: string[], time: number ) { const numShapes = 8; for (let i = 0; i < numShapes; i++) { const x = width * 0.5 + Math.sin(time * 0.001 + i * 0.8) * (width * 0.3); const y = height * 0.5 + Math.cos(time * 0.001 + i * 0.8) * (height * 0.3); const size = 50 + Math.sin(time * 0.002 + i) * 30; const colorIndex = i % colors.length; const opacity = 0.3 + Math.sin(time * 0.003 + i) * 0.2; ctx.save(); ctx.globalAlpha = opacity; ctx.fillStyle = colors[colorIndex]; if (i % 3 === 0) { // Circles ctx.beginPath(); ctx.arc(x, y, size, 0, Math.PI * 2); ctx.fill(); } else if (i % 3 === 1) { // Squares ctx.fillRect(x - size/2, y - size/2, size, size); } else { // Triangles ctx.beginPath(); ctx.moveTo(x, y - size/2); ctx.lineTo(x - size/2, y + size/2); ctx.lineTo(x + size/2, y + size/2); ctx.closePath(); ctx.fill(); } ctx.restore(); } } // Draw line art function drawLineArt( ctx: CanvasRenderingContext2D, width: number, height: number, colors: string[], time: number ) { const numLines = 12; for (let i = 0; i < numLines; i++) { const x1 = Math.sin(time * 0.0005 + i * 0.5) * width; const y1 = Math.cos(time * 0.0005 + i * 0.5) * height; const x2 = Math.sin(time * 0.0005 + i * 0.5 + Math.PI) * width; const y2 = Math.cos(time * 0.0005 + i * 0.5 + Math.PI) * height; const colorIndex = i % colors.length; const opacity = 0.2 + Math.sin(time * 0.001 + i) * 0.1; ctx.save(); ctx.globalAlpha = opacity; ctx.strokeStyle = colors[colorIndex]; ctx.lineWidth = 2 + Math.sin(time * 0.002 + i) * 1; ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); ctx.restore(); } } export function Screensaver({ onClose }: ScreensaverProps) { const canvasRef = useRef(null); const animationRef = useRef(); const { primaryColor } = useThemeStore(); const [lastClearTime, setLastClearTime] = useState(Date.now()); const [interactionEnabled, setInteractionEnabled] = useState(false); useEffect(() => { console.log('Screensaver: useEffect triggered'); const canvas = canvasRef.current; if (!canvas) { console.error('Screensaver: Canvas ref is null'); return; } const ctx = canvas.getContext('2d'); if (!ctx) { console.error('Screensaver: Could not get 2D context'); return; } console.log('Screensaver: Canvas and context initialized'); // Set canvas size const resizeCanvas = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; console.log('Screensaver: Canvas resized to', canvas.width, 'x', canvas.height); }; resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Get complementary colors const complementaryColors = getComplementaryColors(primaryColor); const colors = [primaryColor, ...complementaryColors]; console.log('Screensaver: Using colors', colors); let startTime = Date.now(); let clearInterval = 0; const animate = () => { const currentTime = Date.now(); const elapsed = currentTime - startTime; // Clear screen every 3 minutes (180000ms) like Windows 98 if (currentTime - lastClearTime > 180000) { ctx.clearRect(0, 0, canvas.width, canvas.height); setLastClearTime(currentTime); clearInterval = 0; } // Clear screen every few seconds for variety if (clearInterval > 5000) { ctx.clearRect(0, 0, canvas.width, canvas.height); clearInterval = 0; } // Create gradient background createGradientPattern(ctx, canvas.width, canvas.height, colors, elapsed); // Draw shapes drawShapes(ctx, canvas.width, canvas.height, colors, elapsed); // Draw line art drawLineArt(ctx, canvas.width, canvas.height, colors, elapsed); clearInterval += 16; // ~60fps animationRef.current = requestAnimationFrame(animate); }; console.log('Screensaver: Starting animation'); animate(); return () => { console.log('Screensaver: Cleaning up'); window.removeEventListener('resize', resizeCanvas); if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [primaryColor, lastClearTime]); // Enable interaction detection after a delay to prevent immediate closing useEffect(() => { const timer = setTimeout(() => { console.log('Screensaver: Enabling interaction detection'); setInteractionEnabled(true); }, 1000); // 1 second delay return () => clearTimeout(timer); }, []); // Close on any key press or mouse movement (only when interaction is enabled) useEffect(() => { if (!interactionEnabled) return; const handleInteraction = () => { console.log('Screensaver: User interaction detected, closing'); onClose(); }; window.addEventListener('keydown', handleInteraction); window.addEventListener('mousemove', handleInteraction); window.addEventListener('click', handleInteraction); window.addEventListener('touchstart', handleInteraction); return () => { window.removeEventListener('keydown', handleInteraction); window.removeEventListener('mousemove', handleInteraction); window.removeEventListener('click', handleInteraction); window.removeEventListener('touchstart', handleInteraction); }; }, [onClose, interactionEnabled]); console.log('Screensaver: Rendering component'); return ( ); }