Commit graph pcbjam/web/standalone/src/components
Author SHA1 Message Date
Gergő Törcsvári
ef15c0d863
feat(collab): ship the tuned presence style as per-editor defaults
Defaults picked with the PresenceTuner (2026-07-07):
- pcbnew: exact-outline selection, 6px stroke @0.7, 46% fill, bottom-end
  chips @7.5px, cross cursor 8/3px @1.0 with chip label, 9px pins w/ 3px ring.
- eeschema (eeschemaDefaultStyle): same look softened for the schematic
  canvas — hairline 1px outline, 14% fill, 0.5 cursor alpha.
- New chipBgAlpha knob (default 0.7 = the border alpha) for label AND cursor
  badges, + tuner slider.
- Tuner: per-tool defaults + per-tool localStorage keys; Reset returns each
  editor to its own shipped look; tool prop threaded from WasmTool.
Verified: pure defaults (no style push) render the picked look in both
editors; presence suites green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvqUd4QsJSGHN28aunJRTq
2026-07-07 21:09:23 +02:00
Gergő Törcsvári
21e3df8fd0
feat(collab): nth-in-room colors, comment toolbar + drag + global hide, luminance chip text
- presence.ts: colors claimed by ARRIVAL ORDER — each client takes the lowest
  free palette slot (no birthday-problem hash collisions; the best palette
  colors go first; N ≤ palette-size editors always distinct). Coordination-
  free: claims ride awareness; simultaneous-join collisions converge (lower
  clientID keeps, the other re-claims), same-user tabs adopt one color,
  claims are sticky across eeschema sheet rebinds (skeletons reuse them).
  colorOf(userId) resolves live colors; comment pins/popovers follow it
  (offline authors fall back to the hash). +2 unit tests (verified live:
  alice=slot0, bob=slot1, roster/pins consistent).
- CommentLayer: single comment icon expanding into a horizontal bar — new
  comment · list · show/hide all (eye empties the GAL pin set + DOM targets;
  re-shown on new-comment/panel-jump). Pins are DRAGGABLE: live LWW anchor
  writes while dragging (peers + the GAL dot follow), nearest-item re-snap on
  drop, click-vs-drag by 4px threshold (verified live: drag synced to the
  peer tab exactly). Shared setThreadAnchor + controller moveThread/
  setPinsVisible/colorFor.
- collab_presence_style.h: chip text color by background luminance (dark on
  light chips, white on dark — BitmapText draws with the stroke color).
- comments/presence-roster e2e updated for the bar + passing; collab units
  56/56; shared 122/122; shared pointer bump.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvqUd4QsJSGHN28aunJRTq
2026-07-07 21:09:23 +02:00
Gergő Törcsvári
0f19811602
feat(collab): tuner round 2 — center-anchored labels, exact outlines, varied demo set, clearer color modes
- collab_presence_style.h: GAL BitmapText CENTERS on its position (confirmed
  in GAL::ResetTextAttributes — the mispositioned nameplates); labels/chips
  now hand GAL the block center. New selection shape 5 'exact outline':
  pcbnew hugs real geometry (footprint bounding hull, TransformShapeToPolygon
  for the rest, padding inflates the polygon); eeschema falls back to rect.
- kicadCollabTestDemoSet (both TUs + merged): labeled demo groups — smallest
  + largest footprint and the two busiest nets' segments (symbols + wire
  bundles on sch) — so the style preview covers the real range of shapes.
