@virtualdisplay.io/client 4.0.0-alpha.25 → 4.0.0-alpha.27
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 +20 -0
- package/README.md +50 -16
- package/dist/index.d.ts +265 -26
- package/dist/virtualdisplay.client.cjs.js +97 -6
- package/dist/virtualdisplay.client.es.js +1458 -1180
- package/dist/virtualdisplay.client.umd.js +117 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.0-alpha.27
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
## 4.0.0-alpha.26
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
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.
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
21
|
+
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.
|
|
22
|
+
|
|
3
23
|
## 4.0.0-alpha.25
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -55,6 +55,26 @@ The server defines which attributes and values are available for each model.
|
|
|
55
55
|
The client receives this configuration automatically via postMessage. No
|
|
56
56
|
manual mapping required.
|
|
57
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
|
+
|
|
58
78
|
## How it works
|
|
59
79
|
|
|
60
80
|
The 3D server hosts the viewer in an iframe, keeping WebGL complexity isolated
|
|
@@ -229,7 +249,7 @@ exposes an API to toggle their visibility.
|
|
|
229
249
|
|
|
230
250
|
```typescript
|
|
231
251
|
client.annotations.setEnabled(false); // Hide all markers
|
|
232
|
-
client.annotations.setEnabled(true);
|
|
252
|
+
client.annotations.setEnabled(true); // Show them again
|
|
233
253
|
```
|
|
234
254
|
|
|
235
255
|
Visibility can also be controlled via `viewerSettings.annotations` (see below).
|
|
@@ -243,15 +263,15 @@ settings configured in the Virtualdisplay portal.
|
|
|
243
263
|
|
|
244
264
|
### Available settings
|
|
245
265
|
|
|
246
|
-
| Field
|
|
247
|
-
|
|
|
248
|
-
| `ar`
|
|
249
|
-
| `fullscreen`
|
|
250
|
-
| `screenshot`
|
|
251
|
-
| `cameraControls`
|
|
252
|
-
| `annotations`
|
|
253
|
-
| `language`
|
|
254
|
-
| `configuratorLayout
|
|
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). |
|
|
255
275
|
|
|
256
276
|
### Overriding settings
|
|
257
277
|
|
|
@@ -290,7 +310,10 @@ automatically.
|
|
|
290
310
|
```typescript
|
|
291
311
|
client.onReady(() => {
|
|
292
312
|
const values = client.getValues('Color');
|
|
293
|
-
console.log(
|
|
313
|
+
console.log(
|
|
314
|
+
'Available colours:',
|
|
315
|
+
values.map((v) => v.value)
|
|
316
|
+
);
|
|
294
317
|
});
|
|
295
318
|
```
|
|
296
319
|
|
|
@@ -306,15 +329,26 @@ client.destroy();
|
|
|
306
329
|
import type { ViewerSettings } from '@virtualdisplay.io/client';
|
|
307
330
|
|
|
308
331
|
interface ClientOptions {
|
|
309
|
-
parent: string | HTMLElement;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
332
|
+
parent: string | HTMLElement; // Where to embed the 3D viewer
|
|
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
|
|
335
|
+
model: string; // Which 3D model to load
|
|
336
|
+
debug?: boolean; // Shows console logs for troubleshooting
|
|
337
|
+
language?: string; // Viewer language: 'nl', 'en', or 'de'
|
|
314
338
|
viewerSettings?: Partial<ViewerSettings>; // Override fields from model metadata (see Standard interface)
|
|
315
339
|
}
|
|
316
340
|
```
|
|
317
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
|
+
|
|
318
352
|
## Error codes
|
|
319
353
|
|
|
320
354
|
The client throws `VirtualdisplayError` for known error conditions:
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,59 @@ declare const MESSAGE_TYPES: {
|
|
|
22
22
|
readonly QR_SHOWN: "qr_shown";
|
|
23
23
|
readonly SYNC: "sync";
|
|
24
24
|
readonly SELECTABLE: "selectable";
|
|
25
|
+
readonly APP_RESOLVED: "app_resolved";
|
|
25
26
|
};
|
|
27
|
+
/** The public submit settings of a configurator-type app, as the form renders
|
|
28
|
+
* them. Never the recipient: that section is removed in SQL on the public
|
|
29
|
+
* read and cannot ride any message. Sparse like every config section: an
|
|
30
|
+
* absent field means the renderer's default (the backend stores the section
|
|
31
|
+
* as written and omits empty keys), so nothing here is guaranteed present. */
|
|
32
|
+
interface AppSubmitSettings$1 {
|
|
33
|
+
readonly fields?: string[];
|
|
34
|
+
readonly ctaLabel?: string;
|
|
35
|
+
readonly privacyUrl?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Viewer -> client: which app this viewer resolved, reported up so the parent
|
|
39
|
+
* (the embed host) can load the matching shell without its own resolve (the
|
|
40
|
+
* apps spec's one-read inversion). Sent once, after the resolve, before or
|
|
41
|
+
* with MAPPING.
|
|
42
|
+
*/
|
|
43
|
+
interface AppResolvedMessage {
|
|
44
|
+
readonly type: typeof MESSAGE_TYPES.APP_RESOLVED;
|
|
45
|
+
/** Known values: {@link AppType}. An unknown value means a newer server; the host reports it, never downgrades. */
|
|
46
|
+
readonly appType: string;
|
|
47
|
+
readonly name: string;
|
|
48
|
+
/** The app's public slug, the address a submit POSTs to. Optional on the
|
|
49
|
+
* wire: a pre-task-60 server does not send it, and the pair-form host has
|
|
50
|
+
* no other way to learn it, so the shell treats absence as "submit
|
|
51
|
+
* unavailable" rather than the guard dropping the whole report. */
|
|
52
|
+
readonly slug?: string;
|
|
53
|
+
readonly submit?: AppSubmitSettings$1;
|
|
54
|
+
}
|
|
55
|
+
/** One mesh or material variant in a model, as reported by STATE. */
|
|
56
|
+
interface ModelNodeDto {
|
|
57
|
+
readonly id: string;
|
|
58
|
+
readonly name: string;
|
|
59
|
+
readonly type: string;
|
|
60
|
+
readonly visible: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* State (viewer → client): the meshes and material variants of one instance,
|
|
64
|
+
* with their current visibility.
|
|
65
|
+
*
|
|
66
|
+
* Its shape used to live in server-core, marked "server-only", while its type
|
|
67
|
+
* value sat in the public MESSAGE_TYPES and the client read it: a public
|
|
68
|
+
* message whose shape only one side declared, which is how two shapes of one
|
|
69
|
+
* thing start to drift.
|
|
70
|
+
*/
|
|
71
|
+
interface StateMessage {
|
|
72
|
+
readonly type: typeof MESSAGE_TYPES.STATE;
|
|
73
|
+
/** Whose meshes and variants these are. */
|
|
74
|
+
readonly instance: string;
|
|
75
|
+
readonly nodes: ModelNodeDto[];
|
|
76
|
+
readonly isInitial: boolean;
|
|
77
|
+
}
|
|
26
78
|
interface MutationDto {
|
|
27
79
|
readonly name: string;
|
|
28
80
|
readonly value: string;
|
|
@@ -46,6 +98,8 @@ interface SyncStateSnapshot {
|
|
|
46
98
|
}
|
|
47
99
|
interface MutationMessage {
|
|
48
100
|
readonly type: typeof MESSAGE_TYPES.MUTATION;
|
|
101
|
+
/** Which instance's attributes these mutations change. */
|
|
102
|
+
readonly instance: string;
|
|
49
103
|
readonly mutations: MutationDto[];
|
|
50
104
|
readonly isInitial: boolean;
|
|
51
105
|
/**
|
|
@@ -61,11 +115,19 @@ interface MutationMessage {
|
|
|
61
115
|
* authoritative folded family state: the sync module diffs it against the
|
|
62
116
|
* channel's last-seen state and applies the difference, so a catch-up
|
|
63
117
|
* (empty `mutations`) or a broadcast the hub dropped still converges the
|
|
64
|
-
* subscriber. None of these fields are read by `
|
|
118
|
+
* subscriber. None of these fields are read by `MutationPipeline`.
|
|
65
119
|
*/
|
|
66
120
|
readonly origin?: string;
|
|
67
121
|
readonly seq?: number;
|
|
68
122
|
readonly state?: SyncStateSnapshot;
|
|
123
|
+
/**
|
|
124
|
+
* A drag's intermediate step (parametric dimensions): the viewer applies it
|
|
125
|
+
* so the model follows the hand, but never tracks it, the settled value is
|
|
126
|
+
* the one recorded choice and it arrives as an ordinary mutation without
|
|
127
|
+
* this flag. Only the client knows when a drag settles, which is why this
|
|
128
|
+
* travels on the message instead of being guessed viewer-side.
|
|
129
|
+
*/
|
|
130
|
+
readonly transient?: boolean;
|
|
69
131
|
}
|
|
70
132
|
/**
|
|
71
133
|
* Sync channel status (server → client, ar-session-handoff, sync-channel.md):
|
|
@@ -80,9 +142,18 @@ interface SyncMessage {
|
|
|
80
142
|
}
|
|
81
143
|
interface MutationResultMessage {
|
|
82
144
|
readonly type: typeof MESSAGE_TYPES.MUTATION;
|
|
145
|
+
/** Which instance these results are for; without it a failure cannot say whose. */
|
|
146
|
+
readonly instance: string;
|
|
83
147
|
readonly results: MutationResult[];
|
|
84
148
|
readonly isInitial: boolean;
|
|
85
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* How a model's annotations present to the Shopper: never (hide), always
|
|
152
|
+
* (show), or under the Shopper's own control via a viewer toggle, starting
|
|
153
|
+
* visible (toggle-shown) or hidden (toggle-hidden). Resolved per app; the
|
|
154
|
+
* backend's `Resolve` owns the `hide` default.
|
|
155
|
+
*/
|
|
156
|
+
type AnnotationVisibility$1 = 'hide' | 'show' | 'toggle-shown' | 'toggle-hidden';
|
|
86
157
|
/**
|
|
87
158
|
* Viewer settings from model metadata (server → client via MAPPING).
|
|
88
159
|
* Customer-configured in the portal.
|
|
@@ -92,7 +163,12 @@ interface ViewerSettings$1 {
|
|
|
92
163
|
readonly fullscreen?: boolean;
|
|
93
164
|
readonly screenshot?: boolean;
|
|
94
165
|
readonly cameraControls?: boolean;
|
|
95
|
-
|
|
166
|
+
/**
|
|
167
|
+
* The resolved per-app annotation mode. Present only when the model has
|
|
168
|
+
* annotations, so a control can never render with nothing to govern;
|
|
169
|
+
* absent means there is nothing to show or toggle.
|
|
170
|
+
*/
|
|
171
|
+
readonly annotations?: AnnotationVisibility$1;
|
|
96
172
|
readonly language?: string;
|
|
97
173
|
/** Where the attribute controls sit: in-frame bottom-left (default) or a docked right panel. */
|
|
98
174
|
readonly configuratorLayout?: 'inframe-bottom-left' | 'sidebar-right' | 'hidden';
|
|
@@ -136,19 +212,46 @@ interface SnapshotResultMessage {
|
|
|
136
212
|
readonly data?: string;
|
|
137
213
|
readonly error?: string;
|
|
138
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Marks an attribute as a measurement (a parametric dimension): the values
|
|
217
|
+
* are millimetre integer strings, and this carries what a client needs to
|
|
218
|
+
* render a measurement control and format the unit. Absent for ordinary
|
|
219
|
+
* attributes; a client that ignores it still renders the value list.
|
|
220
|
+
*/
|
|
221
|
+
interface DimensionDescriptor {
|
|
222
|
+
readonly displayUnit: 'mm' | 'cm' | 'm' | 'in';
|
|
223
|
+
readonly minMm: number;
|
|
224
|
+
readonly maxMm: number;
|
|
225
|
+
readonly stepMm: number;
|
|
226
|
+
}
|
|
139
227
|
interface AttributeDefinition {
|
|
140
228
|
readonly name: string;
|
|
141
229
|
readonly values: string[];
|
|
142
230
|
readonly defaultValue?: string;
|
|
231
|
+
/** Present when this attribute is a measurement; see DimensionDescriptor. */
|
|
232
|
+
readonly dimension?: DimensionDescriptor;
|
|
143
233
|
}
|
|
144
234
|
interface MappingMessage {
|
|
145
235
|
readonly type: typeof MESSAGE_TYPES.MAPPING;
|
|
236
|
+
/**
|
|
237
|
+
* Whose attributes these are. One MAPPING per instance, and an instance with
|
|
238
|
+
* no attributes sends an empty one rather than none: silence would leave the
|
|
239
|
+
* client unable to name it, click it or report its interactions.
|
|
240
|
+
*/
|
|
241
|
+
readonly instance: string;
|
|
146
242
|
readonly attributes: AttributeDefinition[];
|
|
147
243
|
}
|
|
148
244
|
/** Per selector, the values the buyer can still pick right now. */
|
|
149
245
|
interface SelectableAttribute {
|
|
150
246
|
readonly name: string;
|
|
151
247
|
readonly selectable: string[];
|
|
248
|
+
/**
|
|
249
|
+
* The control should not render at all: a dimension whose component is
|
|
250
|
+
* currently hidden by its show-when conditions (parametric dimensions).
|
|
251
|
+
* Different from an empty `selectable`, which greys values out but keeps
|
|
252
|
+
* the control visible. Absent means visible.
|
|
253
|
+
*/
|
|
254
|
+
readonly hidden?: boolean;
|
|
152
255
|
}
|
|
153
256
|
/**
|
|
154
257
|
* Selectability snapshot (server → client): sent after the mapping and after
|
|
@@ -160,11 +263,30 @@ interface SelectableAttribute {
|
|
|
160
263
|
*/
|
|
161
264
|
interface SelectableMessage {
|
|
162
265
|
readonly type: typeof MESSAGE_TYPES.SELECTABLE;
|
|
266
|
+
/** Whose attributes these are, mirroring the MAPPING it follows. */
|
|
267
|
+
readonly instance: string;
|
|
163
268
|
readonly attributes: SelectableAttribute[];
|
|
164
269
|
}
|
|
165
270
|
/**
|
|
166
271
|
* Loading indicator (server → client): progress percentage, phase label, or error.
|
|
167
272
|
* Sent during model download and parse so the client loading indicator can update.
|
|
273
|
+
*
|
|
274
|
+
* This message concerns one model at five of its seven call sites (download
|
|
275
|
+
* progress, a load error, the rendering phase) and is fatal with no model to
|
|
276
|
+
* blame at the other two (no WebGL, the boot-failure path), so by the
|
|
277
|
+
* message-scope rule it takes an instance id optionally.
|
|
278
|
+
*
|
|
279
|
+
* It does not carry one yet, on purpose. `publishLoadingProgress` is the only
|
|
280
|
+
* publisher and knows no instance, so the field would be absent at all seven
|
|
281
|
+
* sites while its documented meaning is "absent means the viewer could not
|
|
282
|
+
* start". An integrator branching on that would read every progress update as
|
|
283
|
+
* a viewer failure, which is worse than no field at all.
|
|
284
|
+
*
|
|
285
|
+
* Nothing is lost by waiting: an optional field is additive, so unlike the
|
|
286
|
+
* required ids in this file it carries no alpha-window pressure. Composition
|
|
287
|
+
* task 10 brings a second model and with it the choice the spec parked, thread
|
|
288
|
+
* the id through the five model sites, or split the fatal case onto its own
|
|
289
|
+
* message. Decide it there, with a real second instance to test against.
|
|
168
290
|
*/
|
|
169
291
|
interface LoadingIndicatorMessage {
|
|
170
292
|
readonly type: typeof MESSAGE_TYPES.LOADING_INDICATOR;
|
|
@@ -226,6 +348,13 @@ interface SessionMessage {
|
|
|
226
348
|
*/
|
|
227
349
|
interface AnnotationToggleMessage {
|
|
228
350
|
readonly type: typeof MESSAGE_TYPES.ANNOTATION_TOGGLE;
|
|
351
|
+
/**
|
|
352
|
+
* Whose pins to show. This looks scene-wide because the client draws one
|
|
353
|
+
* button, and it is not: a pin's position is stored in its model's local
|
|
354
|
+
* space, so it means nothing without the root it belongs to. A client that
|
|
355
|
+
* wants one button loops over the instances, which is a UI decision.
|
|
356
|
+
*/
|
|
357
|
+
readonly instance: string;
|
|
229
358
|
readonly enabled: boolean;
|
|
230
359
|
}
|
|
231
360
|
/** Focus actions (client → server). */
|
|
@@ -238,8 +367,8 @@ declare const FOCUS_STATES: {
|
|
|
238
367
|
readonly FOCUSED: "focused";
|
|
239
368
|
readonly UNFOCUSED: "unfocused";
|
|
240
369
|
};
|
|
241
|
-
type FocusAction = typeof FOCUS_ACTIONS[keyof typeof FOCUS_ACTIONS];
|
|
242
|
-
type FocusStateValue = typeof FOCUS_STATES[keyof typeof FOCUS_STATES];
|
|
370
|
+
type FocusAction = (typeof FOCUS_ACTIONS)[keyof typeof FOCUS_ACTIONS];
|
|
371
|
+
type FocusStateValue = (typeof FOCUS_STATES)[keyof typeof FOCUS_STATES];
|
|
243
372
|
/**
|
|
244
373
|
* Focus (client → server): isolate and frame a component, or return to overview.
|
|
245
374
|
* Programmatic — the API, the (future) component menu, and the portal editors all
|
|
@@ -248,6 +377,8 @@ type FocusStateValue = typeof FOCUS_STATES[keyof typeof FOCUS_STATES];
|
|
|
248
377
|
*/
|
|
249
378
|
interface FocusMessage {
|
|
250
379
|
readonly type: typeof MESSAGE_TYPES.FOCUS;
|
|
380
|
+
/** Which instance the component name resolves inside. */
|
|
381
|
+
readonly instance: string;
|
|
251
382
|
readonly action: FocusAction;
|
|
252
383
|
readonly component?: string;
|
|
253
384
|
}
|
|
@@ -258,6 +389,8 @@ interface FocusMessage {
|
|
|
258
389
|
*/
|
|
259
390
|
interface FocusStateMessage {
|
|
260
391
|
readonly type: typeof MESSAGE_TYPES.FOCUS;
|
|
392
|
+
/** Which instance this state is about. */
|
|
393
|
+
readonly instance: string;
|
|
261
394
|
readonly state: FocusStateValue;
|
|
262
395
|
readonly component?: string;
|
|
263
396
|
readonly attributes?: string[];
|
|
@@ -274,7 +407,7 @@ declare const AVAILABILITY$1: {
|
|
|
274
407
|
/** Behaves exactly like an unpriced combination: no price, no button. */
|
|
275
408
|
readonly HIDDEN: "hidden";
|
|
276
409
|
};
|
|
277
|
-
type Availability$1 = typeof AVAILABILITY$1[keyof typeof AVAILABILITY$1];
|
|
410
|
+
type Availability$1 = (typeof AVAILABILITY$1)[keyof typeof AVAILABILITY$1];
|
|
278
411
|
/**
|
|
279
412
|
* One pricing rule: a subset of attribute values mapped to a reference + price.
|
|
280
413
|
* Matches the backend pricing read contract (`GET /api/public/models/.../pricing`).
|
|
@@ -315,9 +448,16 @@ interface PricingDocument$1 {
|
|
|
315
448
|
*/
|
|
316
449
|
interface PricingMessage {
|
|
317
450
|
readonly type: typeof MESSAGE_TYPES.PRICING;
|
|
451
|
+
/**
|
|
452
|
+
* Which instance is priced. Only one is today, so the id has no alternative
|
|
453
|
+
* value to hold; it carries one anyway, because a rule with a "not needed
|
|
454
|
+
* yet" exception erodes, and a scene of separately priced products needs
|
|
455
|
+
* exactly this field.
|
|
456
|
+
*/
|
|
457
|
+
readonly instance: string;
|
|
318
458
|
readonly pricing: PricingDocument$1;
|
|
319
459
|
}
|
|
320
|
-
type Message = HandshakeMessage | SessionMessage | MutationMessage | MutationResultMessage | CameraCommandMessage | CameraPositionMessage | SnapshotRequestMessage | SnapshotResultMessage | MappingMessage | LoadingIndicatorMessage | ArActivateMessage | FullscreenMessage | ConfigMessage | AnnotationToggleMessage | FocusMessage | FocusStateMessage | PricingMessage | QrShownMessage | SyncMessage | SelectableMessage;
|
|
460
|
+
type Message = HandshakeMessage | SessionMessage | MutationMessage | MutationResultMessage | CameraCommandMessage | CameraPositionMessage | SnapshotRequestMessage | SnapshotResultMessage | MappingMessage | LoadingIndicatorMessage | ArActivateMessage | FullscreenMessage | ConfigMessage | AnnotationToggleMessage | FocusMessage | FocusStateMessage | PricingMessage | QrShownMessage | SyncMessage | SelectableMessage | AppResolvedMessage | StateMessage;
|
|
321
461
|
type MessageCallback = (message: Message) => void | Promise<void>;
|
|
322
462
|
|
|
323
463
|
/**
|
|
@@ -432,14 +572,6 @@ declare class IframeMessenger {
|
|
|
432
572
|
* Stop listening and clean up all state
|
|
433
573
|
*/
|
|
434
574
|
destroy(): void;
|
|
435
|
-
/**
|
|
436
|
-
* Whether the messenger is ready to send
|
|
437
|
-
*/
|
|
438
|
-
get ready(): boolean;
|
|
439
|
-
/**
|
|
440
|
-
* Number of messages waiting in the outgoing queue
|
|
441
|
-
*/
|
|
442
|
-
get pendingCount(): number;
|
|
443
575
|
private post;
|
|
444
576
|
private routeToHandler;
|
|
445
577
|
}
|
|
@@ -453,7 +585,11 @@ type SendFn$1 = (message: Message) => void;
|
|
|
453
585
|
*/
|
|
454
586
|
declare class Annotations {
|
|
455
587
|
private readonly send;
|
|
456
|
-
|
|
588
|
+
/** Whose pins this facade toggles: a pin lives in one model's local space. */
|
|
589
|
+
private readonly instance;
|
|
590
|
+
constructor(send: SendFn$1,
|
|
591
|
+
/** Whose pins this facade toggles: a pin lives in one model's local space. */
|
|
592
|
+
instance: string);
|
|
457
593
|
setEnabled(enabled: boolean): void;
|
|
458
594
|
}
|
|
459
595
|
|
|
@@ -685,10 +821,14 @@ type DispatchFn = (state: FocusChange) => void;
|
|
|
685
821
|
declare class Focus {
|
|
686
822
|
private readonly send;
|
|
687
823
|
private readonly dispatch;
|
|
824
|
+
/** Which instance this facade addresses; every message it sends carries it. */
|
|
825
|
+
private readonly instance;
|
|
688
826
|
private focused;
|
|
689
827
|
private attributes;
|
|
690
828
|
private readonly changeCallbacks;
|
|
691
|
-
constructor(send: MessageCallback, dispatch: DispatchFn
|
|
829
|
+
constructor(send: MessageCallback, dispatch: DispatchFn,
|
|
830
|
+
/** Which instance this facade addresses; every message it sends carries it. */
|
|
831
|
+
instance: string);
|
|
692
832
|
/** The focused component name, or null in overview. */
|
|
693
833
|
get current(): string | null;
|
|
694
834
|
/**
|
|
@@ -863,12 +1003,19 @@ declare class Attribute {
|
|
|
863
1003
|
}
|
|
864
1004
|
|
|
865
1005
|
/**
|
|
866
|
-
*
|
|
1006
|
+
* The integrator-facing viewer settings override. Identical to the wire
|
|
1007
|
+
* `ViewerSettings`, except `annotations` also accepts the legacy booleans:
|
|
1008
|
+
* they normalise before the merge (`true` → `show`, `false` → `hide`), so
|
|
1009
|
+
* existing mounts keep compiling while the package is in alpha.
|
|
1010
|
+
*/
|
|
1011
|
+
type ViewerSettingsOverride = Omit<Partial<ViewerSettings$1>, 'annotations'> & {
|
|
1012
|
+
readonly annotations?: boolean | AnnotationVisibility$1;
|
|
1013
|
+
};
|
|
1014
|
+
/**
|
|
1015
|
+
* Options shared by both address forms.
|
|
867
1016
|
*/
|
|
868
|
-
interface
|
|
1017
|
+
interface ClientOptionsBase {
|
|
869
1018
|
readonly parent: string | HTMLElement;
|
|
870
|
-
readonly model: string;
|
|
871
|
-
readonly license: string;
|
|
872
1019
|
readonly debug?: boolean | undefined;
|
|
873
1020
|
readonly language?: string | undefined;
|
|
874
1021
|
/** W3C traceparent string to propagate to the viewer iframe for end-to-end tracing. */
|
|
@@ -884,8 +1031,34 @@ interface ClientOptions {
|
|
|
884
1031
|
* Override viewer settings from metadata. Omitted fields use the metadata value.
|
|
885
1032
|
* A warning is logged when an override differs from the metadata value.
|
|
886
1033
|
*/
|
|
887
|
-
readonly viewerSettings?:
|
|
1034
|
+
readonly viewerSettings?: ViewerSettingsOverride;
|
|
888
1035
|
}
|
|
1036
|
+
/**
|
|
1037
|
+
* How the client addresses what it shows, one of two forms:
|
|
1038
|
+
*
|
|
1039
|
+
* - `{ app }`: an app's public link key (the slug in its URL). The viewer
|
|
1040
|
+
* resolves that app and renders it with its own configuration.
|
|
1041
|
+
* - `{ license, model }`: the classic pair, which resolves to the model's
|
|
1042
|
+
* default app. Every existing integration keeps working unchanged.
|
|
1043
|
+
*
|
|
1044
|
+
* Supplying both is allowed so a migration is additive (add `app`, done):
|
|
1045
|
+
* `app` wins and a warning is logged that the pair was ignored. Supplying
|
|
1046
|
+
* neither is a compile error, which is the guarantee this union buys over a
|
|
1047
|
+
* bag of three optional fields.
|
|
1048
|
+
*/
|
|
1049
|
+
type ClientAddress = {
|
|
1050
|
+
readonly app: string;
|
|
1051
|
+
readonly license?: string | undefined;
|
|
1052
|
+
readonly model?: string | undefined;
|
|
1053
|
+
} | {
|
|
1054
|
+
readonly license: string;
|
|
1055
|
+
readonly model: string;
|
|
1056
|
+
readonly app?: undefined;
|
|
1057
|
+
};
|
|
1058
|
+
/**
|
|
1059
|
+
* Configuration options for VirtualdisplayClient
|
|
1060
|
+
*/
|
|
1061
|
+
type ClientOptions = ClientOptionsBase & ClientAddress;
|
|
889
1062
|
|
|
890
1063
|
/**
|
|
891
1064
|
* The Virtualdisplay client's DOM event names, in one registry.
|
|
@@ -913,6 +1086,20 @@ interface MutationFailedEvent {
|
|
|
913
1086
|
readonly attribute: string;
|
|
914
1087
|
readonly value: string;
|
|
915
1088
|
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Which app this viewer resolved (the apps spec's one-read inversion): the
|
|
1091
|
+
* `onAppResolved` callback payload. The protocol message minus its wire
|
|
1092
|
+
* discriminator; only the parent-safe submit settings ride along, never the
|
|
1093
|
+
* delivery recipient (removed in SQL on the public read).
|
|
1094
|
+
*/
|
|
1095
|
+
interface ResolvedAppInfo {
|
|
1096
|
+
/** Known values: `AppType`. An unknown value means a newer server; report it, never downgrade to a viewer. */
|
|
1097
|
+
readonly appType: string;
|
|
1098
|
+
readonly name: string;
|
|
1099
|
+
/** The app's public slug (the address a submit POSTs to). Absent from a pre-slug server. */
|
|
1100
|
+
readonly slug?: string;
|
|
1101
|
+
readonly submit?: AppSubmitSettings$1;
|
|
1102
|
+
}
|
|
916
1103
|
interface VirtualdisplaySelectEvent {
|
|
917
1104
|
readonly attribute: string;
|
|
918
1105
|
readonly value: string;
|
|
@@ -1020,9 +1207,16 @@ declare class BaseClient {
|
|
|
1020
1207
|
private readonly mutationFailedCallbacks;
|
|
1021
1208
|
/** The viewer's tracking session id, once the server announces it (SESSION). */
|
|
1022
1209
|
private viewerSessionId;
|
|
1210
|
+
/** Which app the viewer resolved, once reported (APP_RESOLVED). */
|
|
1211
|
+
private resolvedApp;
|
|
1212
|
+
private readonly appResolvedCallbacks;
|
|
1023
1213
|
constructor(options: ClientOptions, extensions?: BaseClientExtensions);
|
|
1214
|
+
/** The configurator UI, every callback bridging it to the facade that owns the action. */
|
|
1215
|
+
private createStandardInterface;
|
|
1024
1216
|
/** Wire the inbound viewer protocol to the facades. */
|
|
1025
1217
|
private wireIncomingMessages;
|
|
1218
|
+
/** Store the resolved app and notify every registered `onAppResolved` callback. */
|
|
1219
|
+
private handleAppResolved;
|
|
1026
1220
|
/** Store the tracking session id and hand it to the AR flow, which waits on it for the QR. */
|
|
1027
1221
|
private handleSession;
|
|
1028
1222
|
/** Forward a failed mutation to every registered `onMutationFailed` callback. */
|
|
@@ -1042,6 +1236,14 @@ declare class BaseClient {
|
|
|
1042
1236
|
/** Dispatch a bubbling `vd:configuration-submit` event when the CTA is used. */
|
|
1043
1237
|
private dispatchConfigurationSubmit;
|
|
1044
1238
|
private handleConfig;
|
|
1239
|
+
/**
|
|
1240
|
+
* The difference echo: the viewer already applied the app-config mode
|
|
1241
|
+
* itself, so the client only speaks up when the merged mode (config plus
|
|
1242
|
+
* integrator override) differs from what CONFIG announced, in either
|
|
1243
|
+
* direction. Equal modes echo nothing, so a boot without overrides sends
|
|
1244
|
+
* no toggle at all.
|
|
1245
|
+
*/
|
|
1246
|
+
private echoAnnotationsDifference;
|
|
1045
1247
|
/** Tell the viewer the user entered fullscreen, so it records the engagement. */
|
|
1046
1248
|
private trackFullscreen;
|
|
1047
1249
|
private initializeLogging;
|
|
@@ -1062,6 +1264,12 @@ declare class BaseClient {
|
|
|
1062
1264
|
* fires.
|
|
1063
1265
|
*/
|
|
1064
1266
|
onSelectableChange(callback: (snapshot: SelectableSnapshot) => void): void;
|
|
1267
|
+
/**
|
|
1268
|
+
* Subscribe to which app this viewer resolved (type, name, public submit
|
|
1269
|
+
* settings). Reported once by the viewer after its resolve; fires immediately
|
|
1270
|
+
* when the report already arrived, so registration order does not matter.
|
|
1271
|
+
*/
|
|
1272
|
+
onAppResolved(callback: (app: ResolvedAppInfo) => void): void;
|
|
1065
1273
|
/** The selectable values for one attribute right now, or null when unrestricted. */
|
|
1066
1274
|
getSelectable(name: string): readonly string[] | null;
|
|
1067
1275
|
/**
|
|
@@ -1086,12 +1294,17 @@ declare class BaseClient {
|
|
|
1086
1294
|
}
|
|
1087
1295
|
|
|
1088
1296
|
type Attrs = Record<string, unknown> | unknown;
|
|
1089
|
-
|
|
1297
|
+
/**
|
|
1298
|
+
* The logger the client exposes: the same four levels as the base logger, but
|
|
1299
|
+
* accepting any value as attributes so callers can pass an Error or a bare value.
|
|
1300
|
+
*/
|
|
1301
|
+
interface ClientLogger {
|
|
1090
1302
|
debug: (msg: string, attrs?: Attrs) => void;
|
|
1091
1303
|
info: (msg: string, attrs?: Attrs) => void;
|
|
1092
1304
|
warn: (msg: string, attrs?: Attrs) => void;
|
|
1093
1305
|
error: (msg: string, attrs?: Attrs) => void;
|
|
1094
|
-
}
|
|
1306
|
+
}
|
|
1307
|
+
declare let logger: ClientLogger;
|
|
1095
1308
|
declare function enableDebugMode(): void;
|
|
1096
1309
|
declare function isDebugMode(): boolean;
|
|
1097
1310
|
|
|
@@ -1113,6 +1326,27 @@ declare class VirtualdisplayClient extends BaseClient {
|
|
|
1113
1326
|
onSyncChange(callback: (connected: boolean) => void): void;
|
|
1114
1327
|
}
|
|
1115
1328
|
|
|
1329
|
+
/** The app types this protocol version knows. The wire field stays a plain
|
|
1330
|
+
* string on purpose: a newer viewer may report a type this bundle predates,
|
|
1331
|
+
* and the host must receive it to fail visibly (never a quiet downgrade). */
|
|
1332
|
+
type AppType = 'viewer' | 'configurator';
|
|
1333
|
+
/** The public submit settings of a configurator-type app, as the form renders
|
|
1334
|
+
* them. Never the recipient: that section is removed in SQL on the public
|
|
1335
|
+
* read and cannot ride any message. Sparse like every config section: an
|
|
1336
|
+
* absent field means the renderer's default (the backend stores the section
|
|
1337
|
+
* as written and omits empty keys), so nothing here is guaranteed present. */
|
|
1338
|
+
interface AppSubmitSettings {
|
|
1339
|
+
readonly fields?: string[];
|
|
1340
|
+
readonly ctaLabel?: string;
|
|
1341
|
+
readonly privacyUrl?: string;
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* How a model's annotations present to the Shopper: never (hide), always
|
|
1345
|
+
* (show), or under the Shopper's own control via a viewer toggle, starting
|
|
1346
|
+
* visible (toggle-shown) or hidden (toggle-hidden). Resolved per app; the
|
|
1347
|
+
* backend's `Resolve` owns the `hide` default.
|
|
1348
|
+
*/
|
|
1349
|
+
type AnnotationVisibility = 'hide' | 'show' | 'toggle-shown' | 'toggle-hidden';
|
|
1116
1350
|
/**
|
|
1117
1351
|
* Viewer settings from model metadata (server → client via MAPPING).
|
|
1118
1352
|
* Customer-configured in the portal.
|
|
@@ -1122,7 +1356,12 @@ interface ViewerSettings {
|
|
|
1122
1356
|
readonly fullscreen?: boolean;
|
|
1123
1357
|
readonly screenshot?: boolean;
|
|
1124
1358
|
readonly cameraControls?: boolean;
|
|
1125
|
-
|
|
1359
|
+
/**
|
|
1360
|
+
* The resolved per-app annotation mode. Present only when the model has
|
|
1361
|
+
* annotations, so a control can never render with nothing to govern;
|
|
1362
|
+
* absent means there is nothing to show or toggle.
|
|
1363
|
+
*/
|
|
1364
|
+
readonly annotations?: AnnotationVisibility;
|
|
1126
1365
|
readonly language?: string;
|
|
1127
1366
|
/** Where the attribute controls sit: in-frame bottom-left (default) or a docked right panel. */
|
|
1128
1367
|
readonly configuratorLayout?: 'inframe-bottom-left' | 'sidebar-right' | 'hidden';
|
|
@@ -1139,7 +1378,7 @@ declare const AVAILABILITY: {
|
|
|
1139
1378
|
/** Behaves exactly like an unpriced combination: no price, no button. */
|
|
1140
1379
|
readonly HIDDEN: "hidden";
|
|
1141
1380
|
};
|
|
1142
|
-
type Availability = typeof AVAILABILITY[keyof typeof AVAILABILITY];
|
|
1381
|
+
type Availability = (typeof AVAILABILITY)[keyof typeof AVAILABILITY];
|
|
1143
1382
|
/**
|
|
1144
1383
|
* One pricing rule: a subset of attribute values mapped to a reference + price.
|
|
1145
1384
|
* Matches the backend pricing read contract (`GET /api/public/models/.../pricing`).
|
|
@@ -1176,4 +1415,4 @@ interface PricingDocument {
|
|
|
1176
1415
|
}
|
|
1177
1416
|
|
|
1178
1417
|
export { Annotations, Attribute, AttributeValue, Camera, Configuration, ERROR_CODES, Focus, Photo, Snapshot, VD_EVENTS, VirtualdisplayClient, VirtualdisplayError, enableDebugMode, isDebugMode, logger };
|
|
1179
|
-
export type { Availability, ClientOptions, ErrorCode, FocusState, MutationFailedEvent, PhotoData, PricingDocument, PricingRule, ResolvedConfiguration, SubmitOptions, SubmittedConfiguration, VdEvent, ViewerSettings, VirtualdisplaySelectEvent, VirtualdisplaySelectedEvent, VirtualdisplayUiEventMap, VirtualdisplayUiEventType };
|
|
1418
|
+
export type { AppSubmitSettings, AppType, Availability, ClientOptions, ErrorCode, FocusState, MutationFailedEvent, PhotoData, PricingDocument, PricingRule, ResolvedAppInfo, ResolvedConfiguration, SubmitOptions, SubmittedConfiguration, VdEvent, ViewerSettings, VirtualdisplaySelectEvent, VirtualdisplaySelectedEvent, VirtualdisplayUiEventMap, VirtualdisplayUiEventType };
|