/* global React, ReactDOM, window, useTweaks, TweaksPanel, TweakSection, TweakSlider, TweakToggle, TweakRadio */ // ============================================================ // APP ROOT — language, sound, shell, tweaks // ============================================================ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "turnMs": 900, "sound": false, "cover": "embossed" }/*EDITMODE-END*/; // ---- page-turn sound (synthesised paper flutter, no asset) ---- let _audio = null; function playTurn() { try { _audio = _audio || new (window.AudioContext || window.webkitAudioContext)(); const ctx = _audio; if (ctx.state === "suspended") ctx.resume(); const dur = 0.34; const buf = ctx.createBuffer(1, ctx.sampleRate * dur, ctx.sampleRate); const data = buf.getChannelData(0); for (let i = 0; i < data.length; i++) { const t = i / data.length; const env = Math.sin(Math.PI * t) * (1 - t) * (0.5 + 0.5 * Math.sin(t * 60)); data[i] = (Math.random() * 2 - 1) * env * 0.5; } const src = ctx.createBufferSource(); src.buffer = buf; const bp = ctx.createBiquadFilter(); bp.type = "bandpass"; bp.frequency.value = 2600; bp.Q.value = 0.6; const g = ctx.createGain(); g.gain.value = 0.5; src.connect(bp); bp.connect(g); g.connect(ctx.destination); src.start(); } catch (e) { /* no-op */ } } function App() { const { T, Book, CatWalker } = window; const [tw, setTweak] = useTweaks(TWEAK_DEFAULTS); const [lang, setLang] = React.useState("en"); const t = T[lang]; React.useEffect(() => { document.documentElement.lang = lang; document.documentElement.dir = t.dir; }, [lang]); return ( {t.fieldBook} setTweak("sound", !tw.sound)} title="page-turn sound"> {tw.sound ? "♪" : "♪̸"} {lang === "ar" ? "صوت" : "sound"} setLang("en")}>EN setLang("ar")}>ع setTweak("turnMs", v)} /> setTweak("sound", v)} /> setTweak("cover", v)} /> ); } ReactDOM.createRoot(document.getElementById("app")).render();