Handoff: Flash page (option 2a — console)

Overview

Replacement for /flash.html on serin-labs.com. The page keeps the existing esp-web-tools install flow but rebuilds the surface around it as a single dark console: a left device bay that fills in with what was read off the connected board, and a right panel that is one surface reused across the flow — instructions before flashing, phase progress during, pairing after. The old page's stacked "1. Select your platform / 2. Flash / Notes" structure is gone.

About the design files

flash-2a-reference.dc.html in this folder is a design reference written in HTML — a prototype of the intended look and states, not production code to lift. Recreate it in the target codebase using its own conventions: Jekyll page flash.html, styles into the existing assets/css stylesheet(s), nav and footer from the site's existing includes (the chrome in the prototype is a stand-in).

Open it directly in a browser; it needs support.js and assets/serin_logo.svg, both included here.

Fidelity

High fidelity for layout, color, type and copy — the values below are authoritative. Medium fidelity for the flashing mechanics: how much of the device data you can actually surface depends on what esp-web-tools / the esptool JS exposes (see "Detected data" below). Where a field cannot be read, hide the row — do not show a placeholder or "unknown".


Page frame

Content width 1200px, 48px horizontal gutters on every band. Bands in order:

  1. nav (existing include)
  2. dark header band #1A1B1E
  3. console band #121316 holding the panel
  4. light "after flashing" strip
  5. #F3F3F0 notes row (3 columns)
  6. footer (existing include)

Header band

Console band


The four states

The panel is one component with a state machine. Everything outside the panel is static.

idle ──connect──▶ detected ──install──▶ writing ──▶ done
  ▲                   │                    │          │
  └───────── disconnect / error ───────────┴──────────┘

State 1 — idle (nothing connected)

Bay: status pill "waiting" (grey dot, rgba(255,255,255,0.06) bg). Body is a dashed drop-target block: 1px dashed rgba(255,255,255,0.22), radius 11px, padding 30px 20px, centered — a 54px circle outline labelled "usb", heading "Plug the board in" (16px/700), and 14px/1.5 rgba(255,255,255,0.5) copy "Then press connect and pick it from the browser's list. We read the rest ourselves." Firmware toggle is present and usable already. Primary button: "Connect a device", sub-label "Chrome, Edge or Brave. Nothing is installed."

Right panel: "Two minutes, one cable" + lead; then a What we read off the chip 2×2 monospace list — chip family and revision / flash size and speed / mac address / firmware already installed — followed by 13.5px rgba(255,255,255,0.4): "Read locally in this tab. None of it is sent to us." Bottom card: supported boards (image slot + copy).

State 2 — detected (board read, nothing written)

This is the state shown in the full-page reference. Bay: pill "connected" (green #9BE8B0 dot on rgba(155,232,176,0.12)). Device card: board thumbnail 46px, board name 16.5px/700, and beneath it "matched from USB 303A:1001" (monospace 12px). Then a 4-row monospace table (12.5px, rows separated by 1px solid rgba(255,255,255,0.08), label rgba(255,255,255,0.45) left / value #fff right):

label example value
chip ESP32-C6 rev 0.1
flash 4 MB · quad, 80 MHz
mac A0:85:E3:4C:9F:1D
on board now serin-homekit 2026.4.2 (amber #F09A28)

Below the card: firmware segmented control, then a monospace line showing the build that will be written and how it compares — "serin-homekit 2026.7.1" / "newer than the board" (green). Comparison text is one of: newer than the board (green), same version — reflash (muted), older than the board (amber), different platform (amber). Then the erase toggle: amber-tinted box, checkbox, "Erase everything first" + "Off by default, so Wi-Fi and pairing survive the update. Tick it to hand the board to someone else." Primary button label is version-aware: "Update to 2026.7.1" when a Serin build is already present, "Install firmware" on a blank or foreign board.

Right panel: "Board read. Nothing written yet." + reassurance copy; a three-row "What happens when you press it" list with right-aligned durations (~100 s / ~10 s / your pace); and two bottom cards — "Already installed in the wall?" (push over Wi-Fi instead) and "Switching platforms" (HomeKit → ESPHome wipes pairing; remove from the Home app first). The second card only appears when the selected platform differs from the detected one.

State 3 — writing

Bay: whole column at opacity: 0.55, pill turns amber "busy", device card collapses to name + "ESP32-C6 · 4 MB" + MAC. Segmented control, erase toggle and links are disabled (not hidden). Primary button becomes an inert rgba(255,255,255,0.08) block reading "Writing…" with "Leave the cable alone." beneath.

Right panel: headline "Writing the app partition" (changes per phase), sub-line "Unplugging now is recoverable — just start again.", big monospace percentage #F09A28 30px, then a 6px progress bar (#F09A28 on rgba(255,255,255,0.1)). Below it the phase list, four rows, same grid as state 2 but with status markers: green ✓ + "done" for finished, amber "›" + live byte count for the current one, dimmed index + "queued" for the rest. Footer row: Wi-Fi/2.4 GHz reminder on the left, "view log" (monospace, opens the raw esptool output in a disclosure) on the right.

State 4 — done

Bay: pill "online". Card head is tinted rgba(155,232,176,0.08) and shows the device name Serin-4C9F with the installed build beneath. Table rows: network / address / signal / cn105 link. The CN105 row reads "not connected yet" in amber until the board is plugged into the indoor unit, with a 13.5px note explaining that is expected on a bench. Secondary button "Open the device web UI" (outlined) links to http://<ip>/; monospace line beneath: "saved to this browser · flash another board" (the second half restarts the flow).

Right panel: green ✓ + "Flashed and on your Wi-Fi", elapsed time and "cable can come out" on the right. Two cards side by side:

For the ESPHome build, state 4 replaces the pairing card with an "Adopt in Home Assistant" card (discovery note + link to /esphome/setup.html) and keeps the hotspot card.


Detected data — what to read and from where

All of it is read in-tab over Web Serial; nothing is transmitted. Sources:

Any field that cannot be read is omitted, never shown empty.

Behavior & other requirements

Design tokens

Assets

Files