aboutsummaryrefslogtreecommitdiff
path: root/docs/prototypes/2026-07-02-desktop-settings-panel-scenes-compare.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-scenes-compare.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-scenes-compare.html')
-rw-r--r--docs/prototypes/2026-07-02-desktop-settings-panel-scenes-compare.html177
1 files changed, 177 insertions, 0 deletions
diff --git a/docs/prototypes/2026-07-02-desktop-settings-panel-scenes-compare.html b/docs/prototypes/2026-07-02-desktop-settings-panel-scenes-compare.html
new file mode 100644
index 0000000..9efc1dc
--- /dev/null
+++ b/docs/prototypes/2026-07-02-desktop-settings-panel-scenes-compare.html
@@ -0,0 +1,177 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Desktop Settings — scene control casting: tuner dial vs rotary + split-flap</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;
+ }
+ body.ds-stage { flex-direction: row; flex-wrap: wrap; align-items: flex-start; justify-content: center; gap: 24px; padding-top: 40px; }
+ .page-cap { width: 100%; text-align: center; color: var(--dim); font-size: 12px; letter-spacing: 0.04em; }
+ .page-cap b { color: var(--gold); }
+ .dupre-rotsel { display: inline-block; }
+ .row { display: flex; justify-content: center; align-items: center; gap: 10px; flex-wrap: wrap; }
+ .flap-well { display: flex; justify-content: center; padding: 4px 0 2px; pointer-events: none; }
+ .tuner-well { display: flex; justify-content: center; padding: 6px 0; }
+ .readout { text-align: center; color: var(--steel); font-size: 11px; letter-spacing: 0.1em; min-height: 15px; }
+ .readout b { color: var(--gold-hi); font-weight: 400; }
+
+ /* preset keys under the dial (the direction-6 keys, Dupre Kit tokens) */
+ .presets { display: grid; grid-template-columns: repeat(4,1fr); gap: 6px; }
+ .preset {
+ background-image: linear-gradient(180deg, var(--raise), #131110);
+ border: 1px solid #33302b; border-bottom-width: 3px; border-radius: 6px;
+ padding: 6px 4px; text-align: center; cursor: pointer; color: var(--silver);
+ }
+ .preset:hover { border-color: var(--gold); color: var(--gold); }
+ .preset .num { font-size: 9px; color: var(--dim); display: block; }
+ .preset .nm { font-size: 10.5px; letter-spacing: 0.06em; }
+ .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); }
+
+ /* skin chips for the tuner */
+ .skins { display: flex; gap: 8px; justify-content: center; }
+ .skinchip {
+ color: var(--dim); font-size: 9.5px; letter-spacing: 0.1em; cursor: pointer;
+ border-bottom: 1px dotted var(--wash); text-transform: uppercase;
+ }
+ .skinchip:hover { color: var(--silver); }
+ .skinchip.cur { color: var(--gold-hi); border-color: var(--gold); }
+</style>
+</head>
+<body class="ds-stage">
+ <div class="page-cap">
+ <b>Scene-control casting.</b> Both candidates drive the same scene state —
+ tune one, the other follows. Left: tuner dial (kit slideRule) + preset
+ keys, with skin chips. Right: rotary selector + split-flap board.
+ </div>
+
+ <div class="ds-panel" style="width:300px">
+ <div class="ds-face"><span class="glyph">⚙</span><span class="title">Candidate 1</span></div>
+ <div class="ds-engrave">Tuner dial + presets</div>
+ <div class="tuner-well" id="tuner-well"></div>
+ <div class="skins" id="skins"></div>
+ <div class="presets" id="presets"></div>
+ <div class="readout" id="ro1"></div>
+ </div>
+
+ <div class="ds-panel" style="width:300px">
+ <div class="ds-face"><span class="glyph">⚙</span><span class="title">Candidate 3</span></div>
+ <div class="ds-engrave">Rotary + split-flap</div>
+ <div class="flap-well" id="flap-well"></div>
+ <div class="row" id="rot-well"></div>
+ <div class="readout" id="ro2"></div>
+ </div>
+
+ <div class="ds-toast" id="toast" style="width:300px"></div>
+
+<script src="widgets.js"></script>
+<script src="desktop-settings-shared.js"></script>
+<script>
+const h = DS.h;
+DS.bindToast(document.getElementById("toast"));
+
+const SCENES = Object.keys(DS.SCENES); // Focus Presentation Battery Night
+const STOPS = ["OFF", "FOCUS", "PRES", "BATT", "NIGHT"];
+const BOARD = { Focus: "FOCUS", Presentation: "PRESENT", Battery: "BATTERY", Night: "NIGHT" };
+let booting = true;
+
+function sceneOff(el) {
+ DS.state.scene = null;
+ DS.toast("Scene: off", true);
+ render();
+}
+const sceneIdx = () => (DS.state.scene ? SCENES.indexOf(DS.state.scene) + 1 : 0);
+
+let skin = "warm";
+
+function render() {
+ booting = true;
+
+ // ---- Candidate 1: slideRule tuner + preset keys -------------------------
+ const tw = document.getElementById("tuner-well");
+ tw.innerHTML = "";
+ const dial = DUPRE.slideRule(tw, {
+ values: STOPS,
+ value: STOPS[sceneIdx()],
+ skin,
+ fmt: (v) => v,
+ onChange: (v) => {
+ if (booting) return;
+ if (v === "OFF") sceneOff(tw);
+ else DS.applyScene(SCENES[STOPS.indexOf(v) - 1], tw);
+ },
+ });
+ // string stops: the builder's lit-mark compare is numeric-only; patch it.
+ const glow = () => tw.querySelectorAll(".dupre-mk").forEach((m) =>
+ m.classList.toggle("dupre-on", m.textContent === STOPS[sceneIdx()]));
+ glow();
+
+ const sk = document.getElementById("skins");
+ sk.innerHTML = "";
+ ["warm", "chrome", "black", "blue"].forEach((name) => {
+ const c = h("span", "skinchip" + (name === skin ? " cur" : ""), name);
+ c.onclick = () => { skin = name; dial.setStyle("skin", name); sk.querySelectorAll(".skinchip").forEach((x) => x.classList.toggle("cur", x.textContent === name)); };
+ sk.appendChild(c);
+ });
+
+ const pr = document.getElementById("presets");
+ pr.innerHTML = "";
+ 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);
+ });
+
+ // ---- Candidate 3: rotary selector + split-flap --------------------------
+ const fw = document.getElementById("flap-well");
+ fw.innerHTML = "";
+ const flap = DUPRE.splitFlap(fw, { cells: 8, animate: true, flapMs: 60 });
+ flap.setText(DS.state.scene ? BOARD[DS.state.scene] : "STANDBY");
+
+ const rw = document.getElementById("rot-well");
+ rw.innerHTML = "";
+ const rHost = h("div");
+ DUPRE.rotarySelector(rHost, {
+ values: ["OFF", "FOCUS", "PRESENT", "BATTERY", "NIGHT"],
+ index: sceneIdx(),
+ onChange: (v) => {
+ if (booting) return;
+ if (v === "OFF") sceneOff(rHost);
+ else DS.applyScene(SCENES[["OFF", "FOCUS", "PRESENT", "BATTERY", "NIGHT"].indexOf(v) - 1], rHost);
+ },
+ });
+ rw.appendChild(rHost);
+
+ // ---- readouts -----------------------------------------------------------
+ const cur = DS.state.scene ? DS.state.scene.toUpperCase() : "no scene";
+ document.getElementById("ro1").innerHTML = "tuned: <b>" + cur + "</b>";
+ document.getElementById("ro2").innerHTML = "board: <b>" + cur + "</b>";
+
+ booting = false;
+}
+
+DS.onChange(render);
+render();
+</script>
+</body>
+</html>