| Filename | Latest commit message | Latest commit date |
|---|---|---|
Every standalone tool (eeschema, pcbnew, calculator) boots through common/single_top.cpp, which runs STARTWIZARD::CheckAndRun() — the first-run "KiCad Setup" wizard. It shows whenever any provider (SETTINGS / LIBRARIES / PRIVACY) reports NeedsUserInput(), which is always true on our ephemeral MEMFS with no config, and its modal loop crashes Asyncify. Only eeschema was seeding config, so pcbnew and the calculator hit the wizard. Flip TOOL_NEEDS_CONFIG_SEED to true for pcbnew and calculator so seedKicadConfig runs in preRun for all three (it writes the kicad_common.json privacy flags and the sym/fp/design-block lib-tables the providers check), making NeedsUserInput() false and skipping the wizard. Verified in-browser: pcbnew renders a board at /p/mytest/pcbnew/bottom.kicad_pcb and the calculator loads, both wizard-free. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
| .. | ||
| apps | ||
| packages | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.base.json | ||
| turbo.json | ||
KiCad Web
Single web app to create/open KiCad projects, upload files, and open them in the WASM tools (pcbnew / eeschema / calculator) by URL:
/p/<project>/<tool>/<file-path> e.g. /p/project5/pcbnew/nyak.kicad_pcb
Design + decisions: ../features/web-init/0001-web-app-spec.md.
Stack
- Monorepo: pnpm + turbo
- Frontend: Vite + React + TypeScript + shadcn/ui (
apps/frontend) - Backend: Fastify + ts-rest + Zod (
apps/server) - DB: Postgres + Drizzle (project/file metadata)
- Storage: pluggable
FileStorage(local disk now, S3 later) (packages/storage) - Shared types: ts-rest contract + Zod (
packages/contract)
web/
├── apps/
│ ├── frontend/ # Vite React app
│ └── server/ # Fastify API + WASM static + Drizzle
└── packages/
├── contract/ # ts-rest contract + Zod schemas (FE + BE share this)
└── storage/ # FileStorage interface + LocalDiskStorage
Quick start
cd web
cp .env.example .env # Postgres host port defaults to 54329 (non-default)
pnpm install
pnpm db:up # start Postgres (docker compose)
pnpm db:migrate # apply migrations + seed the default owner
pnpm dev # turbo: server :3050 + frontend :3048
Open http://localhost:3048 — create a project, upload files (multi / folder /
.zip), then open a .kicad_pcb / .kicad_sch in its tool.
WASM artifacts
The runtime artifacts (<tool>.js/.wasm, wx.js, images.tar.gz, plus the
<tool>.html harness pages) are build outputs, not committed here. The
complete set is synced into tests/apps/kicad/ by
tests/scripts/setup-kicad-wasm.sh from repo-root output/ (+ wx.js from
wxwidgets/; output/ alone lacks wx.js). That script is a real sync —
it skips files already byte-identical at the destination, so re-running it does
not rewrite the multi-hundred-MB .wasm.
They must be served same-origin as the app. Under the document's COEP/
cross-origin-isolation (set by the Vite dev server), KiCad WASM refuses to load
its glue/wasm from a different origin. So the app serves them from its own
origin with no extra copy: pnpm dev runs scripts/link-wasm.mjs, which
symlinks apps/frontend/public/wasm → tests/apps/kicad. Vite then serves
them at /wasm (same origin). VITE_WASM_ASSET_BASE_URL defaults to /wasm.
- Point the symlink elsewhere with
WASM_SRC_DIR=/path pnpm --filter @kicad-web/frontend link-wasm. - If the tool won't load, the target dir is probably empty — run
tests/scripts/setup-kicad-wasm.shto populatetests/apps/kicad/.
The tool view (WasmTool.tsx + src/wasm/boot.ts) boots the tool directly in
the React document — no iframe. It replicates the proven harness HTML
(tests/apps/kicad/<tool>.html): builds the same global Emscripten Module
config and preRun steps (create canvas, write images.tar.gz, seed config), then
injects the same wx.js + <tool>.js artifacts into the page. It then syncs the
project tree into MEMFS and drives File→Open. The build is non-modularized
(global Module/FS) and pthread-based, so only one tool runs per page load;
switching tools requires a full navigation. locateFile resolves the wasm and
the pthread worker against <base> so they load regardless of the SPA route.
prod: point VITE_WASM_ASSET_BASE_URL at a CDN URL — but that origin must
itself satisfy the same-origin / COEP constraints (e.g. served under the app's
own origin/path).
Scripts
| Command | What |
|---|---|
pnpm dev |
server + frontend (turbo) |
pnpm db:up / pnpm db:down |
start/stop Postgres |
pnpm db:generate |
generate Drizzle migration SQL from schema |
pnpm db:migrate |
apply migrations + seed default owner |
pnpm db:seed |
(re)seed the default owner |
pnpm typecheck |
typecheck all packages |
pnpm build |
build all packages |
API (shared via packages/contract)
JSON (ts-rest): GET/POST /api/projects, GET/DELETE /api/projects/:project,
GET /api/projects/:project/files.
Binary (raw Fastify, response shapes still shared via Zod):
POST /api/projects/:project/files (multi-file + folder),
POST /api/projects/:project/files/zip,
GET /api/projects/:project/files/* (stream bytes).
Status / next iteration
Working end-to-end: create / open / upload (files, folder, zip) / file download / WASM static serving / project list & detail UI / URL routing.
Booting a tool syncs the whole project tree into MEMFS, then opens the
target file. The open step (apps/frontend/src/wasm/open-flow.ts) prefers a
programmatic hook (Module.kicadOpenFile) and falls back to EXPERIMENTAL UI
automation ported from the e2e tests — this needs in-browser validation against
built artifacts, and exposing a real embind open-entry-point is the intended
follow-up (spec §11.2). Lazy/partial MEMFS loading and save-back land together
in a later iteration (spec §§9, 12).