@virtualdisplay.io/client 4.0.0-alpha.3 → 4.0.0-alpha.30

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 (47) hide show
  1. package/CHANGELOG.md +218 -1
  2. package/README.md +116 -155
  3. package/dist/browser-DfB_An57.cjs +8 -0
  4. package/dist/browser-ey6Iih3j.js +1540 -0
  5. package/dist/index.d.ts +660 -0
  6. package/dist/virtualdisplay.client.cjs.js +606 -1
  7. package/dist/virtualdisplay.client.es.js +4113 -794
  8. package/dist/virtualdisplay.client.umd.js +613 -1
  9. package/package.json +3 -3
  10. package/dist/src/attributes/attribute.d.ts +0 -38
  11. package/dist/src/attributes/attribute.d.ts.map +0 -1
  12. package/dist/src/attributes/attributes.d.ts +0 -43
  13. package/dist/src/attributes/attributes.d.ts.map +0 -1
  14. package/dist/src/attributes/selections.d.ts +0 -16
  15. package/dist/src/attributes/selections.d.ts.map +0 -1
  16. package/dist/src/attributes/value.d.ts +0 -13
  17. package/dist/src/attributes/value.d.ts.map +0 -1
  18. package/dist/src/camera/camera.d.ts +0 -40
  19. package/dist/src/camera/camera.d.ts.map +0 -1
  20. package/dist/src/camera/index.d.ts +0 -4
  21. package/dist/src/camera/index.d.ts.map +0 -1
  22. package/dist/src/camera/position.d.ts +0 -43
  23. package/dist/src/camera/position.d.ts.map +0 -1
  24. package/dist/src/client/client.d.ts +0 -83
  25. package/dist/src/client/client.d.ts.map +0 -1
  26. package/dist/src/client/error.d.ts +0 -26
  27. package/dist/src/client/error.d.ts.map +0 -1
  28. package/dist/src/client/iframe-builder.d.ts +0 -25
  29. package/dist/src/client/iframe-builder.d.ts.map +0 -1
  30. package/dist/src/client/options.d.ts +0 -17
  31. package/dist/src/client/options.d.ts.map +0 -1
  32. package/dist/src/client/ready-state-tracker.d.ts +0 -26
  33. package/dist/src/client/ready-state-tracker.d.ts.map +0 -1
  34. package/dist/src/client/types.d.ts +0 -5
  35. package/dist/src/client/types.d.ts.map +0 -1
  36. package/dist/src/index.d.ts +0 -11
  37. package/dist/src/index.d.ts.map +0 -1
  38. package/dist/src/snapshot/index.d.ts +0 -3
  39. package/dist/src/snapshot/index.d.ts.map +0 -1
  40. package/dist/src/snapshot/photo.d.ts +0 -47
  41. package/dist/src/snapshot/photo.d.ts.map +0 -1
  42. package/dist/src/snapshot/snapshot.d.ts +0 -30
  43. package/dist/src/snapshot/snapshot.d.ts.map +0 -1
  44. package/dist/src/ui/viewer-config.d.ts +0 -63
  45. package/dist/src/ui/viewer-config.d.ts.map +0 -1
  46. package/dist/src/utils/logger.d.ts +0 -11
  47. package/dist/src/utils/logger.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,11 +1,228 @@
1
1
  # Changelog
2
2
 
