Spaces:
Running
Running
File size: 5,500 Bytes
21d7fc3 c4e1f2a 21d7fc3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
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>
);
} |