/* desktop-settings-shared.js — the shared engine behind every direction * prototype. One mock state model, one widget factory, and the net panel's * verify-everything contract simulated: a control applies, then a mock backing * "reads back" the real value and confirms (a green verify flash + a toast). * * Each prototype builds a different LAYOUT from these same widgets and binds to * this same state, so the five directions are genuinely comparable. */ const DS = (() => { // ---- mock state (what the real settings.py engine would read/write) ------ const state = { autodim: false, caffeine: false, touchpad: true, mouse: true, airplane: false, nightlight: false, dnd: false, brightness: 70, kbd: 40, powerprofile: "balanced", // performance | balanced | saver scene: null, // Focus | Presentation | Battery | Night | null wallpaper: { dirs: ["~/pictures/wallpaper", "~/pictures/wallpaper/day-night"], current: 2, day: 1, night: 4, }, laptop: true, // gates the airplane row }; // control metadata: glyph (nerd-font), label, and the "on/off" wording. const TOGGLES = { autodim: { glyph: "\u{f0335}", name: "Auto-dim", on: "dimming", off: "off" }, caffeine: { glyph: "\u{f0176}", name: "Caffeine", on: "awake", off: "idle ok" }, touchpad: { glyph: "\u{f0168}", name: "Touchpad", on: "on", off: "off" }, mouse: { glyph: "\u{f037d}", name: "Mouse", on: "on", off: "off" }, airplane: { glyph: "\u{f001d}", name: "Airplane", on: "on", off: "off" }, nightlight:{ glyph: "\u{f0594}", name: "Night light", on: "warm", off: "off" }, dnd: { glyph: "\u{f009b}", name: "Do Not Disturb",on:"silenced",off: "notifying" }, }; const SLIDERS = { brightness: { glyph: "\u{f00df}", name: "Brightness", floor: 5 }, kbd: { glyph: "\u{f60f}", name: "Keyboard", floor: 0 }, }; const PROFILES = [ { id: "performance", label: "Perf" }, { id: "balanced", label: "Balanced" }, { id: "saver", label: "Saver" }, ]; // scenes flip several toggles + a profile at once (the iOS-Focus pattern). const SCENES = { Focus: { glyph: "\u{f0210}", sets: { dnd: true, autodim: false, nightlight: false, powerprofile: "balanced" } }, Presentation: { glyph: "\u{f0379}", sets: { caffeine: true, autodim: false, dnd: true, powerprofile: "performance" } }, Battery: { glyph: "\u{f0079}", sets: { autodim: true, powerprofile: "saver", nightlight: false } }, Night: { glyph: "\u{f0594}", sets: { nightlight: true, dnd: true, autodim: true, powerprofile: "saver" } }, }; let toastEl = null; const listeners = []; const onChange = (fn) => listeners.push(fn); const emit = () => listeners.forEach((f) => f(state)); function toast(msg, ok) { if (!toastEl) return; toastEl.classList.remove("empty"); toastEl.innerHTML = ok ? `${msg} ✓ verified` : msg; clearTimeout(toast._t); toast._t = setTimeout(() => { toastEl.classList.add("empty"); toastEl.textContent = "waiting…"; }, 2600); } // The verify-everything contract: apply, then a mock backing reads back the // actual value and confirms. cb runs on the confirmed readback. function applyVerified(label, mutate, el, cb) { mutate(); emit(); setTimeout(() => { if (el) { el.classList.remove("verified"); void el.offsetWidth; el.classList.add("verified"); } toast(label, true); if (cb) cb(); }, 140); } function setToggle(key, el) { const meta = TOGGLES[key]; applyVerified( `${meta.name}: ${state[key] ? meta.off : meta.on}`, () => { state[key] = !state[key]; state.scene = null; }, el ); } function setSlider(key, value, el) { const meta = SLIDERS[key]; const v = Math.max(meta.floor, Math.min(100, Math.round(value))); applyVerified(`${meta.name}: ${v}%`, () => { state[key] = v; }, el); } function setProfile(id, el) { applyVerified(`Power profile: ${id}`, () => { state.powerprofile = id; state.scene = null; }, el); } function applyScene(name, el) { const sc = SCENES[name]; applyVerified(`Scene: ${name}`, () => { Object.assign(state, sc.sets); state.scene = name; }, el); } function momentary(label) { toast(`${label}…`, false); } // ---- small render helpers the prototypes call ---------------------------- const h = (tag, cls, txt) => { const e = document.createElement(tag); if (cls) e.className = cls; if (txt != null) e.textContent = txt; return e; }; function toggleKey(key) { const meta = TOGGLES[key]; const on = state[key]; const el = h("div", "ds-key" + (on ? " on" : "")); el.innerHTML = `` + `${meta.glyph}` + `${meta.name}` + `${on ? meta.on : meta.off}`; el.onclick = () => setToggle(key, el); return el; } function toggleTile(key, detailCb) { const meta = TOGGLES[key]; const on = state[key]; const el = h("div", "ds-tile" + (on ? " on" : "")); el.innerHTML = `