Commit graph pcbjam/scripts
Author SHA1 Message Date
Viktor Vaczi
68430fa87c feat(wasm-dom): tests for context menus + scrollbars; fix clean wx build
- Standalone wx test apps (contextmenu, scrollbar) + e2e specs proving the
  wxScrollBar control, the wxScrolledWindow gutter, and DoPopupMenu.
- KiCad in-app specs: pl_editor + pcbnew canvas right-click context menus and
  scrolled-panel scrollbar gutters, with baseline screenshots.
- build-wx-wasm.sh: serial -j1 fallback when the parallel build trips the
  intermittent clean-build race (a generated header read mid-rewrite by
  concurrent compiles; never happens at -j1). Recovery resumes from built
  objects; a genuine error still fails the serial pass.
- Bump wxwidgets submodule (context menus + draggable scrollbars).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 23:15:49 +02:00
Viktor Vaczi
8dbca8a48b ci: fix stale build-wxuniversal-wasm.sh reference after rename
Commit 7dc1bf2 renamed scripts/build-wxuniversal-wasm.sh to
build-wx-wasm.sh but missed the CI workflow, so the "Build wxWidgets
(wxUniversal WASM)" step ran a script that no longer exists. Update the
run command in ci-ubicloud.yml plus the two stale comments referencing
the old name (ci-ubicloud.yml:43, scripts/common/stages.sh:7).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 19:50:23 +02:00
Viktor Vaczi
7dc1bf27e0 refactor: collapse dual-mode plumbing — the DOM port is the only WASM build
The canvas (wxUniversal) mode is gone (wxwidgets submodule); remove every
piece of side-by-side plumbing so there is exactly one build and one test
flow:

- scripts/build-wxuniversal-wasm.sh -> scripts/build-wx-wasm.sh; no
  --dom/--enable-universal; builds into build-wasm/wxwidgets
- build-wasm-test.sh: no DOM_BUILD / apps-dom rsync mirror / PORT=dom;
  apps build straight into tests/apps (Makefile.wasm PORT conditionals
  collapsed; wx.js + wx-dom.js always pre-js)
- docker/build.sh, build-kicad-target.sh, env.sh: WX_PORT / -dom /
  -universal suffixes removed; kicad builds to kicad-<app>, outputs to
  output/; wx.js/wx-dom.js copied from the real source path
  (/workspace/wxwidgets/build/wasm — the old build-wasm path never
  existed and silently failed)
- setup-kicad-wasm.sh: single target dir; the perl wx-dom.js injection is
  gone — the 7 checked-in kicad pages now reference wx-dom.js directly
- playwright configs serve apps/; fixtures drop the test-results/dom and
  logs/wxwidgets/dom namespacing; boot.spec asserts wxDomPort
  unconditionally; pcbnew.spec uses one reference image;
  appearance.spec assertions unconditional
- compare/update-baseline-screenshots.sh: --port removed
- tests/gal-regression/wasm/Makefile: links build-wasm/wxwidgets and
  carries wx-dom.js as a second pre-js — the gal-webgl suite (30 specs)
  now actually builds and runs here (it needed host-side boost+glm via
  scripts/deps; the bundle had been missing, timing the whole spec out)
- tests: clickCanvas() dispatches via page.mouse (DOM widgets
  legitimately cover the canvas; locator actionability refused the
  click); the comprehensive spec drives wxChoice through its native
  <select> (browser-owned popup cannot be coordinate-clicked)
- docs: README/CLAUDE.md/build.md script names and dirs;
  features/wx-dom-port README reframed (DOM is THE port), visual-notes
  bugs 26-28; FindwxWidgets.cmake config label drops 'wasmuniv'
- wxwidgets submodule -> 9dbacc9448 (DOM-only port, fork diff shrunk)

Gate: full wx e2e suite 292 passed / 1 skipped / 0 failed — first run
ever with the gal-webgl specs green (28 scenarios + load + sequential).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:19:48 +02:00
Viktor Vaczi
a275ca735f fix(wasm-dom): serve:dom script; keep kicad bundles out of the apps-dom mirror
The wx test-app mirror (build-wasm-test.sh --dom, rsync --delete) wiped
the kicad bundles setup-kicad-wasm.sh syncs into apps-dom/kicad —
apps/kicad only carries the checked-in HTML. Exclude kicad/ (that dir is
owned by the setup script). Add 'npm run serve:dom' for browsing the
DOM-port apps; plain 'npm run serve' serves the canvas dir, which has no
kicad bundles in this worktree (canvas KiCad was never built here).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:18:48 +02:00
Viktor Vaczi
cc7faadbc1 chore: drop redundant lexer pre-build — upstream f936eb1 fixed it independently
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:18:48 +02:00
Viktor Vaczi
dba03b237f feat(wasm-dom): Phase 7 — KiCad pcbnew builds and runs on the DOM port
Pipeline: lexer generation (pcbcommon) ordered before the embind
pre-compile — a fresh-build-tree chicken-and-egg any new volume would
hit (generated pcb_lexer.h was included via -I${KICAD_BUILD}/common
before anything generated it). DOM flavor outputs flow through the
host-mounted output/dom/. pcbnew e2e on DOM: editor loads fully (layer
manager, toolbars, WebGL viewport, registry metrics), draw-lines test
passes; the header pixel-reference assertion fails as expected (native
DOM styling vs univ pixels) — port-aware references pending.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:18:05 +02:00
Viktor Vaczi
74c392f88f feat(wasm-dom): Phase 7 plumbing — KiCad DOM-flavor build + test pipeline
WX_PORT=dom flows host → docker container → build scripts:
build-kicad-target.sh selects wxwidgets-dom + kicad-<app>-dom trees;
docker/build.sh passes the env through, copies outputs to output-dom/
incl. wx-dom.js, and post-processes there; build-wxuniversal-wasm.sh
honors WX_PORT and accepts --no-clean again (the kicad pipeline calls it
with that flag — Phase 1's stricter parser had broken the canvas docker
path). setup-kicad-wasm.sh syncs DOM bundles into tests/apps-dom/kicad
and injects the wx-dom.js script tag into the kicad pages;
playwright-kicad.config.ts serves apps-dom under WX_PORT=dom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:18:05 +02:00
Viktor Vaczi
e36854bab6 feat(wasm-dom): Phase 5 baselines + visual notes (menus/toolbars/tooltips)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:17:10 +02:00
Viktor Vaczi
1c8fe361d8 feat(wasm-dom): Phase 3 B2 baselines; parallel test-app links
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:17:09 +02:00
Viktor Vaczi
249010a582 feat(wasm-dom): dual-mode build scaffold — native DOM port builds, links and boots alongside canvas
Build/test plumbing for the second wxWidgets mode: --dom on
build-wxuniversal-wasm.sh (build-wasm/wxwidgets-dom, no universal,
--disable-tooltips, libwx_wasmu_* stubs) and build-wasm-test.sh
(mirrors sources into tests/apps-dom, PORT=dom). Build script now
reconfigures when Makefile.in or autoconf_inc.m4 regenerate (bakefile
chain). Harness: WX_PORT=dom serves apps-dom, namespaces screenshots
into test-results/dom and logs into logs/wxwidgets/dom; compare/update
screenshot scripts take --port dom with baseline-screenshots-dom.
New port-agnostic boot.spec.ts (green on both ports).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:17:09 +02:00
Viktor Vaczi
14ca16cbd3 test(asyncify): red-green race harness + ablation flags, unwind-catch shim, spec tightening, decisions docs
The asyncify single-slot work, executed red-green (full ledger:
docs/features/asyncify-arbiter/redgreen.md; decisions record:
docs/features/async/07-decisions-and-outcome.md):