3
- ## 4.0.0-alpha.3
3
+ ## 4.0.0-alpha.30
4
+
5
+ ### Minor Changes
6
+
7
+ Export the types that public signatures already hand you. `SelectableSnapshot`, `SelectableAttribute`, `CameraPosition`, `CameraPositionState`, `CameraLimits`, `AnnotationVisibility`, `ViewerSettingsOverride`, `ClientAddress`, `ClientOptionsBase`, `ClientLogger` and `AVAILABILITY` can now be imported by name, so a callback you register or a value you receive can be typed without redeclaring its shape.
8
+
9
+ ### Patch Changes
10
+
11
+ Drop the internal members from the published type definitions. `camera`, `snapshot`, `focus`, `configuration` and the attribute types now list only what you can call, so editor hover and autocomplete match the documented API.
12
+
13
+ ## 4.0.0-alpha.29
14
+
15
+ ### Major Changes
16
+
17
+ Rename the two selection events from `ui:select` and `ui:selected` to `vd:select` and `vd:selected`, so every event the client dispatches into your page carries the same `vd:` prefix. Change any `client.on('ui:select', ...)` to `client.on('vd:select', ...)`, and the same for `ui:selected`.
18
+
19
+ ### Patch Changes
20
+
21
+ Fix duplicated type names in the published type definitions: `ViewerSettings`, `PricingDocument`, `AppSubmitSettings` and `Availability` no longer show up as `ViewerSettings$1` in editor hover and type errors. Type documentation now describes the API you call instead of how the viewer is built.
22
+
23
+ ## 4.0.0-alpha.28
24
+
25
+ ### Minor Changes
26
+
27
+ Add a marker toggle button to the viewer action bar for apps set to the `toggle-shown` or `toggle-hidden` annotation mode: the visitor shows or hides the annotation markers themselves, with the starting state you configured. The button only appears when the model has annotations and a toggle mode is active.
28
+
29
+ ## 4.0.0-alpha.27
30
+
31
+ ### Minor Changes
32
+
33
+ Add the per-app annotation visibility modes to the `viewerSettings.annotations` override: `'hide'`, `'show'`, `'toggle-shown'` and `'toggle-hidden'`, besides the existing booleans (`true` maps to `'show'`, `false` to `'hide'`). Markers now follow the mode configured on the app, and an override only speaks up when it differs from that mode.
34
+
35
+ ## 4.0.0-alpha.26
36
+
37
+ ### Minor Changes
38
+
39
+ Add `app` option to `ClientOptions` as an alternative to the `license` and `model` pair, mounting one specific app by its link key. The pair keeps working unchanged; when both are supplied, `app` wins and a warning is logged that the pair was ignored.
40
+
41
+ Add `AppType` with the known app types. `ResolvedAppInfo.appType` is typed as a plain string so an app type newer than your bundle still reaches `onAppResolved` for explicit handling.
42
+
43
+ Add a measurement slider for size attributes. A model that offers a size (width, depth, diameter) now renders it as a slider with the formatted value and unit beside it, and the model follows the drag live while only the released value counts as the chosen size.
44
+
45
+ Add `slug` to the `onAppResolved` callback payload: the resolved app's public slug, so a host page can address the app it embeds without knowing the slug in advance.
46
+
47
+ Add `onAppResolved(callback)`. The viewer reports which app it resolved (`appType`, `name`, and public submit settings as `ResolvedAppInfo`), and the callback fires immediately when the report already arrived.
48
+
49
+ ## 4.0.0-alpha.25
50
+
51
+ ### Minor Changes
52
+
53
+ Add `onSelectableChange(callback)` and `getSelectable(name)` to follow which
54
+ attribute values the buyer can still pick under the current selection. The
55
+ standard sidebar greys out values the selection rules out. Models without
56
+ exclusions behave exactly as before.
57
+
58
+ ## 4.0.0-alpha.24
59
+
60
+ ### Patch Changes
61
+
62
+ Accept `undefined` for the optional `ClientOptions` fields `language`, `debug` and `traceparent`. Projects that enable `exactOptionalPropertyTypes` can now pass a possibly-undefined value straight through without a type error.
63
+
64
+ ## 4.0.0-alpha.23
65
+
66
+ ### Patch Changes
67
+
68
+ Fix the sidebar configurator toggle button appearing separated from the AR and fullscreen buttons on narrow embeds. All three controls now sit together in one row instead of the toggle floating above them.
69
+
70
+ ## 4.0.0-alpha.22
71
+
72
+ ### Patch Changes
73
+
74
+ Fix the 3D model rendering behind the docked sidebar panel instead of next to it. On wide embeds the viewer now narrows to leave room for the panel, instead of the panel floating over part of the model.
75
+
76
+ ## 4.0.0-alpha.21
77
+
78
+ ### Minor Changes
79
+
80
+ Add `activateAr()` method to `VirtualdisplayClient`. Triggers the AR flow directly, useful when the built-in AR button is hidden with `viewerSettings.ar = false` and a custom control should start AR instead.
81
+
82
+ Add `client.onSyncChange(callback)`: subscribe to whether the viewer is currently live-syncing its configuration with another device in a hand-off (e.g. a desktop and the phone it was scanned to). The callback receives a boolean and fires again each time the connection state changes.
83
+
84
+ ### Patch Changes
85
+
86
+ Change the desktop AR button's QR code to open the model in AR on the phone with the shopper's current configuration, instead of a fresh default viewer. The QR dialog now opens immediately and shows the code as soon as it is ready.
87
+
88
+ ## 4.0.0-alpha.20
4
89
 
