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
This commit is contained in:
Gergő Törcsvári 2026-07-06 08:35:18 +02:00
commit 62ca571802
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
14 changed files with 570 additions and 27 deletions

View file

@ -9,6 +9,7 @@ import {
projectToolPath,
toolSchema,
ydocHasState,
syncLayoutToY,
yToDoc,
type KicadDoc,
type Tool,
@ -570,6 +571,9 @@ export function WasmTool({
const startedRef = React.useRef(false);
const driftRef = React.useRef<{ stop(): void } | null>(null);
const sheetManagerRef = React.useRef<SheetCollabManager | null>(null);
// The single-room collab doc (pcbnew/pl_editor), for the layout save-sync
// (miss 08B); eeschema routes per sheet through the manager instead.
const collabDocRef = React.useRef<import("yjs").Doc | null>(null);
const [status, setStatus] = React.useState("Loading tool…");
const [logs, setLogs] = React.useState<string[]>([]);
const [showLog, setShowLog] = React.useState(false);
@ -795,6 +799,21 @@ export function WasmTool({
onSaved: (relPath) => {
if (relPath.endsWith(".kicad_sch")) void sheetManagerRef.current?.onboard(relPath);
},
// Non-item document state (title block, paper, setup…) only reaches the
// room at seed time; reconcile it from every save (miss 08B).
onSavedText: (relPath, text) => {
if (sheetManagerRef.current) {
sheetManagerRef.current.syncLayoutFromSave(relPath, text);
return;
}
if (collabDocRef.current && relPath === targetPath) {
try {
syncLayoutToY(fileToDoc(text), collabDocRef.current, "layout-save");
} catch (err) {
append(`[save] layout sync failed: ${String(err)}`);
}
}
},
});
const { session, targetBytes } = await maybeConnectDocSession(win, {
docSource,
@ -865,6 +884,7 @@ export function WasmTool({
log: append,
onStatus: setStatus,
});
collabDocRef.current = collabHandle?.doc ?? null;
if (collabHandle && targetPath && COLLAB_TOOLS.has(tool)) {
driftRef.current = startDriftDetection({
doc: collabHandle.doc,
@ -897,6 +917,7 @@ export function WasmTool({
// onActiveChange(null).
sheetManagerRef.current?.destroy();
sheetManagerRef.current = null;
collabDocRef.current = null;
oom.stop();
};
// Boot is one-shot per mount; deps intentionally exclude files/targetPath so