Commit graph pcbjam/web/standalone
Author SHA1 Message Date
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
Gergő Törcsvári
ae20d49241
feat(standalone): device-capability preflight check (0001)
Add a pre-boot "potato check" for the standalone editor: probe the device
against what the KiCad WASM tools actually require and warn (or block, with an
override) before the expensive WASM asset fetch.

- preflight/capabilities.ts: pure, unit-testable probe → CapabilityReport with
  stable codes. Fatal: no SharedArrayBuffer/cross-origin-isolation, no
  WebAssembly, no Workers+Atomics, no WebGL2. Warn: low deviceMemory, low JS
  heap limit (Chromium), few cores, mobile, small screen. Thresholds in one
  tunable const block.
- preflight/BlockingDialog.tsx: reusable modal blocking overlay (no dismiss
  except via explicit action) — also for feature 0002's terminal dialog.
- preflight/PreflightGate.tsx: runs the probe once on mount; fatal → blocking
  dialog with "Try anyway" (short-circuits the asset fetch); warnings →
  dismissible advisory banner with localStorage "don't show again" keyed to the
  exact warning codes.
- ToolPage: wrap WasmTool in PreflightGate so the gate runs before boot.

Optional micro-bench (phase 3) deferred per the spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:05:35 +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