5
90
  ### Patch Changes
6
91
 
92
+ Internal refactoring.
93
+
94
+ ## 4.0.0-alpha.19
95
+
96
+ ### Patch Changes
97
+
98
+ Fix the AR and fullscreen buttons being hidden behind the configurator open button when the sidebar layout collapses on narrow embeds.
99
+
100
+ ## 4.0.0-alpha.18
101
+
102
+ ### Patch Changes
103
+
104
+ Internal improvements.
105
+
106
+ ## 4.0.0-alpha.17
107
+
108
+ ### Minor Changes
109
+
110
+ Add `client.sessionId`: the viewer's tracking session id, available once the viewer has loaded (and `undefined` before). Use it to correlate your own page events, such as a form conversion, with the viewer session the visitor was using.
111
+
112
+ ## 4.0.0-alpha.16
113
+
114
+ ### Patch Changes
115
+
116
+ Internal: the client now assembles over a shared `@virtualdisplay.io/client-core` engine (transport, base facades, configurator UI). No public API change.
117
+
118
+ ## 4.0.0-alpha.15
119
+
120
+ ### Patch Changes
121
+
122
+ `snapshot.take()` now fails with `SNAPSHOT_FAILED` if the viewer does not return the capture within 30 seconds, instead of leaving `photo.onError()` waiting forever when the iframe is unresponsive. Also removed unused internal `VirtualdisplayError` factory helpers (`noMapping`, `attributeNotFound`, `snapshotFailed`); the error codes and public constructor are unchanged.
123
+
124
+ ## 4.0.0-alpha.14
125
+
126
+ ### Major Changes
127
+
128
+ Remove the `cache` option from `ClientOptions`. It was a test-only override and is gone, so remove it from your options if you passed it.
129
+
130
+ ## 4.0.0-alpha.13
131
+
132
+ ### Minor Changes
133
+
134
+ Add `client.configuration` for configurator commerce: the buyer's selection resolves to a reference and price, read via `client.configuration.current`, `.onChange`, or the bubbling `vd:configuration-change` event. Register a handler with `.onSubmit(cb, { defaultLabel })` to switch on the call-to-action, which renders the price and a customer-labelled button under the controls.
135
+
136
+ Show the submit CTA for a configurator without prices. When no pricing document is present, a registered `onSubmit` handler (with a label) is enough to offer the button, and submitting hands over the buyer's current selection. A model with only attributes, or none at all, can now be submitted. With prices, the CTA stays gated on an `available` price.
137
+
138
+ Add `client.focus` to isolate and frame a single component of a model. `client.focus.set('Seat')` flies the camera to that component and dims the rest; `client.focus.clear()` returns to the overview. Read the current component with `client.focus.current`, react to changes with `client.focus.onChange((state) => ...)`, or listen for the bubbling `vd:focus-change` event (`VD_EVENTS.FOCUS_CHANGE`) on the container. Useful for guiding a buyer through a configurable product part by part.
139
+
140
+ ### Patch Changes
141
+
142
+ Remove the unused `AttributeRegistry.defaults()` method and mark
143
+ `MutationFailedEvent` fields as `readonly`, matching the other public event
144
+ types. Internal cleanup with no API change.
145
+
146
+ ## 4.0.0-alpha.12
147
+
148
+ ### Minor Changes
149
+
150
+ Add `configuratorLayout` to `ViewerSettings`: choose where the attribute selector sits — `'inframe-bottom-left'` (default) or `'sidebar-right'` (a docked, scrollable right-hand panel) — or `'hidden'` to drop it (`client.select()` and `client.onChange()` keep working). Configure it per model in the portal, or override it via `viewerSettings`. This replaces the `attributeControls` field; swap `attributeControls: false` for `configuratorLayout: 'hidden'`.
151
+
152
+ ### Patch Changes
153
+
154
+ Internal build-config fix. Align `vite.config.ts` with vite 7 typings: rename `rolldownOptions` to `rollupOptions` and drop the `oxc` minifier (vite 7 only accepts `terser` and `esbuild` and was silently falling back to esbuild). No change to the published bundle.
155
+
156
+ `VirtualdisplayClient.destroy()` now fails any pending `snapshot.take()` captures with `SNAPSHOT_FAILED` and clears registered `onMutationFailed` callbacks. Previously, callers awaiting `photo.onError()` after a destroy would wait forever and listeners outlived the client they were attached to.
157
+
158
+ ## 4.0.0-alpha.11
159
+
160
+ ### Patch Changes
161
+
162
+ Hide the fullscreen button on iPhone Safari, where the browser has no Fullscreen API for non-video elements. The button continues to work on desktop, iPad, and Android Chrome.
163
+
164
+ ## 4.0.0-alpha.10
165
+
166
+ ### Patch Changes
167
+
168
+ Fix AR button on mobile to launch native AR (Quick Look on iOS, Scene Viewer on Android) instead of the QR modal. Fix fullscreen button on iPhone Safari.
169
+
170
+ ## 4.0.0-alpha.9
171
+
172
+ ### Patch Changes
173
+
174
+ Fix state mix-up when multiple `VirtualdisplayClient` instances are mounted on the same page. Each client now correctly receives only its own server replies, so loading state, mapping, and mutation results no longer cross between viewers.
175
+
176
+ ## 4.0.0-alpha.8
177
+
178
+ ### Minor Changes
179
+
180
+ Add `viewerSettings` option to `ClientOptions`. Pass a partial `ViewerSettings` to override fields like `ar`, `fullscreen`, `cameraControls`, `attributeControls`, or `annotations` without changing the model's metadata.
181
+
182
+ Add annotations API: `client.annotations.setEnabled(enabled)` toggles annotation markers, and `startPlacing()` / `stopPlacing()` / `onPlaced(callback)` enable click-to-place flows for custom annotation editors. New `AnnotationPosition` type is exported.
183
+
184
+ Add `attributeControls` field to `ViewerSettings`. Set to `false` to hide the built-in attribute selector while keeping `client.select()` and `client.onChange()` available.
185
+
186
+ Fix toolbar buttons appearing briefly before the loading indicator clears.
187
+
188
+ Fix annotation markers ignoring `annotations: false` when set via the `viewerSettings` override.
189
+
190
+ Fix loading indicator hanging when model metadata is unavailable.
191
+
192
+ Internal code quality improvements.
193
+
194
+ ## 4.0.0-alpha.7
195
+
196
+ ### Patch Changes
197
+
198
+ Restructure standard interface layout to use corner slots. Camera controls and screenshot button now share a single card when both are enabled.
199
+
200
+ ## 4.0.0-alpha.6
201
+
202
+ ### Patch Changes
203
+
204
+ Fix camera control buttons accumulating clicks correctly.
205
+
206
+ ## 4.0.0-alpha.5
207
+
208
+ ### Patch Changes
209
+
210
+ Internal code quality improvements.
211
+
7
212
  Ship complete TypeScript type definitions for all exports.
