@omrips/viewstl 2.0.0-rc.3 → 2.0.0-rc.5

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.
Files changed (43) hide show
  1. package/README.md +19 -170
  2. package/build/{v2/THIRD-PARTY-NOTICES.txt → THIRD-PARTY-NOTICES.txt} +1006 -1006
  3. package/build/{v2/cad → cad}/occt-import-js.source.txt +19 -19
  4. package/build/chunks/archive-JU75RPTU.js +1 -0
  5. package/build/{v2/chunks/cad-VY42CMP4.js → chunks/cad-4JYHORYZ.js} +1 -1
  6. package/build/{v2/chunks/chunk-JH3H3JWA.js → chunks/chunk-OP2L7L4N.js} +1 -1
  7. package/build/{v2/chunks/chunk-ZMK3UZZE.js → chunks/chunk-PQYEV7WH.js} +1 -1
  8. package/build/{v2/chunks/chunk-R3HNTB5A.js → chunks/chunk-Y6VY64LY.js} +1 -1
  9. package/build/{v2/chunks/export-7OP4ZGGJ.js → chunks/export-VM6OQCQJ.js} +3 -3
  10. package/build/{v2/chunks/export-cad-QPBT756V.js → chunks/export-cad-QXCB6UDU.js} +1 -1
  11. package/build/{v2/chunks/export-zip-EDCWH5PX.js → chunks/export-zip-PVXETVLK.js} +1 -1
  12. package/build/{v2/chunks/print-check-2PU7CSL3.js → chunks/print-check-NJ4IONC5.js} +1 -1
  13. package/build/{v2/chunks/scene-export-KQBS7VRJ.js → chunks/scene-export-ZIXMUNLI.js} +1 -1
  14. package/build/chunks/scene-reader-JGHERXR2.js +1 -0
  15. package/build/{v2/node → node}/analysis-worker.js +11 -11
  16. package/build/{v2/node → node}/index.cjs +7 -7
  17. package/build/{v2/node → node}/index.js +6 -6
  18. package/build/stl_viewer.min.js +51 -0
  19. package/build/{v2/viewstl.cjs → viewstl.cjs} +752 -125
  20. package/build/{v2/viewstl.d.cts → viewstl.d.cts} +50 -0
  21. package/build/{v2/viewstl.d.ts → viewstl.d.ts} +50 -0
  22. package/build/{v2/viewstl.js → viewstl.js} +752 -125
  23. package/build/viewstl.min.js +51 -0
  24. package/build/{v2/viewstl.worker.js → viewstl.worker.js} +1 -1
  25. package/docs/releases/2.0.0-rc.5.md +46 -0
  26. package/docs/v2-compatibility.md +5 -5
  27. package/docs/v2-usage.md +128 -21
  28. package/docs/v2-verification.md +158 -0
  29. package/legacy/README.md +14 -8
  30. package/package.json +18 -35
  31. package/build/v2/README.md +0 -15
  32. package/build/v2/chunks/archive-VGS4J4Z7.js +0 -1
  33. package/build/v2/chunks/scene-reader-APUJOPUR.js +0 -1
  34. package/build/v2/stl_viewer.min.js +0 -51
  35. package/build/v2/viewstl.min.js +0 -51
  36. /package/build/{v2/cad → cad}/occt-import-js.js +0 -0
  37. /package/build/{v2/cad → cad}/occt-import-js.wasm +0 -0
  38. /package/build/{v2/cad → cad}/prepare-cad.source.txt +0 -0
  39. /package/build/{v2/chunks/chunk-U33KANV7.js → chunks/chunk-IK3WL53C.js} +0 -0
  40. /package/build/{v2/chunks/chunk-JS56KC3I.js → chunks/chunk-W5KVLGVR.js} +0 -0
  41. /package/build/{v2/chunks → chunks}/chunk-ZSV4PTIA.js +0 -0
  42. /package/build/{v2/node → node}/index.d.cts +0 -0
  43. /package/build/{v2/node → node}/index.d.ts +0 -0
package/docs/v2-usage.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # ViewSTL 2 — native WebGL2 plugin
2
2
 
3
- Version: `2.0.0-rc.3` (ViewSTL v2.00 release candidate). The npm package is
4
- `@omrips/viewstl`; install the candidate with `npm install @omrips/viewstl@2.0.0-rc.3`.
5
- The legacy plugin remains at its original repository paths and on the production
6
- website. The v2 website is available at https://preview.viewstl.com/.
3
+ Version: `2.0.0-rc.5` (ViewSTL v2.00 release candidate, GitHub and npm).
4
+ On `main`, the new plugin is in `src/` and `build/`; the legacy tree is in
5
+ `legacy/v1/`. The unchanged `master` branch retains original legacy paths.
6
+ The v2 website is available at https://preview.viewstl.com/.
7
7
 
8
8
  ## Quick start
9
9
 
