--- name: canvas-webapp description: >- Render a React/Vite (or any bundled) web app on the pi-dashboard canvas, which loads loopback URLs in a sandboxed opaque-origin iframe. Use when a canvas(target:{kind:"url"|"server"}) target shows up blank white, an empty surface, or a /live/ 500 ECONNREFUSED. Covers why Vite dev servers and non-CORS static servers fail there, and the static-build + CORS-server recipe that works. license: MIT --- # canvas-webapp — show a web app on the dashboard canvas The dashboard opens a loopback `canvas(kind:"url")` target inside a `sandbox="allow-scripts"` iframe with **no `allow-same-origin`** (opaque origin), proxied under `/live//` (`LiveServerViewer.tsx` → `live-server-proxy`). That sandbox breaks two common serving strategies; this skill is the fix. ## When to Use Use when you must display a running web app / React / Vite / MUI mockup on the pi-dashboard canvas via `canvas(target:{kind:"url"|"server"})` and it shows up blank white, an empty surface, or a `/live/` 500 ECONNREFUSED. ## Procedure 1. **Do NOT point the canvas at a Vite DEV server.** The dashboard proxies loopback targets under `/live//`, but Vite dev emits ABSOLUTE asset paths (`/main.tsx`, `/@vite/client`, and runtime fetches like `/__schema.json`) that resolve against the dashboard root, not the proxy prefix → 404 → blank page. 2. **Produce a STATIC production build with a RELATIVE base:** a vite config with `base:'./'`, a dedicated `index.html` entry, and the runtime data (schema/props) IMPORTED statically (no fetch of an absolute path). Run `npx vite build --config `. 3. **Verify** the built `index.html` references `./assets/...` (relative). Copy the entry html to `index.html` so the proxy root (`/live//`) serves it. 4. **Serve the dist with a tiny node static server that sets `Access-Control-Allow-Origin: *`** (and `Cross-Origin-Resource-Policy: cross-origin`) on every response. This is REQUIRED: the opaque-origin iframe fetches `