8
213
 
214
+ ## 4.0.0-alpha.4
215
+
216
+ ### Minor Changes
217
+
218
+ Add `language` option to `ClientOptions`. Pass a BCP 47 language tag (e.g. `"nl"`, `"de"`) to override the viewer language at initialisation time. Useful when embedding the same model on multiple pages with different languages.
219
+
220
+ Add built-in standard interface: loading indicator, toolbar (screenshot, AR, fullscreen), camera controls, and attribute controls are now rendered automatically inside the viewer container. UI is driven by `viewerSettings` received from the server and requires no manual setup.
221
+
222
+ ### Patch Changes
223
+
224
+ Fix build target: use ESNext in tsconfig to resolve rolldown bundling issues with ES2022 class fields.
225
+
9
226
  ## 4.0.0-alpha.2
10
227
 
11
228
  ### Patch Changes
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  TypeScript library for embedding interactive 3D product models in web
4
4
  applications.
5
5
 
6
+ - Built-in viewer UI with loading indicator, product controls, and action bar
6
7
  - Simple attribute-based API for product configurators
7
8
  - Server-driven configuration, no client-side mapping needed
8
9
  - Fire-and-forget messaging with automatic state synchronisation
@@ -54,49 +55,45 @@ The server defines which attributes and values are available for each model.
54
55
  The client receives this configuration automatically via postMessage. No