- tests/apps/standalone/asyncify-races/ + tests/asyncify/ + dedicated
  playwright config: 8 scenarios reproducing the KiCad asyncify failure
  family with the kicad-faithful startup topology (pre-park fiber swap →
  park throw through the live trampoline). Built in 3 variants; the
  SHIM_DISABLE_TRAMPOLINE_HEAL / SHIM_DISABLE_HANDLESLEEP ablation builds
  keep the historical hang and index-out-of-bounds crash reproducible
  forever (mutation-style pins for the existing shims).
- scripts/common/shims/handlesleep.js: catch the "unwind" park sentinel
  in the wakeUp path — when main's last pre-park suspension was a sleep,
  the main-loop park throw escaped through that sleep's promise reaction
  as an uncaught rejection (the calculator/gerbview console errors).
- scripts/common/inject-dyncall-shims.sh: SHIM_DISABLE_* ablation knobs.
- Spec tightening (the acceptance bar): 'uncaught exception: unwind'
  tolerance DELETED from pcbnew/eeschema specs; load-pcb gained a hard
  clean-console gate over 5 asyncify corruption signatures.
- wxwidgets pointer bump: modal LIFO resolvers, pump resolve-on-error,
  sync clipboard IsSupported (014f67e6c1).

Final state: asyncify suite 7/7, wx e2e 291/292 (1 skip), KiCad e2e 40
passed / 2 skipped with ZERO corruption signatures in any log across all
six apps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 17:02:59 +02:00
Gergő Törcsvári
0665b30762
chore(skills): include pcbjam-shared in the git-feature workflow
The pcbjam repo's own git-feature skills + scripts/git-workflow predated the
web/pcbjam-shared submodule and only covered root+kicad+wxwidgets. Add the MIT
contract repo as a fourth member everywhere:

- repos.sh: pcbjam_shared key (path web/pcbjam-shared, main `main`) + layout doc.
- assert-clean.sh / for-each-repo.sh: count-agnostic wording.
- git-feature-start/commit/sync/finish SKILL.md: pcbjam-shared in the branch /
  commit (kicad, wxwidgets, pcbjam-shared, then root) / rebase (root first) /
  finish orders; root pointer-bump staging + diff now include web/pcbjam-shared,
  and the root stage step excludes it like the other submodule trees.

Mirrors the root pcbjam-private skills, which already covered shared.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:05:40 +02:00
Istvan Matejcsok
380206dcfc fix(build): host postprocess silently no-ops when emsdk tools are stubbed
build-kicad-target.sh swaps emsdk's wasm-opt/wasm-emscripten-finalize for
no-op stubs (real binaries preserved as *.real) so emcc skips in-link
asyncify — intended for the CONTAINER emsdk, but a host-mode run left the
host tools/emsdk stubbed (since Jun 9). The stub fakes --version and exits 0,
so every local build's host-side finalize/asyncify/-O2 "succeeded" while
doing nothing: output wasm shipped non-asyncified and aborts at boot with
"asyncify_stop_unwind is not a function" (and stayed 122M vs the correct
187M). get-wasm-opt.sh and apply-finalize.sh now prefer the *.real binary
whenever it exists, making the resolution immune to a stubbed emsdk.

Found while validating the CI e2e fix: pcbnew.wasm built this morning could
not boot in any browser. Local artifacts built since Jun 9 may need their
postprocess re-run (apply-finalize.sh + apply-asyncify.sh).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:25:02 +02:00
Istvan Matejcsok
f936eb17b0 fix(build): generate lexer headers before embind compile — fresh-build CI failed
pcbnew_embind.cpp pulls kicad_clipboard.h -> pcb_io_kicad_sexpr_parser.h ->
pcb_lexer.h, a header GENERATED during make (make_lexer custom command on the
pcbcommon target). The embind compile runs right after CMake configure, so on
a fresh build dir the header doesn't exist yet — the ephemeral Hetzner main CI
(run 27327303032) died on it in minutes. Local/incremental dirs already had the
header, which is why 9dbfddc's -I${KICAD_BUILD}/common alone seemed sufficient.

