Commit graph pcbjam/site
Author SHA1 Message Date
Gergő Törcsvári
4d75d44a78
site(blog): devblog w31 — crash hunt, faster board load, project page
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjAHi3JhwwDNDgdr3Abkvp
2026-08-04 13:38:34 +02:00
Istvan Matejcsok
1b08a5eb06 feat(editor): report uncaught errors to Better Stack
The editor reported nothing when a session died. Evidence lived only in-tab —
an 800-line React array behind a "Show console" button — so diagnosis meant
asking a user to paste a screenshot.

Better Stack's Error Tracking ingests the Sentry wire protocol, so this runs
the stock @sentry/browser against a Better Stack DSN. Sentry.init installs its
own window error/unhandledrejection handlers, so uncaught main-thread errors
and the wasm traps that escape emscripten's DOM event handlers are captured
with no instrumentation at the throw sites. Not their JS tag: it has no
beforeSend or fingerprint hooks, its runtime spawns workers from cross-origin
CDN hosts (this page is COEP: require-corp), and it ships session replay on by
default — which on a CAD canvas records customers' board geometry.

@sentry/browser is imported in exactly one file so the vendor stays swappable,
mirroring how lib/analytics.ts isolates Plausible.

Also replaces the terminal-signature regex with a shared, unit-tested predicate
(wasm/terminal-error.ts) used by BOTH the fatal overlay and the reporter, so
they cannot disagree. The regex was a type check written as a string match and
had three live holes: `RuntimeError` was listed but never appears IN
`.message`; Chrome's bare "unreachable" and "null function" matched nothing
(the v0.1.20 prod log is exactly those); and narrowing "table index is out of
bounds" to `\bindex out of bounds` for Firefox in 197f317 silently stopped
matching Chrome's spelling. Checking the TYPE — every trap in this family is a
WebAssembly.RuntimeError — covers all engines and ends the spelling chase; the
message patterns remain as a fallback for paths that lose the Error object,
such as a worker ErrorEvent crossing the realm boundary with error: null.
197f317's pthread-worker tap, promote() and Firefox findings are kept as-is.

Notes:
- Off unless VITE_ERRORS_DSN is set AND VITE_ALLOW_USER_OVERRIDE !== "1" (dev
  servers and every Playwright harness set the latter, and production builds
  never do), so a production DSN in a local .env still cannot report. With no
  DSN the whole SDK is const-folded out: 1,193,080 vs 1,282,463 bytes of JS.
- browserApiErrors integration removed. It wraps setTimeout/rAF/addEventListener
  in try/catch, which is exactly how KiCad-on-Emscripten drives its main loop.
- Console breadcrumbs off (collab/debug.ts's clog fires per Yjs update and would
  evict the ring before any crash); dom/fetch/navigation breadcrumbs kept.
- beforeSend redacts token/apiKey/Bearer — collab/provider.ts puts the collab
  token in the y-partyserver URL, so a connection-failure string carries a live
  credential — and guards the cascade: one wedge produced 8 errors in prod, and
  after the first terminal event the rest are dropped into cascade_count.

Verified end to end against the real EU host from a cross-origin-isolated page:
POST /api/<id>/envelope/ -> 200, and 4 terminal throws produce 1 event
(control: 1 throw, same count).

Privacy policy 9, cookie policy 6 and the licenses page are updated: Better
Stack is disclosed as an EU processor, and the licenses page now describes the
browser app's own JS dependencies, which it never did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 12:24:30 +02:00
Istvan Matejcsok
20e5eb941c fix: point GPL corresponding-source URLs at the PCBJam org
Every repo moved from emergence-engineering/ to PCBJam/, but the published
source pointers still named the old org. GitHub's transfer redirect resolves
them (all six checked, 301 -> 200), so nothing is broken today — but it stops
working the moment anyone creates a repo at an old path, and a GPLv3
corresponding-source pointer is a poor thing to leave depending on a redirect.

CI never passes --repo, so the hardcoded default is what actually ships: the
editor's version badge on editor.pcbjam.com has been linking users to the old
org for their source.