55
56
  manual mapping required.
56
57
 
58
+ ### Mounting a specific app
59
+
60
+ A model can have several apps (a plain viewer, a configurator), each with its
61
+ own link and its own configuration. Pass the app's link key as `app` to mount
62
+ that exact app:
63
+
64
+ ```typescript
65
+ import { VirtualdisplayClient } from '@virtualdisplay.io/client';
66
+
67
+ const client = new VirtualdisplayClient({
68
+ parent: '#product-container',
69
+ app: 'sofa-deluxe-k3f9dm',
70
+ });
71
+ ```
72
+
73
+ `license` + `model` keeps working unchanged and shows the model's default
74
+ viewer. If both forms are supplied, `app` wins and a warning is logged that the
75
+ pair was ignored, so adding `app` to an existing integration is additive. One
76
+ of the two forms is required; supplying neither is a compile error.
77
+
57
78
  ## How it works
58
79
 
59
80
  The 3D server hosts the viewer in an iframe, keeping WebGL complexity isolated
60
81
  from your application. The client library handles all communication via
61
82
  postMessage.
62
83
 
63
- ```mermaid
64
- graph LR
65
- A[Your application] -->|API calls| B[VirtualdisplayClient]
66
- B -->|postMessage| C[3D server iframe]
67
- C -->|Events| B
68
- B -->|State updates| A
69
- ```
84
+ ![Architecture overview](docs/arch.png)
70
85
 
71
- On startup, the server tells the client what is available (attributes, values,
72
- defaults). After that, the client sends commands and the server responds with
73
- results.
74
-
75
- ```mermaid
76
- sequenceDiagram
77
- participant UI as Your UI
78
- participant Client as VirtualdisplayClient
79
- participant Server as 3D server
80
-
81
- UI ->> Client: new VirtualdisplayClient(options)
82
- Client ->> Client: Create iframe
83
- Note right of Client: Outgoing messages queued
84
- Server ->> Client: READY
85
- Note right of Client: Queue flushed
86
- opt UI or camera options configured
87
- Client ->> Server: CONFIG
88
- end
89
- Server ->> Client: MAPPING (attributes + values + defaults)
90
- Client ->> Client: Store attribute definitions
91
- Server ->> Client: MUTATION result (defaults, isInitial: true)
92
- Client ->> UI: onReady + onChange callbacks fire
93
- ```
86
+ On startup, the server sends the client its configuration (viewer settings,
87
+ camera limits) and available product attributes. The loading indicator shows
88
+ progress while the model downloads.
89
+
90
+ ![Startup sequence](docs/startup-sequence.png)
94
91
 
95
92
  1. Client creates iframe; outgoing messages are queued until the server is ready.
96
- 2. Server sends `READY` when its message listener is active; client flushes queue.
97
- 3. `CONFIG` is only sent if UI or camera options were provided in the constructor.
98
- 4. Server sends `MAPPING` with attribute definitions, loads the 3D model, applies defaults.
99
- 5. `onReady` fires after the first initial mutation result arrives.
93
+ 2. Server sends `HANDSHAKE` when its message listener is active; client flushes queue.
94
+ 3. Server sends progress updates while the model downloads. The client displays these in the loading indicator.
95
+ 4. Server sends `CONFIG` with viewer settings and camera limits from model metadata.
96
+ 5. Server sends `MAPPING` with attribute definitions, values, and defaults. `onReady` fires.
100
97
 
101
98
  ## Product configuration
102
99
 
@@ -104,23 +101,7 @@ Configure product options by selecting attribute values. The server defines
104
101
  which attributes exist (e.g. "Color", "Size") and which values each has
