"use client"; import { useEffect, useState } from "react"; import { setupText, type AnimationEntry } from "@/lib/registry"; type Snippets = Record; let snippetsPromise: Promise | null = null; /** * Source is a static asset, fetched once and shared by every CopyBar — it stays * out of the page bundle, and by click time it is already in memory so the * clipboard write still happens inside the user gesture. */ function loadSnippets(): Promise { snippetsPromise ??= fetch("/snippets.json") .then((r) => (r.ok ? r.json() : {})) .catch(() => ({})); return snippetsPromise; } async function copyText(text: string) { try { await navigator.clipboard.writeText(text); return true; } catch { // Older browsers and insecure origins have no async clipboard. const area = document.createElement("textarea"); area.value = text; area.style.position = "fixed"; area.style.opacity = "0"; document.body.appendChild(area); area.select(); const ok = document.execCommand("copy"); document.body.removeChild(area); return ok; } } type Kind = "setup" | "prompt" | "code"; const LABELS: Record = { setup: "Copy setup", prompt: "Copy prompt", code: "Copy code", }; export default function CopyBar({ entry, compact = false, }: { entry: AnimationEntry; compact?: boolean; }) { const [snippets, setSnippets] = useState(null); const [copied, setCopied] = useState(null); const [failed, setFailed] = useState(false); useEffect(() => { let alive = true; loadSnippets().then((s) => alive && setSnippets(s)); return () => { alive = false; }; }, []); useEffect(() => { if (!copied) return; const id = setTimeout(() => setCopied(null), 1600); return () => clearTimeout(id); }, [copied]); const payload = (kind: Kind) => { if (kind === "setup") return setupText(entry); if (kind === "prompt") return entry.prompt; const files = [entry.file, ...(entry.extraFiles ?? [])]; return files .map((f) => `// ---- src/${f} ----\n${snippets?.[f] ?? ""}`) .join("\n\n"); }; const handle = async (kind: Kind) => { const ok = await copyText(payload(kind)); setFailed(!ok); if (ok) setCopied(kind); }; return (
{(Object.keys(LABELS) as Kind[]).map((kind) => { const isCopied = copied === kind; const disabled = kind === "code" && !snippets; return ( ); })} {failed && ( clipboard blocked )}
); }