import { useEffect, useRef } from 'react'; import ReplayButton from './ReplayButton'; import GifDownloadButton from './GifDownloadButton'; import { loadingSketch } from '../sketches'; export default function SketchRenderer({ generatedCode, isGenerating, error, showCodePanel, onReplay, onGifCapture, isCapturingGif, isInitialSketch }) { const sketchContainer = useRef(null); const currentSketch = useRef(null); useEffect(() => { // Add message listener for GIF capture completion const handleGifComplete = (event) => { if (event.data.type === 'gifCaptureComplete') { onGifCapture(false); // Signal completion } }; window.addEventListener('message', handleGifComplete); return () => { window.removeEventListener('message', handleGifComplete); }; }, [onGifCapture]); useEffect(() => { if (generatedCode && window.p5 && sketchContainer.current) { // Cleanup previous sketch if it exists const cleanup = () => { if (currentSketch.current) { currentSketch.current.remove(); currentSketch.current = null; } }; cleanup(); try { const formattedCodeResponse = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.js"></script> <title>p5.js Sketch</title> <style> body { padding: 0; margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; overflow: hidden; background: black; } canvas { max-width: 100% !important; height: auto !important; } </style> </head> <body> <script> try { ${isGenerating ? loadingSketch : generatedCode} // Add setup wrapper if (typeof window.setup === 'function') { const originalSetup = window.setup; window.setup = function() { originalSetup(); frameRate(30); // Set consistent frame rate for GIF }; } // Add draw wrapper if (typeof window.draw === 'function') { const originalDraw = window.draw; window.draw = function() { originalDraw(); }; } // Handle GIF capture message window.addEventListener('message', function(event) { if (event.data.type === 'startGifCapture') { saveGif('word-to-code-animation', 5, { silent: true }) .then(() => { window.parent.postMessage({ type: 'gifCaptureComplete' }, '*'); }); } }); new p5(); } catch (error) { console.error('Sketch error:', error); document.body.innerHTML = '<div style="color: red; padding: 20px;"><h3>🔴 Error:</h3><pre>' + error.message + '</pre></div>'; } </script> </body> </html> `; const iframe = document.createElement('iframe'); iframe.srcdoc = formattedCodeResponse; iframe.style.width = '100%'; iframe.style.height = '100%'; iframe.style.border = 'none'; iframe.id = 'sketch-iframe'; iframe.onload = () => { iframe.contentWindow.addEventListener('error', (e) => { if (e.message.includes('Receiving end does not exist') || e.message.includes('p5.js seems to have been imported multiple times')) { e.preventDefault(); } }); }; sketchContainer.current.innerHTML = ''; sketchContainer.current.appendChild(iframe); } catch (error) { console.error('Error running p5.js sketch:', error); setError('Error running the animation: ' + error.message); } } return () => { if (sketchContainer.current) { sketchContainer.current.innerHTML = ''; } }; }, [generatedCode, isGenerating]); return ( <div className="w-full aspect-square bg-black rounded-3xl flex items-center justify-center overflow-hidden relative"> <div ref={sketchContainer} className="w-full h-full" /> {isInitialSketch && !error && !isGenerating && ( <div className="absolute inset-0 flex items-end justify-center pb-5 text-gray-300 pointer-events-none z-10"> Type a word below to get started </div> )} {error && ( <div className="w-full h-full flex items-center justify-center text-red-500 p-4 text-center"> {error} </div> )} {showCodePanel && !error && ( <> <ReplayButton onClick={onReplay} title="Replay animation" /> <GifDownloadButton onClick={() => onGifCapture(true)} title="Download as GIF" isCapturing={isCapturingGif} /> </> )} </div> ); }