pcbjam/features/yjs-bridge/0007-eeschema-essential-ops-prompt.md
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

141 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Init prompt — eeschema essential ops (place symbol / power / text / rect / circle)
Paste the block below into a fresh session.
---
We're on branch `feature/yjs-bridge` (kicad-wasm, 3 repos: root + `kicad` + `wxwidgets`
submodules). The Yjs collaborative-editing bridge for eeschema mostly works, and several core
eeschema-in-wasm UI bugs were fixed. **This session's goal: make the five operations that
define an "it works" eeschema actually work — both natively in the editor AND synced over
collab:**
1. **Place symbol** (SCH_SYMBOL)
2. **Place power symbol** (SCH_SYMBOL with a power lib symbol)
3. **Draw text** (SCH_TEXT)
4. **Draw rectangle** (SCH_SHAPE)
5. **Draw circle** (SCH_SHAPE)
Collab can't be called "working" while adding shapes/symbols doesn't sync, and the editor
can't be called "working" while you can't place a symbol or finish a text dialog.
**First read these memory entries** (MEMORY.md): `yjs_bridge_feature`,
`eeschema_collab_asyncify_apply`, `wasm_eeschema_ui_modal_hotkey_fixes`,
`feedback_build_reuse_schematic_cache`, `wasm_embind_relink_gotcha`, `web_wizard_skip_config_seed`.
Also `features/yjs-bridge/0006-eeschema-apply-findings.md`.
## Current state (what works / what doesn't)
**Native editor (wasm):**
- ✅ Backspace deletes, Delete deletes (hotkey fix). Esc cancels the active tool (focus fix) —
but Esc also exits *browser* fullscreen (unpreventable browser behavior; use a maximized
window, not fullscreen).
- ✅ Quasi-modal dialogs no longer freeze the app (nested event loops now pump via Asyncify in
`wxwidgets/src/wasm/evtloop.cpp`; the text-properties dialog opens + closes).
- ◐ **Drawing rectangles/circles works natively** (user-confirmed) — but see collab below.
- ✗ **Dialogs open in the wrong position / are mis-sized so the OK button is clipped or
unclickable** (the text-properties dialog, and almost certainly the **symbol chooser** and
**power-symbol chooser**). This blocks native place-symbol / place-power / finishing draw-text.
Symptom: `winuniv.cpp(925) SetScrollbar: page size can't be greater than range` asserts; the
dialog content overflows its frame. This is the **#1 blocker for the native ops.**
**Collab apply (peer side):**
- ✅ Wire add/move, delete (any type), SCH_TEXT add, SCH_LABEL add, and **move** of
symbols/junctions/labels incl. their text fields (devirtualized `Move` + `moveFields`).
- ✗ **SCH_SHAPE add (rect/circle) does NOT sync** — reconstructing+committing a new shape traps
in **KiCad core** `SCH_COMMIT::Push` → `view->Add` of a new shape (`invoke_viii` →
"memory access out of bounds").
- ✗ **SCH_SYMBOL add does NOT sync** — the s-expr clipboard-blob reconstruction traps in the
parser `LoadContent` (`invoke_iii` → "table index is out of bounds").
## Root causes (read these — they drive the approach)
1. **Asyncify `call_indirect` mis-dispatch from the apply context.** Editor mutation code invoked
from our `CallAfter`/apply path (NOT inside a tool coroutine) mis-dispatches virtual calls
(`invoke_*`) — silently no-ops or traps. We fixed it for **our own** calls by *devirtualizing*
(explicit class-qualified calls → plain `call`, not `call_indirect`) — see
`moveItemTo`/`moveFields` in `wasm/bindings/eeschema_embind.cpp`. But shape-add and symbol-add
trap **inside KiCad core** (`view->Add`→`ViewGetLayers`; the s-expr parser), which the bridge
can't devirtualize.
- **Fix direction (the headline task): route the apply through a tool coroutine.** Native edits
don't trap because the TOOL_MANAGER runs tool actions as coroutines (the Asyncify root /
`emscripten_fiber_swap` context). Make `doApply` run there — e.g. register a custom
`TOOL_ACTION` whose handler does the SCH_COMMIT and `m_toolManager->RunAction(...)` it
(instead of `frame->CallAfter([]{ doApply… })`). This is **untested** but should fix BOTH
shape-add and symbol-add at once, and likely lets the devirtualization hacks be removed.
The dyncall shim (`scripts/common/shims/dyncall-binding.js.tmpl`) only catches the
"signature mismatch" variant — it can't catch these "out of bounds" traps (they fire inside
the wrong function), so a JS-shim fix is not viable here.
2. **Wasm dialog layout/positioning.** `DIALOG_SHIM::finishDialogSettings` calls `Fit()` +
`Centre()`, but in the wasm port the dialog ends up mis-sized/positioned (OK clipped). Likely
in `wxwidgets/src/wasm/toplevel.cpp` (window position/size, `Centre`, client-area origin —
note `GetScreenPositionOfClientOrigin` "no window" asserts) and/or the wasm dialog/sizer Fit.
Fixing this is high-leverage: it unblocks the symbol chooser, power chooser, and text dialog
all at once.
## Suggested plan
- **Thrust A — wasm dialogs usable (unblocks native place-symbol / place-power / draw-text).**
Reproduce the text-properties dialog and the symbol chooser in the real app; fix the
size/position so OK is reachable. Start in `wxwidgets/src/wasm/toplevel.cpp` +
`dialog.cpp`/`evtloop.cpp`; check how the dialog's size/position is computed vs the viewport.
- **Thrust B — collab apply via a tool coroutine (unblocks shape/symbol ADD sync).** Re-enable
the SCH_SHAPE hand converter (itemToJson already emits shape geometry: `stype`/`sx`/`sy`/
`ex`/`ey`/`cx`/`cy`/`width`/`fill`; NOTE `FILL_T::NO_FILL == 1`, not 0) and the SCH_SYMBOL
s-expr-blob converter, but run `doApply` inside a coroutine so `view->Add` and the parser stop
trapping. Verify a circle/rect/symbol added in tab A appears in tab B.
Do them in either order; A is more self-contained, B is the bigger unknown.
## Architecture / key files
- **Collab bridge (root repo, zero kicad-fork change):** `wasm/bindings/eeschema_embind.cpp` —
`doApply` (deferred via `frame->CallAfter`), `makeItem` (per-type construct; SCH_SHAPE add is
currently DEFERRED/logged, SCH_SYMBOL add too), `moveItemTo`/`moveFields` (devirtualized move),
`itemToJson`/`snapshotItems`/`COLLAB_LISTENER` (emit), `kicadCollabApply/Snapshot/GetPos/
TestMoveFirst`.
- **JS reconciler/transport (unchanged, generic):** `web/apps/frontend/src/wasm/collab/`. Collab
gated by `?collab=1` in `WasmTool.tsx`; channel = `kicad-collab:<slug>:<path>`.
- **Native tools:** `kicad/eeschema/tools/sch_drawing_tools.cpp` (createNewText → DIALOG_TEXT_
PROPERTIES::ShowQuasiModal; drawShapes → `new SCH_SHAPE(type, LAYER_NOTES, 0, fill)`;
placeSymbol). Symbol chooser dialog: `kicad/eeschema/dialogs/dialog_symbol_chooser*`.
- **Wasm dialog/event infra:** `wxwidgets/src/wasm/dialog.cpp` (ShowModal via Asyncify
`startModal`), `evtloop.cpp` (nested-loop Asyncify pump for ShowQuasiModal), `toplevel.cpp`
(TLW position/size/Centre/fullscreen), `app.cpp` (key + mouse routing, toolbar-focus fix),
`window.cpp` (ClientToScreen). `kicad/common/dialog_shim.cpp` (ShowQuasiModal, finishDialogSettings).
## Build / test / verify
- **Build (reuse the warm wxWidgets cache — a fresh per-branch container OOMs at exit 137):**
`COMPOSE_PROJECT_NAME=kicad-wasm-feature-schematic KICAD_NO_MONITOR=1 ./docker/build.sh eeschema`
(~8 min embind-only; ~12–15 min if you touch `kicad`/`wxwidgets` since it recompiles those
incrementally + relinks. Auto-relinks embind-only changes. Don't pipe output; it logs to
`logs/build/`. The host-side asyncify + `wasm-opt -O2` pass runs AFTER the docker "build
complete!" line — wait for the final ~104 MB `output/eeschema.wasm`, not the ~189 MB
pre-opt one.) Then `cd tests && npm run setup:kicad`.
- **Headless e2e:** `cd tests && npx playwright test --config=playwright-kicad.config.ts
--project=firefox kicad/eeschema-ui.spec.ts kicad/eeschema-collab.spec.ts`. NOTE: the apply
path DOES work headless (mutates the model) but the GL canvas can't paint headless (incomplete
window → "no window" assert), so dialog/visual checks need the real app. `kicadCollabSnapshot`
only exposes top-level items (not child field positions) — verify field/visual behavior with a
real-app screenshot (force a redraw first, e.g. resize the window — Playwright WebGL captures
go stale otherwise).
- **Real app:** `cd web && pnpm dev` (frontend :3048, server :3050; `pnpm db:up && pnpm
db:migrate` once). Demo project `demo` has `demo.kicad_sch` (the ECC83 push-pull, 26 symbols).
Two tabs: `http://localhost:3048/p/demo/eeschema/demo.kicad_sch?collab=1`. Devtools console
shows `[collab]` logs. (Tab navigation may pop a `beforeunload` dialog — clear with
`window.onbeforeunload = null` first.) `Module.kicadCollabApply(json)` /
`kicadCollabSnapshot()` / `kicadCollabGetPos(uuid)` are callable from the console for probing.
- **Fast-debug:** patch the built `tests/apps/kicad/eeschema.js` with `console.log` and run a
spec WITHOUT a rebuild; `tests/logs/kicad/<spec>/*.log` captures the console. Granular
`EM_ASM({ console.log(...) })` markers in the C++ to bisect a hang/trap.
## Recent commits (this branch)
root: `a00cbd9` (move carries symbol/label fields), `9fa475f` (symbol move devirtualize +
wxwidgets bump), `9b90be0` (quasi-modal no-freeze + Backspace delete), `8b26c64` (SCH_SHAPE emit
+ defer shape/symbol add). kicad: `4132395` (Backspace hotkey). wxwidgets: `ea599f7` (toolbar
focus), `c27fe8b` (quasi-modal Asyncify pump). Use `/git-feature-commit` to commit across the 3
repos. NOTE: `a00cbd9` is **unsigned** (gpg passphrase wasn't cached) — `git commit --amend -S`
it once gpg is unlocked if signed history matters.