/* global React */ /* ============================================================= Tweaks panel — cross-page state via localStorage + window event Knobs: accent : 'green' | 'blue' (which brand color is protagonist) density : 'compact' | 'comfortable' (specialty grid) cardStyle : 'classic' | 'portrait' | 'minimal' (specialty card layout) ============================================================= */ const { useState: useStateTW, useEffect: useEffectTW } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "green", "density": "comfortable", "cardStyle": "classic" }/*EDITMODE-END*/; const TW_LS_KEY = "maxxi-tweaks-v1"; function readTweaks() { try { const raw = localStorage.getItem(TW_LS_KEY); if (!raw) return { ...TWEAK_DEFAULTS }; return { ...TWEAK_DEFAULTS, ...JSON.parse(raw) }; } catch (e) { return { ...TWEAK_DEFAULTS }; } } function writeTweaks(t) { try { localStorage.setItem(TW_LS_KEY, JSON.stringify(t)); } catch (e) {} } function applyTweaksToDoc(t) { document.documentElement.setAttribute("data-accent", t.accent); document.documentElement.setAttribute("data-density", t.density); document.documentElement.setAttribute("data-card-style", t.cardStyle); } /* Hook every component can use to react to tweaks live. */ function useTweaks() { const [t, setT] = useStateTW(() => readTweaks()); useEffectTW(() => { applyTweaksToDoc(t); writeTweaks(t); window.dispatchEvent(new CustomEvent("maxxi:tweaks", { detail: t })); }, [t]); useEffectTW(() => { const onEvt = (e) => { const next = e.detail; setT(prev => { if (JSON.stringify(prev) === JSON.stringify(next)) return prev; return next; }); }; window.addEventListener("maxxi:tweaks", onEvt); // Also listen across tabs const onStorage = (e) => { if (e.key === TW_LS_KEY) setT(readTweaks()); }; window.addEventListener("storage", onStorage); return () => { window.removeEventListener("maxxi:tweaks", onEvt); window.removeEventListener("storage", onStorage); }; }, []); const setTweak = (key, val) => setT(prev => ({ ...prev, [key]: val })); return [t, setTweak]; } /* The floating panel itself. Hidden unless edit mode is active. */ function TweaksPanel() { const [t, setTweak] = useTweaks(); const [active, setActive] = useStateTW(false); const [dismissed, setDismissed] = useStateTW(false); useEffectTW(() => { const onMsg = (e) => { const data = e?.data; if (!data || typeof data !== "object") return; if (data.type === "__activate_edit_mode") setActive(true); if (data.type === "__deactivate_edit_mode") setActive(false); }; window.addEventListener("message", onMsg); // Announce availability AFTER listener is wired try { window.parent.postMessage({ type: "__edit_mode_available" }, "*"); } catch (e) {} return () => window.removeEventListener("message", onMsg); }, []); // Persist via parent so reload survives useEffectTW(() => { try { window.parent.postMessage({ type: "__edit_mode_set_keys", edits: t }, "*"); } catch (e) {} }, [t]); if (!active) return null; const close = () => { setActive(false); try { window.parent.postMessage({ type: "__edit_mode_dismissed" }, "*"); } catch (e) {} }; return (
Tweaks
setTweak("accent", v)} options={[ { value: "green", label: "Verde", swatch: "var(--ms-green-500)" }, { value: "blue", label: "Azul", swatch: "var(--ms-blue-500)" }, ]} /> setTweak("density", v)} options={[ { value: "comfortable", label: "Confortável" }, { value: "compact", label: "Compacta" }, ]} /> setTweak("cardStyle", v)} options={[ { value: "classic", label: "Clássico" }, { value: "portrait", label: "Retrato" }, { value: "minimal", label: "Mínimo" }, ]} vertical />
As preferências valem para todas as páginas.
); } function TweakRadio({ label, value, onChange, options, vertical }) { return (
{label}
{options.map(o => { const sel = o.value === value; return ( ); })}
); } /* Boot: apply persisted tweaks BEFORE React mounts so CSS variables land on first paint and there is no flash of default styles. */ applyTweaksToDoc(readTweaks()); Object.assign(window, { TweaksPanel, useTweaks, readTweaks });