10
- Install with `npm install @omrips/viewstl@2.0.0-rc.3`, then copy the complete
11
- `node_modules/@omrips/viewstl/build/v2/` folder to `/viewstl-v2/` on your website.
10
+ Download the plugin ZIP from the [GitHub release](https://github.com/omrips/viewstl/releases/tag/v2.0.0-rc.5),
11
+ then copy its complete `build/` folder to `/viewstl-v2/` on your website.
12
12
  Paste these three lines into your page's `<body>`:
13
13
 
14
14
  ```html
@@ -23,7 +23,7 @@ required so the viewer can find its worker and optional format assets.
23
23
 
24
24
  ## Browser script
25
25
 
26
- Copy the complete `build/v2/` directory to your server, including the
26
+ Copy the complete `build/` directory to your server, including the
27
27
  worker, its `chunks/` and `cad/` directories and third-party notices. Then:
28
28
 
29
29
  ```html
@@ -58,11 +58,13 @@ The package contains ESM, CommonJS and TypeScript entry points. Importing
58
58
  the package is safe during server-side rendering; constructing a viewer
59
59
  requires a browser DOM and WebGL2.
60
60
 
61
- The package name is `@omrips/viewstl`, under the verified `omrips` npm account.
62
- Release candidates use the `next` dist-tag. Pin the version above for a
63
- repeatable installation; installing a local release tarball is also supported.
61
+ Install with `npm install viewstl`. No version suffix is required: the `latest`
62
+ tag selects the current release candidate. The established `@omrips/viewstl`
63
+ package is also updated with the same plugin; `npm install @omrips/viewstl`
64
+ works without a version suffix. Use either package name consistently in imports
65
+ and asset-copy paths; there is no need to install both.
64
66
 
65
- With a bundler, copy the complete `node_modules/@omrips/viewstl/build/v2/`
67
+ With a bundler, copy the complete `node_modules/viewstl/build/`
66
68
  directory to your app's public assets (for example `public/viewstl-v2/`), keeping
67
69
  the worker, `chunks/`, `cad/` and notices together. Set `worker_url` to its served
68
70
  URL, or provide your bundler's worker factory.
@@ -70,7 +72,7 @@ Do not assume a bundler copies a module worker's dynamic chunks merely
70
72
  because it sees a URL string.
71
73
 
72
74
  ```js
73
- import StlViewer from '@omrips/viewstl';
75
+ import StlViewer from 'viewstl';
74
76
 
75
77
  const viewer = new StlViewer(container, {
76
78
  worker_url: '/viewstl-v2/viewstl.worker.js'
@@ -80,7 +82,7 @@ const viewer = new StlViewer(container, {
80
82
  For Vite, the optional factory form is:
81
83
 
82
84
  ```js
83
- import ViewerWorker from '@omrips/viewstl/worker?worker';
85
+ import ViewerWorker from 'viewstl/worker?worker';
84
86
  const viewer = new StlViewer(container, {
85
87
  worker_factory: () => new ViewerWorker()
86
88
  });
@@ -96,17 +98,17 @@ The CommonJS entry point also requires
96
98
 
97
99
  ## Node.js model analysis
98
100
 
99
- Install `npm install @omrips/viewstl@2.0.0-rc.3`. Node.js 20+ resolves the analysis
101
+ Run `npm install viewstl`. Node.js 20+ resolves the analysis
100
102
  worker and optional CAD WASM directly inside the package; there is no asset-copy
101
103
  step for server use.
102
104
 
103
105
  ```js
104
- import { analyzeFile } from '@omrips/viewstl/node';
106
+ import { analyzeFile } from 'viewstl/node';
105
107
  const report = await analyzeFile('part.stl', { units: 'mm' });
106
108
  console.log(report.dimensions_mm, report.surface_area_mm2, report.volume_mm3);
107
109
  ```
108
110
 
109
- CommonJS is supported with `const { analyzeFile } = require('@omrips/viewstl/node')`.
111
+ CommonJS is supported with `const { analyzeFile } = require('viewstl/node')`.
110
112
  The `/node` entry point is separate from the browser viewer and has no DOM,
111
113
  WebGL or Three.js dependency. It supports STL, OBJ, 3MF, STEP/STP and IGES/IGS.
112
114
  STEP/IGES measurements describe the tessellated mesh, not exact CAD surfaces.
@@ -119,7 +121,7 @@ object. `analyzeBuffer(data, options)` accepts a Node Buffer, Uint8Array or
119
121
  ArrayBuffer, and requires `filename` or `format` to identify the format:
120
122
 
121
123
  ```js
122
- import { analyzeBuffer } from '@omrips/viewstl/node';
124
+ import { analyzeBuffer } from 'viewstl/node';
123
125
  const report = await analyzeBuffer(uploadBuffer, { filename: 'part.stl', units: 'mm' });
124
126
  ```
125
127
 
@@ -151,7 +153,7 @@ The convenience functions share an analyzer with two active workers, up to
151
153
  Create an independent analyzer when the host needs different concurrency:
152
154
 
153
155
  ```js
154
- import { createAnalyzer } from '@omrips/viewstl/node';
156
+ import { createAnalyzer } from 'viewstl/node';
155
157
  const analyzer = createAnalyzer({ concurrency: 1, max_queue: 4 });
156
158
  try {
157
159
  const reports = await Promise.all(paths.map(path => analyzer.analyzeFile(path, {
@@ -220,7 +222,7 @@ app.dispose(); // cancels work and releases listeners, workers and WebGL
220
222
 
221
223
  This controller binds the preview's HTML control layout; the host supplies
222
224
  markup and CSS. The reference layout is `Viewstl/site/index.html` in the sibling
223
- website workspace. `requiredControls` in `src/v2/app.js` validates the control
225
+ website workspace. `requiredControls` in `src/app.js` validates the control
224
226
  IDs before registering handlers. Missing controls and duplicate mounts produce
225
227
  a descriptive error. Custom integrations without that layout use `StlViewer`.
226
228
  Branding, page metadata, advertisements and static help/privacy text belong
@@ -269,7 +271,7 @@ internal geometry or retain its buffers.
269
271
  Use self-contained exports: external part files are not fetched, and native
270
272
  SolidWorks/Solid Edge formats, feature history, PMI and curve-only files are
271
273
  unsupported. Incomplete external-reference assemblies may lack parts.
272
- - Deploy the entire `build/v2/cad/` directory alongside `chunks/` and the worker.
274
+ - Deploy the entire `build/cad/` directory alongside `chunks/` and the worker.
273
275
  CAD code/WASM is loaded only for CAD files; local input bytes are never uploaded.
274
276
  Each job's worker is terminated on completion, cancellation, error or timeout.
275
277
  CAD admission estimates 128 MiB plus 40 times the input size. The WASM heap
@@ -304,6 +306,109 @@ Transparency uses model-level back-to-front sorting; intersecting transparent
304
306
  surfaces can still have ordering artifacts. STL export stores face color,
305
307
  so a 3MF triangle's per-vertex gradient cannot survive STL/VSB export exactly.
306
308
 
309
+ ## Model animation
310
+
311
+ Animation is included in `2.0.0-rc.5` on GitHub and in both npm packages.
312
+ Playback remains inside the native WebGL2 plugin.
313
+
314
+ After a model has loaded, one call starts continuous rotation:
315
+
316
+ ```js
317
+ await viewer.when_idle();
318
+ viewer.animate_model(0, {
319
+ delta: { rotationz: 2 * Math.PI, msec: 5000, loop: true },
320
+ });
321
+ ```
322
+
323
+ The legacy `animation` model option also works. `delta` adds a change to the
324
+ starting pose; `exact` interpolates toward an absolute target. They can operate
325
+ together on different properties and finish independently:
326
+
327
+ ```js
328
+ viewer.animate_model(0, {
329
+ delta: { rotationx: 1, rotationy: 1.1, rotationz: 1.2, msec: 1000, loop: true },
330
+ exact: { x: 1000, y: 150, z: 200, scale: 5, msec: 3000 },
331
+ });
332
+
333
+ viewer.pause_model_animation(0); // Keep the pose and elapsed time.
334
+ viewer.resume_model_animation(0);
335
+ console.log(viewer.get_model_animation_state(0));
336
+ viewer.animate_model(0, null); // Stop at the current pose; discard playback.
337
+ ```
338
+
339
+ Supported properties are `x/y/z`, `rotationx/y/z` in radians and
340
+ `scale` or `scalex/scaley/scalez`. Durations are positive milliseconds, defaulting
341
+ to 300. Scale deltas are additive, as in the legacy API; all resulting scales
342
+ must stay positive. Only delta tracks repeat, accumulating their change each
343
+ cycle. A negative looping scale delta is rejected. Delta and exact may not claim
344
+ the same property. Inputs must be finite and within JavaScript's safe numeric
345
+ range. Invalid replacements leave the current animation intact.
346
+
347
+ Starting a new animation replaces the old one from the current pose. Manual
348
+ position, rotation, scale and unit setters stop that model's playback first.
349
+ Camera interaction, color and viewing-mode changes remain usable. Completed
350
+ tracks no longer write their properties. Removal, `clean()` and `dispose()`
351
+ release playback; paused/completed models do not keep the render loop running.
352
+
353
+ Timing follows elapsed frame timestamps, not frame count. Hidden documents and
354
+ lost WebGL contexts suspend playback without including that gap on resumption.
355
+ User pause is independent of this automatic suspension. State queries return
356
+ `status`, `elapsed_ms`, `suspended` and active `tracks`; unknown IDs return null.
357
+
358
+ Set callbacks in the viewer options:
359
+
360
+ ```js
361
+ const viewer = new StlViewer(container, {
362
+ animation_state_callback(id, state, reason) {
363
+ console.log(id, state.status, reason);
364
+ },
365
+ animation_complete_callback(id, state) {
366
+ console.log('Finished', id, state.elapsed_ms);
367
+ },
368
+ });
369
+ ```
370
+
371
+ Callbacks describe transitions, not every frame, and are deferred until the
372
+ current operation finishes. Completion fires once when every track finishes;
373
+ an animation with a looping track does not complete. Call playback controls on
374
+ loaded models, for example after `when_idle()`.
375
+
376
+ VSB/VSJ preserve the animation definition, original pose, elapsed phase, rotation
377
+ pivot and pause flag. Both scene-loading APIs start incoming animations together
378
+ after all incoming models load. VSJ still needs its referenced model files.
379
+ Animated VSB entries retain unscaled mesh data and explicit scale metadata;
380
+ static entries retain the previous scale-baked convention. This prevents scale
381
+ being applied twice. Older viewers may read the pose but cannot restore the new
382
+ playback-state extension exactly.
383
+
384
+ PNG and modern `export_models()` capture the current pose without stopping
385
+ playback. Legacy `get_stl_bin()` keeps its documented scale/centering-only export
386
+ convention. Regular model files do not store animation. Print checks should run
387
+ while the affected models are paused; subsequent motion makes the report stale.
388
+
389
+ A self-contained example is in `examples/v2/animation.html` in the source
390
+ checkout (serve with `pnpm start`). It includes Play, Pause, Resume, Stop and Reset.
391
+
392
+ The app controller also supports an optional `animation-*` group (see the new
393
+ site's HTML template): `animation-details`, `animation-fields`, `animation-axis`,
394
+ `animation-seconds`, `animation-direction`, `animation-play`, `animation-pause`,
395
+ `animation-stop`, `animation-status` and `animation-error`. Supply the whole group
396
+ or omit it. It exposes selected-model spin around the mesh center, duration
397
+ (0.1–3,600 seconds per turn), direction and playback buttons. Other models keep
398
+ playing when selection changes. Manual tools capture the current animated pose.
399
+ Readouts update at most four times per second; geometry and GPU buffers are reused.
400
+
401
+ `mountViewstlApp` composes `animation_state_callback` and
402
+ `animation_complete_callback` options with its own UI behavior. `app.getState()`
403
+ includes each model's current transform and `animation` state. Ordinary files
404
+ never auto-spin. Imported running animations are paused when the browser reports
405
+ `prefers-reduced-motion: reduce`; explicit user playback remains available.
406
+
407
+ App print checks pause running target models. Optional `print-resume` and
408
+ `print-animation-note` elements expose resumption; closing the panel does not
409
+ silently restart motion. Resuming clears stale results and keeps the panel open.
410
+ Scene saving includes playback; PNG and regular model saving capture a pose.
411
+
307
412
  ## Existing API and intentional differences
308
413
 
309
414
  The existing constructor, model options and documented method names are
@@ -609,6 +714,7 @@ pnpm install --frozen-lockfile
609
714
  pnpm build
610
715
  pnpm test
611
716
  pnpm check:types
717
+ pnpm check:node-types
612
718
  pnpm check:legacy
613
719
  pnpm start
614
720
  pnpm pack
@@ -745,7 +851,8 @@ scene with many remote references can hit the relay's rate limit.
745
851
  Imports append models with new IDs and restore camera, background/grid, transforms,
746
852
  colors/opacity, view modes, smoothing and sections. Saved centering is converted
747
853
  to equivalent model positions without changing existing models' centering. Embed
748
- canvas size, resize/mouse controls and per-model animation are not imported.
854
+ canvas size and resize/mouse controls are not imported. The development animation
855
+ update also restores per-model animation and preserves its original pivot.
749
856
  Scene reading and mesh parsing use workers and the existing memory/triangle/archive
750
857
  limits. Models become visible together after all have loaded. Errors, cancellation,
751
858
  or a throwing progress callback remove the incoming models and retain the existing
@@ -3,6 +3,164 @@
3
3
  This file records dated checks. Older sections describe the alpha at the time
4
4
  of each check; release-candidate results are recorded separately below.
5
5
 
6
+ ## GitHub root-layout release 2.0.0-rc.5 — 2026-11-07
7
+
8
+ The release worktree places the v2 sources/distribution in `src/` and `build/`.
9
+ The complete pre-v2 GitHub tree is archived under `legacy/v1/`; its 23 original
10
+ Git blobs and the 14-file local v1.14 snapshot pass SHA-256/size verification.
11
+ Historical sections below retain the paths and versions used at the time.
12
+
13
+ All **136 unit tests** pass, along with strict browser and Node TypeScript
14
+ consumers. Actual desktop Chrome 156, without device emulation, passes **46
15
+ browser checks**: 15 compatibility, 11 animation, 11 scene and 9 export checks.
16
+ These exercise the relocated classic/ESM entries, module workers, legacy mesh
17
+ bridge, STL/OBJ/3MF/STEP/IGES export/reimport, scene playback, cancellation,
18
+ context restoration and a responsive million-triangle STL export.
19
+
20
+ The packed `@omrips/viewstl@2.0.0-rc.5` tarball installs into a clean consumer
21
+ project. Browser ESM/CommonJS exports, version reporting, worker/CAD asset paths,
22
+ and server analysis of STL, OBJ, 3MF, STEP and IGES pass. Node analysis also
23
+ passes through the CommonJS entry. Archive contents exclude development fixtures,
24
+ legacy runtime copies and site/server files; license notices and lazy assets are included.
25
+
26
+ The layout update also corrects old harness references to the legacy parser and
27
+ worker directory. The compatibility animation assertion now waits for the public
28
+ completion callback and checks the public completed state, rather than assuming
29
+ the old private animation representation or a fixed wall-clock delay.
30
+
31
+ Evidence is retained locally in the original plugin checkout under
32
+ `test-results/github-v2-20261107/`. Physical mobile, Safari and Firefox validation
33
+ remain pending. This release does not update the preview website or npm registry.
34
+
35
+ ## Live animation save/open verification — 2026-11-02
36
+
37
+ Verified the existing `20261102-f04f34e66b7c` release through the actual Save and
38
+ Open controls on `https://preview.viewstl.com/`, in desktop in-app Chromium.
39
+ No runtime changes or redeployment were needed. Two binary STL fixtures have
40
+ offset geometry centers: one runs reverse X rotation at 3 seconds per turn and
41
+ is paused; the other runs forward Z rotation at 7 seconds per turn.
42
+
43
+ Both VSB and VSJ were downloaded, the page was freshly reloaded, and the downloaded
44
+ files were opened and saved again. **40 checks pass** against the resulting files
45
+ and visible controls: two models, camera, position/scale/appearance, animation
46
+ definition, pivot/origin, pause state, exact paused time and pose, advancing running
47
+ time and a pose consistent with that time. VSB mesh coordinates match both their
48
+ original files and the re-saved archive. VSJ file references remain intact. The
49
+ paused model's elapsed time stayed exactly `7400.434000000001` ms in both formats;
50
+ the running model continued from each saved time instead of restarting.
51
+
52
+ A second pair of files was saved with both models paused. PNGs downloaded before
53
+ saving and after independently reopening each format are **byte-for-byte equal**.
54
+ VSB opens alone; VSJ was opened together with its two referenced STL files. Paused
55
+ models expose Resume with the restored axis, duration and direction. There were
56
+ no console errors on the final restored page. This is desktop-browser evidence,
57
+ not physical mobile or other-browser coverage.
58
+
59
+ The website workspace retains actual downloads, UI-state records, PNGs and
60
+ `verification.json` in `results/animation-roundtrip-20261102/`. Recheck those
61
+ artifacts with `node site/qa/verify-animation-downloads.mjs --images`; regenerate
62
+ the offset STL fixtures with `node site/qa/make-animation-fixtures.mjs`.
63
+
64
+ ## Preview-site animation integration — 2026-11-02
65
+
66
+ Deployed to preview as `20261102-f04f34e66b7c`. The optional animation app binding
67
+ is bundled inside viewstl.js; the site's initializer is unchanged. The site adds
68
+ static controls under Models → Animation: axis, seconds per turn, direction and
69
+ Play/Pause/Resume/Stop. Off-center models spin about their center without moving
70
+ their visible pose. Editing uses current transforms, and adding/selecting files
71
+ preserves other models' animation. Print checks pause their targets and offer
72
+ explicit Resume while keeping the results panel open. Reduced-motion preference
73
+ pauses restored playback. Stopped animations also retain an equivalent static
74
+ VSJ pose with the original referenced mesh.
75
+
76
+ All **136 unit tests**, both strict type checks and the legacy guard pass.
77
+ Actual desktop Chromium 156 passes **25 new integration checks** and **96 existing
78
+ site checks**. New coverage includes invalid speed, concurrent models, live-pose
79
+ manual editing, drag entry, print pause/resume, VSB phase/pause restoration,
80
+ stopped off-center VSJ reopening with an identical PNG, callback composition,
81
+ clear/dispose/remount and simulated reduced motion. Keyboard Enter playback was
82
+ also exercised. 320px and 390px viewport checks show no horizontal overflow and
83
+ 44px-high playback buttons; these are emulated widths, not physical touch tests.
84
+
85
+ All 29 public HTTPS files match the local SHA-256 manifest. Live browser smoke
86
+ checks cover loading, keyboard playback and print-check interaction. Nginx, relay
87
+ and Certbot remain active; configuration/service/binary hashes match the previous
88
+ release and SELinux remains enforcing. Initial gzip total is 57,897 bytes; the
89
+ viewer entry is 42,150 bytes. No new million-triangle or total-memory benchmark
90
+ was performed for the app controls; the earlier plugin measurement remains below.
91
+ Physical mobile, Safari/Firefox and actual hidden-tab behavior remain unverified.
92
+
93
+ Evidence: the site workspace's `results/animation-integration/` and
94
+ `results/deploy-20261102-f04f34e66b7c/`. Reproduce the browser checks through the
95
+ local-only `site/qa/animation.html` and `site/qa/index.html` pages. The QA harness
96
+ is excluded from deployment. npm is unchanged (`2.0.0-rc.4`); no GitHub push.
97
+
98
+ ## Model animation — unreleased development update, 2026-11-02
99
+
100
+ The local plugin now has validated legacy delta/exact playback, per-model
101
+ pause/resume/state queries and transition/completion callbacks. Animation uses
102
+ elapsed frame time, retires finished tracks independently, stops on manual
103
+ transform edits and suspends on document hiding or context loss. VSJ and both
104
+ VSB APIs preserve animation phase, pause state, units and rotation pivot; animated
105
+ VSB meshes retain scale in metadata to avoid applying it twice on reopening.
106
+ The native WebGL2 renderer and existing worker architecture remain in use.
107
+
108
+ - All **132 Node tests pass** on Node.js 24.19.0, Windows. Nine new tests cover
109
+ mixed durations, frame cadence, pause/resume/suspension, manual changes,
110
+ replacement and copied state, scale/statistics invalidation, invalid numeric
111
+ inputs and overflow, scene validation, VSJ/VSB restoration and export snapshots.
112
+ - Browser and server-only strict TypeScript checks pass. The legacy guard passes
113
+ for tracked legacy files and all 14 archived files.
114
+ - **11 animation browser checks pass** in actual desktop Chromium 156 on Windows,
115
+ using workers and WebGL2. These cover constructor playback, concurrent models,
116
+ idle behavior, pause/resume/stop, modern and legacy scene restoration, group
117
+ starts, bad-scene rollback, print-check staleness, mesh/PNG snapshots, forced
118
+ context loss/restoration and resource cleanup. Context loss is deliberately
119
+ triggered with `WEBGL_lose_context`, not a hardware failure.
120
+ - Existing browser compatibility (**15**) and scene/rendering (**11**) checks
121
+ also pass: **37 passing browser checks** in total.
122
+ - A generated **1,000,000-triangle** model animated over 120 measured frame
123
+ intervals: median **16.7 ms**, p95 **16.8 ms**, maximum **16.9 ms**. Geometry
124
+ objects and GPU resources retained identity, with two GPU buffers and
125
+ 72,375,000 bytes of resident geometry typed arrays. These are frame intervals
126
+ on one desktop, not GPU timings, allocation profiling or total process memory.
127
+ - The interactive example at `examples/v2/animation.html` runs locally and exposes
128
+ Play, Pause, Resume, Stop and Reset. No models or external assets are fetched.
129
+
130
+ Actual hidden-tab behavior remains unverified: automation tab changes in the
131
+ in-app browser and Chrome did not produce a document `visibilitychange` event.
132
+ The suspension/resumption code is covered by unit tests and the real WebGL
133
+ context-loss check. Firefox, Safari, physical phones/tablets, touch, reduced-motion
134
+ site integration and a total-memory profile have not been tested in this update.
135
+
136
+ Reproduce with `pnpm build`, `pnpm test`, `pnpm check:types`,
137
+ `pnpm check:node-types`, `pnpm check:legacy`, then `pnpm start`. Open
138
+ `tests/v2/animation-browser.html`, `tests/v2/browser.html` and
139
+ `tests/v2/scene-browser.html` and run their checks. The million-triangle and
140
+ background-tab checks have separate buttons on the animation page.
141
+
142
+ Evidence is retained locally in ignored `test-results/animation-*` files. The
143
+ package version is still `2.0.0-rc.4`; this development update has **not** been
144
+ published to npm or deployed. The proposed site work is described in
145
+ [the preview integration plan](v2-animation-site-plan.md).
146
+
147
+ ## Runnable server example 2.0.0-rc.4 — 2026-10-20
148
+
149
+ The npm README now contains a clearly labeled server-only example, including
150
+ installation, the `analyze.mjs` filename, a three-line script that prints JSON,
151
+ and the command to run it. This is a documentation update.
152
+
153
+ The exact example was extracted from the packed README and run with a binary
154
+ STL using the installed package on Node.js 24.19.0 on Windows. The resulting
155
+ JSON contains the expected dimensions, surface area, validated volume and mesh
156
+ checks. All 40 packaged files match the local release sources by SHA-256.
157
+ All 31 existing browser/Node runtime, declaration and CAD assets match the
158
+ public `2.0.0-rc.3` package apart from embedded version strings. The legacy
159
+ guard passes for tracked files and all 14 archived files. No runtime logic was
160
+ changed; the broader API verification below still describes the implementation.
161
+
162
+ Evidence is retained locally in `test-results/npm-2.0.0-rc.4/` (ignored by Git).
163
+
6
164
  ## Node.js model analysis 2.0.0-rc.3 — 2026-10-20
7
165
 
8
166
  The new `@omrips/viewstl/node` export provides `analyzeFile`, `analyzeBuffer`
package/legacy/README.md CHANGED
@@ -1,20 +1,26 @@
1
1
  # Preserved legacy versions
2
2
 
3
- The existing `src/`, top-level `build/` files, and `dependencies/` remain
4
- the GitHub legacy implementation. Keep their existing download paths.
5
- New v2 output belongs under `build/v2/`.
3
+ On the v2 `main` branch, [`v1/`](v1/) contains the complete legacy GitHub
4
+ tree, including its original README, source, build, dependencies and license.
5
+ Its manifest records the original bytes and SHA-256 hashes of all 23 files.
6
+
7
+ The existing [`master` branch](https://github.com/omrips/viewstl/tree/master)
8
+ remains unchanged at the baseline below, preserving explicit master download
9
+ URLs. Existing tags are retained. New v2 source and output live at the root
10
+ under `src/` and `build/` on `main`; callers following the default branch
11
+ receive v2 and should review its migration notes.
6
12
 
7
13
  ## GitHub baseline
8
14
 
9
15
  - Repository: https://github.com/omrips/viewstl
10
- - Original default branch: `master`
16
+ - Legacy branch: `master` (the default before v2)
11
17
  - Baseline commit: `fbb7f5c3894862778c3a6c5d2630573e2496d01f`
12
- - Local preservation tag: `legacy-before-v2-2026-09-30`
13
- - Latest published release when inspected: `v1.13`
18
+ - Preservation tag: `legacy-before-v2-2026-09-30`
19
+ - Latest legacy version tag: `v1.13`
14
20
 
15
21
  The baseline commit includes changes after the `v1.13` tag. Do not treat the
16
- release tag and the current legacy branch as identical. The preservation
17
- tag and development branch are local until explicitly pushed to GitHub.
22
+ release tag and the legacy branch as identical. The v2 release preserves
23
+ both that branch and the existing version tags.
18
24
 
19
25
  ## Local website snapshot
20
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omrips/viewstl",
3
- "version": "2.0.0-rc.3",
3
+ "version": "2.0.0-rc.5",
4
4
  "description": "Local-first STL, OBJ, 3MF, STEP and IGES viewer with native WebGL2 and transferable worker geometry",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,71 +18,54 @@
18
18
  ],
19
19
  "publishConfig": {
20
20
  "access": "public",
21
- "tag": "next",
21
+ "tag": "latest",
22
22
  "registry": "https://registry.npmjs.org/"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",
26
26
  "url": "https://github.com/omrips/viewstl.git"
27
27
  },
28
- "main": "./build/v2/viewstl.cjs",
29
- "module": "./build/v2/viewstl.js",
30
- "types": "./build/v2/viewstl.d.ts",
28
+ "main": "./build/viewstl.cjs",
29
+ "module": "./build/viewstl.js",
30
+ "types": "./build/viewstl.d.ts",
31
31
  "exports": {
32
32
  ".": {
33
33
  "import": {
34
- "types": "./build/v2/viewstl.d.ts",
35
- "default": "./build/v2/viewstl.js"
34
+ "types": "./build/viewstl.d.ts",
35
+ "default": "./build/viewstl.js"
36
36
  },
37
37
  "require": {
38
- "types": "./build/v2/viewstl.d.cts",
39
- "default": "./build/v2/viewstl.cjs"
38
+ "types": "./build/viewstl.d.cts",
39
+ "default": "./build/viewstl.cjs"
40
40
  }
41
41
  },
42
42
  "./node": {
43
43
  "node": {
44
44
  "import": {
45
- "types": "./build/v2/node/index.d.ts",
46
- "default": "./build/v2/node/index.js"
45
+ "types": "./build/node/index.d.ts",
46
+ "default": "./build/node/index.js"
47
47
  },
48
48
  "require": {
49
- "types": "./build/v2/node/index.d.cts",
50
- "default": "./build/v2/node/index.cjs"
49
+ "types": "./build/node/index.d.cts",
50
+ "default": "./build/node/index.cjs"
51
51
  }
52
52
  }
53
53
  },
54
- "./worker": "./build/v2/viewstl.worker.js",
55
- "./browser": "./build/v2/stl_viewer.min.js",
56
- "./assets/*": "./build/v2/*",
54
+ "./worker": "./build/viewstl.worker.js",
55
+ "./browser": "./build/stl_viewer.min.js",
56
+ "./assets/*": "./build/*",
57
57
  "./package.json": "./package.json"
58
58
  },
59
59
  "files": [
60
- "build/v2",
60
+ "build",
61
61
  "LICENSE",
62
62
  "docs/v2-usage.md",
63
63
  "docs/v2-compatibility.md",
64
64
  "docs/v2-verification.md",
65
+ "docs/releases/2.0.0-rc.5.md",
65
66
  "legacy/README.md",
66
67
  "THIRD-PARTY-NOTICES.txt"
67
68
  ],
68
- "scripts": {
69
- "build": "node scripts/build-v2.mjs",
70
- "test": "node --test tests/v2/*.test.mjs",
71
- "check:legacy": "node scripts/check-legacy.mjs",
72
- "check:types": "tsc --noEmit --strict --module NodeNext --moduleResolution NodeNext --target ES2022 --lib ES2022,DOM tests/v2/types.ts",
73
- "check:node-types": "tsc --noEmit --strict --module NodeNext --moduleResolution NodeNext --target ES2022 --lib ES2022 --types node tests/v2/node-types.mts tests/v2/node-types.cts",
74
- "prepack": "node scripts/build-v2.mjs && node scripts/check-legacy.mjs",
75
- "start": "node scripts/serve-v2.mjs"
76
- },
77
- "devDependencies": {
78
- "@types/node": "^20.19.43",
79
- "esbuild": "0.28.2",
80
- "fflate": "0.8.3",
81
- "occt-import-js": "0.0.23",
82
- "prettier": "3.9.9",
83
- "saxes": "6.0.0",
84
- "typescript": "5.9.3"
85
- },
86
69
  "engines": {
87
70
  "node": ">=20"
88
71
  }
@@ -1,15 +0,0 @@
1
- # Native WebGL2 build
2
-
3
- Generated ViewSTL v2.00 release-candidate artifacts. `stl_viewer.min.js` and `viewstl.min.js` are
4
- classic scripts exposing `StlViewer`. `viewstl.js` is the ES module;
5
- `viewstl.cjs` is the CommonJS entry. Keep the module worker, `chunks/` and `cad/`
6
- directories and license notices beside these files when deploying. See [usage](../../docs/v2-usage.md).
7
-
8
- The existing `../stl_viewer.min.js` and its companion files remain the
9
- legacy distribution. A v2 build must never overwrite those files.
10
-
11
- See [the compatibility requirements](../../docs/v2-compatibility.md).
12
-
13
- `node/` contains the separate server analysis entry points and worker, exported
14
- as `@omrips/viewstl/node`. These resolve `cad/` inside the npm package; Node users
15
- do not need to copy assets to a website.
@@ -1 +0,0 @@
1
- import{a,b,c,d}from"./chunk-R3HNTB5A.js";import"./chunk-ZSV4PTIA.js";import"./chunk-JH3H3JWA.js";import"./chunk-JS56KC3I.js";import"./chunk-U33KANV7.js";export{d as parse3MF,a as safePath,b as unzipChecked,c as zipScene};
@@ -1 +0,0 @@
1
- import{b as p}from"./chunk-R3HNTB5A.js";import"./chunk-ZSV4PTIA.js";import{b as d}from"./chunk-JH3H3JWA.js";import"./chunk-JS56KC3I.js";import"./chunk-U33KANV7.js";var w=["flat","smooth","wireframe","edges","mesh","xray","studio","orientation"];function h(e,a){if(!w.includes(e))throw new Error("Invalid display mode");if(typeof a!="boolean")throw new Error("smooth_shading must be a boolean");return{display:e,smooth_shading:e==="smooth"||a}}function g(e){if(e==null)return null;if(!["x","y","z"].includes(e.axis))throw new Error("Section axis must be x, y or z");if(!Number.isFinite(e.position)||e.position<0||e.position>1)throw new Error("Section position must be between 0 and 1");if(e.flip!==void 0&&typeof e.flip!="boolean")throw new Error("Section flip must be a boolean");return{axis:e.axis,position:e.position,flip:e.flip??!1}}var c=["x","y","z"],l=(e,a,n=!1)=>{if(typeof e!="number"||!Number.isFinite(e)||n&&e<=0)throw new Error(`Invalid scene ${a}`);return e},u=e=>String(e||"").match(/\.(vsb|vsj)(?:[?#]|$)/i)?.[1].toLowerCase()||"";function m(e,a){if(!e||!Array.isArray(e.models)||!e.models.length||e.models.length>a.max_archive_entries)throw new Error("Invalid, empty or oversized scene manifest");let n={models:[]};for(let o of["center_models","auto_rotate","grid"])if(e[o]!==void 0){if(typeof e[o]!="boolean")throw new Error(`Invalid scene ${o}`);n[o]=e[o]}if(e.bg_color!==void 0&&(e.bg_color!=="transparent"&&d(e.bg_color),n.bg_color=e.bg_color),e.infinite_grid&&(n.infinite_grid={spacing:l(e.infinite_grid.spacing,"grid spacing",!0),height:l(e.infinite_grid.height,"grid height")}),e.camera_state){let o={};for(let r of["position","target","up"])o[r]=Object.fromEntries(c.map(s=>[s,l(e.camera_state[r]?.[s],"camera")]));let t=c.map(r=>o.position[r]-o.target[r]),i=c.map(r=>o.up[r]);if(!Math.hypot(...t)||!Math.hypot(...i)||!Math.hypot(t[1]*i[2]-t[2]*i[1],t[2]*i[0]-t[0]*i[2],t[0]*i[1]-t[1]*i[0]))throw new Error("Invalid scene camera");n.camera_state=o}for(let o of e.models){if(!o||typeof o.filename!="string"||!o.filename||o.filename.length>8192||/[\x00-\x1f\x7f]/.test(o.filename))throw new Error("Scene model filename is missing or invalid");if(u(o.filename))throw new Error("Nested scene references are not supported");let t={filename:o.filename};typeof o.orig_filename=="string"&&(t.orig_filename=o.orig_filename.slice(0,200));for(let i of c)for(let r of["","rotation","scale"]){let s=r+i;o[s]!==void 0&&(t[s]=l(o[s],s,r==="scale"))}if(o.scale!==void 0&&(t.scale=l(o.scale,"scale",!0)),o.opacity!==void 0&&(t.opacity=l(o.opacity,"opacity"),t.opacity<0||t.opacity>1))throw new Error("Invalid scene opacity");if(o.color!=null&&(o.color!=="transparent"&&d(o.color),t.color=o.color),t.units=o.units??"mm",!["mm","inch"].includes(t.units))throw new Error("Invalid scene units");if(Object.assign(t,h(o.display??"flat",o.smooth_shading??o.display==="smooth")),t.section=g(o.section),o.view_edges!==void 0&&typeof o.view_edges!="boolean")throw new Error("Invalid scene edges");t.view_edges=!!o.view_edges,n.models.push(t)}return n}function S(e,a,n){let o=Object.create(null),t=e;if(a==="vsb"){let i=p(e,n);if(t=i.get("json_data.vsj"),!t)throw new Error("VSB manifest json_data.vsj is missing");let r=m(JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(t)),n);for(let s of r.models){let f=i.get(s.filename);if(!f)throw new Error("VSB model file is missing: "+s.filename);o[s.filename]=f.buffer.slice(f.byteOffset,f.byteOffset+f.byteLength)}return{scene:r,files:o}}return{scene:m(JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(t)),n),files:null}}export{S as readSceneDocument};