Pre-build pcbcommon before compiling the bindings: make resolves the real
dependency graph (no duplicated lexer-generator args), the app target needs
pcbcommon anyway so total build time is unchanged, and incrementally it's a
no-op. Verified by deleting pcb_lexer.h/pcb_keywords.cpp/pcbnew_embind.o inside
the docker build-cache volume and rebuilding: pre-build regenerates the header
before the embind compile succeeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:57:23 +02:00
Istvan Matejcsok
df25275843 bench: wasm-opt benchmark harness — Hetzner config sweeps + local QEMU VM
Tooling that produced the perf findings (committed for rerunnability):
- scripts/bench/wasm-opt-bench.sh + o2-config-sweep.sh + sweep.conf:
  replay asyncify/-O2 over a cached fixture under allocator/THP/core
  matrices on the Hetzner runner.
- scripts/bench/setup-vm.sh + cloud-init/ + vm-build.sh: local QEMU
  (HVF) Ubuntu guest with Docker CE to verify Linux builds without
  burning paid runners.
- .gitignore: bench fixtures/results and the VM image stay local.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 07:48:46 +02:00
Istvan Matejcsok
680eb9dc15 perf(build): 4h05m -> 1h15m full CI build — docker caps, pipelined wasm-opt, Binaryen 130
Three orchestration fixes, validated end-to-end on the Hetzner ccx53
(run 27280051992, 1h14m41s vs 4h05m baseline, e2e identical):

- docker-compose CPU/memory caps were hardcoded to dev-Mac defaults
  (10 CPUs / 32G); now env-tunable via KICAD_DOCKER_CPUS/KICAD_DOCKER_MEM
  (CI exports nproc/110G — the 32-core runner was compiling on 10 cores).
- docker/build.sh: split build_app into compile_app (container) +
  postprocess_app (host-side dyncall shims + finalize + asyncify + -O2)
  and added KICAD_PIPELINE=1 mode that overlaps each tool's host-side
  wasm-opt with the next tool's container compile (max 2 concurrent
  postprocesses — pcbnew -O2 peaks ~34G RSS). Also: comma-separated app
  lists for cheap pipeline repros.
- get-wasm-opt.sh: Binaryen default 121 -> 130 (fixes the v121 -O2 lock
  convoy, ~9x) and BINARYEN_BUILD_FROM_SOURCE=1 support: the official
  x86_64-linux release tarballs (Alpine/musl, no LTO, assertions on) run
  asyncify 4x slower than a stock gcc -O3+LTO build with sha256-identical
  output; self-build takes ~5 min and is instantly repaid.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 07:48:46 +02:00
Gergő Törcsvári
9dbfddc527
feat(pcbnew): collab adds footprints (s-expr blob) + vias/zones (native)
Footprint add reconstructs from the bare `(footprint …)` s-expr clipboard blob via
CLIPBOARD_IO (SetWriter/SetReader redirect it to a string for headless/wasm). The
non-footprint `(kicad_pcb …)` envelope parse (parseBOARD) is asyncify-fragile in wasm
— commit.Add of an envelope-parsed item silently stages nothing and subsequent virtual
dispatch traps ("index out of bounds"), the same wall that deferred the eeschema symbol
blob — so vias and zones reconstruct NATIVELY instead: itemToJson emits drill/layer-pair
for a PCB_VIA and the outline polygon for a ZONE, and makeItem builds a fresh PCB_VIA /
ZONE. flushDiff attaches the blob only to types that need it (footprints/board graphics);
tracks/vias/zones skip it. Includes the dangling-parent fix for the envelope path
(SetParent before delete) which footprints don't hit.

Build: kicad_clipboard.h pulls in the generated pcb_lexer.h (emitted into the common
build subdir by make_lexer) — added -I${KICAD_BUILD}/common to the embind include path.

Tests: sample board gains a via + zone; snapshot asserts their native fields are emitted,
and three round-trip add tests (footprint via blob, via/zone native) delete then re-add
each by uuid and confirm it returns at the same position. 6 passed, 1 skipped, 0 aborts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 06:03:59 +02:00
Gergő Törcsvári
b577c05fae
fix(eeschema): wasm dialog positioning (web .window CSS) + collab SCH_SHAPE add via coroutine fiber stack
Thrust A — dialogs render top-left with OK clipped in the WEB app (not the
test harness): root cause was the React shell missing the .window /
.window-canvas CSS that wx.js relies on (it positions each dialog div via
inline left/top, which need position:absolute). Added the rules to
web/apps/frontend/src/index.css. Native draw-text now works end-to-end;
symbol/power choosers render (placing still blocked by absent libraries).

Thrust B — collab apply of a newly-added SCH_SHAPE trapped in KiCad core
(SCH_COMMIT::Push CHT_ADD -> GAL view->Add, an asyncify invoke_* mis-dispatch)
because doApply ran off a fiber stack. doApply now runs inside a COROUTINE so
it executes on a libcontext fiber, the same context native draws use; the add
dispatches correctly. Re-enabled the SCH_SHAPE converter (rect/circle). Added
thirdparty/libcontext to the embind include path (tool/coroutine.h needs it).
Verified two-tab: rectangle + circle drawn in tab A sync + render in tab B.
Extended eeschema-collab.spec.ts apply test with a SCH_SHAPE add.

All changes root-repo only; kicad and wxwidgets forks untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 13:56:43 +02:00
Gergő Törcsvári
b5b1a0ac05
fix(wasm): dynCall signature-mismatch fallback + eeschema collab wire converters
Two things, both verified in the real web app (two-tab eeschema collab).