105
102
  (e.g. "red", "blue"). You select values; the 3D model updates automatically.
106
103
 
107
- ```mermaid
108
- sequenceDiagram
109
- participant UI as Your UI
110
- participant Client as VirtualdisplayClient
111
- participant Server as 3D server
112
-
113
- UI ->> Client: client.select('Color', 'blue')
114
- Client ->> Server: MUTATION
115
- Note right of Client: Fire-and-forget
116
- Server ->> Server: Apply 3D changes
117
- Server ->> Client: MUTATION result
118
- alt All succeeded
119
- Client ->> UI: onChange callbacks fire
120
- else Some failed
121
- Client ->> UI: onMutationFailed callbacks fire
122
- end
123
- ```
104
+ ![Product configuration sequence](docs/mutation.png)
124
105
 
125
106
  - `select()` returns immediately; the result arrives asynchronously.
126
107
  - Each attribute in the result has its own `success` flag and optional `error`.
@@ -134,8 +115,8 @@ client.select('Color', 'blue');
134
115
  client.select('Size', 'large');
135
116
  ```
136
117
 
137
- Can be called before the server is ready. Mutations are buffered and sent when
138
- the mapping arrives.
118
+ Can be called immediately after construction. Selections are buffered until the
119
+ server handshake completes.
139
120
 
140
121
  ### Tracking changes
141
122
 
@@ -192,22 +173,7 @@ client.onMutationFailed(({ attribute, value }) => {
192
173
  Capture the current 3D view as an image. Useful for product shots, social
193
174
  sharing, or saving configurations.
194
175
 
195
- ```mermaid
196
- sequenceDiagram
197
- participant UI as Your UI
198
- participant Client as VirtualdisplayClient
199
- participant Server as 3D server
200
-
201
- UI ->> Client: client.snapshot.take('photo.png')
202
- Client ->> Server: SNAPSHOT request
203
- alt Success
204
- Server ->> Client: SNAPSHOT data (base64)
205
- Client ->> UI: onDeveloped callback fires
206
- else Failure (e.g. no canvas)
207
- Server ->> Client: SNAPSHOT error
208
- Client ->> UI: onError callback fires
209
- end
210
- ```
176
+ ![Snapshot sequence](docs/snapshot.png)
211
177
 
212
178
  - Filename is validated client-side (extension, no path traversal).
213
179
  - Server always responds, either with image data or an error reason.
@@ -236,21 +202,11 @@ Filename must end with `.png`. The server always returns PNG data.
236
202
 
237
203
  ## Camera
238
204
 
239
- Control the 3D viewer's camera position programmatically. Useful for guided
240
- product tours, preset views, or syncing camera state with your UI.
205
+ The standard interface includes built-in camera controls when they are
206
+ enabled in the Virtualdisplay portal. The camera can also be controlled
207
+ programmatically.
241
208
 
242
- ```mermaid
243
- sequenceDiagram
244
- participant UI as Your UI
245
- participant Client as VirtualdisplayClient
246
- participant Server as 3D server
247
-
248
- UI ->> Client: client.camera.rotate(45).tilt(30).set()
249
- Client ->> Server: CAMERA commands
250
- Server ->> Client: CAMERA position
251
- Client ->> UI: camera.onChange callbacks fire
252
- Note right of Server: Position updates also fire on user<br/>interaction with the 3D viewport
253
- ```
209
+ ![Camera sequence](docs/camera.png)
254
210
 
255
211
  - Commands accumulate via chaining; `.set()` sends them all at once.
256
212
  - `.reset()` sends immediately without chaining.
@@ -263,7 +219,7 @@ sequenceDiagram
263
219
  client.camera.rotate(45).set();
264
220
  client.camera.rotate(90).tilt(45).zoom(150).set();
265
221
 
266
- // Reset to base position (sends immediately)
222
+ // Reset to the default position configured in the portal (sends immediately)
267
223
  client.camera.reset();
268
224
  ```
269
225
 
@@ -283,49 +239,81 @@ console.log(client.cameraPosition.tilt);
283
239
  console.log(client.cameraPosition.zoom);
