feat(site): link the live demo + serve the gerber demo from the versioned CDN

Refer to demo.pcbjam.com from the landing: an accent "Live demo" link in the
header nav, an "Open the live editor" hero link, a note in the gerber section,
and a "Try the live demo now" line in the final CTA.

Rework the embedded gerber viewer (public/gerber-demo/boot.js) to source the
WASM from the deploy pipeline's versioned CDN instead of the hand-synced
assets.pcbjam.com bucket: resolve gerbview's content-addressed folder at runtime
from the release manifest (manifest-latest -> tag -> manifest-<tag> -> gerbview),
load all assets (glue + wasm + images.tar.gz) from cdn.pcbjam.com, and load the
cross-origin pthread worker via a same-origin blob importScripts shim (mirrors
web/standalone/src/wasm/boot.ts). Bump the config-seed KICAD_VERSION_DIR
9.99 -> 10.0 to match the deployed build. Drop the now-obsolete committed glue
mirror and the old assets.pcbjam.com sync/r2-deploy scripts.

Collapse the header nav to the hamburger below 1025px — the added demo link no
longer fits the 1024px-capped bar on a single row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gergő Törcsvári 2026-07-01 20:06:51 +02:00 • committed by Viktor Vaczi
commit 9b41d7321f
15 changed files with 407 additions and 17539 deletions

View file

@ -1,38 +1,65 @@
# Gerber viewer blog demo
# Gerber viewer demo
KiCad's `gerbview` compiled to WebAssembly, embedded (lazily) in the blog post.
Click → it streams the WASM and renders the bundled tiny_tapeout board in-browser.
KiCad's `gerbview` compiled to WebAssembly, embedded (lazily) in the landing page
and the blog post. Click → it streams the WASM and renders the bundled
tiny_tapeout board in-browser.
The WASM is **not** kept here — `boot.js` loads it from the versioned CDN
(`cdn.pcbjam.com`), the same artifacts the demo/app deploy publishes. It resolves
gerbview's immutable, content-addressed folder at runtime from the release
manifest, so this page always shows the **latest deployed** gerbview with no
manual sync:
```
manifest-latest.json -> { tag }
manifest-<tag>.json -> tools.gerbview -> <ver>
base = https://cdn.pcbjam.com/wasm/gerbview/<ver>
```
See `docs/features/demo-deploy/0001-wasm-cdn-versioning.md` (in `pcbjam-private`)
for the CDN layout, manifest shapes, and header matrix.
## Files here (`site/public/gerber-demo/`)
| Path | What it does |
|------|--------------|
| `index.html` | The iframe target — minimal page with the `#main-window` / `#window-container` the WASM needs. |
| `boot.js` | Boot harness: configures Emscripten `Module`, seeds KiCad config, preloads the board into MEMFS, auto-opens it via `Module.arguments`, and injects `wx.js → wx-dom.js → gerbview.js`. Holds `R2_BASE` + the dev/prod asset-base switch. |
| `index.html` | The iframe/standalone target — minimal page with the `#main-window` / `#window-container` the WASM needs. |
| `boot.js` | Boot harness: resolves gerbview's CDN folder from the manifest, configures Emscripten `Module`, seeds KiCad config, preloads the board into MEMFS, auto-opens it via `Module.arguments`, and injects `wx.js → wx-dom.js → gerbview.js` from the CDN. |
| `board/` | The tiny_tapeout Gerber layers (committed) the demo opens. |
| `poster.png` | Static fallback shown to browsers that can't run the live viewer. |
| `wasm/` | `wx.js`/`wx-dom.js`/`gerbview.js` (committed, served same-origin) + `gerbview.wasm`/`kicad-resources.bin` (git-ignored; served from R2 in prod, local in dev). Synced from `/output`. |
The folder is cross-origin to this page (which is COEP `require-corp`); the CDN
sends `Cross-Origin-Resource-Policy: cross-origin` + `Access-Control-Allow-Origin: *`,
and the cross-origin pthread worker is loaded via a same-origin `blob:`
`importScripts` shim (`new Worker(<cross-origin URL>)` is a SecurityError). This
mirrors the standalone editor's `web/standalone/src/wasm/boot.ts`.
## Related pieces (elsewhere in `site/`)
| Path | What it does |
|------|--------------|
| `src/components/GerberDemo.astro` | The embed: lazy click-to-load iframe, cross-origin-isolation reload guard, feature-detect + poster fallback. |
| `src/content/blog/porting-kicad-graphics-to-webgl-with-claude.mdx` | The post that renders `<GerberDemo />`. |
| `src/sections/GerberDemoSection.astro` | The landing-page showcase: a poster + launch button that opens `/gerber-demo/` in a new tab (the landing itself is not cross-origin isolated). |
| `src/components/GerberDemo.astro` | The blog embed: lazy click-to-load iframe, cross-origin-isolation reload guard, feature-detect + poster fallback. |
| `astro.config.mjs` + `src/middleware.ts` | Dev cross-origin-isolation headers (COOP/COEP `require-corp`). |
| `vercel.json` | Prod COOP/COEP, scoped to the post + `/gerber-demo/` routes. |
| `scripts/sync-demo-wasm.sh` | Copy fresh WASM from `/output` into `wasm/` (run after a gerbview rebuild). |
| `scripts/r2-deploy.sh` + `scripts/r2-cors.json` | Upload the heavy binaries to Cloudflare R2 (`pcbjam-assets` → `assets.pcbjam.com`) and set CORS. |
| `vercel.json` | Prod COOP/COEP, scoped to the blog post + `/gerber-demo/` routes. |
## Run / update
## Dev overrides
```bash
# local dev (serves wasm/ from the local mirror)
scripts/sync-demo-wasm.sh && npm run dev # /blog/porting-kicad-graphics-to-webgl-with-claude
`boot.js` reads query params so you can point it elsewhere without a rebuild:
# after a new gerbview build: refresh local mirror, then push binaries to R2
scripts/sync-demo-wasm.sh && scripts/r2-deploy.sh
```
| Param | Effect |
|-------|--------|
| `?tag=<tag>` | Pin a specific release instead of following `manifest-latest.json`. |
| `?cdn=<root>` | Swap the CDN root (e.g. a local mirror serving `manifest-*.json` + `gerbview/<ver>/`). |
| `?base=<folder>` | Use a tool folder verbatim (e.g. a fresh local build) — skips manifest resolution. |
The live viewer needs SharedArrayBuffer + WebGL2 (Chrome/Edge/Firefox, Safari 15.2+);
other browsers get `poster.png`.
other browsers get `poster.png`.
## Updating `KICAD_VERSION_DIR`
`boot.js` seeds KiCad config under a version dir (currently `"10.0"`) to suppress
the first-run wizard. It **must** match the deployed build's
`GetMajorMinorVersion()`. If a future deploy bumps KiCad's major.minor, update the
`KICAD_VERSION_DIR` constant in `boot.js` (same coupling as
`web/standalone/src/wasm/constants.ts`).