From 3a2abc48d95b64d1e88896260b51ef3849004fc0 Mon Sep 17 00:00:00 2001 From: David Montero Date: Fri, 29 May 2026 20:38:03 +0200 Subject: [PATCH] fix(espidf): accurate core-lib warnings + gateway open hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two unrelated polish fixes. espidf_compiler: headers that resolve to an arduino-esp32 CORE lib (WebServer, WiFi, …) were correctly skipped from the user-lib merge but then fell through to a scary "Library for not found — build may fail" warning — even though the build succeeds because the symbols are compiled into the core. Now logs an accurate "provided by arduino-esp32 core — already compiled in, not merging". Same treatment for core headers that aren't standalone lib dirs (Udp.h, IPAddress.h, WiFiUdp.h, …) via a new _CORE_ESP32_HEADERS allowlist. SimulatorCanvas: the WiFi badge's "open IoT gateway" click now consults an optional window.__velxio_iot_gateway_open_gate__ hook before opening the gateway tab. A private overlay can install it to gate the gateway behind a paid plan and show an in-place upgrade modal instead of dumping a 402 page in a new tab. OSS builds have no hook → opens normally. The check is synchronous so it doesn't trip popup blockers. Co-Authored-By: Claude Opus 4.7 --- backend/app/services/espidf_compiler.py | 32 ++++++++++++++++++- .../components/simulator/SimulatorCanvas.tsx | 15 ++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/backend/app/services/espidf_compiler.py b/backend/app/services/espidf_compiler.py index 5a1d1110..64e814a6 100644 --- a/backend/app/services/espidf_compiler.py +++ b/backend/app/services/espidf_compiler.py @@ -619,6 +619,17 @@ class ESPIDFCompiler: 'Preferences', 'Update', 'Ticker', }) + # Headers that ship inside the arduino-esp32 core but don't live in a + # standalone library dir (so _find_library_for_header can't resolve + # them). They're already compiled into the core — pulled transitively + # by WiFi/WebServer/etc. — so a "not found" warning for them is a + # false positive. Treated as core-provided, not "may fail". + _CORE_ESP32_HEADERS: frozenset[str] = frozenset({ + 'Udp.h', 'IPAddress.h', 'Client.h', 'Server.h', 'Stream.h', + 'Print.h', 'Printable.h', 'WiFiUdp.h', 'WiFiClient.h', + 'WiFiServer.h', 'WiFiType.h', 'esp_wifi.h', + }) + def _resolve_library_components( self, ext_headers: list[str], @@ -673,12 +684,22 @@ class ESPIDFCompiler: else None ) + # Tracks the "resolved to a core lib that's already compiled into + # the arduino-esp32 component" case, so we don't fall through to + # the scary "not found — build may fail" warning below for a + # header that WAS found (just not as a mergeable user lib). + is_core_provided = False + if src_root is None and esp32_libs and esp32_libs.is_dir(): esp32_root = self._find_library_for_header(header, esp32_libs) if esp32_root: lib_name = esp32_root.parent.name if esp32_root.name == 'src' else esp32_root.name if lib_name in self._CORE_ESP32_LIBS: - logger.debug(f'[espidf] <{header}> is bundled core lib "{lib_name}", skipping') + is_core_provided = True + logger.info( + f'[espidf] <{header}> provided by arduino-esp32 core ' + f'("{lib_name}") — already compiled in, not merging' + ) else: logger.info(f'[espidf] <{header}> found in esp32_libs as "{lib_name}", merging') src_root = esp32_root @@ -742,6 +763,15 @@ class ESPIDFCompiler: headers_to_resolve.append(th) except OSError: pass + elif is_core_provided or header in self._CORE_ESP32_HEADERS: + # Resolved to an arduino-esp32 core lib, or a known core + # header that lives inside the core (not a standalone lib + # dir). Already compiled in — not a "build may fail" case. + if header in self._CORE_ESP32_HEADERS: + logger.info( + f'[espidf] <{header}> is an arduino-esp32 core header — ' + f'already compiled in, not merging' + ) else: logger.warning(f'[espidf] Library for <{header}> not found — build may fail') diff --git a/frontend/src/components/simulator/SimulatorCanvas.tsx b/frontend/src/components/simulator/SimulatorCanvas.tsx index 5dcca89b..9b8efb5d 100644 --- a/frontend/src/components/simulator/SimulatorCanvas.tsx +++ b/frontend/src/components/simulator/SimulatorCanvas.tsx @@ -2154,10 +2154,23 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => { 'http://localhost:8001/api'; const gatewayUrl = `${backendBase}/gateway/${clientId}/`; + const openGateway = () => { + if (!hasIp) 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 + // an in-place upgrade modal), so we don't open the tab. + // OSS builds have no hook → always open. + const gate = (window as unknown as { + __velxio_iot_gateway_open_gate__?: () => boolean; + }).__velxio_iot_gateway_open_gate__; + if (gate && gate()) return; + window.open(gatewayUrl, '_blank'); + }; return ( hasIp && window.open(gatewayUrl, '_blank')} + onClick={openGateway} title={ hasIp ? `WiFi: ${activeBoard.wifiStatus.ssid ?? 'Velxio-GUEST'} — IP: ${activeBoard.wifiStatus.ip}\nClick to open IoT Gateway ↗`