Spaces:
Running
Running
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> | |
); | |
} |