Covers the source pointers (licenses.md, terms.md 12.4, REPO_URL and its doc
comment, the three build-script --repo defaults, the site footer's build-commit
link) and the two "our GitHub" org links. Bumps the pcbjam-shared pointer for
the same fix there.

Deliberately untouched: emergence-engineering.com, the company domain behind
contact@ and the EE credit block — the trailing slash in the substitution keeps
it out. And docs/security-audit-glm/, which describes a finding rather than
linking anywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 12:19:54 +02:00
Gergő Törcsvári
7c584f6e0f
site(blog): devblog w30 — infra move, comments UX, drift robot, dark mode
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TkP6ssstggv9H4d11jCKk4
2026-07-27 19:39:54 +02:00
Viktor Vaczi
fdc4583288 chore(deploy/site): retire the migration scaffolding, keep the health check
The Vercel -> Cloudflare Pages move is done and the Vercel project is
deleted, so the one-shot scripts have no remaining purpose. Nothing in CI
ever called them — deploy-site.yml runs npm ci / test / build / pages
deploy inline — so this removes 10 files and orphans nothing.

Deleted: 00-baseline (refused to run without x-vercel-id, so permanently
unrunnable), 01-preflight (proved Vercel state and API-token scopes),
07-dns-cutover (the phased cutover; in the end the records were attached
through the dashboard, and the rules/apex phases went unused once we chose
APEX_MODE=serve), 09-detach-vercel (its target project is gone), plus
03-ensure-project, 04-set-secrets, 05-deploy, 06-verify-deploy,
02-verify-local and 99-rollback, all either spent or duplicating CI. Their
lib/cf-api.sh went with them: the survivors use wrangler, so the whole
remaining path needs only `wrangler login` and no zone scopes.

What is kept is the part with ongoing value: lib/parity.sh, the assertion
set that caught five real defects during the migration — the live COOP/COEP
bug on the post's canonical URL, the soft-404 Pages would have introduced,
the cross-site form-POST guard Vercel had been providing for free, the
missing immutable header, and HSTS max-age=0. "Does the page return 200"
catches none of those.

08-verify-prod.sh becomes verify.sh, since the numbered sequence it
belonged to no longer exists. It drops the stamp machinery, the dry-run
plumbing and the Vercel-fallback messaging (there is no fallback now:
recovery is promoting a previous Pages deployment), and gains --skip-dns /
--skip-domains so it can be pointed at a single deployment via PROD_BASE
before promoting it.

The README is rewritten around the four invariants that fail SILENTLY —
never widen _headers to /*, keep both URL forms of the Gerber post, never
delete 404.astro, keep the cross-site form-POST guard — each with the
reason, since the reason is the only thing that stops someone simplifying
them back out.

Verified after: 21 probes, 20 pass, 1 warn (HSTS max-age is 6 months vs
Vercel's 2 years — on, just shorter), 0 fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAmkjM7okPdScp9XLW1JVr
2026-07-27 15:08:56 +02:00
Viktor Vaczi
df38ebafb7 feat(deploy/site): serve the apex from the same Pages project, no redirect rule
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
2026-07-27 14:34:16 +02:00
Viktor Vaczi
7edfade53c feat(site): move the marketing site from Vercel to Cloudflare Pages
www.pcbjam.com was the last piece of the stack on Vercel. It is now a
Cloudflare Pages project (pcbjam-site) deployed by deploy-site.yml on
every push to main touching site/** — content must not wait for a
release tag.

The Astro adapter is gone entirely: the build is pure static and the one
dynamic route, /api/waitlist, is a Pages Function. Going adapter-free
(rather than swapping in @astrojs/cloudflare, which has dropped Pages
support and only targets Workers) removes three problems at once — no
Astro/adapter major-version coupling, Footer.astro's build-time execSync
keeps working because prerendering stays in Node, and image optimisation
stays plain build-time sharp with no Cloudflare Images binding.

Verified against a real Pages runtime (wrangler pages dev): 21/21 parity
probes pass, versus 19/21 on live Vercel. The scripted runbook is in
deploy/site/ — every mutating step is dry-run by default.

Four behaviour differences were found by measurement and are handled here:

- The blog post's COOP/COEP was already broken in production. vercel.json
  scoped the headers to the bare URL, but the page's own canonical is the
  trailing-slash form, which served 200 with no isolation headers — so
  search arrivals lost SharedArrayBuffer and the embedded Gerber viewer
  degraded. public/_headers covers both forms.

- Pages answers unknown URLs with the homepage at HTTP 200 when the
  output has no 404.html — a soft-404 that invites indexing junk URLs as
  the homepage. Hence src/pages/404.astro.

- Vercel's edge refused cross-site form POSTs ("Cross-site POST form
  submissions are forbidden"); Pages does not, and a cross-site <form>
  submit needs no CORS permission to be sent, so the allowlist cannot
  stop it. The Function reproduces the guard; JSON posts stay exempt as
  that is demo.pcbjam.com's allowlisted path.

- Cache-Control: immutable on /_astro/* came from the Vercel adapter's
  generated route config, so it is now an explicit _headers rule.

Secrets move to `wrangler pages secret put --project-name pcbjam-site`
(RESEND_API_KEY, RESEND_SEGMENT_ID, WAITLIST_FROM_EMAIL);
WAITLIST_ALLOWED_ORIGINS stays unset so the allowlist stays in code.
Local dev reads .dev.vars, now gitignored — the root repo's **/.dev.vars
does not cover a nested git repo.

