From a765a0ed04502a9e22a608dbf82feaa3f76322bd Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Wed, 26 Aug 2026 21:50:24 +0200 Subject: [PATCH] feat(canvas): the WiFi badge becomes a split button David's review of the panel-first click: the one-click "icon -> the board's web page" flow is muscle memory and must not grow a second click. So the badge is now a split button, the standard pattern for primary-action-plus-related-surface: - the WiFi glyph keeps its one-click action: with an IP it opens the IoT gateway exactly as always (in-app iframe or new tab); before an IP it opens the overlay's WiFi panel, since there is nothing else to open yet. - an attached caret is the panel's handle -- always reachable, run or not, which is when PCAP matters most. Touch-friendly and visible, unlike the right-click experiment it replaces. The caret renders only when an overlay installed the panel hook; the OSS build shows exactly the badge it always did. --- .../components/simulator/SimulatorCanvas.tsx | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/simulator/SimulatorCanvas.tsx b/frontend/src/components/simulator/SimulatorCanvas.tsx index 029055c0..7bd9e35c 100644 --- a/frontend/src/components/simulator/SimulatorCanvas.tsx +++ b/frontend/src/components/simulator/SimulatorCanvas.tsx @@ -2879,18 +2879,27 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => { 'http://localhost:8001/api'; const gatewayUrl = `${backendBase}/gateway/${clientId}/`; - 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 togglePanel = (): boolean => { + // A private overlay can hang its WiFi panel (networks on + // the air, PCAP, local gateway) off this badge. OSS + // builds install no hook -> false, nothing happens. const panel = ( window as unknown as { __velxio_wifi_panel_toggle__?: () => boolean; } ).__velxio_wifi_panel_toggle__; - if (panel && panel()) return; - if (!hasIp) return; + return !!panel && panel(); + }; + const openGateway = () => { + // With an IP, a click opens the board's web server + // DIRECTLY — the one-click flow users have muscle + // memory for. The panel takes the click only while + // there is nothing to open yet, and right-click + // reaches it any time. + if (!hasIp) { + togglePanel(); + return; + } // A private overlay (velxio.dev) can install a synchronous // gate to keep the IoT gateway behind a paid plan. When it // returns true it has already handled the click (e.g. shown @@ -2918,6 +2927,7 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => { } }; return ( + <> { + {/* Split-button caret: the badge keeps its one-click + gateway action; the caret is the WiFi panel's + handle — always reachable, run or not. Rendered + only when an overlay installed the panel hook, so + the OSS build shows exactly what it always did. */} + {(window as unknown as { __velxio_wifi_panel_toggle__?: unknown }) + .__velxio_wifi_panel_toggle__ ? ( + { + e.stopPropagation(); + togglePanel(); + }} + > + + + + + ) : null} + ); })()}