284
240
  ```
285
241
 
286
- ## UI controls
242
+ ## Annotations
287
243
 
288
- Toggle built-in viewer elements like AR, fullscreen, and the loading indicator.
289
- All are visible by default.
244
+ Annotations are interactive markers placed on the 3D model. They are
245
+ configured and saved in the Virtualdisplay portal. The client library
246
+ exposes an API to toggle their visibility.
290
247
 
291
- ### Via constructor
248
+ ### Toggling annotations
292
249
 
293
250
  ```typescript
294
- const client = new VirtualdisplayClient({
295
- parent: '#viewer',
296
- license: 'your-license',
297
- model: 'product',
298
- ui: {
299
- arEnabled: false,
300
- loadingIndicatorEnabled: false,
301
- },
302
- });
251
+ client.annotations.setEnabled(false); // Hide all markers
252
+ client.annotations.setEnabled(true); // Show them again
303
253
  ```
304
254
 
305
- ### At runtime
255
+ Visibility can also be controlled via `viewerSettings.annotations` (see below).
256
+
257
+ ## Standard interface
258
+
259
+ The client renders a complete viewer UI automatically: loading indicator,
260
+ attribute controls, camera controls, and an action bar with AR, fullscreen,
261
+ and screenshot buttons. Each element is shown or hidden based on viewer
262
+ settings configured in the Virtualdisplay portal.
263
+
264
+ ### Available settings
265
+
266
+ | Field | Type | Effect when `false` |
267
+ | -------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
268
+ | `ar` | `boolean` | Hides the AR button |
269
+ | `fullscreen` | `boolean` | Hides the fullscreen button |
270
+ | `screenshot` | `boolean` | Hides the screenshot button |
271
+ | `cameraControls` | `boolean` | Hides the camera control buttons |
272
+ | `annotations` | `boolean` | Hides all annotation markers |
273
+ | `language` | `string` | Sets the viewer UI language (BCP 47, e.g. `'nl'`, `'en'`, `'de'`) |
274
+ | `configuratorLayout` | `string` | Where the attribute controls sit, or whether they show: `'inframe-bottom-left'` (default), `'sidebar-right'` (a docked, scrollable right-hand panel), or `'hidden'` (no built-in selector, `client.select()` and `client.onChange()` keep working). |
275
+
276
+ ### Overriding settings
277
+
278
+ The portal is the recommended place to change these settings: one source of
279
+ truth, and the viewer behaves the same wherever it is embedded.
280
+
281
+ We know there are situations where the portal alone cannot cover the case at
282
+ hand. For those, `ClientOptions.viewerSettings` accepts a partial override of
283
+ the same fields. Use it sparingly: every override is one more place where
284
+ the embedded viewer disagrees with the portal. Omitted fields fall back to
285
+ the metadata value.
286
+
287
+ Any override that differs from the metadata is logged with `console.warn`.
288
+ The warning is always emitted, so the deviation stays traceable when
289
+ something needs explaining.
306
290
 
307
291
  ```typescript
308
- client.viewer.setArEnabled(false);
309
- client.viewer.setFullscreenEnabled(true);
310
- client.viewer.setLoadingIndicatorEnabled(false);
311
-
312
- // Bulk operations
313
- client.viewer.hideAllUI();
314
- client.viewer.showAllUI();
315
- client.viewer.updateUIConfig({ arEnabled: false, fullscreenEnabled: true });
292
+ const client = new VirtualdisplayClient({
293
+ parent: '#product-container',
294
+ license: 'your-license-key',
295
+ model: 'sneaker',
296
+ viewerSettings: {
297
+ cameraControls: false,
298
+ },
299
+ });
316
300
  ```
317
301
 
318
302
  ## Lifecycle
319
303
 
320
304
  ### Ready state
321
305
 
322
- Use `onReady` to know when the 3D model has loaded. For example, to hide
323
- your own loading spinner. It is **not** required before calling `select()` or
324
- `onChange()`.
306
+ Use `onReady` to know when the model and attribute definitions are available.
307
+ Not required before calling `select()` or `onChange()`, those are buffered
308
+ automatically.
325
309
 
326
310
  ```typescript