- PresenceTuner: Colors section rebuilt as explicit modes (per-user / fixed /
  palette) with preset palettes (default, pastel, vivid, okabe-ito), buffered
  hex editing + Apply (the old always-filtering textarea ate keystrokes), an
  'overlay only' hint; demo injection consumes the varied demo set; 'exact
  outline (pcb)' in the shape list.

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
Gergő Törcsvári
ce99773af4
feat(collab): dev-time presence style tuner (VITE_PRESENCE_TUNER=1)
Parametrizes every visual knob of the presence overlay so we can pick the
shipped look live, then wire the winners into the defaults:
- collab_presence_style.h: shared STYLE struct + drawing (now used by BOTH
  editor TUs — no more duplicated overlay code): selection shape (rect /
  corner brackets / underline / rounded rect / filled-only), border width +
  alpha, infill alpha, padding, corner radius; name tag show/size/chip-
  background/inside-outside/top-bottom/start-end-center/offset; cursor shape
  (cross / pointer / circle+dot), size/width/alpha + label knobs; fixed-color
  and palette-by-name-hash overrides (try palettes without changing what
  senders publish); pin radius/ring/alphas. Defaults == shipped look.
- kicadCollabSetStyle(json) live-patch export + kicadCollabTestListItems(n)
  (real KIIDs for synthetic previews); merged dispatch; pins now carry the
  author name so palette overrides recolor them consistently.
- PresenceTuner.tsx: floating dev panel (env-gated, tree-shaken otherwise) —
  grouped sliders/selects, demo peers+pins injection for SOLO tuning,
  localStorage persistence across reloads, Copy JSON export, reset.

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
Gergő Törcsvári
635aa2926a
feat(comments): figma-like comment pins + threads (collab-presence 0005)
Hybrid pins: the wasm draws the dot (kicadCollabSetPins rides the presence
VIEW_OVERLAY, author color + white ring, drawn above selections; zero
kicad-fork changes), the DOM owns interaction —
- comments.ts: controller gluing the MIT kdoc_comments helpers to the editor:
  anchor resolution per tool IU (pins track item moves via kdoc_items
  observation), throttled pin snapshots, anchorAt nearest-item snap, jumpTo
  via new kicadCollabSetViewport; rebinds per sheet like presence.
- CommentLayer.tsx: comment mode (click catcher + composer), pin hit targets
  over the GAL dots, thread popover (reply/edit/delete own, resolve/reopen,
  delete thread), panel with resolved filter + jump-to (popover centers when
  the pin is off-screen). Resolved pins drop figma-style.
- WasmTool: controller lifecycle beside presence; live viewport feed;
  window.__pcbjamComments test handle (threads persist in the room ydoc).
