Files
cinny/src/playground/LivePreview.tsx
litruv 0da4f0d6cb Add emoji confetti bursts and component playground.
Support app.relay.emoji_confetti on jumbo emoji clicks with canvas physics,
per-emoji particle profiles, and a fixed overlay that does not affect scroll.
Also add a Vite playground for live component previews and theme hover preview.
2026-07-29 20:40:43 +10:00

60 lines
1.6 KiB
TypeScript

import { useEffect, useState } from 'react';
/**
* Dynamically remounts the Vite virtual live module when the harness changes.
*/
export function LivePreview() {
const [revision, setRevision] = useState(0);
const [Comp, setComp] = useState<React.ComponentType | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const onUpdate = () => setRevision((n) => n + 1);
const handler = () => onUpdate();
// Vite custom event from liveTsxPlugin
if (import.meta.hot) {
import.meta.hot.on('playground:live-updated', handler);
}
return () => {
if (import.meta.hot) {
import.meta.hot.off('playground:live-updated', handler);
}
};
}, []);
useEffect(() => {
let cancelled = false;
setError(null);
setComp(null);
import(/* @vite-ignore */ `/@playground/live.tsx?t=${revision}`)
.then((mod) => {
if (cancelled) return;
const candidate = mod.default;
if (typeof candidate !== 'function') {
setError('Live module must `export default` a React component.');
return;
}
setComp(() => candidate);
})
.catch((err: unknown) => {
if (cancelled) return;
const message =
err instanceof Error
? `${err.message}\n\n${err.stack ?? ''}`
: String(err);
setError(message);
});
return () => {
cancelled = true;
};
}, [revision]);
if (error) return <pre className="error-block">{error}</pre>;
if (!Comp) return <div className="muted">Compiling harness</div>;
return <Comp />;
}