1. dynCall crash fix (all apps) — scripts/common/shims/dyncall-binding.js.tmpl.
   Programmatic editor edits trapped with 'indirect call signature mismatch': the
   asyncify-instrumented wasmExports[dynCall_<sig>] trampoline does call_indirect with a
   stale type for some table indices (post-asyncify+O2) even though the table entry is
   valid. Proven by patching the built js: at the trap getWasmTableEntry(index) SUCCEEDS
   where the trampoline fails. Fix: the shim now catches the 'signature mismatch'
   RuntimeError and falls back to getWasmTableEntry; the Asyncify unwind sentinel and real
   exceptions re-throw, so instrumentation/unwind is untouched for normal calls. This
   unblocks ALL programmatic edits, not just collab (e.g. eeschema SCH_ITEM::Move).

2. eeschema collab apply converters (wasm/bindings/eeschema_embind.cpp).
   doApply now handles added-item construction (build the SCH_ITEM with the delta's uuid
   via const_cast — as the s-expr parser does — + commit.Add) and richer SCH_LINE
   serialization (start/end/layer) so wire edits reconstruct on the peer. Implemented for
   SCH_LINE (wires) + SCH_JUNCTION; other types log 'no converter for added type' and are
   skipped (next batch). eeschema re-enabled in the web app collab gate.

Tests: eeschema-collab.spec snapshot (green); apply/two-tab skipped — they no-op headless
because the e2e harness's kicadOpenFile returns false (OpenProjectFiles bails before
building the connectivity graph), so SCH_COMMIT::Push doesn't persist. Verified in-app.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 13:56:42 +02:00
Gergő Törcsvári
4f8c2d1f56
feat(eeschema): collab bridge read/emit + build fixes; apply WIP (yjs-bridge commit 3)
eeschema's half of the Yjs collaborative bridge, reusing the generic reconciler /
BroadcastChannel transport unchanged. Zero kicad-fork change: native SCH_ITEM uuid +
native SCHEMATIC_LISTENER. All in the wasm layer (wasm/bindings/eeschema_embind.cpp).

Working (verified in the web app):
- kicadCollabSnapshot(): enumerate sch.Hierarchy() -> LastScreen()->Items() as
  {id,type,x,y}; registers the listener on first call
- emit: SCHEMATIC_LISTENER subclass -> per-item delta via window.kicadCollab.onDelta;
  fires on real SCH_COMMIT::Push (a real wire move broadcasts added/removed/changed)

