read-only-viewer 0003: no lib catalog for viewers; 3D viewer from the session menu

- kicadShow3DViewer embind (pcbnew-only name, registered in the unguarded
  section so the merged kicad_editor image carries it): runOnCoroutine →
  ACTIONS::show3DViewer.
- Session menu: "3D viewer" row (pcbnew) — the only 3D entry once the wx
  chrome is hidden. Runs the deferred model prescan first.
- Read-only sessions: skip the boot-time enableRealtime scope-room socket
  (would 401 for non-members) and defer the board's 3D-model prescan until
  the viewer is opened (deferBoardModelPrescan / runDeferredModelPrescan).
- kicad submodule → read-only allowlist for the 3D viewer actions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXCntRWNn89M72EkrqMkvc
This commit is contained in:
Gergő Törcsvári 2026-08-27 15:26:30 +02:00
commit 97d4f9657e
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
8 changed files with 152 additions and 6 deletions

View file

@ -240,6 +240,30 @@ export async function collectBoardModelFiles(
return out;
}
/** Board text whose prescan was deferred (read-only sessions): run on the
* first 3D open via {@link runDeferredModelPrescan}. */
let deferredBoardText: string | null = null;
/**
* Read-only viewers download nothing 3D until they actually open the viewer:
* park the board text instead of prescanning it at project open. The last
* board staged wins (one board per session).
*/
export function deferBoardModelPrescan(boardText: string): void {
deferredBoardText = boardText;
}
/** Run the parked prescan (no-op when nothing was deferred or it already ran).
* Resolves when the models are in MEMFS, so a caller can open the 3D viewer
* right after and have it resolve refs locally (the per-model C++ ensure
* still covers any miss). */
export async function runDeferredModelPrescan(): Promise<void> {
const text = deferredBoardText;
if (text === null) return;
deferredBoardText = null;
await prescanBoardModels(text);
}
/**
* Prefetch every model a board references (fire-and-forget from the project
* sync). Bodies land in IDB + MEMFS before the user opens the 3D viewer in the
@ -250,6 +274,7 @@ export async function prescanBoardModels(
concurrency = 6,
): Promise<void> {
if (!installedSource) return;
deferredBoardText = null;
const refs = scanModelRefs(boardText);
if (!refs.length) return;