Vercel was doing the apex->www 308 itself (its "redirect to www" project
setting), so nothing about Cloudflare requires a redirect — the behaviour
just disappears with Vercel. Rather than rebuild it with a zone Redirect
Rule plus a proxied placeholder record, attach pcbjam.com as a SECOND
custom domain on pcbjam-site. Both hosts serve the site and the pages
already emit canonical=www, which is what consolidates them for search.
That drops the riskiest artefact in the migration. Redirect Rules are
zone-scoped and run BEFORE Workers/Pages routing, so a `contains` match
instead of `eq` would 308 app./editor./demo./api. to www — breaking the
product API, not just a marketing page. The sibling hosts are also the
reason this was worth avoiding rather than merely guarding.
APEX_MODE (lib/common.sh) selects the topology, defaulting to `serve`.
08-verify-prod.sh now dispatches through assert_apex: in serve mode it
requires the apex to answer 200 with no hop, to not be a stale Vercel
response, to declare canonical=www, and to expose /api/waitlist. The
`redirect` mode and 07's rules/apex phases are kept for the alternative.
08 also checks the attached domains via wrangler rather than the REST API,
so the whole serve-mode path needs only `wrangler login` — no zone scopes
at all.
Comments that explained themselves via the old redirect are corrected:
astro.config.mjs, web/standalone/src/lib/config.ts and
scripts/deploy/build-demo.mjs. The demo keeps posting to www — not because
the apex redirects, but because a CORS preflight cannot follow one, so
aiming at a host that might ever redirect is a latent breakage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAmkjM7okPdScp9XLW1JVr
All pcbjam properties now load the same new-format Plausible script
(https://plausible.io/js/pa-KjNS9YmidydULZTstsjRg.js — one dashboard,
segment by hostname) instead of per-domain data-domain sites:
- site: replace Vercel Analytics with the pa- snippet in BaseLayout
(is:inline, prod-only, crossorigin="anonymous" for the COEP
require-corp routes in vercel.json); drop @vercel/analytics dep.
Legal pages (cookies/privacy) rewritten Vercel -> Plausible.
- standalone: initAnalytics() rewritten for the new script format
(queue shim + plausible.init(), async, no data-domain). The gate is
now VITE_PLAUSIBLE_SRC (script URL, deploy-time opt-in) — unset means
no tracking, so dev checkouts and third-party GPL builds stay clean.
VITE_PLAUSIBLE_DOMAIN is gone.
- deploy: build-demo/build-editor --plausible now takes the script URL;
release.yml/deploy-demo.yml hardcode the (public) pa- URL, so the
repo Actions variable PLAUSIBLE_DOMAIN is no longer used.
Verified: standalone tsc + vite build (pa- URL present with env, absent
without, crossOrigin kept); astro build has the snippet on all layout
pages and no vercel/insights references; astro dev stays untracked.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The demo (demo.pcbjam.com) cross-posts the waitlist to the marketing site's
serverless endpoint. It targeted the apex pcbjam.com, which 308-redirects to
www on Vercel; a CORS preflight can't follow redirects, so the OPTIONS failed
("Redirect is not allowed for a preflight request") and the POST never landed.
Point the waitlist target at the canonical www host. www.pcbjam.com already
returns 204 with access-control-allow-origin: https://demo.pcbjam.com, so the
preflight passes. Landing/version-badge link stays on the apex.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REwWZGkAz1AZUJw8yqxkCN
- kicad-packages3D is STEP-only from the 10.x tags: ensureModelInMemfs
falls back from a missing .wrl to the same-stem .step, written and
answered under the .step path (the returned path's extension picks
the parsing plugin — no C++ awareness). Verified against the live
CDN with the gallery demo board (4/4 wrl refs served as step).
- upload-models-r2.sh: rclone/S3 bulk upload of a local publish layout
(wrangler-per-object can't move ~14k blobs); published 10.0.3 —
105 libs / 7,238 models, 3.4GB raw → 500MB brotli — to pcbjam-cdn.
- deploy-demo.yml MODELS_TAG=10.0.3 → build-demo --models-tag →
VITE_MODELS_MANIFEST_URL (matches LIB_TAG: model refs come from the
footprints at that release).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014AT7gVHRktDYoQ68S4x6A4
- Waitlist signup form on the home page (between Projects and Tools),
cross-posting to the landing site's /api/waitlist (CORS + OPTIONS added there,
gated by WAITLIST_ALLOWED_ORIGINS).
- Version badge gains a pcbjam.com landing-page link (VITE_LANDING_URL).
- Optional Plausible analytics from VITE_PLAUSIBLE_DOMAIN (off by default);
wired into build-demo + both deploy workflows via vars.PLAUSIBLE_DOMAIN.
- Opening a read-only gallery project auto-"moves to local": it forks into a
writable browser-local project at the same slug, shadowing the gallery row.
- Project + local-folder views use the same iconed tool launcher (ToolGrid)
and a navigable directory FileTree instead of a flat file list.
- Boot overlay shows real wasm download progress (Module.instantiateWasm +
streaming byte counter) and a "taking too long" state after 60s.
- Home Libraries section lists the active libs source (the read-only R2/CDN
set in the demo) with a name filter; useLibs now reads libsSourceConfig.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPJYx4urFVhdMjEmYfMCMD
Backendless, read-only LibsSource that serves the full default symbol+footprint set from versioned immutable CDN snapshots. Each lib is an r2-idb-sync static origin opened as a one-layer SyncStack (1 bundle cold, 0 fetches warm, IDB-cached, offline) — the layer descriptor is built locally from lib id + tag, no resolve endpoint in the loop.
Wired via VITE_LIBS_SOURCE=cdn + VITE_LIBS_MANIFEST_URL; build-demo gains --lib-tag (points the demo at libs/kicad/<tag>/manifest.json; omitted keeps offline static). Unit-tested against a fake CDN built with the real encodeBundle/sha256Hex codecs, pinning the publish format.
Next: publish-libs.mjs (C1.2) emits this format over the full set; CI keyed by lib tag (C1.3).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Loading a folder now imports a COPY into a browser-local IndexedDB project with
its own /p/:slug URL (original disk files untouched): edits persist to IDB across
visits, and the project exports via Download .zip or per-file. Gated by
VITE_LOCAL_PROJECTS=idb (on for the demo; off keeps the File System Access
write-back flow), so it's configurable per deployment.
Modular, swappable project sources behind the shared ProjectSource interface,
each self-describing via a SourceDescriptor whose kind is shown verbatim in the
UI — "Local (this browser)", "Remote · read-only", "Remote · editable" — on the
home page, the project view, and inside the editor, so the user always knows
whether/how Save persists:
- remote → REST backend (remote-rw)
- static → CDN gallery (remote-ro), saves download
- local (new) → idbProjectStore, writable IDB store
A composite layers the local store over the configured remote/gallery source,
routing per slug so imported/saved projects and the gallery share one namespace.
New, dependency-free (matching sync-client's raw-IDB ethos):
- lib/idb-project-store.ts raw IndexedDB store + create/delete/rename/export
- lib/zip.ts store-only ZIP writer (verified via system unzip)
- lib/import-folder.ts FSA/webkitdirectory folder → in-memory bytes
- lib/project-source-shared.ts source descriptors + deterministic uuid
- components/SourceChip, components/LocalProjectsSection
Home lists local projects (open/export/rename/delete). Verified: typecheck,
project-source tests, prod build, and IDB key-range project isolation in-browser
(prefix-colliding slugs don't leak).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A small fixed bottom-right overlay (VersionBadge) shown app-wide — on the home
page AND inside the loaded editor — surfacing this build's release tag and a
link to the source. The tag links to the exact commit when known (the pcbjam
commit pins the kicad + wxwidgets submodule revisions → our GPLv3
corresponding-source pointer, mirroring site Footer's BUILD_SHA), else the tag's
release page, else the repo root.
build-demo.mjs injects VITE_APP_TAG / VITE_GIT_SHA / VITE_REPO_URL (new --repo
flag, defaults to github.com/emergence-engineering/pcbjam).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>