diff options
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.html | 177 |
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> |