Apply is a documented follow-up (gated off so a peer tab can't crash): SCH_ITEM::Move
traps with 'indirect call signature mismatch' when invoked outside a KiCad tool
coroutine (Asyncify+fiber+exception-trampoline). Modify/Clone/GetPosition all work;
only the virtual Move write traps. Fix direction: route apply through TOOL_MANAGER.

Also: build-kicad-target.sh now force-relinks when only <app>_embind.cpp changed (the
embind .o isn't a make dep, so new bindings silently vanished), and adds the
expected/rtree/fmt thirdparty includes the eeschema bindings need.

Tests: eeschema-collab.spec.ts covers snapshot (green); apply/two-tab skipped with the
blocker noted. WasmTool gates collab to pl_editor only until eeschema apply works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 13:56:41 +02:00
Istvan Matejcsok
c148443b7b refactor: 💡 remove orphaned files 2026-06-05 13:18:24 +02:00
Istvan Matejcsok
8e413f89ec docs: ✏️ cleanup and organize docs 2026-06-05 12:16:13 +02:00
Balint Ipkovich
4467bcd2c4 feat: gerbview WASM port 2026-06-03 14:23:57 +02:00
Gergő Törcsvári
18a9de0449
fix(wasm): self-heal fiber trampoline so schematic load doesn't hang
The emscripten fiber glue gates Fibers.trampoline() on Fibers.trampolineRunning
and resets it at the end of its loop. At startup emscripten_set_main_loop(...,1)
throws "unwind" to establish the main loop, and KiCad does so from inside a tool
coroutine, so the throw propagates THROUGH the trampoline and skips the reset —
leaving the flag stuck true. Every fiber swap after startup then becomes a silent
no-op, so opening a schematic (SetScreen -> RunAction(selectionClear) -> fiber
swap) hangs forever with the editor stuck on "untitled".

Wrap the trampoline loop in try/finally (inject-dyncall-shims.sh section "3c") so
the flag is always reset. Add tests/kicad/eeschema-load.spec.ts, which opens a
small wires/junctions schematic via Module.kicadOpenFile and asserts the editor
title switches away from "untitled": it times out (RED) without the shim and
passes (GREEN) with it. Also add features/web-init/0002-url-regex-modal-followup.md
capturing the unrelated URL-detection wxRegEx modal surfaced once loading works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 20:32:25 +02:00
Gergő Törcsvári
ddd959fbc2
fix(web): eeschema schematic open path — Asyncify/dynCall fixes + bump submodules
- inject-dyncall-shims.sh: add a dynCallLegacy -> wasmExports fallback so embind's
  generic dynCall path finds the DYNCALLS=1 trampolines. Without it, an Asyncify
  unwind/rewind through an embind call (kicadOpenFile -> OpenProjectFiles) died
  with "f is not a function".
- open-flow.ts: kicadOpenFile runs OpenProjectFiles under Asyncify, so its sync
  return is a falsy placeholder. Invoke it and poll the frame title for the load
  instead of trusting the return value, and never fall back to UI automation
  while the hook is in flight (it would re-enter the suspended Asyncify call).
- .gitignore: ignore .playwright-mcp scratch.
- bump kicad + wxwidgets submodules to the wasm schematic-open fixes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 20:32:24 +02:00
Balint Ipkovich
e28f473040 feat: symbol_editor WASM port 2026-06-02 13:37:26 +02:00
Istvan Matejcsok
c7a71ff2a7 chore: 🤖 add build monitor 2026-06-01 17:37:00 +02:00
Balint Ipkovich
9ab7158057 fix(skills): make git-workflow script paths portable
All four git-feature-* skills hardcoded /Users/torcsi/dev/kicad-wasm/ for
the helper-script invocations and one documentation example. That path
only resolved on the original author's machine — anyone else picking up
/git-feature-{start,commit,sync,finish} would hit "No such file or
directory" before the first pre-flight check ran.

Replaces the absolute paths with relative ones (`bash scripts/...`) and
rewrites the rebase-conflict handoff message in git-feature-sync.md to use
`git -C kicad ...` style instead of a hardcoded cd.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 14:59:09 +02:00
Balint Ipkovich
d735779e23 feat: pl_editor WASM port + browser file dialog fixes
Brings up KiCad's pagelayout_editor (drawing-sheet editor) in the
browser, to roughly the same "boots, canvas visible, partially usable
in-session" level as the existing pcbnew/eeschema/calculator ports.

Build:
- docker/build.sh: add pl_editor to the unified app dispatch (case,
  subdir map, all-loop).
- scripts/kicad/build-kicad-target.sh: add pl_editor to the case;
  upstream target name pl_editor under source subdir pagelayout_editor.
- scripts/kicad/build-pl_editor.sh: 7-line thin wrapper matching the
  pcbnew/eeschema/calculator pattern.
- tests/scripts/setup-kicad-wasm.sh: copy_app pl_editor.

App glue:
- wasm/stubs/nl_pl_editor_plugin_stub.cpp: no-op SpaceMouse plugin so
  pl_editor_frame.cpp's NL_PL_EDITOR_PLUGIN symbols resolve. Mirrors
  nl_pcbnew_plugin_stub.cpp.
- tests/apps/kicad/pl_editor.html: browser shell. preRun creates
  /home/kicad and FS.chdir there so file dialogs land somewhere
  friendly instead of MEMFS root (/dev/, /proc/, etc.).

E2E coverage:
- tests/kicad/pl_editor.spec.ts: 5 tests — smoke (canvas, no abort),
  wizard, File menu has Open/Save As, file-dialog folder-navigation
  regression, canvas + toolbar metrics.
- tests/e2e/filedialog-folder-nav.spec.ts: wxWidgets-level twin of
  the regression test (exercises the underlying widget directly via
  the standalone filedialog_test app).

Submodule bumps:
- kicad → feature/pl-editor (WASM gating in pagelayout_editor's
  CMakeLists + navlib stub).
- wxwidgets → feature/pl-editor (wxGenericFileDialog::OnOk navigates
  into selected directories; wasm/mouse.cpp emits wxEVT_LEFT_DCLICK
  via timestamp-based double-click detection — the latter benefits
  every wxWidgets-WASM app).

See features/pl-editor/ for the design doc + per-repo diff patches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:30:56 +02:00
Viktor Vaczi
b5be072461 asyncify: add wasm-opt -O2 pass + bump wxwidgets (modal promise fix)
Two related fixes for Chrome-specific WASM-runtime issues reported when
running a manually-loaded session (line tool wouldn't even toggle on click;
log filled with 'Uncaught (in promise) unwind' and stderr-tagged
[WASM_FCONTEXT]/[DIAG_*] spam):

1) scripts/common/apply-asyncify.sh — run 'wasm-opt -O2' as a separate
   pass after '--asyncify'.  Without this, large asyncify-instrumented
   coroutine-entry trampolines (notably libcontext's wasm_fcontext_entry
   and COROUTINE<int,TOOL_EVENT const&>::callerStub) exceed V8's
   per-function locals limit and silently stall on first fiber entry,
   leaving the toolbar click dispatched in C++ but the tool never
   activating its 'running=1'/[checked] state in the user's Chrome.
   Firefox tolerates the unoptimised version, so tests on Firefox passed
   while real Chrome stalled.  The -O2 pass shrinks every instrumented
   function back under the threshold, fixing the family of stalls
   systemically (no more per-function removelist whack-a-mole).
   The removelist still contains setupUIConditions() etc. as a safety
   net — they're now redundant under -O2 but harmless.
   Bundle: 338 MB -> 187 MB raw (~45% smaller); test runtime nearly
   halves because parse is faster.  See DEBUG.md §7 and
   memory/bundle-size-asyncify-optimization.md.

2) wxwidgets submodule bump (d1d1627 -> a998a8d) — wasm/dialog.cpp:
   startModal()'s setTimeout-based runEventLoop now awaits
   ccall('ProcessEvents', ..., {async:true}) so the Promise rejection
   from an asyncify-suspended ProcessEvents is caught by the existing
   try/catch instead of escaping as an 'Uncaught (in promise) unwind'
   page error.

Verified: npm run test:kicad:chrome and test:kicad:firefox both pass on
the rebuilt wasm; zero pageerror events; user-reported manual flow now
selects the Draw Lines tool and draws successfully.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 08:32:32 +02:00
Gergő Törcsvári
d6d743340d
refactor(build): fold calculator into unified docker/build.sh dispatch
Convert the calculator port (commit 0464470) from the parallel/copy-paste
pattern to the dispatch pattern used by pcbnew + eeschema.

- docker/build.sh: <app> is now required (no pcbnew default); missing,
  unknown, and --help paths all print "pcbnew | eeschema | calculator | all".
  Adds calculator to build_app() via kicad_subdir_for() (pcb_calculator
  inner subdir, calculator.* output basename). Also fixes the
  EMSDK=/emsdkkicad-wasm-builder typo that snuck in during the calc merge.
- scripts/kicad/build-kicad-target.sh: accepts calculator; introduces
  KICAD_TARGET (pcb_calculator for calc, == APP_NAME otherwise) used for
  the make target, embind include path, and final-log line.
