@preference-sl/pref-viewer 2.16.0-beta.3 → 2.16.0-beta.4

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/LICENSE.md ADDED
@@ -0,0 +1,13 @@
1
+ # Proprietary License
2
+
3
+ Copyright © 2026 Preference S.L.
4
+ All rights reserved.
5
+
6
+ This software and its associated documentation are proprietary to Preference S.L.
7
+ The package is made available to authorized customers and partners only for use under the applicable commercial agreement, subscription, order form, statement of work, or other written agreement with Preference S.L.
8
+
9
+ Unless expressly permitted by Preference S.L. in writing, you may not copy, modify, sublicense, redistribute, resell, publish, reverse engineer, or make this software available to third parties.
10
+
11
+ No open-source license is granted by this package. In particular, publication to npm does not grant permission to use the software outside the rights expressly provided by Preference S.L.
12
+
13
+ For licensing questions, contact Preference S.L.
package/README.md ADDED
@@ -0,0 +1,463 @@
1
+ # @preference-sl/pref-viewer
2
+
3
+ `@preference-sl/pref-viewer` is a framework-agnostic Web Component for embedding an interactive 2D/3D product viewer in web applications. It renders glTF/GLB models with Babylon.js, supports SVG drawings, localized UI controls, runtime render settings, asset caching, and export flows for GLB, glTF ZIP, and USDZ.
4
+
5
+ ## Features
6
+
7
+ - Native custom elements: `<pref-viewer>`, `<pref-viewer-2d>`, `<pref-viewer-3d>`, `<pref-viewer-dialog>`, and `<pref-viewer-menu-3d>`.
8
+ - Babylon.js-based 3D visualization for glTF and GLB assets.
9
+ - 2D SVG drawing support with pan and zoom controls.
10
+ - Unified `<pref-viewer>` wrapper that coordinates 2D, 3D, menu, dialog, localization, and loading lifecycle.
11
+ - Sequential task queue for deterministic config, model, scene, materials, drawing, options, and culture updates.
12
+ - Direct URL, base64/data URI, and IndexedDB-backed asset loading.
13
+ - File cache validation using size and timestamp metadata.
14
+ - Runtime render settings for anti-aliasing, ambient occlusion, IBL, shadows, lighting time of day, and hover highlight.
15
+ - Localized UI copy for `en-EN` and `es-ES`.
16
+ - Export helpers for model, scene, or combined model + scene downloads.
17
+ - Type declarations for TypeScript and JSX usage.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @preference-sl/pref-viewer
23
+ ```
24
+
25
+ ## Basic usage
26
+
27
+ Import the package once in your application entry point. The import registers the custom elements globally.
28
+
29
+ ```js
30
+ import "@preference-sl/pref-viewer";
31
+ ```
32
+
33
+ Then use the element in HTML:
34
+
35
+ ```html
36
+ <pref-viewer
37
+ style="display: block; width: 100%; height: 600px;"
38
+ mode="3d"
39
+ culture="en-EN"
40
+ config='{
41
+ "model": {
42
+ "storage": {
43
+ "url": "https://example.com/assets/model.glb"
44
+ },
45
+ "visible": true
46
+ },
47
+ "scene": {
48
+ "storage": {
49
+ "url": "https://example.com/assets/scene.gltf"
50
+ },
51
+ "visible": true
52
+ }
53
+ }'
54
+ ></pref-viewer>
55
+ ```
56
+
57
+ ## JavaScript API usage
58
+
59
+ You can configure the viewer declaratively with attributes or imperatively with JavaScript. Prefer the JavaScript API when passing large objects, dynamic values, or user-driven updates.
60
+
61
+ ```js
62
+ const viewer = document.querySelector("pref-viewer");
63
+
64
+ viewer.loadConfig({
65
+ model: {
66
+ storage: {
67
+ url: "https://example.com/assets/model.glb",
68
+ },
69
+ visible: true,
70
+ },
71
+ scene: {
72
+ storage: {
73
+ url: "https://example.com/assets/scene.gltf",
74
+ },
75
+ visible: true,
76
+ },
77
+ culture: "en-EN",
78
+ });
79
+
80
+ viewer.setOptions({
81
+ camera: "MainCamera",
82
+ ibl: {
83
+ url: "https://example.com/assets/environment.hdr",
84
+ intensity: 1,
85
+ shadows: true,
86
+ },
87
+ lighting: {
88
+ timeOfDay: 0.5,
89
+ },
90
+ });
91
+ ```
92
+
93
+ ## Configuration model
94
+
95
+ The main `config` payload can include model, scene, materials, drawing, options, and culture data.
96
+
97
+ ```js
98
+ viewer.loadConfig({
99
+ model: {
100
+ storage: {
101
+ url: "https://example.com/model.glb",
102
+ },
103
+ visible: true,
104
+ },
105
+ scene: {
106
+ storage: {
107
+ url: "https://example.com/scene.gltf",
108
+ },
109
+ visible: true,
110
+ },
111
+ materials: {
112
+ storage: {
113
+ url: "https://example.com/materials.glb",
114
+ },
115
+ },
116
+ drawing: {
117
+ storage: {
118
+ url: "https://example.com/drawing.svg",
119
+ },
120
+ },
121
+ options: {
122
+ camera: "MainCamera",
123
+ ibl: {
124
+ url: "https://example.com/environment.hdr",
125
+ intensity: 1,
126
+ shadows: true,
127
+ },
128
+ render: {
129
+ antiAliasingEnabled: true,
130
+ ambientOcclusionEnabled: true,
131
+ iblEnabled: true,
132
+ shadowsEnabled: false,
133
+ },
134
+ },
135
+ culture: "en-EN",
136
+ });
137
+ ```
138
+
139
+ ## Asset storage descriptors
140
+
141
+ Storage descriptors are passed through `model.storage`, `scene.storage`, `materials.storage`, or `drawing.storage`.
142
+
143
+ ### Remote URL
144
+
145
+ ```js
146
+ {
147
+ storage: {
148
+ url: "https://example.com/assets/model.glb"
149
+ }
150
+ }
151
+ ```
152
+
153
+ ### IndexedDB-backed asset
154
+
155
+ ```js
156
+ {
157
+ storage: {
158
+ db: "PrefConfiguratorDB",
159
+ table: "gltfModels",
160
+ id: "product-model-id",
161
+ url: "https://example.com/assets/fallback-model.glb"
162
+ }
163
+ }
164
+ ```
165
+
166
+ ### Base64 or data URI
167
+
168
+ ```js
169
+ {
170
+ storage: {
171
+ url: "data:model/gltf-binary;base64,BASE64_CONTENT"
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## Attributes
177
+
178
+ | Attribute | Description |
179
+ | --- | --- |
180
+ | `config` | JSON string with the full viewer configuration. |
181
+ | `model` | JSON string with a model payload. |
182
+ | `scene` | JSON string with a scene/environment payload. |
183
+ | `materials` | JSON string with a materials payload. |
184
+ | `drawing` | JSON string with a 2D drawing payload. |
185
+ | `options` | JSON string with viewer options. |
186
+ | `culture` | UI locale. Supported values: `en-EN`, `es-ES`. |
187
+ | `mode` | Viewer mode: `3d` or `2d`. Defaults to `3d`. |
188
+ | `show-model` | Shows or hides the current model. Use `true` or `false`. |
189
+ | `show-scene` | Shows or hides the current scene/environment. Use `true` or `false`. |
190
+ | `show-menu` | Shows or hides the 3D render settings menu. Defaults to visible. |
191
+ | `accent-color` | Forwarded to the 3D component and menu for theme/accent styling when supported by the internal UI. |
192
+ | `show-animation-menu` | Enables or disables the animation menu when animations are available. |
193
+ | `highlight-enabled` | Enables or disables hover highlight. |
194
+ | `highlight-color` | CSS color used for hover highlight. Defaults to `#ff6700`. |
195
+
196
+ ## Public methods
197
+
198
+ ### Loading and configuration
199
+
200
+ | Method | Description |
201
+ | --- | --- |
202
+ | `loadConfig(config)` | Queues a full configuration update. Accepts an object or JSON string. |
203
+ | `loadModel(model)` | Queues a model update. |
204
+ | `loadScene(scene)` | Queues a scene/environment update. |
205
+ | `loadMaterials(materials)` | Queues a materials update. |
206
+ | `loadDrawing(drawing)` | Queues a 2D drawing update. |
207
+ | `setOptions(options)` | Queues camera, material, IBL, lighting, or render option updates. |
208
+ | `setCulture(culture)` | Changes the active UI locale. |
209
+ | `setMode(mode)` | Switches between `2d` and `3d`. |
210
+
211
+ ### Parallel loading helpers
212
+
213
+ | Method | Description |
214
+ | --- | --- |
215
+ | `loadModelParallel(model)` | Loads a model without waiting for the standard task queue. |
216
+ | `loadSceneParallel(scene)` | Loads a scene/environment without waiting for the standard task queue. |
217
+ | `loadMaterialsParallel(materials)` | Loads materials without waiting for the standard task queue. |
218
+
219
+ ### Visibility and navigation
220
+
221
+ | Method | Description |
222
+ | --- | --- |
223
+ | `showModel()` / `hideModel()` | Shows or hides the loaded model. |
224
+ | `showScene()` / `hideScene()` | Shows or hides the loaded scene/environment. |
225
+ | `syncVisibility(name, isVisible)` | Synchronizes `model` or `scene` visibility with the corresponding reflected attribute. |
226
+ | `zoomCenter()` | Centers the 2D drawing view. |
227
+ | `zoomExtentsAll()` | Fits all 2D drawing content in view. |
228
+ | `zoomIn()` / `zoomOut()` | Controls 2D drawing zoom. |
229
+ | `focusModel()` | Moves focus to the current model interaction target when available. |
230
+ | `getDebugState()` | Returns a 3D runtime debug snapshot when the internal 3D component exposes one. |
231
+
232
+ ### Animation controls
233
+
234
+ | Method | Description |
235
+ | --- | --- |
236
+ | `getAnimations()` | Returns available model animations. |
237
+ | `playAnimation(name, action)` | Plays, pauses, stops, or otherwise controls a named animation. |
238
+ | `setAnimationProgress(name, progress)` | Sets animation progress from `0` to `1`. |
239
+ | `setAnimationLoop(name, loop)` | Enables or disables looping for a named animation. |
240
+
241
+ ### Dialogs and downloads
242
+
243
+ | Method | Description |
244
+ | --- | --- |
245
+ | `openDialog(title, content, footer)` | Opens a viewer dialog. |
246
+ | `closeDialog()` | Closes the active viewer dialog. |
247
+ | `downloadModelGLB()` / `downloadModelGLTF()` / `downloadModelUSDZ()` | Exports the current model. |
248
+ | `downloadSceneGLB()` / `downloadSceneGLTF()` / `downloadSceneUSDZ()` | Exports the current scene/environment. |
249
+ | `downloadModelAndSceneGLB()` / `downloadModelAndSceneGLTF()` / `downloadModelAndSceneUSDZ()` | Exports the combined model and scene. |
250
+
251
+ ## Public properties
252
+
253
+ ### Setters
254
+
255
+ These setters are convenience bridges for framework bindings and property-based integration.
256
+
257
+ | Property | Description |
258
+ | --- | --- |
259
+ | `config` | Forwards to `loadConfig(value)`. |
260
+ | `model` | Forwards to `loadModel(value)`. |
261
+ | `scene` | Forwards to `loadScene(value)`. |
262
+ | `materials` | Forwards to `loadMaterials(value)`. |
263
+ | `drawing` | Forwards to `loadDrawing(value)`. |
264
+ | `options` | Forwards to `setOptions(value)`. |
265
+ | `culture` | Forwards to `setCulture(value)`. |
266
+ | `mode` | Forwards to `setMode(value)`. |
267
+ | `highlightEnabled` | Reflects to the `highlight-enabled` attribute. |
268
+ | `highlightColor` | Reflects to the `highlight-color` attribute. |
269
+
270
+ ### Getters
271
+
272
+ | Property | Description |
273
+ | --- | --- |
274
+ | `culture` | Current resolved locale. |
275
+ | `isInitialized` | Whether the root viewer has completed initialization. |
276
+ | `isLoaded` | Whether the root viewer considers the current load complete. |
277
+ | `isLoading` | Whether a load operation is currently in progress. |
278
+ | `isMode2D` | `true` when the current mode is `2d`. |
279
+ | `isMode3D` | `true` when the current mode is `3d`. |
280
+ ## Events
281
+
282
+ The component emits custom events so host applications can integrate loading indicators, error states, and viewer controls.
283
+
284
+ | Event | Description |
285
+ | --- | --- |
286
+ | `scene-loading` | A 3D scene/model/material operation has started. |
287
+ | `scene-loaded` | A 3D scene/model/material operation completed. Includes operation details when available. |
288
+ | `scene-error` | A scene operation failed. |
289
+ | `scene-setting-options` | Viewer options are being applied. |
290
+ | `scene-set-options` | Viewer options were applied. |
291
+ | `model-loading` | A parallel model load started. |
292
+ | `model-loaded` | A model container loaded successfully. In the parallel path, this means the independent model load completed. |
293
+ | `model-error` | A parallel model load failed. |
294
+ | `environment-loaded` | The environment/scene container loaded successfully. |
295
+ | `materials-loading` | A parallel materials load started. |
296
+ | `materials-loaded` | A parallel materials load completed. |
297
+ | `materials-error` | A parallel materials load failed. |
298
+ | `drawing-loading` | A 2D drawing load started. |
299
+ | `drawing-loaded` | A 2D drawing load completed. |
300
+ | `drawing-zoom-changed` | The 2D drawing zoom changed. |
301
+ | `model-first-painted` | The first frame for a newly loaded model was painted. |
302
+ | `prefviewer-animation-changed` | A model animation state changed. |
303
+ | `pref-viewer-menu-3d-apply` | The 3D settings menu submitted render-setting changes. |
304
+ | `scene-load-assets-complete` | Performance/progress event emitted after asset-container loading completes. |
305
+ | `scene-load-merge-complete` | Performance/progress event emitted after loaded containers are merged into the scene. |
306
+ | `scene-load-shaders-complete` | Performance/progress event emitted after render/shader work completes. |
307
+ | `scene-load-container-complete` | Performance/progress event emitted after an independent container load completes. |
308
+
309
+ Example:
310
+
311
+ ```js
312
+ const viewer = document.querySelector("pref-viewer");
313
+
314
+ viewer.addEventListener("scene-loaded", (event) => {
315
+ console.log("Viewer scene loaded", event.detail);
316
+ });
317
+
318
+ viewer.addEventListener("scene-error", (event) => {
319
+ console.error("Viewer scene failed", event.detail);
320
+ });
321
+ ```
322
+
323
+ ## Progressive model loading / eager model path
324
+
325
+ `loadModelParallel(model)` is the eager/progressive model-loading API. It bypasses the root task queue and asks the 3D controller to load the model container independently while the existing render loop keeps running.
326
+
327
+ How it behaves:
328
+
329
+ 1. The model payload is parsed from an object or JSON string.
330
+ 2. `model-loading` is dispatched immediately from `<pref-viewer>`.
331
+ 3. The 3D component calls `loadModelIndependent(model)`.
332
+ 4. The Babylon controller uses `loadContainerIndependent("model", ...)`.
333
+ 5. If the model is not already claimed by a full scene load, it follows the optimistic `par` path: download/prepare the glTF container immediately, then merge it atomically.
334
+ 6. If a full scene load already claimed the model container, it follows the `claimed` path to avoid a double-load conflict while still letting the independent path own the replacement.
335
+ 7. The previous model is replaced without rebuilding camera-dependent effects during the merge (`releaseEffects: false`), reducing gray-frame/flicker risk.
336
+ 8. After merge, the controller requests a few render frames and waits for paint before dispatching `model-first-painted`.
337
+ 9. The returned detail includes `success`, `error`, `loadedContainers`, and `loadSessionId` when provided by the payload.
338
+
339
+ Example:
340
+
341
+ ```js
342
+ const loadSessionId = crypto.randomUUID();
343
+
344
+ viewer.addEventListener("model-first-painted", (event) => {
345
+ console.log("Model first painted", event.detail);
346
+ });
347
+
348
+ await viewer.loadModelParallel({
349
+ loadSessionId,
350
+ storage: {
351
+ url: "https://example.com/assets/model.glb",
352
+ },
353
+ visible: true,
354
+ });
355
+ ```
356
+
357
+ Important constraints:
358
+
359
+ - The eager model path requires the internal Babylon scene to already exist. If there is no scene, the controller returns `success: false` with `No scene available`.
360
+ - Only one independent model load can run at a time. A concurrent call returns `success: false` with `Already loading model independently`.
361
+ - `model-loaded` means the model container load finished; `model-first-painted` is the stronger signal for UI handoff because it waits for the browser to paint after the atomic merge.
362
+ - Use this path when the product model should appear as soon as possible while slower scene/environment/material work continues. Use `loadConfig()` when deterministic sequential loading is more important than progressive display.
363
+ ## Render settings
364
+
365
+ Render settings can be passed through `setOptions`, initial config, or the built-in 3D menu.
366
+
367
+ ```js
368
+ viewer.setOptions({
369
+ render: {
370
+ antiAliasingEnabled: true,
371
+ ambientOcclusionEnabled: true,
372
+ iblEnabled: true,
373
+ shadowsEnabled: false,
374
+ lightingTimeOfDay: 0.5,
375
+ highlightEnabled: true,
376
+ highlightColor: "#ff6700",
377
+ },
378
+ });
379
+ ```
380
+
381
+ Render settings are persisted in `localStorage` under the `pref-viewer/render-settings` key when changed through the runtime settings flow.
382
+
383
+ ## Localization
384
+
385
+ The default locale is `en-EN`. The viewer currently includes translations for:
386
+
387
+ - `en-EN`
388
+ - `es-ES`
389
+
390
+ ```html
391
+ <pref-viewer culture="es-ES"></pref-viewer>
392
+ ```
393
+
394
+ ```js
395
+ viewer.setCulture("en-EN");
396
+ ```
397
+
398
+ ## Development
399
+
400
+ Install dependencies:
401
+
402
+ ```bash
403
+ npm install
404
+ ```
405
+
406
+ Run tests:
407
+
408
+ ```bash
409
+ npm test
410
+ ```
411
+
412
+ Start the local playground:
413
+
414
+ ```bash
415
+ npm start
416
+ ```
417
+
418
+ The playground is available from `playground/index.html` and serves the project through `http-server` after bundling the local source.
419
+
420
+ ## Package scripts
421
+
422
+ | Script | Description |
423
+ | --- | --- |
424
+ | `npm run build` | Bundles `src/index.js` into `dist/bundle.js` with esbuild. |
425
+ | `npm run build:prefweb` | Creates a production webpack build. |
426
+ | `npm run build:prefweb:dev` | Creates a development webpack build. |
427
+ | `npm start` | Builds the bundle and starts a local HTTP server on port `8080`. |
428
+ | `npm test` | Runs the Vitest test suite. |
429
+ | `npm run release` | Creates a standard-version release. |
430
+ | `npm run release:beta` | Creates a beta prerelease with standard-version. |
431
+
432
+ ## TypeScript and JSX
433
+
434
+ The package ships `index.d.ts`, including JSX support for `<pref-viewer>`.
435
+
436
+ ```tsx
437
+ export function ProductViewer() {
438
+ return (
439
+ <pref-viewer
440
+ model={JSON.stringify({
441
+ storage: {
442
+ url: "https://example.com/assets/model.glb",
443
+ },
444
+ })}
445
+ />
446
+ );
447
+ }
448
+ ```
449
+
450
+ ## Notes for integrators
451
+
452
+ - The component is a Web Component, so it can be used from plain HTML, React, Vue, Angular, or any framework that supports custom elements.
453
+ - Give the element an explicit width and height. Babylon.js needs a measurable canvas container.
454
+ - Use object-based JavaScript APIs for complex payloads. Attributes must be valid JSON strings.
455
+ - Remote assets must be reachable by the browser and must satisfy the expected CORS policy.
456
+ - glTF files with external resources are resolved and cached by the viewer when possible.
457
+ - Avoid repeatedly replacing large assets if only visibility, camera, lighting, or material options changed.
458
+
459
+ ## License
460
+
461
+ Proprietary. Copyright (c) 2026 Preference S.L. All rights reserved.
462
+
463
+ This package is published for authorized Preference S.L. customers and partners. Use, distribution, and access are governed by the applicable commercial agreement with Preference S.L. See [LICENSE.md](./LICENSE.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.16.0-beta.3",
3
+ "version": "2.16.0-beta.4",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
@@ -19,7 +19,7 @@
19
19
  "publishConfig": {
20
20
  "access": "public"
21
21
  },
22
- "license": "MIT",
22
+ "license": "SEE LICENSE IN LICENSE.md",
23
23
  "type": "module",
24
24
  "main": "src/index.js",
25
25
  "types": "index.d.ts",
package/Readme.md DELETED
@@ -1,8 +0,0 @@
1
- # @preference-sl/pref-viewer
2
-
3
- A Web Component for visualizing GLTF models using Babylon.js.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @preference-sl/pref-viewer