327
311
  client.onReady(() => {
328
- document.getElementById('loading-spinner').hidden = true;
312
+ const values = client.getValues('Color');
313
+ console.log(
314
+ 'Available colours:',
315
+ values.map((v) => v.value)
316
+ );
329
317
  });
330
318
  ```
331
319
 
@@ -338,69 +326,42 @@ client.destroy();
338
326
  ## Constructor options
339
327
 
340
328
  ```typescript
329
+ import type { ViewerSettings } from '@virtualdisplay.io/client';
330
+
341
331
  interface ClientOptions {
342
332
  parent: string | HTMLElement; // Where to embed the 3D viewer
343
- license: string; // License key from your Virtualdisplay account
333
+ app?: string; // An app's link key: the alternative to license + model (see Mounting a specific app)
334
+ license: string; // License key from your Virtualdisplay account; with model, the classic address form
344
335
  model: string; // Which 3D model to load
345
336
  debug?: boolean; // Shows console logs for troubleshooting
346
- language?: string; // UI language: 'nl', 'en', or 'de' (default: 'nl')
347
-
348
- // UI elements (all visible by default)
349
- ui?: {
350
- arEnabled?: boolean;
351
- fullscreenEnabled?: boolean;
352
- loadingIndicatorEnabled?: boolean;
353
- };
354
-
355
- // Camera behaviour
356
- camera?: {
357
- initialRotate?: number; // Starting view angle (-180 to 180, 0 = front)
358
- initialTilt?: number; // Starting elevation (0 = top view, 90 = side view)
359
- initialZoom?: number; // Starting distance (100 = default, may need adjustment)
360
- minZoom?: number; // Minimum zoom level constraint
361
- maxZoom?: number; // Maximum zoom level constraint
362
- minTilt?: number; // Minimum tilt angle constraint
363
- maxTilt?: number; // Maximum tilt angle constraint
364
- };
337
+ language?: string; // Viewer language: 'nl', 'en', or 'de'
338
+ viewerSettings?: Partial<ViewerSettings>; // Override fields from model metadata (see Standard interface)
365
339
  }
366
340
  ```
367
341
 
342
+ One of the two address forms is required: `app`, or `license` + `model`. The
343
+ real type is a union that enforces this at compile time; the block above is the
344
+ flattened reference.
345
+
346
+ ```typescript
347
+ type ClientAddress =
348
+ | { app: string } // an app's link key
349
+ | { license: string; model: string }; // the classic address form
350
+ ```
351
+
368
352
  ## Error codes
369
353
 
370
354
  The client throws `VirtualdisplayError` for known error conditions:
371
355
 
372
356
  | Code | Meaning |
373
- |-----------------------|--------------------------------------------------|
357
+ | --------------------- | ------------------------------------------------ |
358
+ | `NO_MAPPING` | Attribute data not yet available from the server |
374
359
  | `ATTRIBUTE_NOT_FOUND` | Requested attribute doesn't exist in the mapping |
375
360
  | `VALUE_NOT_FOUND` | Requested value doesn't exist for attribute |
376
361
  | `PARENT_NOT_FOUND` | Parent element for iframe not found |
377
362
  | `INVALID_PARAMETER` | Invalid parameter (e.g. snapshot filename) |
378
363
  | `SNAPSHOT_FAILED` | Server could not capture the snapshot |
379
364
 
380
- ## Real-world example
381
-
382
- ```typescript
383
- const client = new VirtualdisplayClient({
384
- parent: '#product-viewer',
385
- license: 'your-license',
386
- model: 'dining-table',
387
- });
388
-
389
- // Wire up your product option buttons. Selections are buffered until ready.
390
- document.querySelectorAll('.color-option').forEach((button) => {
391
- button.addEventListener('click', () => {
392
- client.select('topMaterial', button.dataset.value);
393
- });
394
- });
395
-
396
- // Keep buttons in sync with 3D state
397
- client.onChange('topMaterial', (selectedValue) => {
398
- document.querySelectorAll('.color-option').forEach((btn) => {
399
- btn.classList.toggle('active', btn.dataset.value === selectedValue.value);
400
- });
401
- });
402
- ```
403
-
404
365
  ## Troubleshooting
405
366
 
406
367
  **Iframe not loading:**