word-to-code / components /SketchRenderer.js
tinazone's picture
Update components/SketchRenderer.js
c4e1f2a verified
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>
);
}