aboutsummaryrefslogtreecommitdiff
path: root/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-9.html
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-22 15:15:02 -0500
committerCraig Jennings <c@cjennings.net>2026-07-22 15:15:02 -0500
commitf1c661f50d6af2a115fc1430ae0478f2acef2dec (patch)
treeb894ea2e14327794bb75671b962f37185cff22ea /docs/prototypes/2026-07-02-desktop-settings-panel-prototype-9.html
parentf594068bed75302aba50793ccf5e6ce9cf53c7f8 (diff)
downloadarchsetup-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-9.html')
-rw-r--r--docs/prototypes/2026-07-02-desktop-settings-panel-prototype-9.html211
1 files changed, 211 insertions, 0 deletions
diff --git a/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-9.html b/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-9.html
new file mode 100644
index 0000000..bc15e94
--- /dev/null
+++ b/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-9.html
@@ -0,0 +1,211 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Desktop Settings — Prototype 9 (engine room, Dupre Kit)</title>
+<link rel="stylesheet" href="desktop-settings-shared.css">
+<style>
+ /* Dupre Kit token set (from the gallery :root / tokens.json) — the kit
+ builders read these; they override the shared prototype gold. */
+ :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;
+ }
+ .row { display: flex; justify-content: center; align-items: flex-start; gap: 8px; flex-wrap: wrap; }
+ /* kit spans that carry width/height need block layout outside the gallery */
+ .dupre-rotsel, .dupre-keylock { display: inline-block; }
+ .cellcap { color: var(--steel); font-size: 9px; letter-spacing: 0.14em; text-align: center; margin-top: 2px; }
+ .flap-well { display: flex; justify-content: center; padding: 6px 0 2px; pointer-events: none; }
+</style>
+</head>
+<body class="ds-stage">
+ <div class="ds-caption">
+ <b>Direction 9 — Engine room (Dupre Kit).</b> Real kit instruments: the
+ DRUM ROLLER sets both levels (drag the drums), a split-flap board reads the
+ scene tuned on the rotary selector, breaker panels carry the toggles,
+ airplane is a guarded lever, power is an engine-order telegraph (click to
+ ring the next order). Lock/suspend/wallpaper are arm-to-fire.
+ </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); // 4 names
+const SEL = ["OFF", "FOCUS", "PRESENT", "BATTERY", "NIGHT"]; // rotary detents
+let booting = true;
+
+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();
+}
+
+const debounce = (fn, ms) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; };
+
+// breakerPanel has no set(): sync its hardcoded initial states to DS by
+// clicking the transparent hit rects while booting swallows the onChange.
+function breakers(names, keys) {
+ const host = h("div");
+ const bp = DUPRE.breakerPanel(host, {
+ names,
+ onChange: (states) => {
+ if (booting) return;
+ states.forEach((st, i) => {
+ const on = st === "on";
+ if (on !== DS.state[keys[i]]) DS.setToggle(keys[i], host);
+ });
+ },
+ });
+ const hits = host.querySelectorAll('rect[fill="transparent"]');
+ bp.get().forEach((st, i) => {
+ if ((st === "on") !== DS.state[keys[i]])
+ hits[i].dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ });
+ return host;
+}
+
+function render() {
+ booting = true;
+ body.innerHTML = "";
+ const col = h("div", "ds-col");
+
+ // Scenes: split-flap reads the tuned scene, rotary selector picks it.
+ col.appendChild(DS.engrave("Scene board"));
+ const flapWell = h("div", "flap-well");
+ const flap = DUPRE.splitFlap(flapWell, { cells: 8, animate: true, flapMs: 60 });
+ const BOARD = { Focus: "FOCUS", Presentation: "PRESENT", Battery: "BATTERY", Night: "NIGHT" };
+ flap.setText(DS.state.scene ? BOARD[DS.state.scene] : "STANDBY");
+ col.appendChild(flapWell);
+ const selRow = h("div", "row");
+ const selHost = h("div");
+ DUPRE.rotarySelector(selHost, {
+ values: SEL,
+ index: DS.state.scene ? SCENES.indexOf(DS.state.scene) + 1 : 0,
+ onChange: (v) => {
+ if (booting) return;
+ if (v === "OFF") sceneOff();
+ else DS.applyScene(SCENES[SEL.indexOf(v) - 1], selHost);
+ },
+ });
+ selRow.appendChild(selHost);
+ selRow.appendChild(Object.assign(h("div", "cellcap", "SCENE SELECT"), { style: "align-self:center" }));
+ col.appendChild(selRow);
+
+ // Levels: the drum roller (Craig favorite #1) — drums are 1-10, ×10 = percent.
+ col.appendChild(DS.engrave("Levels"));
+ const drumRow = h("div", "row");
+ const drumHost = h("div");
+ const commitDrums = debounce((vals) => {
+ const b = Math.round(vals[0]) * 10, k = Math.round(vals[1]) * 10;
+ if (b !== DS.state.brightness) DS.setSlider("brightness", b, drumHost);
+ if (k !== DS.state.kbd) DS.setSlider("kbd", k, drumHost);
+ }, 350);
+ DUPRE.drumRoller(drumHost, {
+ title: "LEVELS ×10",
+ channels: [
+ { name: "BRIGHT", v: Math.max(1, Math.round(DS.state.brightness / 10)) },
+ { name: "KBD", v: Math.max(1, Math.round(DS.state.kbd / 10)) },
+ ],
+ onChange: (vals) => { if (!booting) commitDrums(vals); },
+ });
+ drumRow.appendChild(drumHost);
+ drumRow.appendChild(h("div"));
+ drumRow.lastChild.appendChild(h("div", "cellcap", "DRAG DRUM"));
+ col.appendChild(drumRow);
+
+ // Toggles: breaker panels + a guarded lever for airplane.
+ col.appendChild(DS.engrave("Breakers"));
+ const brRow = h("div", "row");
+ brRow.appendChild(breakers(["DIM", "CAFF", "TPAD", "MOUSE"], ["autodim", "caffeine", "touchpad", "mouse"]));
+ brRow.appendChild(breakers(["NIGHT", "DND", "WXGEO"], ["nightlight", "dnd", "weathergeo"]));
+ col.appendChild(brRow);
+
+ if (DS.state.laptop) {
+ const gRow = h("div", "row");
+ const gHost = h("div");
+ DUPRE.guardedToggle(gHost, {
+ on: DS.state.airplane,
+ onLabel: "AIRPLANE",
+ offLabel: "RADIO OK",
+ onChange: (on) => { if (!booting && on !== DS.state.airplane) DS.setToggle("airplane", gHost); },
+ });
+ gRow.appendChild(gHost);
+ col.appendChild(gRow);
+ }
+
+ // Power: engine-order telegraph — ring the next order.
+ col.appendChild(DS.engrave("Engine order — power"));
+ const tRow = h("div", "row");
+ const tHost = h("div");
+ DUPRE.telegraphIndicator(tHost, {
+ states: ["PERF", "BAL", "SAVER"],
+ index: DS.PROFILES.findIndex((p) => p.id === DS.state.powerprofile),
+ onChange: (i) => { if (!booting) DS.setProfile(DS.PROFILES[i].id, tHost); },
+ });
+ tRow.appendChild(tHost);
+ col.appendChild(tRow);
+
+ // Actions: arm-to-fire.
+ col.appendChild(DS.engrave("Actions"));
+ const aRow = h("div", "row");
+ [["LOCK", () => DS.momentary("Lock")],
+ ["SUSPEND", () => DS.momentary("Suspend")],
+ ["WALLPAPER", openWallpaper]].forEach(([label, fn]) => {
+ const host = h("div");
+ DUPRE.armButton(host, {
+ label,
+ armLabel: label + "?",
+ onChange: (st) => { if (!booting && st === "fired") fn(); },
+ });
+ aRow.appendChild(host);
+ });
+ col.appendChild(aRow);
+
+ body.appendChild(col);
+ document.getElementById("scene-sub").textContent = DS.state.scene ? "▶ " + DS.state.scene : "";
+ booting = false;
+}
+
+DS.onChange(render);
+render();
+</script>
+</body>
+</html>