// generator-app.jsx — sidebar + form + live preview + PNG export const { useState, useMemo, useRef } = React; const TPLS = window.LIROU_TEMPLATES; function showToast(msg) { const t = document.getElementById("toast"); t.textContent = msg; t.classList.add("show"); clearTimeout(window.__lirouToastT); window.__lirouToastT = setTimeout(() => t.classList.remove("show"), 2200); } function loadState() { try { return JSON.parse(localStorage.getItem("lirou-gen") || "{}"); } catch { return {}; } } function saveState(s) { // strip image data URLs (too big for localStorage; user re-uploads on reload) const cleaned = { activeId: s.activeId, data: {} }; for (const [id, fields] of Object.entries(s.data || {})) { cleaned.data[id] = { ...fields }; if (cleaned.data[id].image && cleaned.data[id].image.startsWith("data:")) { cleaned.data[id].image = ""; } } try { localStorage.setItem("lirou-gen", JSON.stringify(cleaned)); } catch {} } function App() { const initial = loadState(); const [activeId, setActiveId] = useState(initial.activeId || TPLS[0].id); const [allData, setAllData] = useState(() => { const d = {}; TPLS.forEach(t => { d[t.id] = { ...t.defaults, ...(initial.data && initial.data[t.id] || {}) }; }); return d; }); const tpl = useMemo(() => TPLS.find(t => t.id === activeId), [activeId]); const data = allData[activeId]; const previewRef = useRef(null); function updateField(k, v) { setAllData(prev => { const next = { ...prev, [activeId]: { ...prev[activeId], [k]: v } }; saveState({ activeId, data: next }); return next; }); } function selectTpl(id) { setActiveId(id); saveState({ activeId: id, data: allData }); } function resetTpl() { setAllData(prev => { const next = { ...prev, [activeId]: { ...tpl.defaults } }; saveState({ activeId, data: next }); return next; }); } async function exportPng() { if (!previewRef.current) return; showToast("renderizando…"); try { const node = previewRef.current.querySelector(".tpl-canvas"); // export at native size — clear scale transform so the PNG matches tpl.w × tpl.h exactly, // sem espaço em volta (o problema do "espaço grande" no preview do mac vinha do scale CSS). const prevTransform = node.style.transform; node.style.transform = "none"; const dataUrl = await window.htmlToImage.toPng(node, { width: tpl.w, height: tpl.h, canvasWidth: tpl.w, canvasHeight: tpl.h, pixelRatio: 3, cacheBust: true, style: { transform: "none", margin: "0", padding: "0" } }); node.style.transform = prevTransform; const a = document.createElement("a"); a.download = `lirou-${tpl.id}-${Date.now()}.png`; a.href = dataUrl; a.click(); showToast("imagem baixada ✦"); } catch (e) { console.error(e); showToast("erro ao exportar :("); } } // Group sidebar const groups = useMemo(() => { const g = {}; TPLS.forEach(t => { g[t.group] = g[t.group] || []; g[t.group].push(t); }); return g; }, []); return ( <> {/* SIDEBAR */} {ReactDOM.createPortal( <>
{tpl.w} × {tpl.h} · edita aqui, exporta na direita
{tpl.fields.map(f => (