diff options
Diffstat (limited to 'docs/prototypes/2026-07-02-desktop-settings-panel-prototype-8.html')
| -rw-r--r-- | docs/prototypes/2026-07-02-desktop-settings-panel-prototype-8.html | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-8.html b/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-8.html new file mode 100644 index 0000000..13fed0b --- /dev/null +++ b/docs/prototypes/2026-07-02-desktop-settings-panel-prototype-8.html @@ -0,0 +1,360 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Desktop Settings — Prototype 8 (gauge cluster)</title> +<link rel="stylesheet" href="desktop-settings-shared.css"> +<style> + /* 60s dashboard: twin needle gauges in a binnacle, a rocker-switch bank, + push-button drive modes for scenes, an ignition rotary for power. */ + .binnacle { + background: + radial-gradient(140px 90px at 25% 20%, #ffffff07, transparent), + linear-gradient(180deg, #1c1a17, #121110); + border: 1px solid var(--plate-edge); + border-radius: 14px; + padding: 12px 10px 8px; + display: grid; grid-template-columns: 1fr 1fr; gap: 10px; + } + .gauge { + position: relative; + width: 100%; aspect-ratio: 1; + border-radius: 50%; + background: radial-gradient(circle at 38% 30%, #16140f, #0a0908 78%); + border: 3px solid #2c2820; + box-shadow: inset 0 0 16px #000c, 0 4px 12px #0009; + cursor: pointer; + touch-action: none; + } + .gauge .tk { + position: absolute; left: 50%; top: 8px; width: 1.5px; height: 7px; + margin-left: -0.75px; + background: #7a7057; + transform-origin: 50% calc(var(--gr) * 1px); + pointer-events: none; + } + .gauge .tk.mj { width: 2.5px; height: 11px; background: var(--cream); } + .gauge .num { + position: absolute; left: 50%; top: 50%; + color: var(--steel); font-size: 9px; + transform: translate(-50%, -50%); + pointer-events: none; + } + .gauge .needle { + position: absolute; left: 50%; bottom: 50%; width: 3px; height: 42%; + margin-left: -1.5px; + background: linear-gradient(180deg, var(--fail) 60%, #7e4530); + transform-origin: 50% 100%; + border-radius: 3px 3px 0 0; + box-shadow: 0 0 6px #cb6b4d66; + transition: transform .18s ease-out; + pointer-events: none; + } + .gauge .hub { + position: absolute; left: 50%; top: 50%; width: 14px; height: 14px; + margin: -7px 0 0 -7px; border-radius: 50%; + background: radial-gradient(circle at 35% 30%, #4a453b, #211e19); + border: 1px solid #383328; + pointer-events: none; + } + .gauge .g-lbl { + position: absolute; width: 100%; text-align: center; top: 27%; + color: var(--steel); font-size: 8px; letter-spacing: 0.2em; + pointer-events: none; + } + .odo { + position: absolute; width: 100%; text-align: center; bottom: 17%; + pointer-events: none; + } + .odo span { + display: inline-block; + background: #060505; border: 1px solid #2a2620; border-radius: 3px; + color: var(--cream); font-size: 11px; font-variant-numeric: tabular-nums; + padding: 1px 6px; letter-spacing: 0.12em; + } + + /* Rocker bank: tilting switches with engraved legends. */ + .rockers { display: grid; grid-template-columns: repeat(4,1fr); gap: 6px; } + .rocker { + display: flex; flex-direction: column; align-items: center; gap: 4px; + cursor: pointer; padding: 5px 2px 4px; + border-radius: 6px; border: 1px solid transparent; + } + .rocker:hover { border-color: var(--key-edge); } + .rk { + width: 26px; height: 38px; border-radius: 5px; + background: linear-gradient(180deg, var(--key-top), var(--key-bot)); + border: 1px solid var(--key-edge); + position: relative; + perspective: 60px; + } + .rk .cap { + position: absolute; inset: 2px; border-radius: 3px; + background: linear-gradient(180deg, #2e2b25 48%, #191712 52%); + transform: rotateX(14deg); + transition: transform .12s, background .12s; + } + .rocker.on .rk { border-color: var(--gold); } + .rocker.on .rk .cap { + transform: rotateX(-14deg); + background: linear-gradient(180deg, #191712 48%, #4a3d16 52%); + } + .rocker .lgd { + font-size: 7.5px; letter-spacing: 0.06em; color: var(--dim); + text-transform: uppercase; text-align: center; + } + .rocker.on .lgd { color: var(--gold-bright); text-shadow: 0 0 6px #dab53d88; } + .rocker .g { font-size: 13px; color: var(--steel); } + .rocker.on .g { color: var(--gold); } + .rocker.verified { animation: dsVerify 0.9s ease-out; } + + /* Drive-mode push buttons (scenes): the push-button transmission column. */ + .modes { display: grid; grid-template-columns: repeat(4,1fr); gap: 6px; } + .mode { + background: linear-gradient(180deg, var(--key-top), var(--key-bot)); + border: 1px solid var(--key-edge); + border-bottom-width: 4px; + border-radius: 6px; + padding: 7px 2px; + text-align: center; cursor: pointer; + } + .mode .ltr { display: block; color: var(--cream); font-size: 15px; font-weight: 700; } + .mode .nm { display: block; color: var(--dim); font-size: 7.5px; letter-spacing: 0.08em; text-transform: uppercase; margin-top: 2px; } + .mode:hover { border-color: var(--gold); } + .mode.cur { + border-color: var(--gold); border-bottom-width: 1px; + transform: translateY(3px); + background: linear-gradient(180deg, #2a2618, #1d1a12); + } + .mode.cur .ltr { color: var(--gold-bright); text-shadow: 0 0 8px #dab53d99; } + .mode.cur .nm { color: var(--gold); } + + /* Ignition rotary (power profile). */ + .ignition { display: flex; align-items: center; gap: 14px; padding: 2px 6px; } + .ig-knob { + width: 54px; height: 54px; border-radius: 50%; + background: radial-gradient(circle at 35% 30%, #35322c, #191715 70%); + border: 2px solid var(--key-edge); + box-shadow: 0 3px 8px #000a; + position: relative; cursor: pointer; + } + .ig-knob .slot { + position: absolute; left: 50%; top: 50%; width: 5px; height: 26px; + margin: -13px 0 0 -2.5px; + background: #0a0908; border-radius: 3px; border: 1px solid #3a352c; + transform-origin: 50% 50%; + transition: transform .2s; + } + .ig-labels { display: flex; flex-direction: column; gap: 3px; } + .ig-labels .det { color: var(--dim); font-size: 10px; letter-spacing: 0.12em; cursor: pointer; } + .ig-labels .det:hover { color: var(--silver); } + .ig-labels .det.cur { color: var(--gold-bright); text-shadow: 0 0 7px #dab53d77; } + .ig-lbl { margin-left: auto; color: var(--steel); font-size: 9px; letter-spacing: 0.16em; } + + /* Dash pull-knob actions. */ + .pulls { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; } + .pull { + display: flex; flex-direction: column; align-items: center; gap: 4px; + cursor: pointer; padding: 5px 2px; + border-radius: 6px; border: 1px solid transparent; + } + .pull:hover { border-color: var(--key-edge); } + .pull .cap { + width: 30px; height: 30px; border-radius: 50%; + background: radial-gradient(circle at 35% 30%, #3a362e, #1c1a15); + border: 2px solid var(--key-edge); + box-shadow: 0 3px 6px #000a; + display: flex; align-items: center; justify-content: center; + color: var(--steel); font-size: 13px; + } + .pull:hover .cap { border-color: var(--gold); color: var(--gold); } + .pull .lgd { font-size: 8px; letter-spacing: 0.1em; color: var(--dim); text-transform: uppercase; } +</style> +</head> +<body class="ds-stage"> + <div class="ds-caption"> + <b>Direction 8 — Gauge cluster.</b> A dashboard binnacle: brightness and + keyboard are needle gauges (drag across the dial), toggles are a rocker + bank with backlit legends, scenes are push-button drive modes, power is an + ignition rotary, actions are dash pull-knobs. + </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="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 SCENE_ORDER = Object.keys(DS.SCENES); +const SWEEP = 240, START = -120; // gauge sweep + +function openWallpaper() { + subview.innerHTML = ""; + subview.appendChild(DS.wallpaperSubview(() => subview.classList.remove("open"))); + subview.classList.add("open"); +} + +function valueAt(e, el, floor) { + const r = el.getBoundingClientRect(); + const dx = e.clientX - (r.left + r.width / 2); + const dy = e.clientY - (r.top + r.height / 2); + let a = Math.atan2(dx, -dy) * 180 / Math.PI; + a = Math.max(START, Math.min(START + SWEEP, a)); + return Math.round(floor + ((a - START) / SWEEP) * (100 - floor)); +} + +function gauge(key, label) { + const meta = DS.SLIDERS[key]; + const g = h("div", "gauge"); + + // ticks + numerals — radius derives from the rendered size after mount + requestAnimationFrame(() => { + const R = g.clientWidth / 2; + for (let i = 0; i <= 10; i++) { + const t = h("div", "tk" + (i % 2 === 0 ? " mj" : "")); + t.style.setProperty("--gr", R - 8); + t.style.transform = `rotate(${START + (i / 10) * SWEEP}deg)`; + g.appendChild(t); + if (i % 2 === 0) { + const a = (START + (i / 10) * SWEEP - 90) * Math.PI / 180; + const nr = R - 26; + const n = h("div", "num", i * 10); + n.style.left = 50 + (Math.cos(a) * nr / R) * 50 + "%"; + n.style.top = 50 + (Math.sin(a) * nr / R) * 50 + "%"; + g.appendChild(n); + } + } + }); + + g.appendChild(h("div", "g-lbl", label)); + const nd = h("div", "needle"); + const odo = h("div", "odo"); + const odoSpan = h("span"); + odo.appendChild(odoSpan); + const paint = (v) => { + nd.style.transform = `rotate(${START + ((v - meta.floor) / (100 - meta.floor)) * SWEEP}deg)`; + odoSpan.textContent = String(v).padStart(3, "0"); + }; + paint(DS.state[key]); + g.appendChild(nd); + g.appendChild(h("div", "hub")); + g.appendChild(odo); + + let live = DS.state[key]; + g.onpointerdown = (e) => { + g.setPointerCapture(e.pointerId); + live = valueAt(e, g, meta.floor); paint(live); + g.onpointermove = (m) => { live = valueAt(m, g, meta.floor); paint(live); }; + g.onpointerup = () => { g.onpointermove = null; DS.setSlider(key, live, g); }; + }; + return g; +} + +function rockerBank() { + const bank = h("div", "rockers"); + const keys = ["autodim", "caffeine", "touchpad", "mouse", "nightlight", "dnd", "weathergeo"]; + if (DS.state.laptop) keys.push("airplane"); + keys.forEach((k) => { + const meta = DS.TOGGLES[k]; + const el = h("div", "rocker" + (DS.state[k] ? " on" : "")); + el.innerHTML = + `<span class="g">${meta.glyph}</span>` + + `<span class="rk"><span class="cap"></span></span>` + + `<span class="lgd">${meta.name}</span>`; + el.onclick = () => DS.setToggle(k, el); + bank.appendChild(el); + }); + return bank; +} + +function driveModes() { + const m = h("div", "modes"); + SCENE_ORDER.forEach((n) => { + const el = h("div", "mode" + (DS.state.scene === n ? " cur" : "")); + el.innerHTML = `<span class="ltr">${n[0]}</span><span class="nm">${n}</span>`; + el.onclick = () => DS.applyScene(n, el); + m.appendChild(el); + }); + return m; +} + +function ignition() { + const ig = h("div", "ignition"); + const kn = h("div", "ig-knob"); + const slot = h("div", "slot"); + const idx = DS.PROFILES.findIndex((p) => p.id === DS.state.powerprofile); + slot.style.transform = `rotate(${idx * 45 - 45}deg)`; + kn.appendChild(slot); + kn.onclick = () => DS.setProfile(DS.PROFILES[(idx + 1) % DS.PROFILES.length].id, ig); + ig.appendChild(kn); + const labels = h("div", "ig-labels"); + DS.PROFILES.forEach((p) => { + const d = h("span", "det" + (p.id === DS.state.powerprofile ? " cur" : ""), p.label.toUpperCase()); + d.onclick = () => DS.setProfile(p.id, ig); + labels.appendChild(d); + }); + ig.appendChild(labels); + ig.appendChild(h("span", "ig-lbl", "IGNITION")); + return ig; +} + +function pulls() { + const p = h("div", "pulls"); + const mk = (glyph, label, fn) => { + const el = h("div", "pull"); + el.innerHTML = `<span class="cap">${glyph}</span><span class="lgd">${label}</span>`; + el.onclick = fn || (() => DS.momentary(label)); + return el; + }; + p.appendChild(mk("\u{f033e}", "Lock")); + p.appendChild(mk("\u{f04b2}", "Suspend")); + p.appendChild(mk("\u{f021c}", "Wallpaper", openWallpaper)); + return p; +} + +function render() { + body.innerHTML = ""; + const col = h("div", "ds-col"); + + const bn = h("div", "binnacle"); + bn.appendChild(gauge("brightness", "BRIGHTNESS")); + bn.appendChild(gauge("kbd", "KEYBOARD")); + col.appendChild(bn); + + col.appendChild(DS.engrave("Switches")); + col.appendChild(rockerBank()); + + col.appendChild(DS.engrave("Drive modes")); + col.appendChild(driveModes()); + + col.appendChild(DS.engrave("Ignition")); + col.appendChild(ignition()); + + col.appendChild(DS.engrave("Actions")); + col.appendChild(pulls()); + + body.appendChild(col); + document.getElementById("scene-sub").textContent = DS.state.scene ? "▶ " + DS.state.scene : ""; +} + +DS.onChange(render); +render(); +</script> +</body> +</html> |
