@plasius/gpu-shared 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,25 @@ All notable changes to this project will be documented in this file.
16
16
  - **Security**
17
17
  - (placeholder)
18
18
 
19
+ ## [0.1.3] - 2026-03-26
20
+
21
+ - **Added**
22
+ - Public-contract tests that lock the package export surface for the shared
23
+ runtime and bundled brigantine asset.
24
+ - Public `destroy()` teardown hook on `mountGpuShowcase()` so browser consumers can clean up the shared runtime safely on route/page unmount.
25
+
26
+ - **Changed**
27
+ - Documented the import-map pattern for browser demos so consumers stay on the
28
+ published `@plasius/gpu-shared` package surface instead of deep internal
29
+ paths.
30
+ - README usage now documents the shared teardown contract for public consumers.
31
+
32
+ - **Fixed**
33
+ - (placeholder)
34
+
35
+ - **Security**
36
+ - (placeholder)
37
+
19
38
  ## [0.1.2] - 2026-03-23
20
39
 
21
40
  - **Added**
@@ -65,3 +84,4 @@ All notable changes to this project will be documented in this file.
65
84
  [0.1.0]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.0
66
85
  [0.1.1]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.1
67
86
  [0.1.2]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.2
87
+ [0.1.3]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.3
package/README.md CHANGED
@@ -34,12 +34,29 @@ npm install @plasius/gpu-shared
34
34
  ```js
35
35
  import { mountGpuShowcase } from "@plasius/gpu-shared";
36
36
 
37
- await mountGpuShowcase({
37
+ const showcase = await mountGpuShowcase({
38
38
  root: document.getElementById("app"),
39
39
  packageName: "@plasius/gpu-demo-viewer",
40
40
  title: "Flag by the Sea",
41
41
  subtitle: "Shared 3D validation scene for the gpu-* family.",
42
42
  });
43
+
44
+ // Teardown is safe to call repeatedly from a route/page cleanup.
45
+ showcase.destroy();
46
+ ```
47
+
48
+ For browser-only demos served without a bundler, keep the import surface on the
49
+ published package name and resolve it with an import map rather than importing a
50
+ viewer-private or workspace-private source file:
51
+
52
+ ```html
53
+ <script type="importmap">
54
+ {
55
+ "imports": {
56
+ "@plasius/gpu-shared": "../node_modules/@plasius/gpu-shared/dist/index.js"
57
+ }
58
+ }
59
+ </script>
43
60
  ```
44
61
 
45
62
  ## Asset Helpers
@@ -80,6 +97,7 @@ surface for these family demos.
80
97
  ## API
81
98
 
82
99
  - `mountGpuShowcase(options)`
100
+ - Returns `{ state, shipModel, canvas, destroy() }`
83
101
  - `loadGltfModel(url)`
84
102
  - `resolveShowcaseAssetUrl(baseUrl?)`
85
103
  - `showcaseFocusModes`
package/dist/index.cjs CHANGED
@@ -7802,6 +7802,9 @@ function syncTextState(state, shipModel) {
7802
7802
  async function mountGpuShowcase(options = {}) {
7803
7803
  injectStyles();
7804
7804
  const root = options.root ?? document.body;
7805
+ const previousMarkup = root.innerHTML;
7806
+ const previousRenderGameToText = window.render_game_to_text;
7807
+ const previousAdvanceTime = window.advanceTime;
7805
7808
  const focus = options.focus ?? new URLSearchParams(window.location.search).get("focus") ?? "integrated";
7806
7809
  const dom = buildDemoDom(root, {
7807
7810
  packageName: options.packageName ?? "@plasius/gpu-demo-viewer",
@@ -7818,7 +7821,12 @@ async function mountGpuShowcase(options = {}) {
7818
7821
  state.demoDescription = resolveSceneDescription(state, options, shipModel).description;
7819
7822
  syncTextState(state, shipModel);
7820
7823
  const ctx = dom.canvas.getContext("2d");
7824
+ let destroyed = false;
7825
+ let frameHandle = null;
7821
7826
  const renderFrame = (nowMs) => {
7827
+ if (destroyed) {
7828
+ return;
7829
+ }
7822
7830
  if (!state.paused) {
7823
7831
  if (state.lastTimeMs == null) {
7824
7832
  state.lastTimeMs = nowMs;
@@ -7835,27 +7843,53 @@ async function mountGpuShowcase(options = {}) {
7835
7843
  state.demoDescription = resolveSceneDescription(state, options, shipModel).description;
7836
7844
  renderScene(ctx, dom.canvas, state, shipModel, dom);
7837
7845
  syncTextState(state, shipModel);
7838
- requestAnimationFrame(renderFrame);
7846
+ frameHandle = requestAnimationFrame(renderFrame);
7839
7847
  };
7840
- dom.pauseButton.addEventListener("click", () => {
7848
+ const handlePauseClick = () => {
7841
7849
  state.paused = !state.paused;
7842
7850
  dom.pauseButton.textContent = state.paused ? "Resume" : "Pause";
7843
- });
7844
- dom.stressToggle.addEventListener("change", () => {
7851
+ };
7852
+ const handleStressChange = () => {
7845
7853
  state.stress = dom.stressToggle.checked;
7846
- });
7847
- dom.focusMode.addEventListener("change", () => {
7854
+ };
7855
+ const handleFocusChange = () => {
7848
7856
  state.focus = dom.focusMode.value;
7849
7857
  Object.assign(state.camera, {
7850
7858
  ...CAMERA_PRESETS[state.focus],
7851
7859
  target: vec3(...CAMERA_PRESETS[state.focus].target)
7852
7860
  });
7853
- });
7854
- requestAnimationFrame(renderFrame);
7861
+ };
7862
+ dom.pauseButton.addEventListener("click", handlePauseClick);
7863
+ dom.stressToggle.addEventListener("change", handleStressChange);
7864
+ dom.focusMode.addEventListener("change", handleFocusChange);
7865
+ frameHandle = requestAnimationFrame(renderFrame);
7855
7866
  return {
7856
7867
  state,
7857
7868
  shipModel,
7858
- canvas: dom.canvas
7869
+ canvas: dom.canvas,
7870
+ destroy() {
7871
+ if (destroyed) {
7872
+ return;
7873
+ }
7874
+ destroyed = true;
7875
+ if (frameHandle != null) {
7876
+ cancelAnimationFrame(frameHandle);
7877
+ }
7878
+ dom.pauseButton.removeEventListener("click", handlePauseClick);
7879
+ dom.stressToggle.removeEventListener("change", handleStressChange);
7880
+ dom.focusMode.removeEventListener("change", handleFocusChange);
7881
+ root.innerHTML = previousMarkup;
7882
+ if (typeof previousRenderGameToText === "function") {
7883
+ window.render_game_to_text = previousRenderGameToText;
7884
+ } else {
7885
+ delete window.render_game_to_text;
7886
+ }
7887
+ if (typeof previousAdvanceTime === "function") {
7888
+ window.advanceTime = previousAdvanceTime;
7889
+ } else {
7890
+ delete window.advanceTime;
7891
+ }
7892
+ }
7859
7893
  };
7860
7894
  }
7861
7895
  function updatePhysicsSnapshot(state, shipModel) {