From 375b952d472c3072e92076f410c1744614efdae6 Mon Sep 17 00:00:00 2001 From: David Montero Date: Mon, 15 Jun 2026 15:54:44 +0200 Subject: [PATCH] refactor(examples): move Pico W WiFi examples to the pro overlay seam The Pico W WiFi showcase examples are a paid-overlay feature now (the WiFi engine moved to the overlay). Read them through a build-time `@pro` seam so the SSR prerender + gallery + sitemap include them when built with the overlay, and OSS gets an empty stub. - data/examples.ts: import { proExamples } from '@pro/data/proExamples' (static, build-time) instead of the local examples-picow-wifi.ts; delete that file. - src/__pro_stub__/data/proExamples.ts: OSS no-op (empty list) for the @pro alias. - vitest.config.ts: mirror the @pro alias (stub by default / overlay when VITE_PRO_BUILD) so tests loading examples.ts resolve it. - scripts/generate-sitemap.mjs: also parse /data/proExamples.ts when building with the overlay (the script reads example IDs from source text, so it can't follow the alias). - Tests: drop the picow-wifi import/usage from the 5 OSS example tests (they validate the OSS set now); prune the 4 obsolete picow netlist snapshots. The overlay's proExamples get their own coverage in pro/.../__tests__/. --- frontend/scripts/generate-sitemap.mjs | 17 + frontend/src/__pro_stub__/data/proExamples.ts | 8 + .../examples-netlist-snapshot.test.ts.snap | 29 -- .../__tests__/board-kinds-coverage.test.ts | 2 - .../components-metadata-integrity.test.ts | 2 - .../examples-all-buckets-smoke.test.ts | 2 - .../examples-netlist-snapshot.test.ts | 2 - .../library-compile.integration.test.ts | 2 - frontend/src/data/examples-picow-wifi.ts | 368 ------------------ frontend/src/data/examples.ts | 9 +- frontend/vitest.config.ts | 11 + 11 files changed, 43 insertions(+), 409 deletions(-) create mode 100644 frontend/src/__pro_stub__/data/proExamples.ts delete mode 100644 frontend/src/data/examples-picow-wifi.ts diff --git a/frontend/scripts/generate-sitemap.mjs b/frontend/scripts/generate-sitemap.mjs index b591f55d..fd6f7fe6 100644 --- a/frontend/scripts/generate-sitemap.mjs +++ b/frontend/scripts/generate-sitemap.mjs @@ -57,6 +57,23 @@ const exampleIds = [ ...parseExampleIds(circuitSource), ]; +// Pro overlay examples (e.g. the Pico W WiFi showcase) live in +// /data/proExamples.ts and are spread into examples.ts via the +// `@pro` alias at build time. This script parses example IDs from source TEXT +// (it never executes the module), so it can't follow the alias — read the +// overlay file directly when building with the overlay. OSS builds skip this. +if (process.env.VITE_PRO_BUILD && process.env.PRO_OVERLAY_PATH) { + try { + const proSource = readFileSync( + resolve(process.env.PRO_OVERLAY_PATH, 'data/proExamples.ts'), + 'utf-8', + ); + exampleIds.push(...parseExampleIds(proSource)); + } catch { + // No overlay examples file — nothing to add. + } +} + const exampleUrls = exampleIds.map((id) => ({ loc: `${DOMAIN}/examples/${id}`, lastmod: TODAY, diff --git a/frontend/src/__pro_stub__/data/proExamples.ts b/frontend/src/__pro_stub__/data/proExamples.ts new file mode 100644 index 00000000..8bed4f1a --- /dev/null +++ b/frontend/src/__pro_stub__/data/proExamples.ts @@ -0,0 +1,8 @@ +// Open-source no-op stub for `@pro/data/proExamples` (see vite.config.ts). +// OSS builds ship NO pro examples — the Pico W WiFi showcase is a paid overlay +// feature, so the velxio-prod overlay replaces this with the real list at build +// time (VITE_PRO_BUILD=true + PRO_OVERLAY_PATH). The static import in +// data/examples.ts resolves here in OSS, contributing an empty spread. +import type { ExampleProject } from '../../data/examples'; + +export const proExamples: ExampleProject[] = []; diff --git a/frontend/src/__tests__/__snapshots__/examples-netlist-snapshot.test.ts.snap b/frontend/src/__tests__/__snapshots__/examples-netlist-snapshot.test.ts.snap index 3a665487..5d314957 100644 --- a/frontend/src/__tests__/__snapshots__/examples-netlist-snapshot.test.ts.snap +++ b/frontend/src/__tests__/__snapshots__/examples-netlist-snapshot.test.ts.snap @@ -3449,32 +3449,3 @@ R_autopull_n5 n5 0 100Meg .op .end" `; - -exports[`netlist snapshot — picow-wifi (4 examples) > picow-wifi-async-led 1`] = ` -"* Velxio circuit -.op -.end" -`; - -exports[`netlist snapshot — picow-wifi (4 examples) > picow-wifi-relay-web-server 1`] = ` -"* Velxio circuit -V_relay-led_sense n0 relay-led_sense_mid DC 0 -D_relay-led relay-led_sense_mid n1 LED_RED -R_autopull_n0 n0 0 100Meg -R_autopull_n1 n1 0 100Meg -.model LED_RED D(Is=1e-20 N=1.7) -.op -.end" -`; - -exports[`netlist snapshot — picow-wifi (4 examples) > picow-wifi-servo-web 1`] = ` -"* Velxio circuit -.op -.end" -`; - -exports[`netlist snapshot — picow-wifi (4 examples) > picow-wifi-websocket-led 1`] = ` -"* Velxio circuit -.op -.end" -`; diff --git a/frontend/src/__tests__/board-kinds-coverage.test.ts b/frontend/src/__tests__/board-kinds-coverage.test.ts index 0cffd37d..6c92b336 100644 --- a/frontend/src/__tests__/board-kinds-coverage.test.ts +++ b/frontend/src/__tests__/board-kinds-coverage.test.ts @@ -21,7 +21,6 @@ import { analogExamples } from '../data/examples-analog'; import { digitalExamples } from '../data/examples-digital'; import { hundredDaysExamples } from '../data/examples-100-days'; import { epaperExamples } from '../data/examples-displays-epaper'; -import { picowWifiExamples } from '../data/examples-picow-wifi'; import { circuitExamples } from '../data/examples-circuits'; import type { ExampleProject } from '../data/examples'; @@ -30,7 +29,6 @@ const ALL_EXAMPLES: ExampleProject[] = [ ...digitalExamples, ...hundredDaysExamples, ...epaperExamples, - ...picowWifiExamples, ...circuitExamples, ]; diff --git a/frontend/src/__tests__/components-metadata-integrity.test.ts b/frontend/src/__tests__/components-metadata-integrity.test.ts index daeea6e6..dd5f3c21 100644 --- a/frontend/src/__tests__/components-metadata-integrity.test.ts +++ b/frontend/src/__tests__/components-metadata-integrity.test.ts @@ -25,7 +25,6 @@ import { analogExamples } from '../data/examples-analog'; import { digitalExamples } from '../data/examples-digital'; import { hundredDaysExamples } from '../data/examples-100-days'; import { epaperExamples } from '../data/examples-displays-epaper'; -import { picowWifiExamples } from '../data/examples-picow-wifi'; import { circuitExamples } from '../data/examples-circuits'; import { stripBrandPrefix, @@ -62,7 +61,6 @@ const ALL_EXAMPLE_SOURCES = { digital: digitalExamples, '100-days': hundredDaysExamples, 'epaper-displays': epaperExamples, - 'picow-wifi': picowWifiExamples, circuits: circuitExamples, }; diff --git a/frontend/src/__tests__/examples-all-buckets-smoke.test.ts b/frontend/src/__tests__/examples-all-buckets-smoke.test.ts index a2be7cc6..eef58c66 100644 --- a/frontend/src/__tests__/examples-all-buckets-smoke.test.ts +++ b/frontend/src/__tests__/examples-all-buckets-smoke.test.ts @@ -25,7 +25,6 @@ import { describe, it, expect } from 'vitest'; import { hundredDaysExamples } from '../data/examples-100-days'; import { epaperExamples } from '../data/examples-displays-epaper'; -import { picowWifiExamples } from '../data/examples-picow-wifi'; import { circuitExamples } from '../data/examples-circuits'; import { exampleToBuildNetlistInput } from '../utils/exampleToBuildNetlistInput'; import { solveInput } from './helpers/solveInput'; @@ -91,5 +90,4 @@ function buildSuite(bucket: string, examples: ExampleProject[]): void { buildSuite('100-days', hundredDaysExamples); buildSuite('epaper-displays', epaperExamples); -buildSuite('picow-wifi', picowWifiExamples); buildSuite('circuits', circuitExamples); diff --git a/frontend/src/__tests__/examples-netlist-snapshot.test.ts b/frontend/src/__tests__/examples-netlist-snapshot.test.ts index 88daa00d..a561c013 100644 --- a/frontend/src/__tests__/examples-netlist-snapshot.test.ts +++ b/frontend/src/__tests__/examples-netlist-snapshot.test.ts @@ -26,7 +26,6 @@ import { analogExamples } from '../data/examples-analog'; import { digitalExamples } from '../data/examples-digital'; import { hundredDaysExamples } from '../data/examples-100-days'; import { epaperExamples } from '../data/examples-displays-epaper'; -import { picowWifiExamples } from '../data/examples-picow-wifi'; import { circuitExamples } from '../data/examples-circuits'; import { exampleToBuildNetlistInput } from '../utils/exampleToBuildNetlistInput'; import { buildNetlist } from '../simulation/spice/NetlistBuilder'; @@ -62,7 +61,6 @@ const BUCKETS: Bucket[] = [ { name: 'digital', examples: digitalExamples }, { name: '100-days', examples: hundredDaysExamples }, { name: 'epaper', examples: epaperExamples }, - { name: 'picow-wifi', examples: picowWifiExamples }, { name: 'circuits', examples: circuitExamples }, ]; diff --git a/frontend/src/__tests__/library-compile.integration.test.ts b/frontend/src/__tests__/library-compile.integration.test.ts index 35af1ee2..e6606ff5 100644 --- a/frontend/src/__tests__/library-compile.integration.test.ts +++ b/frontend/src/__tests__/library-compile.integration.test.ts @@ -27,7 +27,6 @@ import { analogExamples } from '../data/examples-analog'; import { digitalExamples } from '../data/examples-digital'; import { hundredDaysExamples } from '../data/examples-100-days'; import { epaperExamples } from '../data/examples-displays-epaper'; -import { picowWifiExamples } from '../data/examples-picow-wifi'; import { circuitExamples } from '../data/examples-circuits'; import type { ExampleProject } from '../data/examples'; import { BOARD_KIND_FQBN, type BoardKind } from '../types/board'; @@ -39,7 +38,6 @@ const ALL_EXAMPLES: ExampleProject[] = [ ...digitalExamples, ...hundredDaysExamples, ...epaperExamples, - ...picowWifiExamples, ...circuitExamples, ]; diff --git a/frontend/src/data/examples-picow-wifi.ts b/frontend/src/data/examples-picow-wifi.ts deleted file mode 100644 index bc6fe36a..00000000 --- a/frontend/src/data/examples-picow-wifi.ts +++ /dev/null @@ -1,368 +0,0 @@ -/** - * Pico W WiFi showcase — curated examples that highlight Velxio's - * new CYW43439 chip emulation (see frontend/src/simulation/cyw43/). - * - * Source projects come from - * https://github.com/KritishMohapatra/100_Days_100_IoT_Projects - * (cloned at third-party/100_Days_100_IoT_Projects/) — every example - * here is a verbatim copy of the upstream `Main Files/main.py` for the - * matching project, with the WiFi credentials replaced by the synthetic - * `Velxio-GUEST` AP that the emulator advertises. - * - * Each example is wired up to load with: - * boardType: 'pi-pico-w' ← triggers Cyw43Bridge attachment - * languageMode: 'micropython' ← MicroPythonLoader - * files: [{ name: 'main.py', ... }] - * - * The end-to-end harness that proves these work end-to-end lives at - * test/test_Raspberry_Pi_Pico_W/test_code/tests/07_picow_iot_projects.test.ts - */ - -import type { ExampleProject } from './examples'; - -const TAGS_WIFI = ['100-days', 'pi-pico-w', 'micropython', 'wifi', 'cyw43']; - -/** Replace placeholder SSID/password lines with our virtual AP. */ -function withVelxioGuest(source: string): string { - return source - .replace(/SSID\s*=\s*"[^"]*"/g, 'SSID = "Velxio-GUEST"') - .replace(/ssid\s*=\s*"[^"]*"/g, 'ssid = "Velxio-GUEST"') - .replace(/WIFI_SSID\s*=\s*"[^"]*"/g, 'WIFI_SSID = "Velxio-GUEST"') - .replace(/PASSWORD\s*=\s*"[^"]*"/g, 'PASSWORD = ""') - .replace(/password\s*=\s*"[^"]*"/g, 'password = ""') - .replace(/PASS\s*=\s*"[^"]*"/g, 'PASS = ""') - .replace(/WIFI_PASSWORD\s*=\s*"[^"]*"/g, 'WIFI_PASSWORD = ""'); -} - -// ─── Project sources ───────────────────────────────────────────────── -// -// Pasted from upstream `Main Files/main.py`. Comments at the top of each -// string credit the original author/repo. Anyone can re-extract these by -// running `python test/test_100_days/_emit_examples_data.py`. - -const ASYNC_LED_CONTROL_PY = withVelxioGuest(`# Pico W Async LED Control — MicroPython -# Source: github.com/KritishMohapatra/100_Days_100_IoT_Projects -# Project: Pico_W_Async_LED_Control_(MicroPython) -# -# Connects to Velxio-GUEST and runs a tiny async HTTP server on :80 -# that toggles the on-board LED via Pin('LED'). The LED is wired -# through the CYW43439 chip — Velxio's new emulator picks up the -# gpioout IOCTL and drives the LED in the canvas. - -import uasyncio as asyncio -import network -from machine import Pin - -SSID = "Velxio-GUEST" -PASSWORD = "" - -led = Pin("LED", Pin.OUT) -led.off() - -async def wifi_connect(): - wlan = network.WLAN(network.STA_IF) - wlan.active(True) - wlan.connect(SSID, PASSWORD) - print("Connecting WiFi...") - while not wlan.isconnected(): - await asyncio.sleep(0.5) - ip = wlan.ifconfig()[0] - print("IP:", ip) - print("Open browser: http://%s/" % ip) - -led_on = False - -def page(): - # The board's onboard LED isn't drawn on the canvas, so show a big - # state indicator here; the buttons reload so you can see it flip. - color = "#22c55e" if led_on else "#666" - label = "LED ON" if led_on else "LED OFF" - return ("" - "" - "" - "

Pico W Onboard LED

" - "
%s
" - "" - "" - "") % (color, label) - -async def handle_client(reader, writer): - global led_on - request = (await reader.readline()).decode() - while await reader.readline() != b"\\r\\n": - pass - if "GET /on" in request: - led.on(); led_on = True - elif "GET /off" in request: - led.off(); led_on = False - writer.write(("HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\n\\r\\n" + page()).encode()) - await writer.drain() - await writer.wait_closed() - -async def main(): - await wifi_connect() - await asyncio.start_server(handle_client, "0.0.0.0", 80) - print("Server running on port 80") - while True: - await asyncio.sleep(1) - -asyncio.run(main()) -`); - -const RELAY_WEB_SERVER_PY = withVelxioGuest(`# IoT Relay Control Web Server — MicroPython -# Source: github.com/KritishMohapatra/100_Days_100_IoT_Projects -# Project: IoT_Relay_Control_Web_Server_(Raspberry_Pi_Pico_2W) -# -# A blocking-style HTTP server on port 80 that flips a relay on -# GP2 in response to GET /on and GET /off. The server runs on the -# IP that Velxio's emulator hands out (10.13.37.42). - -import network -import socket -import machine -import time - -relay = machine.Pin(2, machine.Pin.OUT) -# Active-high so the LED wired to GP2 on the canvas lights when ON. -relay_state = 0 -relay.value(relay_state) - -ssid = "Velxio-GUEST" -password = "" - -wlan = network.WLAN(network.STA_IF) -wlan.active(True) -wlan.connect(ssid, password) - -print("Connecting...") -while not wlan.isconnected(): - time.sleep(0.5) - -ip = wlan.ifconfig()[0] -print("Connected at:", ip) - -s = socket.socket() -s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -s.bind((ip, 80)) -s.listen(1) - -print("Open browser: http://%s/" % ip) - -def page(): - on = (relay_state == 1) - color = "#22c55e" if on else "#666" - label = "RELAY ON" if on else "RELAY OFF" - return ("" - "" - "

Pico W Relay (GP2)

" - "
%s
" - "" - "" - "") % (color, label) - -# Wrap each client in try/except: a browser that drops the connection -# mid-response would otherwise raise ECONNRESET and kill the server loop. -while True: - try: - conn, addr = s.accept() - request = str(conn.recv(1024)) - if "/on" in request: - relay_state = 1; relay.value(relay_state) - elif "/off" in request: - relay_state = 0; relay.value(relay_state) - conn.send("HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\n\\r\\n" + page()) - conn.close() - except OSError: - try: - conn.close() - except Exception: - pass -`); - -const SERVO_WEB_PY = withVelxioGuest(`# Pico W Web Servo Controller — MicroPython -# Source: github.com/KritishMohapatra/100_Days_100_IoT_Projects -# Project: Pico_W_Web_Servo_Controller - -import network -import socket -from machine import Pin, PWM -import time - -ssid = "Velxio-GUEST" -password = "" - -print("Connecting to WiFi...") -sta = network.WLAN(network.STA_IF) -sta.active(True) -sta.connect(ssid, password) -while not sta.isconnected(): - time.sleep(0.5) -print("Connected!", sta.ifconfig()[0]) - -servo = PWM(Pin(15), freq=50) - -def write_servo(angle): - angle = max(0, min(180, angle)) - pulse_us = 500 + (2500 - 500) * (angle / 180) - duty = int((pulse_us / 20000) * 65535) - servo.duty_u16(duty) - -def webpage(pos): - return ("

Servo {p}°

" - "").format(p=pos) - -s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -s.bind(('', 80)) -s.listen(5) - -current_pos = 90 -write_servo(current_pos) -print("Web server started. Open browser: http://%s/" % sta.ifconfig()[0]) - -while True: - conn, addr = s.accept() - request = conn.recv(1024).decode('utf-8') - if "GET /?value=" in request: - try: - v = int(request.split("/?value=")[1].split(" ")[0]) - if 0 <= v <= 180: - current_pos = v; write_servo(v) - except (ValueError, IndexError): - pass - conn.send('HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\n\\r\\n') - conn.sendall(webpage(current_pos).encode('utf-8')) - conn.close() -`); - -const WS_LED_PY = withVelxioGuest(`# WebSocket LED Control — MicroPython -# Source: github.com/KritishMohapatra/100_Days_100_IoT_Projects -# Project: WebSocket_LED_Control_using_Raspberry_Pi_Pico_W - -import socket, network, time, ubinascii, uhashlib -from machine import Pin - -led = Pin(15, Pin.OUT); led.value(0) - -SSID = "Velxio-GUEST" -PASSWORD = "" - -def ws_accept(key): - GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - return ubinascii.b2a_base64(uhashlib.sha1((key + GUID).encode()).digest()).strip().decode() - -def ws_decode(data): - if len(data) < 6: return "" - payload_len = data[1] & 127 - mask = data[2:6] - payload = data[6:6 + payload_len] - return bytes(payload[i] ^ mask[i % 4] for i in range(len(payload))).decode() - -wlan = network.WLAN(network.STA_IF); wlan.active(True); wlan.connect(SSID, PASSWORD) -while not wlan.isconnected(): - time.sleep(1) -print("Connected!", wlan.ifconfig()[0]) - -server = socket.socket() -server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -server.bind(("0.0.0.0", 80)); server.listen(1) - -while True: - conn, addr = server.accept() - try: - raw = conn.recv(1024).decode() - if "Sec-WebSocket-Key" in raw: - key = raw.split("Sec-WebSocket-Key: ")[1].split("\\r\\n")[0].strip() - conn.send(("HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\n" - "Connection: Upgrade\\r\\nSec-WebSocket-Accept: %s\\r\\n\\r\\n" % ws_accept(key))) - while True: - data = conn.recv(1024) - if not data: break - msg = ws_decode(data) - if msg == "ON": led.value(1); reply = "LED IS ON" - elif msg == "OFF": led.value(0); reply = "LED IS OFF" - else: reply = "OK" - conn.send(bytearray([0x81, len(reply)]) + reply.encode()) - finally: - conn.close() -`); - -// ─── Curated entries ───────────────────────────────────────────────── - -export const picowWifiExamples: ExampleProject[] = [ - { - id: 'picow-wifi-async-led', - title: 'Pico W — Async LED control over Wi-Fi', - description: - 'Pico W joins Velxio-GUEST then runs an async HTTP server on :80. /on and /off toggle the on-board LED through the CYW43 chip — the same path the real driver takes.', - category: 'communication', - difficulty: 'beginner', - boardType: 'pi-pico-w', - languageMode: 'micropython', - files: [{ name: 'main.py', content: ASYNC_LED_CONTROL_PY }], - code: '', - components: [], - wires: [], - tags: TAGS_WIFI, - }, - { - id: 'picow-wifi-relay-web-server', - title: 'Pico W — IoT relay web server', - description: - 'Blocking HTTP server that drives a relay on GP2. Hit http://10.13.37.42/on and /off to flip it. Source: 100_Days_100_IoT_Projects/IoT_Relay_Control_Web_Server.', - category: 'communication', - difficulty: 'beginner', - boardType: 'pi-pico-w', - languageMode: 'micropython', - files: [{ name: 'main.py', content: RELAY_WEB_SERVER_PY }], - code: '', - components: [ - // A red LED wired to GP2 so the relay state is visible on the canvas: - // GP2 HIGH (relay ON) lights it. Driven via the wire (anode->GP2, - // cathode->GND), the same path an Arduino LED uses. - { type: 'wokwi-led', id: 'relay-led', x: 380, y: 40, properties: { color: 'red', pin: 2 } }, - ], - wires: [ - { id: 'w-relay-a', start: { componentId: 'pi-pico-w', pinName: 'GP2' }, end: { componentId: 'relay-led', pinName: 'A' }, color: '#ef4444' }, - { id: 'w-relay-c', start: { componentId: 'relay-led', pinName: 'C' }, end: { componentId: 'pi-pico-w', pinName: 'GND.1' }, color: '#1f2937' }, - ], - tags: TAGS_WIFI.concat(['relay']), - }, - { - id: 'picow-wifi-servo-web', - title: 'Pico W — Web servo controller', - description: - 'Drives a servo on GP15 from a slider on a web page served by the Pico W. Source: 100_Days_100_IoT_Projects/Pico_W_Web_Servo_Controller.', - category: 'robotics', - difficulty: 'intermediate', - boardType: 'pi-pico-w', - languageMode: 'micropython', - files: [{ name: 'main.py', content: SERVO_WEB_PY }], - code: '', - components: [], - wires: [], - tags: TAGS_WIFI.concat(['servo']), - }, - { - id: 'picow-wifi-websocket-led', - title: 'Pico W — WebSocket-controlled LED', - description: - 'Hand-rolled WebSocket server. Browser opens an upgrade request, then sends "ON"/"OFF" frames to toggle the LED on GP15. Source: 100_Days_100_IoT_Projects/WebSocket_LED_Control_using_Raspberry_Pi_Pico_W.', - category: 'communication', - difficulty: 'advanced', - boardType: 'pi-pico-w', - languageMode: 'micropython', - files: [{ name: 'main.py', content: WS_LED_PY }], - code: '', - components: [], - wires: [], - tags: TAGS_WIFI.concat(['websocket']), - }, -]; diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index f427666f..57b39ace 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -8,7 +8,12 @@ import { circuitExamples } from './examples-circuits'; import { analogExamples } from './examples-analog'; import { digitalExamples } from './examples-digital'; import { hundredDaysExamples } from './examples-100-days'; -import { picowWifiExamples } from './examples-picow-wifi'; +// Pro overlay examples (the Pico W WiFi showcase) — resolves to the real list +// when built with the overlay (VITE_PRO_BUILD), else an empty stub in OSS. +// Static import so the build-time SSR prerender + gallery + sitemap pick them +// up. See pro/frontend/src/pro/data/proExamples.ts (overlay) and +// src/__pro_stub__/data/proExamples.ts (OSS no-op). +import { proExamples } from '@pro/data/proExamples'; import { epaperExamples } from './examples-displays-epaper'; import { retroIntelExamples } from './examples-retro-intel'; import { robotDesktopExamples } from './examples-robot-desktop'; @@ -9735,7 +9740,7 @@ export const exampleProjects: ExampleProject[] = [ ...analogExamples, ...digitalExamples, ...hundredDaysExamples, - ...picowWifiExamples, + ...proExamples, ...epaperExamples, ...retroIntelExamples, ...robotDesktopExamples, diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index cc41fdab..d4f1e013 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -22,11 +22,22 @@ import path from 'path'; * used by overlay tests (e.g. pro/.../snapshot.test.ts importing * `@velxio/store/useEditorStore`) must be declared here too or * test files explode with "Cannot find package '@velxio/...'". + * - `@pro` likewise mirrors vite.config.ts: it resolves to the OSS no-op + * stub by default, or the real overlay when VITE_PRO_BUILD + + * PRO_OVERLAY_PATH are set. data/examples.ts statically imports + * `@pro/data/proExamples`, so without this alias every test that loads + * examples.ts would explode with "Cannot find package '@pro/...'". */ +const proOverlayPath = + process.env.VITE_PRO_BUILD && process.env.PRO_OVERLAY_PATH + ? path.resolve(process.env.PRO_OVERLAY_PATH) + : path.resolve(__dirname, 'src/__pro_stub__'); + export default defineConfig({ resolve: { alias: { '@velxio': path.resolve(__dirname, 'src'), + '@pro': proOverlayPath, }, }, // Allow vitest to import test files / sources from outside this