- scripts/kicad/build-calculator.sh: 305-line copy of build-pcbnew.sh
  collapsed to a 7-line wrapper around build-kicad-target.sh.
- tests/scripts/setup-kicad-wasm.sh: copy_app calculator added with the
  same pcb_calculator subdir mapping for the docker-volume fallback path.
- tests/package.json: test:calculator* routed through the shared
  playwright-kicad.config.ts kicad/calculator.spec.ts (mirrors eeschema).
- Delete: docker/build-calculator.sh, tests/scripts/setup-calculator-wasm.sh,
  tests/playwright-calculator.config.ts.

Bumps wxwidgets d1d1627 -> 6fb2eac (origin/wasm-port). The new sha includes
"unic/combobox: add GetCurrentSelection() inline default" which calc's
kicad/pcb_calculator/widgets/unit_selector.cpp needs to compile. Without
this bump the unified dispatch would expose calc as a buildable target but
the build itself would fail. Verified: build.sh all completes clean across
all three apps; pcbnew/eeschema e2e pass; calc compiles and launches (test
stability separate from this refactor).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 16:01:18 +02:00
Gergő Törcsvári
645f89b816
chore(git-workflow): add 3-repo feature branch skills + helper scripts 2026-05-29 16:01:17 +02:00
Gergő Törcsvári
730147d690
feat(schematic): eeschema WASM build + e2e harness 2026-05-29 16:01:17 +02:00
Istvan Matejcsok
0464470733 add calculator build 2026-05-28 17:28:17 +02:00
Viktor Vaczi
7331619404 diagnostics: configurable --diag logging flags + asyncify setupUIConditions fix
- build-pcbnew.sh: add --diag=<gal,coroutine,ctor,all> -> -DKICAD_DIAG_*,
  off by default (forwarded by docker/build.sh)
- diagnostics.js: emit at console.log level (no longer error/warn); still
  gated by SHIM_DIAGNOSTICS=1
- apply-asyncify.sh: exclude PCB_EDIT_FRAME::setupUIConditions() from
  asyncify instrumentation (V8 cannot run the instrumented huge function
  on the rewound ctor stack -> Chrome startup stall; Firefox unaffected)
- DEBUG.md: reusable WASM/asyncify/browser debugging guide, diagnostic
  flag docs, and a production-build (release + -O2 asyncify) recipe
- tests: standalone coroutine vcall/gl repro probes
- bump kicad + wxwidgets submodules (diagnostic gating / debug cleanup)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 16:14:03 +02:00
Viktor Vaczi
01dce40dc9 test(wasm): coroutine crash reproduction harness + per-engine runner
Investigation scaffolding for the Chrome-only KiCad coroutine renderer crash.
Adds isolated reproduction probes exercising the coroutine/Asyncify/fiber layer
under KiCad-like conditions, runnable in BOTH Firefox and system Chrome.

- tests/playwright-coroutine.config.ts + test:coroutine:firefox|chrome npm
  scripts: run the coroutine specs in Firefox AND system Chrome (the old e2e
  config only used bundled Chromium, which never reproduced the crash).
- tests/apps/standalone/coroutine-pthread/: no-wx + pthreads reproduction probes
  (fiber-in-main, nested invoke_/dynCall boundaries, RunMainStack, embind,
  main-loop/rAF activation) + worker_dom_stub.js for wx+pthreads builds.
- tests/apps/Makefile.wasm: coroutine-pthread{,-main,-nested,-nested-ex,-wx,
  -embind,-mainloop} targets.
- scripts/common/shims/diagnostics.js: add EM_ASYNC_JS handleSleep enter/wake
  tracking (DIAG_SLEEP) to detect nested-async at the crash.

Findings (details in research notes): every isolated factor so far — direct /
nested / RunMainStack fiber, wx event loop + all 13 scenarios incl EM_ASYNC_JS,
pthreads, and main-loop/rAF activation — runs CLEAN in system Chrome. The
coroutine/Asyncify layer is exonerated; GL/WebGL is the remaining untested factor
(next). The reliable FF-pass/Chrome-fail repro is still the KiCad pcbnew e2e.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 18:44:12 +02:00
Viktor Vaczi
a4ad69412e fix(wasm): bind dynCall_* to real DYNCALLS=1 exports; refactor shim into files
The shim bound bare dynCall_* names to JS getWasmTableEntry() calls, bypassing
the asyncify-instrumented dynCall_* wasm trampolines that -sDYNCALLS=1 provides.
That broke Asyncify unwind/rewind through indirect calls -> "indirect call
signature mismatch" (caught every frame in Firefox; fatal renderer crash in
Chrome). Bind the bare names to wasmExports["dynCall_<sig>"] instead.

Result: the PCBnew "select draw lines" e2e is green in Firefox (tool selects and
draws, zero page errors). Dropped the fiber-stabilization block, the shipped
diagnostic block, and the exportCallStack JS hack (all compensated for the wrong
binding); shim shrank 521 -> ~250 lines.

- scripts/common/inject-dyncall-shims.sh: orchestrator only; injected JS extracted
  to scripts/common/shims/
