Redesign the VTK.wasm JavaScript API + docs overhaul#45
Conversation
Replace the scattered VtkWASMLoader/createNamespace entry points with a single loadVtkWasm() that returns a cached VtkWasmRuntime, the factory for StandaloneSession and RemoteSession. Both sessions wrap their C++ handle as a private #native and expose dispose()/[Symbol.dispose] for clean teardown. - runtime.js: loadVtkWasm + VtkWasmRuntime (module cache, session factories) - standaloneSession.js / remoteSession.js: session wrappers with lifecycle - remoteSession: rely on user-provided canvas ids (bindCanvas) instead of a hard-coded internal selector; never create/remove canvas elements - index.js: public exports + <script id="vtk-wasm"> bootstrap - drop legacy (<9.5) support: sessionFactory, stateDecorators, dual-path branches in proxy/scriptLoader, and the old remote.js shims - vite: build from src/index.js (esm "index" + umd "vtk") BREAKING CHANGE: VtkWASMLoader, createNamespace, and the standalone/remote entry modules are removed in favor of loadVtkWasm() and runtime.create*Session().
Update the simple-app example and the plain-javascript demos to use
loadVtkWasm({ url }).createStandaloneSession().vtk instead of the removed
VtkWASMLoader/createNamespace API.
- package.json: collapse exports to the new "." (index) + "./viewer" entry points; add docs:api script (run before docs:dev/docs:build) - add TypeDoc (+ markdown/vitepress theme) and mermaid dev deps - typedoc.json + tsconfig.json: generate the API reference from source JSDoc into docs/api (allowJs, no TS migration) - gitignore the generated docs/api output
- new conceptual guides: Loading VTK.wasm (with a mermaid path diagram), Standalone Session, and Remote Session, each linking into the generated API reference rather than restating signatures - merge HTML Script Tag + Bundler Integration into "Adding VTK.wasm to a Project"; remove the JS Getting started page - reorder the "For JavaScript developers" sidebar and repoint inbound links - wire the TypeDoc-generated API reference into the nav/sidebar (expanded by default) and enable mermaid via vitepress-plugin-mermaid
The C++ vtkRemoteSession has no create/destroy, so creating or deleting objects through a remote session's vtk proxy would throw. Gate both on typeof checks: warn and return undefined/false instead. Document that a remote session can only control server-owned objects via getVtkObject.
|
fyi @ansbbrooks your input will be much appreciated. |
|
I love that this refactor takes advantage of the new However, can we add the
In Trame for example, there are several methods that return Trame.connect(): Promise<void> // would be nice if it were "connectAsync()"
WebsocketSession.close() : Promise<void> // would be nice if it were "closeAsync()"In this PR: loadVtkWasm(): Promise<VtkWasmRuntime> // should be "loadVtkWasmAsync()"
createViewer(): Promise<ExportViewer> // should be "createViewerAsync()"
createScriptURL(): Promise<string> // should be "createScriptURLAsync()" |
sounds reasonable to me. i like this idea. |
RemoteSession can now bind a render window to a canvas element passed directly, not just by DOM id. When the Emscripten build exposes specialHTMLTargets, the element is registered there so it needs neither an id nor to be attached to the document; otherwise a CSS selector built from the element id is used as a fallback. - patch the glue source to expose Module.specialHTMLTargets - pass the module into RemoteSession and track the canvas->target mapping (canvasTargets) plus bound render windows (boundRenderWindows) - bind render windows lazily during update() once their state arrives, and clean up specialHTMLTargets entries on unbindCanvas/dispose
Rename every method and function under src/ that returns a promise so its name ends in `Async`, leaving synchronous methods (e.g. VtkWasmRuntime.isAsync) untouched. Docs and examples are updated to match. BREAKING CHANGE: the following promise-returning APIs were renamed: - loadVtkWasm -> loadVtkWasmAsync - createViewer -> createViewerAsync - RemoteSession.update/setSize/fetchState/fetchHash/pushHash gain the `Async` suffix (updateAsync, setSizeAsync, fetchStateAsync, fetchHashAsync, pushHashAsync) - ExportViewer.load -> loadAsync Callers must update to the new names.
|
@ansbbrooks i pushed two more commits. one enables direct html canvas, second adds an "Async" suffix to promise returning methods. @jourdain please review. |
| function exposeSpecialHTMLTargets(buffer) { | ||
| const text = new TextDecoder().decode(buffer); | ||
| if (text.includes(SPECIAL_TARGETS_PATCH) || !text.includes(SPECIAL_TARGETS_DECL)) { | ||
| return buffer; | ||
| } | ||
| return new TextEncoder().encode(text.replace(SPECIAL_TARGETS_DECL, SPECIAL_TARGETS_PATCH)).buffer; |
There was a problem hiding this comment.
we can avoid this after https://gitlab.kitware.com/vtk/vtk/-/merge_requests/13345 is merged.
| this.remoting.sceneManager.bindRenderWindow(rwId, selector); | ||
| this.remoting.sceneManager.startEventLoop(rwId); | ||
| const target = this.remoting.bindCanvas(rwId, canvas); | ||
| this.remoting.native.bindRenderWindow(rwId, target); |
There was a problem hiding this comment.
shoud this.remoting.bindCanvas(rwId, canvas); and this.remoting.native.bindRenderWindow(rwId, target); be only one line?
|
|
||
| this.canvasTargets.set(rwId, { canvas, target }); | ||
| addCanvasEventListeners(canvas); | ||
| return target; |
There was a problem hiding this comment.
why don't we bind the target on the native side right now? Is it because the rw may not be available yet?
Summary
Replaces the scattered, confusingly-named loader/session API with a single, lean entry point and adds clean teardown. Also drops legacy (<9.5) support, regenerates the docs around the new API, and wires up an auto-generated API reference.
Motivation
The previous API had three pain points:
createNamespace()(free function),VtkWASMLoader.createNamespace(), andVtkWASMLoader.createStandaloneSession()— no canonical path.RemoteSessionclass shadowed the C++vtkRemoteSessionit wrapped (exposed as.sceneManager), andload()meant three different things.What changed
Core API
loadVtkWasmAsync(options)→ cachedVtkWasmRuntime, the single factory for sessions.runtime.createStandaloneSession()/runtime.createRemoteSession(); the C++ handle is private (#native), andsession.vtkis the one way to the namespace.dispose()/[Symbol.dispose]on runtime and both sessions for real teardown (usingworks).RemoteSessionnow binds to user-provided canvases (bindCanvas) instead of a hard-coded internal selector, and never creates/removes canvas elements. A canvas element can be passed directly — when the build exposesspecialHTMLTargets, the element is registered there, so it needs neither anidnor to be attached to the document (a CSS selector from the element id is the fallback). Render windows are bound lazily duringupdate()once their state arrives, andspecialHTMLTargetsentries are cleaned up onunbindCanvas/dispose.create/destroythrough a remote session'svtkproxy are now guarded — the C++vtkRemoteSessionhas no such methods, so they warn and returnundefined/falseinstead of throwing. A remote session can only control server-owned objects viagetVtkObject.sessionFactory,stateDecorators, and the dual-path branches inproxy/scriptLoader/remoteare gone.Naming — every promise-returning method/function under
src/is suffixed withAsync; synchronous methods (e.g.VtkWasmRuntime.isAsync) are untouched. Docs and examples are updated to match.flowchart TD ST["HTML script tag (global vtkWASM)"] BD["Bundler import"] AN["Annotation script tag"] ST --> LF["loadVtkWasmAsync(options)"] BD --> LF LF --> RT["VtkWasmRuntime — cached per url + config"] RT -->|createStandaloneSession| SS["StandaloneSession"] RT -->|createRemoteSession| RS["RemoteSession"] SS --> NS["session.vtk: create & render objects"] RS --> RW["bindNetwork + bindCanvas + update"] AN -.->|auto: loads + standalone session| NS NS --> DS["session.dispose()"] RW --> DS RT --> DR["runtime.dispose()"]Packaging — exports collapse to
.(index) +./viewer; vite builds fromsrc/index.js.Docs
Migration
new VtkWASMLoader(); await l.load(url, cfg, name)await loadVtkWasmAsync({ url, ...cfg, wasmBaseName: name })createNamespace(url, cfg)(await loadVtkWasmAsync({ url, ...cfg })).createStandaloneSession().vtknew RemoteSession(); await r.load(url, cfg)(await loadVtkWasmAsync({ url, ...cfg })).createRemoteSession()remote.sceneManager.xxx()remote.vtk/remotemethods (#nativeis private)import … from "@kitware/vtk-wasm/vtk"//remoteimport { loadVtkWasmAsync } from "@kitware/vtk-wasm"session.dispose(),runtime.dispose(),usingPromise-returning APIs were also renamed to carry an
Asyncsuffix:loadVtkWasmloadVtkWasmAsynccreateViewercreateViewerAsyncExportViewer.loadExportViewer.loadAsyncRemoteSession.update/setSize/fetchState/fetchHash/pushHashupdateAsync/setSizeAsync/fetchStateAsync/fetchHashAsync/pushHashAsyncTesting
npm run build— ESM + both UMD bundles build clean.npm run lint— passes.npm run docs:build— builds with no dead links (only the pre-existing mermaid chunk-size advisory).