- e2e tests/web/comments.spec.ts: two-tab create → reply → resolve → panel
  filter → delete, passing vs real partykit; presence suites + collab units
  stay green; shared pointer bump (0004 model).

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
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
Gergő Törcsvári
8ad1cc673a
feat(collab): presence P2 — pcbnew selection/cursor emit + remote VIEW_OVERLAY render (collab-presence 0002)
Zero kicad-fork changes — all in the embind layer:
- pcbnew_embind.cpp: presence section — canvas wx Bind() triggers (motion/
  leave/up/key/wheel) + COLLAB_LISTENER piggyback → post-settle selection
  check (dedupe) → onSelection; throttled cursor emit via VIEW::ToWorld;
  viewport push/pull (px-per-IU via the GAL matrix — GetScale() is the zoom
  and sized the first cut's overlay nm-small); kicadCollabSetRemote renders
  peers' cursors (cross + name) and selection bbox outlines into one
  per-user-colored VIEW_OVERLAY (CallAfter+COROUTINE), never touching local
  selection; PresenceStart/GetSelection/TestSelectFirst/TestClearSelection.
- kicad_editor_embind.cpp: merged-image dispatch (pcb-only until 0003).
- presence-kicad.ts: bindKicadPresence — routes emits into awareness (0001)
  and pushes trailing-throttled peer snapshots into the wasm; wired from
  WasmTool.startPresence (pcbnew-gated). +5 unit tests.
- tests/kicad/presence-pcbnew.spec.ts: 5 e2e — programmatic + real box-select
  emit, throttled cursor, remote render with no-leak + pixel restore,
  viewport unit band. Existing pcbnew-collab/items-bridge suites stay green.

Verified live: two tabs over partykit — peer cursor cross + label + selection
outline visible on the other tab's canvas.

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
Gergő Törcsvári
22528f450b
feat(collab): presence P1 — awareness transport + who's-here roster (collab-presence 0001)
- provider.ts: expose awareness on YjsProvider (partykit/hocuspocus surface
  their own; BroadcastChannel gets a sibling-channel relay via awareness-bc.ts)
- presence.ts: createPresence — publish local PresenceState, subscribable
  deduped peers() roster, setCursor/setSelection for P2, pagehide fast removal
- PresenceRoster.tsx + WasmTool wiring: facepile chip next to SourceChip;
  eeschema rebinds per active sheet (sheet-manager ActiveSheet now carries the
  room's provider); config presenceUser() = slug + colorForUser
- new dep y-protocols; unit tests (presence.test.ts) + e2e
  tests/web/presence-roster.spec.ts (two tabs, verified vs real partykit)
- bump pcbjam-shared: presence-wire schema (MIT)

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
Gergő Törcsvári
62ca571802
feat(ysync): lib_symbols travel + layout save-sync (miss 08), TS hot-path opts (12), diff-on-rebind adopt (13) — doc 18
Miss 08A: the binding stores wire-carried (lib_symbols …) definitions in
kdoc_libsymbols and prefixes them on apply wires — a joiner that never saw a
symbol adopts it WITH its definition (new e2e ysync-libsymbols.spec.ts).
Miss 08B: registerSaveHook gains onSavedText; WasmTool routes saved-file text
to syncLayoutToY (per sheet room via the manager's syncLayoutFromSave, or the
single-room doc) so title block / paper / setup edits converge instead of
drifting. Opt 12 (TS half): zod off the observer hot path (yToItemUnchecked),
children index built once per conversion. Opt 13: seed()'s adopt diffs the
editor snapshot against the doc view and applies only the doc-authoritative
difference — clean rebinds apply nothing, the adopt undo entry shrinks to the
real changed set. Opt 14 deliberately deferred (doc 18). All TS-side; no wasm
rebuild (the C++ blob/findLib sides already carried definitions).

Verified: shared 107, standalone 79 (+2 known pre-existing wasm-assets),
ysync e2e 21/21 chromium, collab regression 21/3-skip firefox.

Bumps: web/pcbjam-shared (lib_symbols channel + syncLayoutToY + opts).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgThWXtdvrYLK47EDFoGdq
2026-07-06 08:57:58 +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
Istvan Matejcsok
c6716e6b60 feat(wasm): unify editor builds — footprint/symbol editors via runtime --frame
The footprint and symbol editors are no longer separate WASM bundles: the frontend loads the parent pcbnew/eeschema bundle and passes --frame=fpedit / --frame=symedit (TOOL_BUNDLE + TOOL_FRAME -> Module.arguments in boot). Drops the two duplicate build+deploy targets and their wrapper scripts + vestigial embind; adds low-level harnesses (footprint_editor.html, symbol_editor.html) and a runtime-frame spec. Bumps the kicad submodule to the runtime --frame launcher.

The frame-runtime spec is listed in PCBNEW_FAMILY_SPECS so CI routes it to the chromium-ci (V8) project — its footprint case boots the pcbnew module, which OOMs SpiderMonkey on x86 CI. Includes the editor-unification dossier (research docs 01-04 + the as-built implementation record 05).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 17:56:02 +02:00
Gergő Törcsvári
6f3f3bcf00
feat(3d): lazy 3D model delivery R2→IDB→WASM
- cdnModelsSource: sparse per-lib stacks over the models CDN layout
  (libs/kicad-models/<tag>/<lib>/manifest + content-addressed blobs)
- models-bridge: board prescan ((model …) scan → prefetch, 4/4 in 74ms
  on the demo board) + the kind=model3d 'ensure' op answering absolute
  MEMFS paths; only board-referenced bodies ever enter IDB
- boot/runner/config/WasmTool wiring (VITE_MODELS_MANIFEST_URL,
  KICAD*_3DMODEL_DIR seeding, prefetch badge)
- StorageUsageCard on HomePage: per-kind cached sizes + delete-3D-cache
- publish-models.ts (brotli content-addressed publish, 606→93MB for the
  pic_programmer lib set) + dev-demo --models-tag/--models-local
- e2e: 3d-viewer-models.spec.ts (bridge normalize/dedup + STEP/WRL
  render); submodule bumps: kicad (static 3D plugins + ensure hook),
  pcbjam-shared (sparse sync layer)

Spec + findings: docs/features/3d-models (private repo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014AT7gVHRktDYoQ68S4x6A4
2026-07-02 11:35:36 +02:00
Gergő Törcsvári
88aaba99c9
feat(libs): react load overlay + progress bar; mimalloc mallinfo stub; framed tests
- WasmTool/source.ts: LIB_LOADING_EVENT brackets the fat-load; full-cover
  overlay ('Loading <kind> libraries…') with a per-lib progress bar (done/total
  from listLibs), show-on-first / hide-after-last coalescing.
- mallinfo stub (wasm/shims/mallinfo_stub.c + build-kicad-target.sh): -sMALLOC=
  mimalloc doesn't export glibc mallinfo() that OpenCASCADE OSD_MemInfo (pcbnew
  3D) needs; zeroed no-op unblocks the pcbnew link.
- source/cdn-source tests: updated to the framed Uint8Array 'bodies' contract.
- bump kicad -> e8db3d3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 09:56:32 +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
Gergő Törcsvári
30645dbae5
feat(libs): fat-list provider + getAllItems + parallel lib pre-sync; re-land onto KiCad 10 wasm-port
Replays the symbol-editor fast-lib-load work after the kicad fork was rebased
onto KiCad 10.0.4 and wasm-port was force-pushed. Carries the standalone libs
TS code (fat-list provider, getAllItems, parallel lib pre-sync) and bumps the
submodule pointers to the re-applied tips:

- kicad      -> c63009046 (lazy load on expand + chooser preview + fat-load enumerate)
- wxwidgets  -> 5d40228   (flush events + repaint after modal button click)
- pcbjam-shared -> c6a7e00 (bulk readAll for fat-load lib warm-up)

The 824e8cc build-kicad-target.sh sym-converter-contamination fix is omitted:
the KiCad 10 integration (ce344da) already pins KICAD_SYM_CONVERTER_WASM=OFF for
non-sym_convert targets. Verified: eeschema WASM kiface compiles + links; libs
unit tests 10/10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:31:25 +02:00
Gergő Törcsvári
142e2d4e5e
feat(standalone): project-centric new-file flow + fork gallery; clean idb key-sep source
Mirrors the closed app: create a templated file then open it, so you always edit a real file that saves back.

- lib/new-file.ts: minimal valid KiCad templates (pcbnew/eeschema/pl_editor, from the e2e fixtures) + document-tool helpers.

- NewFileDialog: in-project mode (file name) and home mode (pick/create a browser-local project + file name); writes via the active source uploadFileBytes (local IDB or remote-rw backend), then opens by path.

- HomePage: a document tool from the grid (local store on) opens the dialog instead of booting a throwaway blank doc.

- ProjectView: project-centric New-file row (writable projects) + Save-a-copy-to-browser fork for read-only gallery projects (copies into editable IDB), alongside files/export.

Also replaces a stray NUL byte that had been written into idb-project-store.ts in place of a space (made the file parse as binary) with an explicit unicode escape for the key separator (NUL, sorts below every slug char so project key ranges cannot collide) and the range upper bound. Runtime behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:02:36 +02:00
Gergő Törcsvári
4839a9d73a
polish(standalone): unify home Projects list + solid source chips
Home page feedback: merge "Your projects" (local) and the gallery/backend list
into one "Projects" section where each ROW carries its own source chip (instead
of a per-section header chip). Sections are now Open → Projects → Tools →
Libraries. Local rows keep open/export/rename/delete; remote rows just open.
LocalProjectsSection → ProjectsSection.

Source chips were too pale on the editor canvas — switch from translucent pastel
to solid fills with white text + an inset ring, so they read on any backdrop
(home, dark boot screen, light editor canvas).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 08:53:16 +02:00
Gergő Törcsvári
ce9ede1e9e
feat(standalone): browser-local (IndexedDB) virtual projects
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>
2026-06-20 08:24:14 +02:00
Gergő Törcsvári
0f53984407
feat(standalone): bottom-right version + source badge
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>
2026-06-20 08:01:50 +02:00
Gergő Törcsvári
5aae2a0d16
feat(demo): demo.pcbjam.com deploy — versioned WASM CDN + static gallery + tag CI
Cross-origin WASM CDN (cdn.pcbjam.com, R2): per-tool content-addressed,
immutable folders + a per-release runtime manifest (snapshot from registry).
boot.ts loads pthread workers cross-origin via a same-origin blob shim.
Static no-backend project source (demo gallery; Save downloads to local),
api.uploadFileBytes kept and config-gated. demo.pcbjam.com on Cloudflare Pages.
deploy-demo.yml (tag v*) snapshots WASM + content + builds + deploys;
publish-wasm.yml builds on Ubicloud. Design/spec docs live in pcbjam-private.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:39:09 +02:00
Gergő Törcsvári
b1320afab9
feat(eeschema): hierarchical subschema collaboration — per-sheet rooms, lazy seed, save flow, embind + e2e
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:55:30 +02:00
Gergő Törcsvári
0efeb5d3d9
feat(ysync): drift-detection loop + fix second-tab ydoc adopt
- drift-detect.ts: every-N-edits + beforeunload check via kicadSave* (no save-hook), reports drift
- WasmTool: start drift detection after collab; return collab handle; stop on cleanup
- fix uuid-less docs (pl_editor): use ydocHasState in maybeConnectDocSession + seed() so a 2nd tab materializes the room instead of refetching the stale file
- api: reportDrift + reportDriftBeacon (sendBeacon/keepalive)
- global.d.ts: FS.unlink
- bump web/pcbjam-shared pointer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 16:34:34 +02:00
Gergő Törcsvári
92b5fd7a42
feat: editor loading chrome — boot overlay, item spinner, error toast
The WASM tools boot + fetch lib items with no visible feedback, so a big tool
download looks like a freeze and a failed body fetch (e.g. a backend 404) looks
like nothing happened. Add a full-screen boot overlay that stays until the wx UI
has actually built (waitForWxUi, gated on wxElementRegistry — not just boot
resolve, which flashes a blank editor), a debounced spinner while an item is
being opened/saved, and an auto-dismissing error toast when a body can't be
loaded. Driven by events the libs bridge now dispatches (LIB_BUSY/LIB_ERROR).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 16:34:33 +02:00
Gergő Törcsvári
4b404f3771
feat: open library files + lib-scoped editors; fix wx-dom.js boot
Open a single library scoped to itself in its editor: a backend lib (HomePage
chip → scopedLibsSource) or a local .kicad_sym/.kicad_mod file (LocalProjectView
→ localFileLibsSource + a browser-side multi-symbol .kicad_sym parser). WasmTool
gains an optional libsSource override (transparent default). All via the existing
lib bridge — no MEMFS doc open, no fork changes.

Also fix a systemic editor boot failure: the build split the wx glue into
wx.js + wx-dom.js, but boot.ts only injected wx.js + <tool>.js. wx-dom.js defines
window.wxDomCreateControl (used by every tool to build its UI), so without it the
canvas rendered but no wx controls did (ReferenceError: wxDomCreateControl is not
defined). Inject wx-dom.js between wx.js and the tool, best-effort (older non-DOM
builds tolerated). Bump pcbjam-shared for LIB_EXTENSION_TOOL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 16:34:33 +02:00
Gergő Törcsvári
b0b7f69846
feat: standalone home rework — tool launcher + backend libraries listing
Rework HomePage into KiCad-launcher-style zones: local file-load, a Tools grid,
backend projects, and a backend libraries listing (useLibs over the shared
listLibs). The Tools grid shows all seven tools in KiCad's standalone order
(Schematic/Symbol/PCB/Footprint editors, Gerber Viewer, Calculator, Drawing
Sheet); clicking a tool or a library launches the editor with no project
(launchedTool → WasmTool slug=local) — file-less editors browse backend libs,
document editors open blank. Harden the web e2e global-setup to assert the
self-provisioned origin libs (Device, Resistor_SMD) are present, failing fast.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 16:28:21 +02:00
Gergő Törcsvári
f04915ee1d
feat: libs 0005 — standalone sends the project header on lib requests (project-scoped server-side resolution); bump pcbjam-shared
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 16:28:20 +02:00
Gergő Törcsvári
d2d2c19a8c
feat: libs 0003 — remote provider + example backend serving; bump kicad, shared
standalone: LibsSource abstraction (remote over the contract + static fallback),
generic window.kicadLibs provider, sym-lib-table generated from the backend's
lib list at boot (replaces the spike). backend: serve pre-built self-contained
symbol bodies from LIBS_DIR (listLibs/listLibItems + raw item-body route).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-16 16:28:20 +02:00
Gergő Törcsvári
5830f18393
feat: local-folder project view + suppress browser save dialog on Cmd/Ctrl+S
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 14:05:40 +02:00
Gergő Törcsvári
8131bf9bac
feat: standalone save/load routing + VITE_DOC_SOURCE ydoc mode
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 14:05:40 +02:00
Gergő Törcsvári
07863da416
feat: ysync 0008 Stage D — wire the v2 items collab into the app + verify
- WasmTool: hard cutover to startKicadCollab (gates on
  kicadCollabSnapshotItems; scalar reconciler no longer runs in the app)
- file-seed (ysync 0005 tie-in): empty rooms are seeded from the opened
  file via fileToDoc/docToY (meta+layout+items — file recoverable from the
  Y.Doc alone); populated rooms still adopt (seed-once unchanged)
- binding: gate UP applies until seed() — the provider's initial state
  sync otherwise streams the full doc into an editor that already holds
  the file (trapped eeschema's paste path in the real app)
- pcbnew blobForItem: footprints Format a uuid-corrected copy directly —
  SaveSelection's copy regenerated mandatory-field uuids (FOOTPRINT copy
  ctor assigns into fresh fields), breaking wire identity (rebuild)
- roundtrip.spec on the v2 items wire; new passing pcbnew
  footprint-containment round trip (the 0004 containment win); full
  fixture stays fixme on the tracked envelope-parse limit
- real-app two-tab emit verified for eeschema + pcbnew (see 0008 doc)

Full kicad suite: 45 passed, 3 skipped (known), 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:05:40 +02:00
Gergő Törcsvári
0773acefaa
feat(standalone): "open in a new tab" escape hatch on OOM dialog (0002)
The automatic recovery uses an in-place location.replace reload, which does NOT
reliably release the OOM'd wasm heap in every browser (notably Firefox), so the
respawn can land right back in an out-of-memory state.

Add a user-gesture escape hatch to the terminal MemoryExhaustedDialog: an "Open
in a new tab" button that opens the editor in a fresh top-level browsing context
(clean retry chain) and closes the current tab. Because it runs from a real
click, window.open is not popup-blocked; window.close() may still be refused on
the original user-opened tab, which is fine — the new tab is the survivor. Copy
tells the user this sometimes helps where the in-place reload didn't.

- oom-watch.ts: export respawnInNewTab(win) — fresh-tab respawn with ?oomRetry
  cleared.
- MemoryExhaustedDialog: "Open in a new tab" (primary) + "Reload this tab"
  (secondary), with honest copy about Firefox + unsaved edits.
- WasmTool: wire onOpenNewTab to respawnInNewTab.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:05:39 +02:00
Gergő Törcsvári
0d2f88b437
feat(standalone): OOM tab recovery (0002)
Automatically recover from an out-of-memory by respawning a fresh browsing
context (fresh wasm heap) and discarding the dead one — the manual "duplicate
tab, close the OOM'd one" fix, automated and capped at 2 retries so a too-small
machine stops instead of looping.

- recovery/oom-watch.ts: the recover() state machine with the retry counter in
  the URL (?oomRetry=N, cap 2). Mode A soft detection — Module.onAbort plus
  window error/unhandledrejection matched against an OOM signature, behind an
  idempotent latch. Mode B hard-kill detection — a per-tab localStorage
  heartbeat sentinel; a stale sentinel on the next load continues the retry
  chain (heartbeat keys avoid mistaking a second live tab for a crash). Healthy
  session (alive HEALTHY_RESET_MS) resets the chain. Default strategy is
  location.replace; window.open+close is behind ?oomStrategy=newtab. Platform
  access goes through injectable win/storage seams for testability.
- recovery/MemoryExhaustedDialog.tsx: terminal "out of memory" UI, reusing
  0001's BlockingDialog; copy is honest about lost unsaved edits.
- wasm/boot.ts: forward emscripten Module.onAbort to the watcher (optional
  BootOptions.onAbort).
- WasmTool: install the watcher, gate boot on its proceed flag (skip when the
  chain is already exhausted), and render the terminal dialog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:05:39 +02:00
Istvan Matejcsok
ee451c9de2 pcb-schema switch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:26:45 +02:00
Gergő Törcsvári
285efe7a06
feat(standalone): env-selected Yjs provider abstraction (ysync 0004)
Generalize the collab transport into a provider registry (none |
broadcastchannel | partykit | hocuspocus), one active per env. startCollab
takes { provider, room } and awaits provider.whenSynced() before seed/adopt.
Network libs lazily imported; room id from @pcbjam/shared collabRoomId.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:37:30 +02:00
Gergő Törcsvári
b6a77ee93c
refactor(web): split into GPL standalone editor + thin reference backend
Reduce web/ to a single generic editor app plus a minimal example backend,
moving all project-specific/app code out to the closed root repo:

- apps/frontend -> standalone (@pcbjam/standalone): the GPL editor. Strips the
  project-management UI (ProjectsPage/ProjectDetailPage/UploadDropzone and the
  write half of lib/api.ts). WasmTool now takes fetchBytes + assetBaseUrl as
  injected props (decoupled from the API client) so it can be driven by either
  a backend or a local folder. New HomePage (local-folder loader + backend
  project list) and read-only ProjectView. BroadcastChannel collab unchanged.
- backend (@pcbjam/backend-example): thin Fastify+ts-rest reference impl of the
  @pcbjam/shared contract — serves a single project off the local filesystem
  (PROJECT_DIR), no DB/auth/uploads.
- packages/contract -> web/pcbjam-shared (git submodule, MIT @pcbjam/shared).
- Remove apps/server, packages/storage, packages/contract, docker-compose
  (server + storage move to the closed root repo; preserved via subtree
  branches). Rewrite pnpm-workspace/turbo/.env for the frontend-only layout.
- Add tests/fixtures/demo so the editor + example backend run out of the box.

Standalone typechecks + builds; backend serves the contract end-to-end.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 12:35:46 +02:00