- scripts/common/shims/dyncall-binding.js.tmpl: per-signature binding template
- scripts/common/shims/handlesleep.js: nested-Asyncify handleSleep fix (#9153)
- scripts/common/shims/diagnostics.js: logging-only, opt-in via SHIM_DIAGNOSTICS=1
- tests/package.json: add test:kicad:firefox / test:kicad:chrome scripts

Known issue (tracked separately): Chrome still renderer-crashes on the first
coroutine resume. Asyncify.doRewind replays the deep main-context call stack and
exceeds V8's execution-stack limit (Firefox tolerates the same wasm). Proper fix
is JSPI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 15:07:10 +02:00
Viktor Vaczi
9a04217788 wip: nested-asyncify fix, wxAuiToolBar registration, tests, research docs
Main-repo side of a multi-part WIP covering the KiCad WASM tool-selection
and nested-Asyncify work. Submodule commits are in kicad@f6e9239aaa
(libcontext hygiene) and wxwidgets@bb80f91e8b (auibar registration +
dialog diagnostics).

## scripts/common/inject-dyncall-shims.sh

Wrap Asyncify.handleSleep / allocateData to save-and-restore Asyncify.currData
around each EM_ASYNC_JS sleep. This fixes the nested Asyncify collision where
a fiber swap that fired during a modal's event loop clobbered currData, and
the modal's later doRewind used the fiber's buffer and hit "RuntimeError:
index out of bounds". Root cause documented as Emscripten Issue #9153
(wontfix upstream).

Diagnostic-rewind logging (forcedBottomOfCallStack, callStack traces) is
retained to help future debugging of Asyncify state corruption.

## tests/

- tests/playwright-kicad.config.ts: add `channel: 'chrome'` for the
  chromium project so --project=chromium --headed uses system Chrome
  (real GPU) instead of SwiftShader on ARM Mac. Also switch trace to
  retain-on-failure + screenshot on-failure for easier E2E debugging.
- tests/kicad/pcbnew.spec.ts: replace `tool.checked` assertions with a
  label-suffix check (`[checked]`) since our auibar registration encodes
  checked state in the label (no schema change to the registry).
- tests/apps/Makefile.wasm: add `coroutine-nested` build target + include
  it in the all: list.
- tests/apps/standalone/coroutine/: kicad_coroutine_harness.h + test app
  reproducing KiCad COROUTINE semantics against real libcontext.
- tests/apps/standalone/coroutine-nested/: nested_test.cpp reproduces the
  EM_ASYNC_JS-modal + fiber-swap nesting bug in isolation. 8 scenarios
  from baseline_modal_alone through nested_fibers_inside_modal.
- tests/e2e/coroutine.spec.ts + coroutine-nested.spec.ts: Playwright specs
  that load the standalone apps and assert all case cases pass via
  [COROUTINE_TEST] SUMMARY log parsing.

## research/ and features/browser-tools/

Three background docs capturing the investigation trajectory:

- features/browser-tools/0001-kicad-wasm-tool-activation-investigation.md
  Early investigation: why tools don't activate; initial dynCall-empty-
  callback hypothesis.
- features/browser-tools/0002-wasm-coroutine-deep-dive.md
  Deep dive on Asyncify internals, fiber API, QEMU's coroutine-wasm
  reference implementation.
- features/browser-tools/0003-wxauitoolbar-registration-fix.md
  The narrow fix: why wxAuiToolBar needs a registration block, where to
  add it, what the fallback plan is.
- research/threading_1.md: corrected root-cause analysis after reading
  runtime logs — nested-Asyncify currData collision, Emscripten #9153.
- research/threading_2.md: extended research on alternative approaches
  (JSPI/WasmFX/state-machines) and why they don't help here.

## Submodule pointer updates

kicad: f6e9239aaa (wip: libcontext WASM hygiene cleanup)
wxwidgets: bb80f91e8b (wip: wxAuiToolBar element-registry registration +
            dialog diagnostics)

## Open threads not yet in scope

- Firefox/Chrome divergent behavior: "indirect call signature mismatch"
  traps in Firefox vs renderer crash in system Chrome (tracked in
  plans/peaceful-hugging-pnueli.md and the research docs).
- E2E pixel-diff for Draw Lines fails because the test's diff region does
  not cover where the line is actually drawn; tool activation works, the
  line is visible in test-results/pcbnew-draw-lines-02-after-drawing.png.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 13:58:16 +02:00
Viktor Vaczi
6fa6c9207f feat(webgl): Migrate to pure WebGL 2.0 and fix GL canvas layering
Update submodules and build config for pure WebGL 2.0 (drop -sFULL_ES3):
- kicad: VBO-based drawing, compositor FBO rewrite, shader conversion
- wxwidgets: Fix z-index layering so GL canvas renders above 2D UI canvas
- Build: Remove FULL_ES3 from linker flags
- GAL tests: White background, opaque alpha, shared shader converter,
  updated baseline screenshots

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:38:24 +01:00
Viktor Vaczi
d557b21eac fix(docker): Replace emsdk image with Ubuntu + emsdk from source
The Docker build used emscripten/emsdk:4.0.2-arm64 as base image but
env.sh couldn't find emsdk there, installing a second copy. The build
then applied wasm-opt/finalize stubs to the wrong emsdk (hardcoded
/emsdk/), so the real wasm-emscripten-finalize ran in Docker and got
OOM-killed.

- Use ubuntu:22.04 base with emsdk installed from source at /emsdk/
- Make stub paths dynamic via $EMSDK instead of hardcoded /emsdk/
- Skip local emsdk install in env.sh when $EMSDK is already active

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:44:26 +01:00
Viktor Vaczi
b46b4b69f1 fix(wasm): Fix asyncify rewind with asyncify-aware dynCall shims
Emscripten 4.x removed dynCall_* WASM exports, breaking asyncify
rewind through indirect calls (modal dialogs, event handlers).
Generate JS shims that track Asyncify.exportCallStack and register
in wasmExports so doRewind can find them.

Also fixes empty callback functions ((() => {})) generated by
Emscripten 4.x + pthreads for HTML5 events, pthread entry,
sighandler, async timer, and main loop callbacks.

Build pipeline improvements:
- Stub wasm-opt/finalize in Docker (RAM limits), run on host
- Add setup-emsdk.sh for reproducible Emscripten setup
- Simplify env.sh and version management

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:26:48 +01:00
Viktor Vaczi
014816b7a5 fix(wasm): Fix Emscripten empty callback functions for pthreads builds
Emscripten with pthreads generates empty arrow functions `{}` for callback
paths it assumes won't be used. However, when registering HTML5 events from
the main browser thread, targetThread is 0 and the direct call path IS taken.

This fix post-processes the generated JS to replace empty callbacks with
actual dynCall invocations for:
- HTML5 event callbacks (dynCall_iiii) - 7 instances
- pthread entry points (dynCall_ii) - 1 instance
- Signal handlers (dynCall_vi) - 1 instance
- Async timers (dynCall_vi) - 1 instance

Also improves build logging to show completion status and exit code.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:19:13 +01:00
Viktor Vaczi
f5f81b2137 fix(build): Source env.sh in build-wasm-test.sh for Python 3.10+
Emscripten 4.0.22+ requires Python 3.10+ (uses match statement).
The env.sh script sets EMSDK_PYTHON to use Homebrew's Python
instead of the system Python 3.9.6.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 21:24:51 +01:00
Viktor Vaczi
71d2444729 feat(wasm): Disable 3D viewer with comprehensive stubs
- Set KICAD_BUILD_3D_VIEWER_WASM=OFF in build script
- Add comprehensive 3D canvas stubs (~500 lines) including:
  - EDA_3D_CANVAS with wxWidgets event table
  - BOARD_ADAPTER, EDA_3D_VIEWER_SETTINGS
  - PANEL_PREVIEW_3D_MODEL with all event handlers
  - DIALOG_SELECT_3DMODEL
  - TRACK_BALL camera
  - BBOX_2D, BBOX_3D, BVH_CONTAINER_2D
  - OGL_ATT_LIST::GetAttributesList
- Add 3D scenegraph stubs for VRML export
- Update KiCad submodule with WASM guards
- Update WebGL GAL implementation plan to COMPLETE status

All 4 phases of the WebGL GAL implementation are now complete:
- Phase 1: Native test harness (28 scenarios)
- Phase 2: WebGL GAL implementation (~27,800 lines)
- Phase 3: Complete API coverage
- Phase 4: KiCad integration with 3D viewer stubs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 15:15:50 +01:00
Viktor Vaczi
37d973f064 refactor(webgl): Move WebGL GAL from test harness to KiCad source tree
Migrate WebGL GAL implementation from tests/gal-regression/wasm/webgl/
to kicad/common/gal/webgl/ and kicad/include/gal/webgl/.

This integrates the WebGL GAL properly into KiCad's build system:
- Update test Makefile to use sources from kicad/ instead of local copies
- Update build scripts for new source locations
- Add test-gal-webgl.sh script for running WebGL regression tests
- Update Docker to Emscripten 4.0.22

The WebGL GAL passes all 28 regression tests (matching baseline).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 13:15:58 +01:00
Viktor Vaczi
1b1a9d1633 fix(test): Exclude transform-api from comparison and add diagnostics
Changes:
- Exclude gal-transform-api.png from native/WebGL comparison since
  Transform() is dead code in KiCad (never called, has no effect)
- Add verbose diagnostic output showing pixel difference details,
  content bounds, and sample pixels for failing scenarios
- Update README to document that Transform() is dead code in both
  native and WebGL implementations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-09 14:48:23 +01:00
Viktor Vaczi
51bdcffacb fix(build): Source common env.sh in wxuniversal build script
Ensures EMSDK_PYTHON is set correctly for Emscripten 4.0.22+ which
requires explicit Python path configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-09 14:48:23 +01:00
Viktor Vaczi
92466d728c fix(webgl): Add ClearTarget and always rebuild in test script
1. gal_webgl_test.cpp: Add ClearTarget(TARGET_NONCACHED) before ClearScreen()
   - Matches native test harness behavior
   - Fixes accumulated content from previous scenarios
   - arc-segments now 10% different (was 86% due to content accumulation)

2. test-gal-regression.sh: Remove "skip if already built" check
   - Always rebuild to pick up code changes
   - Single script is the source of truth for full test cycle

Current results:
- 3 scenarios MATCH (<1%): basic-lines, arcs, transforms
- Many scenarios close (~10%): arc-segments, circles, hole-walls, etc.
- Some features not implemented (97-100%): glyphs, text-attrs, bitmap

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:37:41 +01:00
Viktor Vaczi
8f803b45ea fix(test): Fix WebGL screenshot capture and comparison
1. Fix gal-webgl.spec.ts sequential test:
   - Wait for isReady() not just module existence
   - Use .gl-canvas selector (same as individual tests)
   - Increase timeout from 50ms to 100ms
   - This fixes blank screenshots in full test runs

2. Fix test-gal-regression.sh comparison:
   - Normalize PNG format before comparing (flatten + sRGB TrueColor)
   - This handles RGBA vs RGB and palette differences
   - gal-basic-lines now passes comparison (0.34% different)

Results after fixes:
- Native vs Baseline: PASSED (28/28)
- WebGL screenshots now capture actual rendered content
- gal-basic-lines matches native (first successful scenario!)
- Other scenarios have rendering differences to investigate

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:32:39 +01:00
Viktor Vaczi
d0e2500048 feat(webgl): Enable all 28 scenarios for WebGL test harness
- Update wasm/Makefile to compile ALL scenario files using wildcard
- Add conditional compilation to scenario_bitmap.cpp (#ifdef __EMSCRIPTEN__)
  to handle OpenGL-specific shader workaround code for native only
- Change canvas dimensions to 1600x1200 to match native baseline (2x Retina)
- Set white background color to match native screenshots
- Update gal-webgl.spec.ts to hide UI overlay before taking screenshots
- Improve test-gal-regression.sh with better ImageMagick comparison

All 28 scenarios now compile and run on both native and WebGL backends.
Native vs baseline: PASSED (28/28 matching)
WebGL rendering: In progress (primitives not yet visible)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:11:03 +01:00