privacy.md and cookies.md named Vercel as a GDPR Art. 28 processor; those
mentions are removed and the existing Cloudflare entry widened to cover
website hosting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAmkjM7okPdScp9XLW1JVr
2026-07-27 13:41:51 +02:00
Gergő Törcsvári
6a939f75a8
site(blog): devblog w29 — closed-beta prep, self-healing docs, circuit sim
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TkP6ssstggv9H4d11jCKk4
2026-07-20 13:11:33 +02:00
Viktor Vaczi
f33a5e2726 Refund policy. 2026-07-20 12:26:00 +02:00
Istvan Matejcsok
74afb7b726 harden the marketing site: gate demo overrides, rate-limit + redact waitlist
- gerber-demo/boot.js honors the base/cdn/tag script-source overrides only on a
  loopback hostname, so a shipped page ignores them.
- /api/waitlist gains a best-effort per-IP/per-address rate limit and masks the
  submitter address in the no-key log line.
Adds vitest to the site: test/boot-overrides.test.ts (happy-dom) and
test/waitlist.test.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:46:17 +02:00
Istvan Matejcsok
40b53ad530 analytics: consolidate everything onto one shared Plausible pa- script
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>
2026-07-15 19:34:09 +02:00
Gergő Törcsvári
f9091a28c5
site(blog): devblog w28 — collab week (presence videos, y-sexpr v2, platform)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dz6jMCAoU9ip7mEshjZci7
2026-07-13 17:57:57 +02:00
Istvan Matejcsok
1d62dc89a9 feat(site): pricing copy refinements (commenter role, pro seats, collab bullet)
Commenter role in the viewers/commenters FAQ (reader = view, commenter =
+add comments, editor = +edit); real-time collab listed on Free only, Pro/
Enterprise inherit via 'Everything from ...'; Team states seats are Pro
seats scoped to that team's projects only, plus a matching FAQ entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RQcUBzy8mpYXkup9eNY1z
2026-07-10 16:33:03 +02:00
Istvan Matejcsok
89fcdf24c4 feat(site): pricing page (plan cards, billing toggle, coming-soon dialog)
Free/Pro live, Team/Enterprise as coming-soon cards; monthly/yearly price
toggle; CTAs open a coming-soon dialog pointing at the waitlist; Pricing
link in header nav + footer; header chip nowrap + earlier hamburger
breakpoint; CheckItem gains an optional dimmed note suffix.

