diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-22 15:15:02 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-22 15:15:02 -0500 |
| commit | f1c661f50d6af2a115fc1430ae0478f2acef2dec (patch) | |
| tree | b894ea2e14327794bb75671b962f37185cff22ea /docs/prototypes/2026-07-02-desktop-settings-panel-prototype-12.html | |
| parent | f594068bed75302aba50793ccf5e6ce9cf53c7f8 (diff) | |
| download | archsetup-f1c661f50d6af2a115fc1430ae0478f2acef2dec.tar.gz archsetup-f1c661f50d6af2a115fc1430ae0478f2acef2dec.zip | |
docs(settings-panel): seal the prototype arc and open the build
Prototypes P12-P37 land, along with the night-watch and levels-compare pages plus the shared-engine updates and the kit-candidate file. The spec flips from DRAFT through READY to DOING with rewritten implementation phases and seven recorded build findings. The build tasks are filed in todo.org, and phase 1 is closed against the dotfiles commits.
Diffstat (limited to 'docs/prototypes/2026-07-02-desktop-settings-panel-prototype-12.html')
| -rw-r--r-- | docs/prototypes/2026-07-02-desktop-settings-panel-prototype-12.html | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-12.html b/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-12.html new file mode 100644 index 0000000..866fa80 --- /dev/null +++ b/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-12.html @@ -0,0 +1,283 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Desktop Settings — Prototype 12 (the casting composite)</title> +<link rel="stylesheet" href="desktop-settings-shared.css"> +<style> + :root{ + --ground:#151311; --panel:#100f0f; --well:#0a0c0d; --raise:#1a1917; + --silver:#bfc4d0; --cream:#f3e7c5; --steel:#969385; --dim:#7c838a; + --slate:#424f5e; --slate-hi:#54677d; --wash:#2c2f32; + --pass:#74932f; --fail:#cb6b4d; + --phos:#7fe0a0; --phos-dim:#1c3626; --vfd:#63e6c8; + --sevgrn:#57d357; --sevred:#e2543f; --sevoff:#1a1613; + --jewel-r:#ff5b45; --jewel-a:#ffb43a; --jewel-g:#6fce33; + --gold:#e2a038; --gold-hi:#ffbe54; + --amber-grad-top:#f2c76a; --amber-grad-mid:#d29638; --amber-grad-bot:#8f671f; + --amber-edge:#7d5c16; --amber-warn:#f0b552; + --glow-hi:255,190,84; --glow-lo:226,160,56; + --mono:"BerkeleyMono Nerd Font","Berkeley Mono",monospace; + --pulse-rate:1s; + } + /* receiver plate: brushed texture behind each section (direction 6 chrome) */ + .plate { + background: + repeating-linear-gradient(90deg, #ffffff05 0 1px, transparent 1px 3px), + linear-gradient(180deg, #1d1a16, #141210); + border: 1px solid #262320; + border-radius: 10px; + padding: 10px; + } + .row { display: flex; justify-content: center; align-items: center; gap: 12px; flex-wrap: wrap; } + .row-split { display: flex; justify-content: space-around; align-items: flex-start; } + .cellcap { color: var(--steel); font-size: 8.5px; letter-spacing: 0.14em; text-align: center; margin-top: 2px; text-transform: uppercase; } + .vcell { display: flex; flex-direction: column; align-items: center; } + .dupre-rotsel, .dupre-keylock { display: inline-block; } + /* kit spans need a blockifying host outside the gallery */ + .khost { display: flex; justify-content: center; } + + /* tuner well + presets (direction 6, over the kit slideRule) */ + .tuner-well { display: flex; justify-content: center; padding: 4px 0 6px; } + .presets { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 6px; } + .preset { + background-image: linear-gradient(180deg, var(--raise), #131110); + border: 1px solid #33302b; border-bottom-width: 3px; border-radius: 6px; + padding: 5px 2px; text-align: center; cursor: pointer; color: var(--silver); + } + .preset:hover { border-color: var(--gold); color: var(--gold); } + .preset .num { font-size: 8px; color: var(--dim); display: block; } + .preset .nm { font-size: 9.5px; letter-spacing: 0.04em; } + .preset.cur { + border-color: var(--gold); border-bottom-width: 1px; transform: translateY(2px); + background-image: linear-gradient(180deg, #2a2618, #1d1a12); color: var(--cream); + } + .preset.cur .num { color: var(--gold); } + + /* fader line: label left, fader flexing, readout right */ + .fline { display: flex; align-items: center; gap: 8px; } + .fline .fname { color: var(--silver); font-size: 11px; width: 58px; } + .fline .fpct { color: var(--cream); font-size: 12px; width: 38px; text-align: right; font-variant-numeric: tabular-nums; } + .fline .fhost { flex: 1; display: flex; } + .fline .fhost .dupre-dfader { width: 100%; } + + /* rocker bank */ + .rockers { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px 6px; justify-items: center; } + + /* wallpaper row (console key) */ + .wprow { + display: flex; align-items: center; gap: 8px; + background-image: linear-gradient(180deg, var(--raise), #131110); + border: 1px solid #33302b; border-radius: 8px; + padding: 8px 10px; cursor: pointer; color: var(--silver); font-size: 12px; + } + .wprow:hover { border-color: var(--gold); color: var(--gold); } + .wprow .k-state { margin-left: auto; color: var(--dim); font-size: 10px; } +</style> +</head> +<body class="ds-stage"> + <div class="ds-caption"> + <b>Prototype 12 — the casting composite.</b> Every control as cast + 2026-07-21: tuner-dial scenes + presets, detent fader, single drum, + chicken-head power, rocker bank, guarded airplane, wind-up caffeine + timer, wallpaper sub-view. Warm-gold receiver chrome. Lock/suspend + live on wlogout, not here. + </div> + + <div class="ds-bar"><span>19:24</span><span class="gear">⚙</span></div> + + <div class="ds-panel" id="panel"> + <div class="ds-face"> + <span class="glyph">⚙</span> + <span class="title">Settings</span> + <span class="sub" id="scene-sub"></span> + <span class="x">✕</span> + </div> + <div id="body"></div> + <div class="ds-toast" id="toast"></div> + <div class="ds-subview" id="subview"></div> + </div> + +<script src="widgets.js"></script> +<script src="dupre-kit-additions.js"></script> +<script src="desktop-settings-shared.js"></script> +<script> +const h = DS.h; +const body = document.getElementById("body"); +const subview = document.getElementById("subview"); +DS.bindToast(document.getElementById("toast")); + +const SCENES = Object.keys(DS.SCENES); +const STOPS = ["OFF", "FOCUS", "PRES", "BATT", "NIGHT"]; +const ROCKERS = [ + ["autodim", "Auto-dim"], ["touchpad", "Touchpad"], ["mouse", "Mouse"], + ["nightlight", "Night light"], ["dnd", "DND"], ["weathergeo", "Weather geo"], +]; +let booting = true; +let caffMinutes = 0; // survives re-renders + +const sceneIdx = () => (DS.state.scene ? SCENES.indexOf(DS.state.scene) + 1 : 0); +const debounce = (fn, ms) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; }; + +function openWallpaper() { + subview.innerHTML = ""; + subview.appendChild(DS.wallpaperSubview(() => subview.classList.remove("open"))); + subview.classList.add("open"); +} + +function sceneOff() { + DS.state.scene = null; + DS.toast("Scene: off", true); + render(); +} + +function render() { + booting = true; + body.innerHTML = ""; + const col = h("div", "ds-col"); + + // ---- Scenes: the hero — tuner dial + preset keys ------------------------ + const scenesPlate = h("div", "plate"); + const tw = h("div", "tuner-well"); + DUPRE.slideRule(tw, { + values: STOPS, + value: STOPS[sceneIdx()], + skin: "warm", + fmt: (v) => v, + onChange: (v) => { + if (booting) return; + if (v === "OFF") sceneOff(); + else DS.applyScene(SCENES[STOPS.indexOf(v) - 1], tw); + }, + }); + // string stops: the builder's lit-mark compare is numeric-only; patch it + tw.querySelectorAll(".dupre-mk").forEach((m) => + m.classList.toggle("dupre-on", m.textContent === STOPS[sceneIdx()])); + scenesPlate.appendChild(tw); + const pr = h("div", "presets"); + SCENES.forEach((n, i) => { + const k = h("div", "preset" + (DS.state.scene === n ? " cur" : "")); + k.innerHTML = `<span class="num">PRESET ${i + 1}</span><span class="nm">${n}</span>`; + k.onclick = () => DS.applyScene(n, k); + pr.appendChild(k); + }); + scenesPlate.appendChild(pr); + col.appendChild(scenesPlate); + + // ---- Brightness: full-width detent fader -------------------------------- + col.appendChild(DS.engrave("Brightness")); + const fl = h("div", "fline"); + fl.appendChild(h("span", "fname", "Screen")); + const fhost = h("span", "fhost"); + const fpct = h("span", "fpct", DS.state.brightness + "%"); + const commitB = debounce((v) => { + const pct = Math.max(DS.SLIDERS.brightness.floor, v); + if (pct !== DS.state.brightness) DS.setSlider("brightness", pct, fl); + }, 350); + DUPRE.detentFader(fhost, { + value: DS.state.brightness, + detents: [25, 50, 75], + onChange: (v) => { + fpct.textContent = v + "%"; + if (!booting) commitB(v); + }, + }); + fl.appendChild(fhost); + fl.appendChild(fpct); + col.appendChild(fl); + + // ---- Keyboard drum + power chicken-head --------------------------------- + col.appendChild(DS.engrave("Keyboard · Power")); + const duo = h("div", "row-split"); + const dcell = h("div", "vcell"); + const dhost = h("div", "khost"); + const commitK = debounce((vals) => { + const pct = Math.round(vals[0]) * 10; + if (pct !== DS.state.kbd) DS.setSlider("kbd", pct, dcell); + }, 350); + DUPRE.drumRoller(dhost, { + title: "BACKLIGHT ×10", + channels: [{ name: "KBD", v: Math.round(DS.state.kbd / 10) }], + min: 0, max: 10, + onChange: (vals) => { if (!booting) commitK(vals); }, + }); + dcell.appendChild(dhost); + duo.appendChild(dcell); + + const pcell = h("div", "vcell"); + const phost = h("div", "khost"); + const pIdx = DS.PROFILES.findIndex((p) => p.id === DS.state.powerprofile); + DUPRE.chickenHead(phost, { + items: [["PERF", -60], ["BAL", 0], ["SAVER", 60]], + index: pIdx, + onChange: (i) => { if (!booting && i !== pIdx) DS.setProfile(DS.PROFILES[i].id, pcell); }, + }); + pcell.appendChild(phost); + pcell.appendChild(h("div", "cellcap", "power")); + duo.appendChild(pcell); + col.appendChild(duo); + + // ---- Caffeine: the wind-up timer --------------------------------------- + col.appendChild(DS.engrave("Caffeine — wind to stay awake")); + const trow = h("div", "row"); + const thost = h("div", "khost"); + DUPRE.timerDial(thost, { + minutes: caffMinutes, + onChange: (min) => { + caffMinutes = min; + const on = min > 0; + if (!booting && on !== DS.state.caffeine) DS.setToggle("caffeine", thost); + }, + }); + trow.appendChild(thost); + col.appendChild(trow); + + // ---- Toggle bank: rockers ---------------------------------------------- + col.appendChild(DS.engrave("Switches")); + const bank = h("div", "rockers"); + ROCKERS.forEach(([k, name]) => { + const cell = h("div", "vcell"); + const host = h("div", "khost"); + DUPRE.rocker(host, { + on: DS.state[k], + onChange: (on) => { if (!booting && on !== DS.state[k]) DS.setToggle(k, cell); }, + }); + cell.appendChild(host); + cell.appendChild(h("div", "cellcap", name)); + bank.appendChild(cell); + }); + col.appendChild(bank); + + // ---- Airplane: the guarded lever (laptop-only) -------------------------- + if (DS.state.laptop) { + const grow = h("div", "row"); + const gcell = h("div", "vcell"); + const ghost = h("div", "khost"); + DUPRE.guardedToggle(ghost, { + on: DS.state.airplane, + onLabel: "AIRPLANE", + offLabel: "RADIO OK", + onChange: (on) => { if (!booting && on !== DS.state.airplane) DS.setToggle("airplane", gcell); }, + }); + gcell.appendChild(ghost); + grow.appendChild(gcell); + col.appendChild(grow); + } + + // ---- Wallpaper: sub-view row ------------------------------------------- + const wp = h("div", "wprow"); + wp.innerHTML = `<span>\u{f021c}</span><span>Wallpaper</span><span class="k-state">manage ›</span>`; + wp.onclick = openWallpaper; + col.appendChild(wp); + + body.appendChild(col); + document.getElementById("scene-sub").textContent = DS.state.scene ? "▶ " + DS.state.scene : ""; + booting = false; +} + +DS.onChange(render); +render(); +</script> +</body> +</html> |
