// tweaks-app.jsx — small bridge between TweaksPanel and the static page.
// The page is canonical HTML for direct-edit; this script just toggles
// theme, swaps the client name, recolors the accent, etc.
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"client": "",
"greeting": "Ciao",
"date": "Luglio 2026",
"theme": "dark",
"accent": "#3b5f4a",
"showWhatsApp": true,
"whatsappNumber": "+39 327 444 6400",
"email": "info@albertomaddaloni.com"
}/*EDITMODE-END*/;
const ACCENTS = [
"#b85a2c", // terracotta (default)
"#7a3f2a", // bruciato
"#3b5f4a", // sage scuro
"#1f3a5f", // navy notturno
"#8a7a52", // ottone
];
// ── Share-link helpers ──────────────────────────────────────────────────────
// "client" / "email" / "whatsappNumber" are reversed-and-Base64ed in the URL
// so the bar doesn't reveal the client's name at a glance. Pure cosmetic —
// the JS that decodes is public, so this is masking not encryption. Tema /
// accent / toggle stay in clear text (they don't identify people and keep
// the URL short).
function encodeMasked(s) {
try {
const reversed = String(s).split('').reverse().join('');
return btoa(unescape(encodeURIComponent(reversed)));
} catch (e) { return ''; }
}
function decodeMasked(s) {
try {
const reversed = decodeURIComponent(escape(atob(s)));
return reversed.split('').reverse().join('');
} catch (e) { return null; }
}
function readUrlOverrides() {
const p = new URLSearchParams(window.location.search);
const out = {};
const c = p.get('c'); if (c) { const v = decodeMasked(c); if (v) out.client = v; }
const em = p.get('em'); if (em) { const v = decodeMasked(em); if (v) out.email = v; }
const wa = p.get('wa'); if (wa) { const v = decodeMasked(wa); if (v) out.whatsappNumber = v; }
const g = p.get('g'); if (g) { const v = decodeMasked(g); if (v) out.greeting = v; }
const d = p.get('d'); if (d) out.date = d;
const th = p.get('th'); if (th === 'light' || th === 'dark') out.theme = th;
const a = p.get('a'); if (a && /^#[0-9a-fA-F]{6}$/.test(a)) out.accent = a;
const w = p.get('w'); if (w === '0' || w === '1') out.showWhatsApp = w === '1';
return out;
}
// Builds an URL that only includes params for values diverging from
// defaults — keeps the link as short as possible.
function buildShareUrl(t) {
const p = new URLSearchParams();
if (t.client && t.client !== TWEAK_DEFAULTS.client) p.set('c', encodeMasked(t.client));
if (t.theme && t.theme !== TWEAK_DEFAULTS.theme) p.set('th', t.theme);
if (t.accent && t.accent !== TWEAK_DEFAULTS.accent) p.set('a', t.accent);
if (t.email && t.email !== TWEAK_DEFAULTS.email) p.set('em', encodeMasked(t.email));
if (t.showWhatsApp !== TWEAK_DEFAULTS.showWhatsApp) p.set('w', t.showWhatsApp ? '1' : '0');
if (t.whatsappNumber && t.whatsappNumber !== TWEAK_DEFAULTS.whatsappNumber) {
p.set('wa', encodeMasked(t.whatsappNumber));
}
if (t.greeting && t.greeting !== TWEAK_DEFAULTS.greeting) p.set('g', encodeMasked(t.greeting));
if (t.date && t.date !== TWEAK_DEFAULTS.date) p.set('d', t.date);
const qs = p.toString();
const base = `${location.origin}${location.pathname}`;
return qs ? `${base}?${qs}` : base;
}
function App() {
// URL overrides win over the defaults baked into this file, but we read the
// URL only once at mount: if the user edits in the panel, those edits aren't
// clobbered by a re-read.
const initial = React.useMemo(
() => ({ ...TWEAK_DEFAULTS, ...readUrlOverrides() }),
[],
);
const [t, setTweak] = useTweaks(initial);
const [copied, setCopied] = React.useState(false);
// Apply theme + accent + content bindings live
React.useEffect(() => {
document.body.setAttribute('data-theme', t.theme || 'light');
document.documentElement.style.setProperty('--accent', t.accent || '#b85a2c');
// Greeting + client name (hero), date (frame address + signature), footer.
// Greeting is a free prefix ("Ciao" default, "Gentile" etc.) joined to the
// name with a plain space — reads right for formal and informal openers.
// Empty name → greeting alone; empty greeting/date → fall back to defaults.
const safe = (t.client || '').trim();
const greet = (t.greeting || '').trim() || TWEAK_DEFAULTS.greeting;
const dateStr = (t.date || '').trim() || TWEAK_DEFAULTS.date;
document.querySelectorAll('[data-bind="greeting"]').forEach((el) => {
el.textContent = greet;
});
document.querySelectorAll('[data-bind="client"]').forEach((el) => {
el.textContent = safe ? ` ${safe}` : '';
});
document.querySelectorAll('[data-bind="date"]').forEach((el) => {
el.textContent = dateStr;
});
document.querySelectorAll('[data-bind="frame-address"]').forEach((el) => {
el.textContent = safe ? `Per ${safe} · ${dateStr}` : dateStr;
});
document.querySelectorAll('[data-bind="foot-address"]').forEach((el) => {
el.textContent = safe ? `Presentazione per ${safe}` : 'Presentazione';
});
document.title = safe
? `Alberto Maddaloni — Presentazione per ${safe}`
: 'Alberto Maddaloni — Presentazione';
// Contact block: email + optional whatsapp
const block = document.querySelector('[data-bind="contact-block"]');
if (block) {
const emailRow = block.querySelector('a[href^="mailto:"]');
if (emailRow) {
emailRow.setAttribute('href', `mailto:${t.email || ''}`);
const val = emailRow.querySelector('.val');
if (val) val.textContent = t.email || '';
}
const waRow = block.querySelector('[data-contact="whatsapp"]');
if (waRow) {
waRow.style.display = t.showWhatsApp ? '' : 'none';
const num = (t.whatsappNumber || '').trim();
const digits = num.replace(/[^\d]/g, '');
waRow.setAttribute('href', digits ? `https://wa.me/${digits}` : '#');
const val = waRow.querySelector('.val');
if (val) val.textContent = num;
}
}
}, [t.theme, t.accent, t.client, t.greeting, t.date, t.email, t.showWhatsApp, t.whatsappNumber]);
const copyShareLink = async () => {
const url = buildShareUrl(t);
try {
await navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} catch (e) {
// Clipboard API can fail in non-HTTPS or older browsers — fallback to
// a prompt so the user can still grab the URL manually.
window.prompt('Copia il link:', url);
}
};
return (
setTweak('client', v)}
/>
setTweak('greeting', v)}
/>
setTweak('date', v)}
/>
setTweak('theme', v)}
/>
setTweak('accent', v)}
/>
setTweak('email', v)}
/>
setTweak('showWhatsApp', v)}
/>
{t.showWhatsApp && (
setTweak('whatsappNumber', v)}
/>
)}
);
}
ReactDOM.createRoot(document.getElementById('tweaks-root')).render();