Author SHA1 Message Date
Jackson AndresandClaude Opus 5.5 5bc298375c Shared palette for the wizard and installer page
Installer / Build (push) Failing after 3s
Installer / Publish (push) Skipped
Warm grey text on near-black, amber #c28947, dark brown solid buttons. The
wizard strip no longer labels LEDs before the LED count is known.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:19:39 -04:00
4 changed files with 2759 additions and 2750 deletions

No files matched your search

+2 -1
View File
@@ -48,9 +48,10 @@ jobs:
BUILD_DIR: ${{ steps.build.outputs.name }} BUILD_DIR: ${{ steps.build.outputs.name }}
VERSION: ${{ steps.version.outputs.version }} VERSION: ${{ steps.version.outputs.version }}
run: | run: |
mkdir -p site mkdir -p site/fonts
cp -r "$BUILD_DIR" site/firmware cp -r "$BUILD_DIR" site/firmware
rm -f site/firmware/*.elf rm -f site/firmware/*.elf
cp wizard/fonts/plex-sans.woff2 wizard/fonts/plex-mono-400.woff2 wizard/fonts/OFL.txt site/fonts/
sed "s/__VERSION__/$VERSION/g" installer/index.html > site/index.html sed "s/__VERSION__/$VERSION/g" installer/index.html > site/index.html
ls -R site ls -R site
+2726 -2719
View File
File diff suppressed because it is too large. Load diff
+13 -14
View File
@@ -8,33 +8,32 @@
<script type="module" src="https://unpkg.com/esp-web-tools@10.4.0/dist/web/install-button.js?module"></script> <script type="module" src="https://unpkg.com/esp-web-tools@10.4.0/dist/web/install-button.js?module"></script>
<style> <style>
:root{ :root{
--bg:#ffffff; --text:#1f1f1f; --muted:#5f5f5f; --line:#e2e2e2; --accent:#b35c00; --warn-bg:#fff5e8; --bg:#07090a; --panel:#0e0e10; --text:#dfd8c9; --muted:#8d8980; --dim:#55534f;
--button:#1f1f1f; --button-text:#ffffff; --line:#171514; --line-2:#1f1d16; --accent:#c28947;
color-scheme:light dark; --button:#2e281b; --button-text:#dddad6;
color-scheme:dark;
} }
@media (prefers-color-scheme:dark){ @font-face{font-family:"IBM Plex Sans";font-weight:100 700;font-display:swap;src:url(fonts/plex-sans.woff2) format("woff2")}
:root{--bg:#121212; --text:#e8e8e8; --muted:#a0a0a0; --line:#2e2e2e; --accent:#ffb35a; --warn-bg:#2a2016; @font-face{font-family:"IBM Plex Mono";font-weight:400;font-display:swap;src:url(fonts/plex-mono-400.woff2) format("woff2")}
--button:#e8e8e8; --button-text:#121212} body{margin:0;background:var(--bg);color:var(--text);font:16px/1.6 "IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif}
}
body{margin:0;background:var(--bg);color:var(--text);font:16px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif}
main{max-width:640px;margin:0 auto;padding:48px 16px 64px} main{max-width:640px;margin:0 auto;padding:48px 16px 64px}
h1{font-size:28px;line-height:1.2;margin:0 0 12px} h1{font-size:28px;line-height:1.2;font-weight:600;margin:0 0 12px}
h2{font-size:19px;margin:40px 0 10px} h2{font-size:19px;margin:40px 0 10px}
p{margin:0 0 12px} p{margin:0 0 12px}
.muted{color:var(--muted)} .muted{color:var(--muted)}
ol{padding-left:22px;margin:0 0 20px} ol{padding-left:22px;margin:0 0 20px}
li{margin:0 0 6px} li{margin:0 0 6px}
a{color:var(--accent)} a{color:var(--accent)}
code{font:14px ui-monospace,Menlo,monospace} code{font:14px "IBM Plex Mono",ui-monospace,Menlo,monospace;color:var(--text)}
.install{margin:24px 0 0} .install{margin:24px 0 0}
.install button{border:0;border-radius:6px;background:var(--button);color:var(--button-text);font:600 16px/1 system-ui,sans-serif;padding:13px 20px;cursor:pointer} .install button{border:1px solid var(--line-2);border-radius:3px;background:var(--button);color:var(--button-text);font:500 15px/1 "IBM Plex Sans",system-ui,sans-serif;padding:14px 20px;cursor:pointer}
esp-web-install-button[install-unsupported] button{display:none} esp-web-install-button[install-unsupported] button{display:none}
.warn{background:var(--warn-bg);border-left:3px solid var(--accent);padding:10px 14px;margin:20px 0 0;font-size:15px} .warn{background:var(--panel);border-left:2px solid var(--accent);padding:10px 14px;margin:20px 0 0;font-size:15px;color:var(--text)}
footer{border-top:1px solid var(--line);margin-top:48px;padding-top:16px;font-size:14px;color:var(--muted)} footer{border-top:1px solid var(--line);margin-top:48px;padding-top:16px;font-size:14px;color:var(--muted)}
footer ul{list-style:none;padding:0;margin:0 0 12px} footer ul{list-style:none;padding:0;margin:0 0 12px}
footer li{display:inline;margin-right:16px} footer li{display:inline;margin-right:16px}
/* ESP Web Tools dialogs */ /* ESP Web Tools dialogs */
esp-web-install-button{--improv-primary-color:var(--accent);--mdc-theme-primary:var(--accent)} esp-web-install-button{--improv-primary-color:var(--accent);--improv-text-color:var(--text);--mdc-theme-primary:var(--accent);--mdc-theme-surface:var(--panel);--mdc-dialog-heading-ink-color:var(--text);--mdc-dialog-content-ink-color:var(--muted)}
</style> </style>
</head> </head>
<body> <body>
@@ -68,7 +67,7 @@ esp-web-install-button{--improv-primary-color:var(--accent);--mdc-theme-primary:
<li><a href="https://github.com/RandomPedestrian07/follow-me-stairs">Source and documentation</a></li> <li><a href="https://github.com/RandomPedestrian07/follow-me-stairs">Source and documentation</a></li>
<li><a href="https://github.com/RandomPedestrian07/follow-me-stairs/blob/main/docs/settings.md">Settings</a></li> <li><a href="https://github.com/RandomPedestrian07/follow-me-stairs/blob/main/docs/settings.md">Settings</a></li>
</ul> </ul>
MIT license. Installer by <a href="https://esphome.github.io/esp-web-tools/">ESP Web Tools</a>. MIT license. IBM Plex fonts: <a href="fonts/OFL.txt">SIL Open Font License 1.1</a>. Installer by <a href="https://esphome.github.io/esp-web-tools/">ESP Web Tools</a>.
</footer> </footer>
</main> </main>
</body> </body>
+18 -16
View File
@@ -61,11 +61,12 @@
@font-face{font-family:"IBM Plex Mono";font-weight:400;font-display:swap;src:url(fonts/plex-mono-400.woff2) format("woff2")} @font-face{font-family:"IBM Plex Mono";font-weight:400;font-display:swap;src:url(fonts/plex-mono-400.woff2) format("woff2")}
@font-face{font-family:"IBM Plex Mono";font-weight:500 600;font-display:swap;src:url(fonts/plex-mono-500.woff2) format("woff2")} @font-face{font-family:"IBM Plex Mono";font-weight:500 600;font-display:swap;src:url(fonts/plex-mono-500.woff2) format("woff2")}
:root{ :root{
--bg:#0a0a0b; --panel:#0e0e10; --bg:#07090a; --panel:#0e0e10;
--ink:#ece8df; --ink-2:rgba(236,232,223,.62); --ink-3:rgba(236,232,223,.36); --ink-4:rgba(236,232,223,.14); --ink:#dfd8c9; --ink-2:rgba(223,216,201,.62); --ink-3:rgba(223,216,201,.36); --ink-4:rgba(223,216,201,.14);
--line:rgba(236,232,223,.1); --line-2:rgba(236,232,223,.2); --line:rgba(223,216,201,.1); --line-2:rgba(223,216,201,.2);
--warm:#ffb35a; --warm-2:#ffd6a0; --warm:#c28947; --warm-2:#c2a37a;
--bad:#ff8a7a; --fill:#2e281b; --fill-ink:#dddad6; --fill-hover:#3a3324; /* solid buttons, toast, progress */
--bad:#c2695d;
--sans:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; --sans:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
--mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace; --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
color-scheme:dark; color-scheme:dark;
@@ -104,7 +105,7 @@ button,input{font:inherit;color:inherit}
.draw{position:relative;background:var(--panel);border-right:1px solid var(--line);overflow:hidden; .draw{position:relative;background:var(--panel);border-right:1px solid var(--line);overflow:hidden;
background-image:linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px); background-image:linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px);
background-size:32px 32px;background-position:-1px -1px} background-size:32px 32px;background-position:-1px -1px}
.draw::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse at center,transparent 40%,rgba(10,10,11,.85) 100%);pointer-events:none} .draw::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse at center,transparent 40%,rgba(7,9,10,.85) 100%);pointer-events:none}
.draw canvas{position:absolute;inset:0;width:100%;height:100%;touch-action:none} .draw canvas{position:absolute;inset:0;width:100%;height:100%;touch-action:none}
.draw .crop{position:absolute;width:14px;height:14px;border-color:var(--ink-3);border-style:solid;border-width:0;pointer-events:none} .draw .crop{position:absolute;width:14px;height:14px;border-color:var(--ink-3);border-style:solid;border-width:0;pointer-events:none}
.crop.tl{left:12px;top:12px;border-left-width:1px;border-top-width:1px} .crop.tl{left:12px;top:12px;border-left-width:1px;border-top-width:1px}
@@ -136,7 +137,7 @@ button,input{font:inherit;color:inherit}
.opt:hover b{color:var(--ink)} .opt:hover b{color:var(--ink)}
.opt.sel b{color:var(--ink)} .opt.sel b{color:var(--ink)}
.opt.sel svg{opacity:1;color:var(--ink)} .opt.sel svg{opacity:1;color:var(--ink)}
.opt.sel i{background:var(--warm);border-color:var(--warm);box-shadow:0 0 10px rgba(255,179,90,.6)} .opt.sel i{background:var(--warm);border-color:var(--warm);box-shadow:0 0 10px rgba(194,137,71,.6)}
.opt .val{font:13px/1 var(--mono);color:var(--ink-3)} .opt .val{font:13px/1 var(--mono);color:var(--ink-3)}
.opt.sel .val{color:var(--warm-2)} .opt.sel .val{color:var(--warm-2)}
@@ -151,9 +152,9 @@ button,input{font:inherit;color:inherit}
.num output small{font-size:11px;color:var(--ink-3);margin-left:3px} .num output small{font-size:11px;color:var(--ink-3);margin-left:3px}
/* buttons */ /* buttons */
.go{display:inline-flex;align-items:center;justify-content:center;gap:10px;border:0;background:var(--ink);color:#111; .go{display:inline-flex;align-items:center;justify-content:center;gap:10px;border:0;background:var(--fill);color:var(--fill-ink);
padding:13px 18px;font:500 14px/1 var(--sans);letter-spacing:.01em;cursor:pointer;border-radius:3px} padding:13px 18px;font:500 14px/1 var(--sans);letter-spacing:.01em;cursor:pointer;border-radius:3px}
.go:hover{background:#fff} .go:hover{background:var(--fill-hover)}
.go:disabled{background:var(--ink-4);color:var(--ink-3);cursor:default} .go:disabled{background:var(--ink-4);color:var(--ink-3);cursor:default}
.go.wide{width:100%;padding:16px} .go.wide{width:100%;padding:16px}
.go.rec{background:var(--warm);color:#1b1006} .go.rec{background:var(--warm);color:#1b1006}
@@ -201,14 +202,14 @@ input[type=range]::-moz-range-thumb{width:12px;height:12px;background:var(--bg);
/* footer */ /* footer */
.bar{position:fixed;left:0;right:0;bottom:0;height:73px;display:flex;align-items:center;gap:14px;padding:0 20px env(safe-area-inset-bottom); .bar{position:fixed;left:0;right:0;bottom:0;height:73px;display:flex;align-items:center;gap:14px;padding:0 20px env(safe-area-inset-bottom);
border-top:1px solid var(--line);background:rgba(10,10,11,.86);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);z-index:5} border-top:1px solid var(--line);background:rgba(7,9,10,.86);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);z-index:5}
.prog{flex:1;display:flex;flex-direction:column;gap:7px;align-items:center} .prog{flex:1;display:flex;flex-direction:column;gap:7px;align-items:center}
.prog .count{font:11px/1 var(--mono);letter-spacing:.16em;color:var(--ink-3)} .prog .count{font:11px/1 var(--mono);letter-spacing:.16em;color:var(--ink-3)}
.prog .ticks{display:flex;gap:4px} .prog .ticks{display:flex;gap:4px}
.prog .ticks i{width:18px;height:2px;background:var(--ink-4)} .prog .ticks i{width:18px;height:2px;background:var(--ink-4)}
.prog .ticks i.done{background:var(--ink-3)} .prog .ticks i.done{background:var(--ink-3)}
.prog .ticks i.on{background:var(--ink)} .prog .ticks i.on{background:var(--fill)}
.toast{position:fixed;left:50%;bottom:88px;transform:translate(-50%,8px);opacity:0;background:var(--ink);color:#111;padding:10px 14px; .toast{position:fixed;left:50%;bottom:88px;transform:translate(-50%,8px);opacity:0;background:var(--fill);color:var(--fill-ink);padding:10px 14px;
font-size:13.5px;border-radius:3px;transition:.2s;pointer-events:none;z-index:9;white-space:nowrap} font-size:13.5px;border-radius:3px;transition:.2s;pointer-events:none;z-index:9;white-space:nowrap}
.toast.show{opacity:1;transform:translate(-50%,0)} .toast.show{opacity:1;transform:translate(-50%,0)}
@@ -980,8 +981,8 @@ function ledAt(d, a) {
} }
// ---------- drawing ---------- // ---------- drawing ----------
const ink = a => `rgba(236,232,223,${a})`; const ink = a => `rgba(223,216,201,${a})`;
const WARM = a => `rgba(255,179,90,${a})`; const WARM = a => `rgba(194,137,71,${a})`;
const MONO = (px, wt = 400) => `${wt} ${px}px "IBM Plex Mono", ui-monospace, monospace`; const MONO = (px, wt = 400) => `${wt} ${px}px "IBM Plex Mono", ui-monospace, monospace`;
function fit(cv) { function fit(cv) {
const r = cv.getBoundingClientRect(), dpr = window.devicePixelRatio || 1; const r = cv.getBoundingClientRect(), dpr = window.devicePixelRatio || 1;
@@ -1114,10 +1115,11 @@ function drawStrip(lit, marks) {
const {ctx, w, h} = fit($('strip')), n = S.ledCount, cw = w / n, by = 6, bh = 10; const {ctx, w, h} = fit($('strip')), n = S.ledCount, cw = w / n, by = 6, bh = 10;
ctx.clearRect(0, 0, w, h); ctx.clearRect(0, 0, w, h);
ctx.fillStyle = ink(.07); ctx.fillRect(0, by, w, bh); ctx.fillStyle = ink(.07); ctx.fillRect(0, by, w, bh);
if (!n) return; // LED count not known yet: empty strip, no labels
ctx.save(); ctx.shadowColor = WARM(.9); ctx.shadowBlur = 10; ctx.save(); ctx.shadowColor = WARM(.9); ctx.shadowBlur = 10;
for (let i = 1; i <= n; i++) { for (let i = 1; i <= n; i++) {
const v = lit(i); if (v <= .004) continue; const v = lit(i); if (v <= .004) continue;
ctx.fillStyle = `rgba(255,${200 + 30 * v | 0},${140 + 60 * v | 0},${v})`; ctx.fillStyle = `rgba(194,${137 + 26 * v | 0},${71 + 51 * v | 0},${v})`; // --warm to --warm-2
ctx.fillRect((i - 1) * cw, by, Math.max(cw - (cw > 3 ? 1 : 0), 1), bh); ctx.fillRect((i - 1) * cw, by, Math.max(cw - (cw > 3 ? 1 : 0), 1), bh);
} }
ctx.restore(); ctx.restore();
@@ -1673,7 +1675,7 @@ function frame(now) {
const d0 = clamp(g.center - g.half, dB, dT), d1 = clamp(g.center + g.half, dB, dT), pts2 = []; const d0 = clamp(g.center - g.half, dB, dT), d1 = clamp(g.center + g.half, dB, dT), pts2 = [];
for (let d = d0; d <= d1; d += 25) pts2.push(wallPoint(tp, mapAlong(d, rp.feats, tp.feats))); for (let d = d0; d <= d1; d += 25) pts2.push(wallPoint(tp, mapAlong(d, rp.feats, tp.feats)));
ctx.save(); ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.save(); ctx.lineCap = 'round'; ctx.lineJoin = 'round';
ctx.shadowColor = WARM(g.alpha); ctx.shadowBlur = 22; ctx.strokeStyle = `rgba(255,214,160,${g.alpha})`; ctx.lineWidth = 4; ctx.shadowColor = WARM(g.alpha); ctx.shadowBlur = 22; ctx.strokeStyle = `rgba(194,163,122,${g.alpha})`; ctx.lineWidth = 4;
polyline(ctx, V, pts2); ctx.stroke(); ctx.restore(); polyline(ctx, V, pts2); ctx.stroke(); ctx.restore();
} }
const a = anchors(rp, true); const a = anchors(rp, true);