feat(canvas): the WiFi badge renders pre-Run and can hand its click to an overlay panel

The badge used to exist only once a run produced a wifiStatus, which
left no stable place to hang a WiFi panel off. It now renders from the
moment a radio-capable board is active (disconnected style before a
Run), and its click consults window.__velxio_wifi_panel_toggle__
first -- a private overlay claims it for its panel, which carries the
gateway-open action forward. OSS builds have no hook, so the badge
behaves exactly as always: nothing before an IP, gateway on click
after one.
This commit is contained in:
David Montero Crespo 2026-08-26 21:38:47 +02:00
parent 7cfeb7399d
commit 4e5ba634f2
1 changed files with 19 additions and 7 deletions

View File

@ -2853,20 +2853,22 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
pairing on velxio.dev). Empty in the OSS build. */} pairing on velxio.dev). Empty in the OSS build. */}
<span data-velxio-slot="wifi-panel" /> <span data-velxio-slot="wifi-panel" />
{/* WiFi status indicator + IoT-gateway launcher (ESP32 + Pico W) */} {/* WiFi status indicator + IoT-gateway launcher (ESP32 + Pico W).
Renders from the moment a radio-capable board is active —
before a Run it shows as disconnected, so the icon is a
stable place to click (the overlay hangs its panel off it). */}
{activeBoard && {activeBoard &&
(isEsp32Kind(activeBoard.boardKind) || activeBoard.boardKind === 'pi-pico-w') && (isEsp32Kind(activeBoard.boardKind) || activeBoard.boardKind === 'pi-pico-w') &&
activeBoard.wifiStatus &&
(() => { (() => {
// The Pico W virtual net assigns its IP deterministically when // The Pico W virtual net assigns its IP deterministically when
// the sketch connects; the bridge reports 'started' carrying the // the sketch connects; the bridge reports 'started' carrying the
// IP. Treat that as got_ip so the badge matches the ESP32 (green, // IP. Treat that as got_ip so the badge matches the ESP32 (green,
// clickable → the same /api/gateway proxy). // clickable → the same /api/gateway proxy).
const rawStatus = activeBoard.wifiStatus.status; const rawStatus = activeBoard.wifiStatus?.status ?? 'disconnected';
const status = const status =
activeBoard.boardKind === 'pi-pico-w' && activeBoard.boardKind === 'pi-pico-w' &&
rawStatus === 'started' && rawStatus === 'started' &&
activeBoard.wifiStatus.ip activeBoard.wifiStatus?.ip
? 'got_ip' ? 'got_ip'
: rawStatus; : rawStatus;
const hasIp = status === 'got_ip'; const hasIp = status === 'got_ip';
@ -2878,6 +2880,16 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
const gatewayUrl = `${backendBase}/gateway/${clientId}/`; const gatewayUrl = `${backendBase}/gateway/${clientId}/`;
const openGateway = () => { const openGateway = () => {
// A private overlay can claim the click for its WiFi
// panel (networks on the air, PCAP, local gateway); the
// panel carries the gateway-open action forward. OSS
// builds have no hook -> the badge behaves as always.
const panel = (
window as unknown as {
__velxio_wifi_panel_toggle__?: () => boolean;
}
).__velxio_wifi_panel_toggle__;
if (panel && panel()) return;
if (!hasIp) return; if (!hasIp) return;
// A private overlay (velxio.dev) can install a synchronous // A private overlay (velxio.dev) can install a synchronous
// gate to keep the IoT gateway behind a paid plan. When it // gate to keep the IoT gateway behind a paid plan. When it
@ -2907,13 +2919,13 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => {
}; };
return ( return (
<span <span
className={`canvas-wifi-badge canvas-wifi-${status}${hasIp ? ' canvas-wifi-clickable' : ''}`} className={`canvas-wifi-badge canvas-wifi-${status}${hasIp || (window as unknown as { __velxio_wifi_panel_toggle__?: unknown }).__velxio_wifi_panel_toggle__ ? ' canvas-wifi-clickable' : ''}`}
onClick={openGateway} onClick={openGateway}
title={ title={
hasIp hasIp
? `WiFi: ${activeBoard.wifiStatus.ssid ?? 'Velxio-GUEST'} — IP: ${activeBoard.wifiStatus.ip}\nClick to open IoT Gateway ↗` ? `WiFi: ${activeBoard.wifiStatus?.ssid ?? 'Velxio-GUEST'} — IP: ${activeBoard.wifiStatus?.ip}\nClick to open IoT Gateway ↗`
: status === 'connected' : status === 'connected'
? `WiFi: ${activeBoard.wifiStatus.ssid ?? 'Velxio-GUEST'} — Connecting...` ? `WiFi: ${activeBoard.wifiStatus?.ssid ?? 'Velxio-GUEST'} — Connecting...`
: status === 'initializing' : status === 'initializing'
? 'WiFi: Initializing...' ? 'WiFi: Initializing...'
: 'WiFi: Disconnected' : 'WiFi: Disconnected'