Copy source of truth: docs/features/pricing/copy.md (root repo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RQcUBzy8mpYXkup9eNY1z
2026-07-10 16:33:03 +02:00
Gergő Törcsvári
93e73c590e
footer build SHA auto-resolve + devblog w27
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dz6jMCAoU9ip7mEshjZci7
2026-07-07 21:09:23 +02:00
Gergő Törcsvári
6bc54de92b
feat(collab): presence P3 — eeschema port (collab-presence 0003)
- eeschema_embind.cpp: presence section (0002 pattern, zero fork changes) —
  wx canvas triggers + SCHEMATIC_LISTENER piggyback → post-settle
  SCH_SELECTION_TOOL emit; throttled cursor; remote VIEW_OVERLAY render
  (SCHEMATIC::ResolveItem, name tags, screen-constant via GAL matrix);
  schCollab{PresenceStart,SetRemote,GetViewport,GetSelection,TestSelectFirst,
  TestClearSelection}; kicad_editor_embind dispatches by active frame.
- sheet-manager: parked rooms carry SKELETON awareness states
  ({user,tool,sheetPath=bound sheet}) via publishSkeletons on switch + late
  warm-up — the bound room's awareness then holds every project peer, so no
  multi-room aggregation; presence.ts publishSkeleton helper.
- PresenceRoster: sheet-aware — peers on another sheet render dimmed with
  'on <sheet>' tooltip; WasmTool un-gates the kicad presence bridge for
  eeschema and threads activeSheetPath.
- tests: presence-eeschema.spec.ts (5 e2e, mirrors pcbnew incl. the px/IU
  band at eeschema's 1e4/mm IU); presence.test.ts +2 (skeleton visibility,
  no ghost cursor after rebind). eeschema-collab/subschema stay green.

Verified live: two tabs on demo.kicad_sch — peer cursor cross + label,
selection box + name tag, roster avatar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvqUd4QsJSGHN28aunJRTq
2026-07-07 21:09:22 +02:00
Viktor Vaczi
f9696ac503 site(landing): demo-first CTAs, light theme, honest copy; restore positioning map
Now that demo.pcbjam.com is live, the landing page leads with it instead
of "coming soon":

- Hero: "Open the live demo" is the primary CTA; the coming-soon panel is
  replaced by a DemoLaunchCard (screenshot + play overlay, new tab — the
  editor needs COOP/COEP so it can never be iframed here); waitlist demoted
  to a ghost button under a "Want early access to multiplayer?" lead-in.
- Removed the Gerber demo and positioning-map sections, then restored the
  positioning map per review — recolored for light theme and without the
  multiplayer badge/legend ("Everyone picks one. We do both.").
- Light theme: flipped the :root ink tokens, darkened accent/signal for
  contrast on white; fixed two dark-assumption bugs it exposed (header CTA
  text losing to .nav>a specificity, DemoVideo play scrim derived from --bg).
- Copy: positioned around what's live (real KiCad in a tab) with
  multiplayer explicitly "coming — join the waitlist"; cut untrue claims
  (GitHub import, self-host, offline PWA -> roadmap); cut the jam metaphor
  outside the product name; TrustBar fake-stat grid -> plain claims strip;
  footer tagline + fallback meta description aligned.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REwWZGkAz1AZUJw8yqxkCN
2026-07-04 21:04:37 +02:00
Viktor Vaczi
bd00bef96d copy(waitlist): drop "no spam, unsubscribe anytime" from demo + marketing
Trim the waitlist microcopy on the standalone demo and the marketing site's
CTA/hero forms to just the value prop, removing the "no spam, unsubscribe
anytime" reassurance line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REwWZGkAz1AZUJw8yqxkCN
2026-07-04 12:37:33 +02:00
Viktor Vaczi
e86be160a6 feat(site,demo): OG share cards + demo page meta ahead of the HN post
Demo (web/standalone): retitle "KiCad Web" -> "PCBJam - KiCad demo", add
description + OG/Twitter tags (absolute URLs pinned to demo.pcbjam.com),
ship favicons (page had none), add the og-demo.png share card, and show an
"Early-access alpha" badge next to the home-page heading.

Site: add the missing og/og-home.png (BaseLayout referenced it but the
asset never existed -> og:image 404ed in prod) and set astro `site` so
canonical/OG URLs resolve absolutely (prerendered pages shipped
canonical=http://localhost:4321/ to production).

Cards are 1200x630, generated from the favicon.svg logo mark on a pure
white background.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 09:14:44 +02:00
Gergő Törcsvári
9b41d7321f 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>
2026-07-02 07:40:30 +02:00
Gergő Törcsvári
ff9b966109
docs(site): dev blog updates (w26 + webgl-porting post)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:54:50 +02:00
Gergő Törcsvári
344a36799a
chore: add dev:demo script (web) + refresh site lockfile
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:54:49 +02:00
Gergő Törcsvári
7bc5260bc7
fix(scopes): standalone project list label showed old /p/<slug> instead of scope/projects/<slug>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:54:49 +02:00
Gergő Törcsvári
234e5fe189
feat(scopes): phase 0003 — standalone scope routing, tool inference, scoped API client, Demo/Local/Cloud badges
Routes → /:scope/projects/:name/* (+ -/:tool) and /:scope/libs/:name; tool inferred from file ext / lib kind (?tool= override); currentScope()/userSlug() in config; SCOPE/USER headers + scoped paths through project + libs sources; @local/demo scopes on constructed projects; badge relabel (drop scary read-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:54:49 +02:00
Gergő Törcsvári
39cb04b127
feat(standalone): demo follow-ups — waitlist, analytics, move-to-local, file tree, load progress
- 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
2026-06-27 07:59:09 +02:00
Viktor Vaczi
61760ea155 Updated vercel.json blog url. 2026-06-18 15:46:08 +02:00
Viktor Vaczi
91cb62ffc1 Rename blogpost. 2026-06-18 15:29:07 +02:00
Viktor Vaczi
429ac206f9 fix: 🐛 Gerber demo: survive a stale cross-origin-isolation state
The demo showed "Live demo unavailable in this browser" on fully capable
browsers (e.g. Chrome) whenever a page load wasn't cross-origin isolated —
a soft SPA navigation, or a visit cached from before the COOP/COEP headers
went live. The old reload guard used a sticky sessionStorage flag that, once
set during a non-isolated load, never cleared, so it skipped the recovery
reload and showed a misleading "unsupported browser" message.

- Reload guard now uses a timestamp, not a sticky flag: a stale flag from an
  earlier visit no longer permanently strands the demo (reload loop still
  prevented within a 10s debounce).
- Non-isolated pages now open the standalone viewer (/gerber-demo/, isolated
  on its own and always works) in a new tab instead of dead-ending.
- The "needs a recent browser" note only shows when provably incapable
  (isolated but missing SharedArrayBuffer/WebGL2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 14:33:55 +02:00
Viktor Vaczi
8b438b48b2 feat: WebGL graphics blog post, Gerber demo on landing page, open-source license notices
- Blog: porting-kicad-graphics-to-webgl post + GAL native/WebGL comparison images and <GalCompare>
- Landing page: embed lazily-loaded Gerber demo section + footer "Live demo" link
- Licensing: /licenses page (GPLv3 + wxWindows/LGPL-v2 breakdown, ahilss credit, corresponding-source offer) and a footer build-SHA link
- Docs: CLAUDE.md + README note to bump the hardcoded footer BUILD_SHA on each release

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 14:06:37 +02:00
Viktor Vaczi
57f1f53d26 feat: lazily-loaded Gerber viewer demo in the blog, served from R2
Embed KiCad's gerbview WASM in the graphics-to-WebGL blog post as a
click-to-load demo that opens the tiny_tapeout board entirely in-browser.

- GerberDemo.astro: lazy iframe embed + cross-origin-isolation reload guard
  + WebGL2/SharedArrayBuffer feature-detect with a poster fallback
- public/gerber-demo/{index.html,boot.js}: self-contained gerbview boot
  harness (config seed, MEMFS board preload, argv auto-open)
- board/ tiny_tapeout layers + poster.png committed; wasm/ glue JS committed
  (served same-origin — pthread worker can't be cross-origin), while
  gerbview.wasm + kicad-resources.bin are git-ignored and served from
  Cloudflare R2 (assets.pcbjam.com)
- COOP/COEP require-corp scoped to the post + /gerber-demo routes
  (dev: astro.config.mjs + middleware.ts; prod: vercel.json)
- post converted to MDX (@astrojs/mdx) so it can embed the component
- scripts/: sync-demo-wasm.sh, r2-deploy.sh, r2-cors.json

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 10:28:06 +02:00
Viktor Vaczi
54ab7e0dfc lp screenshots. 2026-06-16 08:37:10 +02:00
Viktor Vaczi
3978bf5ff1 feat(site): add favicon.ico + apple-touch-icon so the tab icon renders
Chrome also fires an implicit request for /favicon.ico; with only an SVG
link declared and no .ico at the site root, that 404 left the tab without
an icon. Add a square multi-res .ico (48/32/16) and a 180x180
apple-touch-icon — both derived from the jar art already embedded in
favicon.svg — and declare the full icon set (ico + svg + apple-touch) in
BaseLayout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 17:24:31 +02:00
Viktor Vaczi
2206266755 docs(site): tighten Cookie Policy wording — analytics consent reasoning, date bump 2026-06-10 14:14:02 +02:00
Viktor Vaczi
0b45d7ea54 feat(site): publish Privacy Policy at /privacy — waitlist coverage, conditional GPC
Replace the /privacy placeholder with the full GDPR-first policy (dated
10 Jun 2026). Beyond the reviewed draft: cover the waitlist end-to-end
(data row, consent basis, marketing section, retention), add a staged-
rollout clause so future-product processing is described honestly, make
the CCPA/GPC commitment conditional (no voluntary promise while nothing
is sold/shared), and fix the CCPA look-back wording.

Point of collection follows suit: the waitlist form microcopy links the
policy, and the confirmation email now offers reply-to-remove, promises
an unsubscribe link in every update, and links /privacy.
2026-06-10 14:13:56 +02:00
Viktor Vaczi
107fc7513f feat(site): publish Terms of Service at /terms
- replace the placeholder terms page with the real ToS (EU/Hungarian
  consumer law, Paddle Merchant-of-Record billing, GPLv3 corresponding-
  source offer), dated 10 June 2026
- serve legal pages from a markdown content collection rendered by
  src/pages/[legal].astro; drop the per-page placeholder .astro files
  (privacy/cookies keep placeholder content, now as markdown)
- add prose/table styles for legal pages
- footer link text: "Terms & Conditions" -> "Terms of Service"

Canonical working copy + clause-by-clause legal sourcing live in
features/ (untracked research).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:52:05 +02:00
Viktor Vaczi
bb6dbfc76e feat(site): add Vercel Analytics
Install @vercel/analytics and render <Analytics /> from BaseLayout so it loads on every page. Auto-activates in production on Vercel (no-op locally).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:39:01 +02:00
Viktor Vaczi
c8c09cc3d7 fix(site): close mobile menu when a nav item is tapped
The CSS-only hamburger (checkbox toggle) only reset on astro:page-load, which fires for real navigations (Blog) but not for same-page anchor links (How it works / Open & yours / Join the waitlist) that scroll without navigating — so the menu stayed open after tapping them. Add a one-time delegated click handler on the nav that unchecks the toggle on any link tap. Items stay identical to desktop; scrolling unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:32:57 +02:00
Viktor Vaczi
37a1df2315 fix(site): wire Resend waitlist via astro:env, fix TS errors + deprecations
- Move secrets to Astro's typed astro:env/server (schema in astro.config.mjs);
  removes process.env usage and the 3 TS2580 "Cannot find name 'process'" errors
  without adding @types/node.
- Replace deprecated contacts.create({ audienceId }) with segments: [{ id }]
  (env var RESEND_AUDIENCE_ID -> RESEND_SEGMENT_ID).
- Check the { error } return on both Resend calls: contact add is best-effort
  (non-fatal); a failed confirmation send now returns 502 instead of a false 200.
- Type the waitlist fetch result in WaitlistForm.astro.
- Add @astrojs/check + typescript devDeps and an `npm run check` script.

Verified: npm run check (0 errors), npm run build, secret not inlined into the
build output, and a runtime smoke test of the validation/honeypot/GET paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:26:54 +02:00
Viktor Vaczi
4951174487 fix(site): waitlist sender → hello@pcbjam.com
Use PCBJam's actual address (hello@pcbjam.com) as the waitlist confirmation
sender, in the API route default and .env.example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:29:59 +02:00
Viktor Vaczi
34ae85b2b5 fix(site): mobile waitlist input height, "Demo coming soon" panel, blog cleanup
- WaitlistForm: reset the input's flex when the form stacks — the 16rem
  flex-basis was applying to the height in column layout (256px-tall input).
- DemoVideo: add a comingSoon mode that shows a big "Demo coming soon" panel
  instead of the internal PRODUCE placeholder; enable it in the hero and the
  multiplayer section.
- Blog: retitle the post to "Porting KiCad's Graphics to WebGL with Claude"
  (clean slug), set body/description to "Coming soon.", and drop the
  placeholder text from the blog index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:15:02 +02:00
Viktor Vaczi
80042a0672 fix(site): remove AI icon from the AI section
Drop the ai.svg glyph (and its import/flex styling) from the "AI when you want
it" section per founder review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 17:44:33 +02:00
Viktor Vaczi
735d8a48ee feat(site): 🫙 PCBJam landing revisions — branding, accuracy, positioning graphic
Founder review pass on the landing page, plus commit the landing-page
research/spec docs under features/lp.

Branding & assets:
- New PCBJam mark: convert the jam-jar image to favicon.svg (trimmed, rounded
  tile) and use it in the browser tab, header, and footer.
- Footer "Built by": flower mark + "Emergence Engineering" in white Oswald (the
  blog's title font, self-hosted via @fontsource) so it's legible on dark.
- Produce the positioning quadrant graphic (public/graphics) and drop the placeholder.

Copy / accuracy:
- Remove header GitHub link; point "Built by EE" + footer "About" to
  emergence-engineering.com; Built-by CTA -> mailto:contact@emergence-engineering.com.
- Replace the wrong testimonial with Greg Detre's real quote from EE's site.
- Drop blog icons from Multiplayer/Pillars/How-it-works; add "Coming soon" to
  live-cursors & pin-comment; drop the hard-coded Yjs mention.
- Rework monetization to free+local / paid cloud-sync; remove "no metered AI",
  "no per-seat toll", and the Flux price claim; soften the AI section.
- Reframe waitlist copy (incl. confirmation email) to newsletter + early access.

Fixes:
- Offset only #waitlist for the sticky header (scroll-margin-top); section
  anchors keep their original behavior.

Docs:
- Commit features/lp (copy spec, brand guide, competitive research) via force-add
  over the /features/ gitignore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 17:14:19 +02:00
Viktor Vaczi
7f9dbdd068 feat(site): 🛬 PCBJam landing page — waitlist, sections, Tailwind
Replace the placeholder homepage with the PCBJam waitlist conversion page
(13 sections from the copy spec), shared chrome, and a real email-capture
backend, keeping the site zero-runtime-JS / zero-web-font.

- Tailwind v4 via @tailwindcss/postcss (the Vite plugin breaks on Astro 6
  rolldown-vite); Preflight skipped; brand tokens in :root + @theme inline.
- Waitlist: /api/waitlist serverless route (prerender=false) + Resend, with
  dual JSON/303 responses; WaitlistForm progressive enhancement bound on
  astro:page-load (survives ClientRouter swaps); honeypot + validation.
- New components (Icon, Placeholder, DemoVideo, SectionBand, Chip, EECredit,
  StatBar, Pillar, Step, CheckItem, WaitlistForm) + 13 section components.
- Redesigned Header (sticky, mobile menu, CTA) + 4-col Footer (KiCad
  attribution); BaseLayout gains full-bleed mode + OG/canonical meta.
- Assets scaffolded with marked placeholders; EE/generic/client SVGs copied.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 14:59:13 +02:00
Viktor Vaczi
d55a8a0afa feat(site): 🚀 add Astro marketing/content site
Standalone Astro 6 site in /site (decoupled from the /web app monorepo):
landing page, /blog with one post (Content Layer), and terms/privacy/cookie
legal pages. Pure Astro, server-rendered to static HTML.

- Static by default via @astrojs/vercel adapter; any route can opt into SSR
  with `export const prerender = false` (deploys as a Vercel Function).
- SPA-style navigation with <ClientRouter />, transition:persist on the header
  (no icon flash), and viewport prefetch — ~6 KB gzip JS, no React.
- Deploy on Vercel by setting Root Directory to 'site' (no vercel.json needed).

See site/README.md for dev and deploy instructions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 08:57:37 +02:00