@virtualdisplay.io/client 4.0.0-alpha.29 → 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.
package/dist/index.d.ts CHANGED
@@ -1,29 +1,3 @@
1
- /**
2
- * Virtualdisplay iframe messaging protocol
3
- *
4
- * All message types exchanged between client (parent page)
5
- * and server (iframe viewer engine) via postMessage.
6
- */
7
- declare const MESSAGE_TYPES: {
8
- readonly HANDSHAKE: "handshake";
9
- readonly SESSION: "session";
10
- readonly MUTATION: "mutation";
11
- readonly STATE: "state";
12
- readonly LOADING_INDICATOR: "loading_indicator";
13
- readonly CAMERA: "camera";
14
- readonly SNAPSHOT: "snapshot";
15
- readonly MAPPING: "mapping";
16
- readonly AR_ACTIVATE: "ar_activate";
17
- readonly FULLSCREEN: "fullscreen";
18
- readonly CONFIG: "config";
19
- readonly ANNOTATION_TOGGLE: "annotation_toggle";
20
- readonly FOCUS: "focus";
21
- readonly PRICING: "pricing";
22
- readonly QR_SHOWN: "qr_shown";
23
- readonly SYNC: "sync";
24
- readonly SELECTABLE: "selectable";
25
- readonly APP_RESOLVED: "app_resolved";
26
- };
27
1
  /** The app types this protocol version knows. The wire field stays a plain
28
2
  * string on purpose: a newer viewer may report a type this bundle predates,
29
3
  * and the host must receive it to fail visibly (never a quiet downgrade). */
@@ -35,100 +9,6 @@ interface AppSubmitSettings {
35
9
  readonly ctaLabel?: string;
36
10
  readonly privacyUrl?: string;
37
11
  }
38
- /**
39
- * Viewer -> client: which app this viewer resolved, reported up so the parent
40
- * (the embed host) can load the matching shell without its own resolve (the
41
- * apps spec's one-read inversion). Sent once, after the resolve, before or
42
- * with MAPPING.
43
- */
44
- interface AppResolvedMessage {
45
- readonly type: typeof MESSAGE_TYPES.APP_RESOLVED;
46
- /** Known values: {@link AppType}. An unknown value means a newer server; the host reports it, never downgrades. */
47
- readonly appType: string;
48
- readonly name: string;
49
- /** The app's public slug, the address a submit POSTs to. Optional on the
50
- * wire: a pre-task-60 server does not send it, and the pair-form host has
51
- * no other way to learn it, so the shell treats absence as "submit
52
- * unavailable" rather than the guard dropping the whole report. */
53
- readonly slug?: string;
54
- readonly submit?: AppSubmitSettings;
55
- }
56
- /** One mesh or material variant in a model, as reported by STATE. */
57
- interface ModelNodeDto {
58
- readonly id: string;
59
- readonly name: string;
60
- readonly type: string;
61
- readonly visible: boolean;
62
- }
63
- /**
64
- * State (viewer → client): the meshes and material variants of one instance,
65
- * with their current visibility.
66
- */
67
- interface StateMessage {
68
- readonly type: typeof MESSAGE_TYPES.STATE;
69
- /** Whose meshes and variants these are. */
70
- readonly instance: string;
71
- readonly nodes: ModelNodeDto[];
72
- readonly isInitial: boolean;
73
- }
74
- interface MutationDto {
75
- readonly name: string;
76
- readonly value: string;
77
- }
78
- interface MutationResult {
79
- readonly name: string;
80
- readonly value: string;
81
- readonly success: boolean;
82
- readonly error?: string;
83
- }
84
- /** One instance's configuration: attribute name to the value selected for it. */
85
- type InstanceAttributes = Readonly<Record<string, string>>;
86
- /**
87
- * The folded session state carried by a live sync broadcast: the same shape the
88
- * public session-state read returns. It is authoritative, where `mutations` on
89
- * the same message is only the change that triggered the broadcast.
90
- */
91
- interface SyncStateSnapshot {
92
- readonly license: string;
93
- readonly model: string;
94
- /** The folded configuration, keyed by instance and then by attribute name. */
95
- readonly attributes: Readonly<Record<string, InstanceAttributes>>;
96
- }
97
- interface MutationMessage {
98
- readonly type: typeof MESSAGE_TYPES.MUTATION;
99
- /** Which instance's attributes these mutations change. */
100
- readonly instance: string;
101
- readonly mutations: MutationDto[];
102
- readonly isInitial: boolean;
103
- /** The sending page's session id, present when the mutation came from another device. */
104
- readonly origin?: string;
105
- /** The sender's per-session sequence number. */
106
- readonly seq?: number;
107
- /** The authoritative session state at the moment of the broadcast. */
108
- readonly state?: SyncStateSnapshot;
109
- /**
110
- * A drag's intermediate step: the viewer applies it so the model follows the
111
- * hand but does not record it. The settled value arrives afterwards as an
112
- * ordinary mutation without this flag.
113
- */
114
- readonly transient?: boolean;
115
- }
116
- /**
117
- * Sync status (viewer → client): whether the live channel is connected to the
118
- * hand-off session. One-way and latest-wins, with no history or buffering. The
119
- * client exposes it as `onSyncChange`.
120
- */
121
- interface SyncMessage {
122
- readonly type: typeof MESSAGE_TYPES.SYNC;
123
- readonly connected: boolean;
124
- }
125
- interface MutationResultMessage {
126
- readonly type: typeof MESSAGE_TYPES.MUTATION;
127
- /** Which instance these results are for; without it a failure cannot say whose. */
128
- readonly instance: string;
129
- readonly results: MutationResult[];
130
- readonly isInitial: boolean;
131
- }
132
12
  /**
133
13
  * How a model's annotations present to the visitor: never (hide), always
134
14
  * (show), or under their own control via a viewer toggle, starting visible
@@ -169,61 +49,6 @@ interface CameraLimits {
169
49
  readonly minRotate?: number;
170
50
  readonly maxRotate?: number;
171
51
  }
172
- interface CameraCommand {
173
- readonly action: 'rotate' | 'tilt' | 'zoom' | 'reset';
174
- readonly value?: number;
175
- }
176
- interface CameraParameter {
177
- readonly type: 'rotation' | 'tilt' | 'zoom';
178
- readonly value: number;
179
- }
180
- interface CameraCommandMessage {
181
- readonly type: typeof MESSAGE_TYPES.CAMERA;
182
- readonly commands: CameraCommand[];
183
- }
184
- interface CameraPositionMessage {
185
- readonly type: typeof MESSAGE_TYPES.CAMERA;
186
- readonly transforms: CameraParameter[];
187
- }
188
- interface SnapshotRequestMessage {
189
- readonly type: typeof MESSAGE_TYPES.SNAPSHOT;
190
- readonly filename: string;
191
- }
192
- interface SnapshotResultMessage {
193
- readonly type: typeof MESSAGE_TYPES.SNAPSHOT;
194
- readonly filename: string;
195
- readonly data?: string;
196
- readonly error?: string;
197
- }
198
- /**
199
- * Marks an attribute as a measurement (a parametric dimension): the values
200
- * are millimetre integer strings, and this carries what a client needs to
201
- * render a measurement control and format the unit. Absent for ordinary
202
- * attributes; a client that ignores it still renders the value list.
203
- */
204
- interface DimensionDescriptor {
205
- readonly displayUnit: 'mm' | 'cm' | 'm' | 'in';
206
- readonly minMm: number;
207
- readonly maxMm: number;
208
- readonly stepMm: number;
209
- }
210
- interface AttributeDefinition {
211
- readonly name: string;
212
- readonly values: string[];
213
- readonly defaultValue?: string;
214
- /** Present when this attribute is a measurement; see DimensionDescriptor. */
215
- readonly dimension?: DimensionDescriptor;
216
- }
217
- interface MappingMessage {
218
- readonly type: typeof MESSAGE_TYPES.MAPPING;
219
- /**
220
- * Whose attributes these are. One MAPPING per instance, and an instance with
221
- * no attributes sends an empty one rather than none: silence would leave the
222
- * client unable to name it, click it or report its interactions.
223
- */
224
- readonly instance: string;
225
- readonly attributes: AttributeDefinition[];
226
- }
227
52
  /** Per selector, the values the buyer can still pick right now. */
228
53
  interface SelectableAttribute {
229
54
  readonly name: string;
@@ -236,132 +61,6 @@ interface SelectableAttribute {
236
61
  */
237
62
  readonly hidden?: boolean;
238
63
  }
239
- /**
240
- * Selectability snapshot (viewer → client): which values can still be picked,
241
- * sent after the mapping and after every choice that changes the picture.
242
- * Always the complete state, never a difference; a value not in its list is
243
- * not pickable and the client greys it out.
244
- */
245
- interface SelectableMessage {
246
- readonly type: typeof MESSAGE_TYPES.SELECTABLE;
247
- /** Whose attributes these are, mirroring the MAPPING it follows. */
248
- readonly instance: string;
249
- readonly attributes: SelectableAttribute[];
250
- }
251
- /**
252
- * Loading indicator (viewer → client): download progress, the phase it is in, or
253
- * an error, sent while the model loads so the client's loading UI can follow.
254
- */
255
- interface LoadingIndicatorMessage {
256
- readonly type: typeof MESSAGE_TYPES.LOADING_INDICATOR;
257
- /** 0–100 */
258
- readonly progress: number;
259
- /** Human-readable phase label, e.g. "downloading" */
260
- readonly phase: string;
261
- /** Set when loading failed; client shows error state instead of hiding the overlay. */
262
- readonly error?: string;
263
- }
264
- /** AR activate (client → server): user clicked the AR button. Server handles activation. */
265
- interface ArActivateMessage {
266
- readonly type: typeof MESSAGE_TYPES.AR_ACTIVATE;
267
- }
268
- /**
269
- * Fullscreen (client → server): the user entered fullscreen via the client's
270
- * toolbar button. Fullscreen itself is handled client-side; this only lets the
271
- * server record the engagement against the tracking session it owns.
272
- */
273
- interface FullscreenMessage {
274
- readonly type: typeof MESSAGE_TYPES.FULLSCREEN;
275
- }
276
- /**
277
- * QR shown (client → viewer): the desktop hand-off QR was displayed. One-way and
278
- * fire-and-forget, with nothing to act on.
279
- */
280
- interface QrShownMessage {
281
- readonly type: typeof MESSAGE_TYPES.QR_SHOWN;
282
- }
283
- /**
284
- * Config (server → client): viewer settings and camera limits from model metadata.
285
- * Sent early — before MAPPING — so the client can show/hide controls and disable
286
- * camera buttons at limits as soon as metadata is available.
287
- */
288
- interface ConfigMessage {
289
- readonly type: typeof MESSAGE_TYPES.CONFIG;
290
- readonly viewerSettings: ViewerSettings;
291
- readonly cameraLimits?: CameraLimits;
292
- }
293
- interface HandshakeMessage {
294
- readonly type: typeof MESSAGE_TYPES.HANDSHAKE;
295
- }
296
- /**
297
- * Session (server → client): the tracking session id, announced once the viewer
298
- * has established it (the id is generated in the iframe, so this is how the
299
- * client learns it). The client exposes it so a host page can correlate its own
300
- * actions (e.g. a configurator conversion) with the viewer session.
301
- */
302
- interface SessionMessage {
303
- readonly type: typeof MESSAGE_TYPES.SESSION;
304
- readonly sessionId: string;
305
- }
306
- /**
307
- * Annotation toggle (parent → server): show or hide annotation markers.
308
- * Only works when annotations exist in metadata (grand switch).
309
- */
310
- interface AnnotationToggleMessage {
311
- readonly type: typeof MESSAGE_TYPES.ANNOTATION_TOGGLE;
312
- /**
313
- * Whose pins to show. This looks scene-wide because the client draws one
314
- * button, and it is not: a pin's position is stored in its model's local
315
- * space, so it means nothing without the root it belongs to. A client that
316
- * wants one button loops over the instances, which is a UI decision.
317
- */
318
- readonly instance: string;
319
- readonly enabled: boolean;
320
- /**
321
- * Who sent it: `control` marks the client's own visible toggle button, the
322
- * only sender that counts as Shopper use in the viewer statistics. Absent
323
- * means programmatic (the boot-time config echo, an integrator's
324
- * `client.annotations` call), which must never be measured as interest.
325
- */
326
- readonly origin?: 'control';
327
- }
328
- /** Focus actions (client → server). */
329
- declare const FOCUS_ACTIONS: {
330
- readonly FOCUS: "focus";
331
- readonly UNFOCUS: "unfocus";
332
- };
333
- /** Focus states (server → client). */
334
- declare const FOCUS_STATES: {
335
- readonly FOCUSED: "focused";
336
- readonly UNFOCUSED: "unfocused";
337
- };
338
- type FocusAction = (typeof FOCUS_ACTIONS)[keyof typeof FOCUS_ACTIONS];
339
- type FocusStateValue = (typeof FOCUS_STATES)[keyof typeof FOCUS_STATES];
340
- /**
341
- * Focus (client → viewer): isolate and frame one component, or return to the
342
- * overview. `component` is a name from the model's components, or a mesh name as
343
- * a fallback, and is required when the action is `focus`.
344
- */
345
- interface FocusMessage {
346
- readonly type: typeof MESSAGE_TYPES.FOCUS;
347
- /** Which instance the component name resolves inside. */
348
- readonly instance: string;
349
- readonly action: FocusAction;
350
- readonly component?: string;
351
- }
352
- /**
353
- * Focus state (server → client): confirms the focus state after the camera and
354
- * opacity transition. `attributes` lists which attributes are relevant to the
355
- * focused component, so a custom UI can filter its selectors.
356
- */
357
- interface FocusStateMessage {
358
- readonly type: typeof MESSAGE_TYPES.FOCUS;
359
- /** Which instance this state is about. */
360
- readonly instance: string;
361
- readonly state: FocusStateValue;
362
- readonly component?: string;
363
- readonly attributes?: string[];
364
- }
365
64
  /**
366
65
  * What the configurator does with a matched configuration.
367
66
  */
@@ -408,172 +107,6 @@ interface PricingDocument {
408
107
  readonly vatDisplay: string;
409
108
  readonly rules: PricingRule[];
410
109
  }
411
- /**
412
- * Pricing (server → client): the model's pricing document. Sent once at startup
413
- * after MAPPING so the client's resolver can price the current configuration.
414
- */
415
- interface PricingMessage {
416
- readonly type: typeof MESSAGE_TYPES.PRICING;
417
- /**
418
- * Which instance is priced. Only one is today, so the id has no alternative
419
- * value to hold; it carries one anyway, because a rule with a "not needed
420
- * yet" exception erodes, and a scene of separately priced products needs
421
- * exactly this field.
422
- */
423
- readonly instance: string;
424
- readonly pricing: PricingDocument;
425
- }
426
- type Message = HandshakeMessage | SessionMessage | MutationMessage | MutationResultMessage | CameraCommandMessage | CameraPositionMessage | SnapshotRequestMessage | SnapshotResultMessage | MappingMessage | LoadingIndicatorMessage | ArActivateMessage | FullscreenMessage | ConfigMessage | AnnotationToggleMessage | FocusMessage | FocusStateMessage | PricingMessage | QrShownMessage | SyncMessage | SelectableMessage | AppResolvedMessage | StateMessage;
427
- type MessageCallback = (message: Message) => void | Promise<void>;
428
-
429
- /**
430
- * Options for IframeMessenger
431
- */
432
- interface MessengerOptions {
433
- /**
434
- * Buffer incoming messages until markConsumerReady().
435
- * When false (default), incoming messages are processed immediately.
436
- */
437
- bufferIncoming?: boolean;
438
- /**
439
- * Which protocol to validate incoming messages against, never whether to
440
- * validate: both sides always reject unknown types and browser-extension
441
- * noise. Defaults to the public protocol.
442
- */
443
- isValid?: (data: unknown) => boolean;
444
- }
445
- /**
446
- * A bidirectional message port between a parent page and an iframe.
447
- *
448
- * @example Client (parent page, publisher queue only)
449
- * ```ts
450
- * const messenger = new IframeMessenger(() => iframe.contentWindow);
451
- * messenger.on('mutation', handleMutationResult);
452
- * messenger.on('mapping', handleMapping);
453
- * messenger.start();
454
- * // When iframe loads:
455
- * messenger.markReady();
456
- * messenger.send({ type: 'mutation', mutations, isInitial: true });
457
- * ```
458
- *
459
- * @example Viewer (inside the iframe, both queues)
460
- * ```ts
461
- * const messenger = new IframeMessenger(
462
- * () => window.parent !== window ? window.parent : null,
463
- * { bufferIncoming: true },
464
- * );
465
- * messenger.on('mutation', processMutation);
466
- * messenger.on('config', applyConfig);
467
- * messenger.start();
468
- * messenger.markReady();
469
- * // When model loads:
470
- * messenger.markConsumerReady();
471
- * ```
472
- */
473
- declare class IframeMessenger {
474
- private readonly getTarget;
475
- private readonly handlers;
476
- private readonly publisher;
477
- private readonly consumer;
478
- private listener;
479
- private readonly sourceId;
480
- private targetOrigin;
481
- private readonly isValid;
482
- constructor(getTarget: () => Window | null, options?: MessengerOptions);
483
- /**
484
- * Register a handler for a specific message type
485
- */
486
- on(type: string, handler: MessageCallback): void;
487
- /**
488
- * Update the target origin for outgoing postMessage calls.
489
- * Use when the target origin is discovered at runtime (e.g. from the first incoming message).
490
- */
491
- setTargetOrigin(origin: string): void;
492
- /**
493
- * Send a message to the target window
494
- * Queued until markReady() is called
495
- */
496
- send(message: Message): void;
497
- /**
498
- * Route a message as if it had arrived by postMessage, without the round trip.
499
- * Still validated against `isValid` and still subject to the consumer queue.
500
- */
501
- receive(message: Message): void;
502
- /**
503
- * Enable sending and flush queued outgoing messages
504
- */
505
- markReady(): void;
506
- /**
507
- * Enable processing of buffered incoming messages
508
- * Only relevant when constructed with bufferIncoming: true
509
- */
510
- markConsumerReady(): void;
511
- /**
512
- * Check if an incoming message of a specific type is pending in the consumer buffer
513
- */
514
- consumerHasPending(type: string): boolean;
515
- /**
516
- * Start listening for incoming messages
517
- */
518
- start(): void;
519
- /**
520
- * Stop listening and clean up all state
521
- */
522
- destroy(): void;
523
- private post;
524
- private routeToHandler;
525
- }
526
-
527
- /**
528
- * The client's DOM event names, in one registry. The two selection events are
529
- * what `client.on(...)` subscribes to; the other three bubble out of the
530
- * container, so the host page can reach them with `addEventListener`.
531
- */
532
- declare const VD_EVENTS: {
533
- /** Attribute selection requested (cancelable, does not bubble). `detail`: `{ attribute, value }`. */
534
- readonly SELECT: "vd:select";
535
- /** Attribute selection applied (does not bubble). `detail`: `{ attribute, value }`. */
536
- readonly SELECTED: "vd:selected";
537
- /** Focus changed (bubbles). `detail`: `{ component, attributes }`, or `null` for the overview. */
538
- readonly FOCUS_CHANGE: "vd:focus-change";
539
- /** Resolved configuration changed (bubbles). `detail`: the resolved configuration, or `null` when nothing matches. */
540
- readonly CONFIGURATION_CHANGE: "vd:configuration-change";
541
- /** Configuration submitted (bubbles). `detail`: the resolved configuration handed to the host. */
542
- readonly CONFIGURATION_SUBMIT: "vd:configuration-submit";
543
- };
544
- type VdEvent = typeof VD_EVENTS[keyof typeof VD_EVENTS];
545
-
546
- /** Hands a message to the viewer iframe. */
547
- type SendFn = (message: Message) => void;
548
- interface MutationFailedEvent {
549
- readonly attribute: string;
550
- readonly value: string;
551
- }
552
- /**
553
- * Which app the viewer resolved, as handed to an `onAppResolved` callback.
554
- */
555
- interface ResolvedAppInfo {
556
- /** Known values: `AppType`. An unknown value means a newer server; report it, never downgrade to a viewer. */
557
- readonly appType: string;
558
- readonly name: string;
559
- /** The app's public slug (the address a submit POSTs to). Absent from a pre-slug server. */
560
- readonly slug?: string;
561
- readonly submit?: AppSubmitSettings;
562
- }
563
- interface VirtualdisplaySelectEvent {
564
- readonly attribute: string;
565
- readonly value: string;
566
- preventDefault(): void;
567
- }
568
- interface VirtualdisplaySelectedEvent {
569
- readonly attribute: string;
570
- readonly value: string;
571
- }
572
- type VirtualdisplayUiEventMap = {
573
- [VD_EVENTS.SELECT]: VirtualdisplaySelectEvent;
574
- [VD_EVENTS.SELECTED]: VirtualdisplaySelectedEvent;
575
- };
576
- type VirtualdisplayUiEventType = keyof VirtualdisplayUiEventMap;
577
110
 
578
111
  /**
579
112
  * Annotation display: show or hide the model's existing markers.
@@ -582,9 +115,6 @@ declare class Annotations {
582
115
  private readonly send;
583
116
  /** Whose pins this facade toggles: a pin lives in one model's local space. */
584
117
  private readonly instance;
585
- constructor(send: SendFn,
586
- /** Whose pins this facade toggles: a pin lives in one model's local space. */
587
- instance: string);
588
118
  setEnabled(enabled: boolean): void;
589
119
  }
590
120
 
@@ -597,7 +127,6 @@ type SelectableSnapshot = readonly SelectableAttribute[];
597
127
  declare class Camera {
598
128
  private readonly send;
599
129
  private commands;
600
- constructor(send: MessageCallback);
601
130
  /**
602
131
  * Rotate camera horizontally
603
132
  * @param degrees - Absolute rotation in degrees (0 = front view)
@@ -645,7 +174,6 @@ declare class CameraPosition {
645
174
  private readonly state;
646
175
  private limitsState;
647
176
  private readonly changeCallbacks;
648
- constructor();
649
177
  /**
650
178
  * Get current rotation in degrees
651
179
  */
@@ -659,9 +187,9 @@ declare class CameraPosition {
659
187
  */
660
188
  get zoom(): number;
661
189
  /**
662
- * Subscribe to camera position changes.
663
- * Also fires when limits are applied via applyLimits() so components
664
- * can re-evaluate disabled states without a separate callback.
190
+ * Subscribe to camera position changes. Also fires when the viewer reports new
191
+ * movement limits, so controls can re-evaluate their disabled state without a
192
+ * second callback.
665
193
  */
666
194
  onChange(callback: (position: CameraPositionState) => void): void;
667
195
  /**
@@ -669,24 +197,8 @@ declare class CameraPosition {
669
197
  * Undefined until the server sends limits in its CONFIG payload.
670
198
  */
671
199
  get limits(): CameraLimits | undefined;
672
- /**
673
- * Apply camera limits received from the server CONFIG message.
674
- * Fires onChange callbacks so button disabled states update immediately.
675
- */
676
- applyLimits(limits: CameraLimits | undefined): void;
677
- /**
678
- * Handle incoming camera position message from server
679
- */
680
- handlePositionUpdate(message: CameraPositionMessage): void;
681
- /**
682
- * Update camera position from server transforms
683
- */
684
- update(transforms: CameraParameter[]): void;
685
200
  }
686
201
 
687
- /** The buyer's current configuration as attribute name→value pairs. */
688
- type Selection = Record<string, string>;
689
-
690
202
  /**
691
203
  * The buyer's current configuration once it resolves to a price. `availability`
692
204
  * is `available` (price + submit) or `view_only` (price, no submit); a matched
@@ -710,9 +222,6 @@ interface SubmittedConfiguration {
710
222
  readonly attributes: Record<string, string>;
711
223
  readonly reference: string;
712
224
  }
713
- type ChangeCallback = (configuration: ResolvedConfiguration | undefined) => void;
714
- type SubmitCallback = (configuration: SubmittedConfiguration) => void | Promise<unknown>;
715
- type Dispatch<T> = (detail: T) => void;
716
225
  /** Options for {@link Configuration.onSubmit}. */
717
226
  interface SubmitOptions {
718
227
  /** Fallback button label when the customer authored none (the adapter brings it). */
@@ -734,7 +243,6 @@ declare class Configuration {
734
243
  private readonly renderCallbacks;
735
244
  private submitCallback;
736
245
  private submitDefaultLabel;
737
- constructor(dispatchChange: Dispatch<ResolvedConfiguration | undefined>, dispatchSubmit: Dispatch<SubmittedConfiguration>);
738
246
  /** The resolved configuration, or undefined when nothing matches. */
739
247
  get current(): ResolvedConfiguration | undefined;
740
248
  /** The VAT label for bare embeds (e.g. "incl. btw"), or undefined without pricing. */
@@ -762,20 +270,13 @@ declare class Configuration {
762
270
  * Subscribe to configuration changes. Fires after every selection change with
763
271
  * the resolved configuration, including undefined when nothing matches.
764
272
  */
765
- onChange(callback: ChangeCallback): void;
273
+ onChange(callback: (configuration: ResolvedConfiguration | undefined) => void): void;
766
274
  /**
767
275
  * Register the submit handler the CTA hands the configuration to. Registering
768
276
  * one is what switches the button on (capability driven). `defaultLabel` fills
769
277
  * in when the customer authored no button label.
770
278
  */
771
- onSubmit(callback: SubmitCallback, options?: SubmitOptions): void;
772
- /**
773
- * Subscribe the built-in UI to re-render the price + CTA. Fires on every
774
- * resolve and when a submit handler is registered (which can switch the button
775
- * on without a configuration change). Separate from `onChange`, which stays the
776
- * host-facing, mutation-driven contract. Returns a disposer.
777
- */
778
- onRender(callback: () => void): () => void;
279
+ onSubmit(callback: (configuration: SubmittedConfiguration) => void | Promise<unknown>, options?: SubmitOptions): void;
779
280
  /**
780
281
  * Hand the current configuration to the registered handler and fire the
781
282
  * bubbling `vd:configuration-submit` event. No-op without a handler or a
@@ -783,10 +284,6 @@ declare class Configuration {
783
284
  * reflect the result (bezig / gelukt / mislukt).
784
285
  */
785
286
  submit(): Promise<void>;
786
- /** Called by the client when a PRICING message arrives from the viewer. */
787
- handlePricingReceived(pricing: PricingDocument): void;
788
- /** Called by the client after every mutation result, with the full selection. */
789
- handleSelectionChanged(selection: Selection): void;
790
287
  private recompute;
791
288
  private notifyRender;
792
289
  private resolve;
@@ -797,9 +294,6 @@ interface FocusState {
797
294
  readonly component: string;
798
295
  readonly attributes: string[];
799
296
  }
800
- type FocusChange = FocusState | null;
801
- type FocusChangeCallback = (state: FocusChange) => void;
802
- type DispatchFn = (state: FocusChange) => void;
803
297
  /**
804
298
  * Isolate and frame one component, or return to the overview. Alongside
805
299
  * `onChange` the container dispatches a bubbling `vd:focus-change` event, so a
@@ -813,9 +307,6 @@ declare class Focus {
813
307
  private focused;
814
308
  private attributes;
815
309
  private readonly changeCallbacks;
816
- constructor(send: MessageCallback, dispatch: DispatchFn,
817
- /** Which instance this facade addresses; every message it sends carries it. */
818
- instance: string);
819
310
  /** The focused component name, or null in overview. */
820
311
  get current(): string | null;
821
312
  /**
@@ -829,9 +320,7 @@ declare class Focus {
829
320
  * Subscribe to focus changes. The callback fires with the focus state when a
830
321
  * component is focused, and with null when returning to overview.
831
322
  */
832
- onChange(callback: FocusChangeCallback): void;
833
- /** Called by the client when a FocusStateMessage is received from the viewer. */
834
- handleStateReceived(message: FocusStateMessage): void;
323
+ onChange(callback: (state: FocusState | null) => void): void;
835
324
  }
836
325
 
837
326
  /**
@@ -872,7 +361,6 @@ declare class Photo {
872
361
  private developed;
873
362
  private failed;
874
363
  private photoData;
875
- constructor(filename: string);
876
364
  /**
877
365
  * Register a callback for when the photo is developed
878
366
  * If the photo is already developed, the callback is called immediately
@@ -892,15 +380,6 @@ declare class Photo {
892
380
  * Get the filename for this photo
893
381
  */
894
382
  getFilename(): string;
895
- /**
896
- * Mark the photo as developed and notify all callbacks
897
- * This method is called by Snapshot when the photo data arrives from the server
898
- */
899
- develop(photoData: PhotoData): void;
900
- /**
901
- * Mark the photo as failed and notify error callbacks
902
- */
903
- fail(error: VirtualdisplayError): void;
904
383
  }
905
384
 
906
385
  /**
@@ -911,7 +390,6 @@ declare class Snapshot {
911
390
  private readonly send;
912
391
  private readonly validExtensions;
913
392
  private readonly pendingPhotos;
914
- constructor(send: MessageCallback);
915
393
  /**
916
394
  * Take a snapshot of the current 3D model view
917
395
  *
@@ -920,11 +398,6 @@ declare class Snapshot {
920
398
  * @throws VirtualdisplayError if filename is invalid
921
399
  */
922
400
  take(filename: string): Photo;
923
- /**
924
- * Handle incoming snapshot result from server
925
- * Matches the message to a pending photo by filename, then develops or fails it
926
- */
927
- handleSnapshotResult(message: SnapshotResultMessage): void;
928
401
  /**
929
402
  * Fail any pending photos and forget them. Called from client destroy
930
403
  * so callers awaiting onError() see a deterministic outcome instead of
@@ -945,17 +418,9 @@ declare class AttributeValue {
945
418
  readonly value: string;
946
419
  readonly attribute: string;
947
420
  private isSelectedValue;
948
- constructor(value: string, attribute: string);
949
421
  get isSelected(): boolean;
950
- setSelected(selected: boolean): void;
951
422
  }
952
423
 
953
- interface AttributeOptions {
954
- readonly name: string;
955
- readonly values: string[];
956
- readonly onSelect: (name: string, value: string) => void;
957
- readonly defaultValue?: string;
958
- }
959
424
  /**
960
425
  * Represents a single attribute with its available values
961
426
  * Flat API - no source/target translation, works with server values directly
@@ -966,7 +431,6 @@ declare class Attribute {
966
431
  private readonly values;
967
432
  private readonly onSelect;
968
433
  private onChangeCallback?;
969
- constructor(options: AttributeOptions);
970
434
  /**
971
435
  * Select a value for this attribute
972
436
  * @throws {VirtualdisplayError} When value not found
@@ -981,90 +445,58 @@ declare class Attribute {
981
445
  * Receives the newly selected AttributeValue
982
446
  */
983
447
  onChange(callback: (selectedValue: AttributeValue) => void): void;
984
- /**
985
- * Update selection state from server mutation result
986
- * Direct value comparison - no source/target translation
987
- */
988
- handleResult(serverValue: string): void;
989
448
  private createValues;
990
449
  }
991
450
 
992
451
  /**
993
- * The integrator-facing viewer settings override. Identical to the wire
994
- * `ViewerSettings`, except `annotations` also accepts the legacy booleans:
995
- * they normalise before the merge (`true` → `show`, `false` → `hide`), so
996
- * existing mounts keep compiling while the package is in alpha.
452
+ * The client's DOM event names, in one registry. The two selection events are
453
+ * what `client.on(...)` subscribes to; the other three bubble out of the
454
+ * container, so the host page can reach them with `addEventListener`.
997
455
  */
998
- type ViewerSettingsOverride = Omit<Partial<ViewerSettings>, 'annotations'> & {
999
- readonly annotations?: boolean | AnnotationVisibility;
456
+ declare const VD_EVENTS: {
457
+ /** Attribute selection requested (cancelable, does not bubble). `detail`: `{ attribute, value }`. */
458
+ readonly SELECT: "vd:select";
459
+ /** Attribute selection applied (does not bubble). `detail`: `{ attribute, value }`. */
460
+ readonly SELECTED: "vd:selected";
461
+ /** Focus changed (bubbles). `detail`: `{ component, attributes }`, or `null` for the overview. */
462
+ readonly FOCUS_CHANGE: "vd:focus-change";
463
+ /** Resolved configuration changed (bubbles). `detail`: the resolved configuration, or `null` when nothing matches. */
464
+ readonly CONFIGURATION_CHANGE: "vd:configuration-change";
465
+ /** Configuration submitted (bubbles). `detail`: the resolved configuration handed to the host. */
466
+ readonly CONFIGURATION_SUBMIT: "vd:configuration-submit";
1000
467
  };
1001
- /**
1002
- * Options shared by both address forms.
1003
- */
1004
- interface ClientOptionsBase {
1005
- readonly parent: string | HTMLElement;
1006
- readonly debug?: boolean | undefined;
1007
- readonly language?: string | undefined;
1008
- /** W3C traceparent string to propagate to the viewer iframe for end-to-end tracing. */
1009
- readonly traceparent?: string | undefined;
1010
- /**
1011
- * A source viewer session id to restore into this mount (ar-session-handoff): the
1012
- * iframe's metadata fetch merges that session's latest configuration into the
1013
- * attribute defaults, so the viewer boots straight into it. Internal option, used
1014
- * by the AR landing, not a customer-facing setting.
1015
- */
1016
- readonly session?: string | undefined;
1017
- /**
1018
- * Override viewer settings from metadata. Omitted fields use the metadata value.
1019
- * A warning is logged when an override differs from the metadata value.
1020
- */
1021
- readonly viewerSettings?: ViewerSettingsOverride;
468
+ type VdEvent = typeof VD_EVENTS[keyof typeof VD_EVENTS];
469
+
470
+ interface MutationFailedEvent {
471
+ readonly attribute: string;
472
+ readonly value: string;
1022
473
  }
1023
474
  /**
1024
- * What the client shows, given either way of naming it: `{ app }`, an app's
1025
- * public link key, or `{ license, model }`, which resolves to that model's
1026
- * default app. Given both, `app` wins and the pair is ignored with a warning.
1027
- */
1028
- type ClientAddress = {
1029
- readonly app: string;
1030
- readonly license?: string | undefined;
1031
- readonly model?: string | undefined;
1032
- } | {
1033
- readonly license: string;
1034
- readonly model: string;
1035
- readonly app?: undefined;
1036
- };
1037
- /**
1038
- * Configuration options for VirtualdisplayClient
475
+ * Which app the viewer resolved, as handed to an `onAppResolved` callback.
1039
476
  */
1040
- type ClientOptions = ClientOptionsBase & ClientAddress;
1041
-
1042
- /** The AR flow behind the standard interface's AR button. */
1043
- interface ArFlow {
1044
- /** The AR button handler. */
1045
- activate(): void;
1046
- /** The engine-announced tracking session id (SESSION), which the flow may wait on. */
1047
- onSession(sessionId: string): void;
1048
- destroy(): void;
477
+ interface ResolvedAppInfo {
478
+ /** Known values: `AppType`. An unknown value means a newer server; report it, never downgrade to a viewer. */
479
+ readonly appType: string;
480
+ readonly name: string;
481
+ /** The app's public slug (the address a submit POSTs to). Absent from a pre-slug server. */
482
+ readonly slug?: string;
483
+ readonly submit?: AppSubmitSettings;
1049
484
  }
1050
- /** The live channel's connection status, as the client surfaces it. */
1051
- interface SyncTracker {
1052
- /** Latest-wins, no buffering: the channel's current connection state. */
1053
- handleSync(connected: boolean): void;
485
+ interface VirtualdisplaySelectEvent {
486
+ readonly attribute: string;
487
+ readonly value: string;
488
+ preventDefault(): void;
1054
489
  }
1055
- /** What a subclass layers onto the base composition instead of re-implementing it. */
1056
- interface BaseClientExtensions {
1057
- readonly messengerOptions?: MessengerOptions;
1058
- readonly registerHandlers?: (messenger: IframeMessenger, send: SendFn) => void;
1059
- /** The viewer host this client's iframe targets. Unset means the default viewer. */
1060
- readonly serverHost?: string;
1061
- /** A client type's fixed capability flags, appended to the viewer iframe URL. */
1062
- readonly iframeParams?: Readonly<Record<string, string>>;
1063
- /** Builds the AR flow behind the standard interface's AR button. */
1064
- readonly createArActivation?: (send: SendFn) => ArFlow;
1065
- /** Builds the tracker behind `onSyncChange`. */
1066
- readonly createSyncTracker?: () => SyncTracker;
490
+ interface VirtualdisplaySelectedEvent {
491
+ readonly attribute: string;
492
+ readonly value: string;
1067
493
  }
494
+ type VirtualdisplayUiEventMap = {
495
+ [VD_EVENTS.SELECT]: VirtualdisplaySelectEvent;
496
+ [VD_EVENTS.SELECTED]: VirtualdisplaySelectedEvent;
497
+ };
498
+ type VirtualdisplayUiEventType = keyof VirtualdisplayUiEventMap;
499
+
1068
500
  /**
1069
501
  * The shared client composition root: wires the transport, facades and UI, and
1070
502
  * exposes the integrator-facing API.
@@ -1085,52 +517,27 @@ declare class BaseClient {
1085
517
  private readonly options;
1086
518
  private readonly serverHost;
1087
519
  private readonly iframeParams;
1088
- /** The composed AR flow, or undefined in a build that composes none. */
1089
520
  private readonly arActivation;
1090
- /** The composed sync tracker, or undefined in a build that composes none. */
1091
521
  private readonly syncTracker;
1092
522
  private iframe;
1093
523
  private container;
1094
524
  private readyCallbacks;
1095
525
  private readonly mutationFailedCallbacks;
1096
- /** The viewer's tracking session id, once the server announces it (SESSION). */
1097
526
  private viewerSessionId;
1098
- /** Which app the viewer resolved, once reported (APP_RESOLVED). */
1099
527
  private resolvedApp;
1100
528
  private readonly appResolvedCallbacks;
1101
- constructor(options: ClientOptions, extensions?: BaseClientExtensions);
1102
- /** The configurator UI, every callback bridging it to the facade that owns the action. */
1103
529
  private createStandardInterface;
1104
530
  private sendControlAnnotationToggle;
1105
- /** Wire the inbound viewer protocol to the facades. */
1106
531
  private wireIncomingMessages;
1107
- /** Store the resolved app and notify every registered `onAppResolved` callback. */
1108
532
  private handleAppResolved;
1109
- /** Store the tracking session id and hand it to the AR flow, which waits on it for the QR. */
1110
533
  private handleSession;
1111
- /** Forward a failed mutation to every registered `onMutationFailed` callback. */
1112
534
  private notifyMutationFailed;
1113
- /** Flush the queued `onReady` callbacks once, then let the standard interface know. */
1114
535
  private notifyReady;
1115
- /**
1116
- * Dispatch a bubbling `vd:focus-change` event on the container so a page can
1117
- * react to focus without registering an `onChange` callback.
1118
- */
1119
536
  private dispatchFocusChange;
1120
- /**
1121
- * Dispatch a bubbling `vd:configuration-change` event on the container so a
1122
- * page or tag manager can react to price changes without a client callback.
1123
- */
1124
537
  private dispatchConfigurationChange;
1125
- /** Dispatch a bubbling `vd:configuration-submit` event when the CTA is used. */
1126
538
  private dispatchConfigurationSubmit;
1127
539
  private handleConfig;
1128
- /**
1129
- * Tell the viewer about the annotation mode only when the merged mode differs
1130
- * from the one it announced.
1131
- */
1132
540
  private echoAnnotationsDifference;
1133
- /** Tell the viewer the user entered fullscreen, so it records the engagement. */
1134
541
  private trackFullscreen;
1135
542
  private initializeLogging;
1136
543
  private createAndConnectIframe;
@@ -1174,16 +581,65 @@ declare class BaseClient {
1174
581
  destroy(): void;
1175
582
  }
1176
583
 
1177
- type Attrs = Record<string, unknown> | unknown;
584
+ /**
585
+ * The integrator-facing viewer settings override. Identical to the wire
586
+ * `ViewerSettings`, except `annotations` also accepts the legacy booleans:
587
+ * they normalise before the merge (`true` → `show`, `false` → `hide`), so
588
+ * existing mounts keep compiling while the package is in alpha.
589
+ */
590
+ type ViewerSettingsOverride = Omit<Partial<ViewerSettings>, 'annotations'> & {
591
+ readonly annotations?: boolean | AnnotationVisibility;
592
+ };
593
+ /**
594
+ * Options shared by both address forms.
595
+ */
596
+ interface ClientOptionsBase {
597
+ readonly parent: string | HTMLElement;
598
+ readonly debug?: boolean | undefined;
599
+ readonly language?: string | undefined;
600
+ /** W3C traceparent string to propagate to the viewer iframe for end-to-end tracing. */
601
+ readonly traceparent?: string | undefined;
602
+ /**
603
+ * A source viewer session id to restore into this mount (ar-session-handoff): the
604
+ * iframe's metadata fetch merges that session's latest configuration into the
605
+ * attribute defaults, so the viewer boots straight into it. Internal option, used
606
+ * by the AR landing, not a customer-facing setting.
607
+ */
608
+ readonly session?: string | undefined;
609
+ /**
610
+ * Override viewer settings from metadata. Omitted fields use the metadata value.
611
+ * A warning is logged when an override differs from the metadata value.
612
+ */
613
+ readonly viewerSettings?: ViewerSettingsOverride;
614
+ }
615
+ /**
616
+ * What the client shows, given either way of naming it: `{ app }`, an app's
617
+ * public link key, or `{ license, model }`, which resolves to that model's
618
+ * default app. Given both, `app` wins and the pair is ignored with a warning.
619
+ */
620
+ type ClientAddress = {
621
+ readonly app: string;
622
+ readonly license?: string | undefined;
623
+ readonly model?: string | undefined;
624
+ } | {
625
+ readonly license: string;
626
+ readonly model: string;
627
+ readonly app?: undefined;
628
+ };
629
+ /**
630
+ * Configuration options for VirtualdisplayClient
631
+ */
632
+ type ClientOptions = ClientOptionsBase & ClientAddress;
633
+
1178
634
  /**
1179
635
  * The logger the client exposes: the same four levels as the base logger, but
1180
636
  * accepting any value as attributes so callers can pass an Error or a bare value.
1181
637
  */
1182
638
  interface ClientLogger {
1183
- debug: (msg: string, attrs?: Attrs) => void;
1184
- info: (msg: string, attrs?: Attrs) => void;
1185
- warn: (msg: string, attrs?: Attrs) => void;
1186
- error: (msg: string, attrs?: Attrs) => void;
639
+ debug: (msg: string, attrs?: unknown) => void;
640
+ info: (msg: string, attrs?: unknown) => void;
641
+ warn: (msg: string, attrs?: unknown) => void;
642
+ error: (msg: string, attrs?: unknown) => void;
1187
643
  }
1188
644
  declare let logger: ClientLogger;
1189
645
  declare function enableDebugMode(): void;
@@ -1200,5 +656,5 @@ declare class VirtualdisplayClient extends BaseClient {
1200
656
  onSyncChange(callback: (connected: boolean) => void): void;
1201
657
  }
1202
658
 
1203
- export { Annotations, Attribute, AttributeValue, Camera, Configuration, ERROR_CODES, Focus, Photo, Snapshot, VD_EVENTS, VirtualdisplayClient, VirtualdisplayError, enableDebugMode, isDebugMode, logger };
1204
- export type { AppSubmitSettings, AppType, Availability, ClientOptions, ErrorCode, FocusState, MutationFailedEvent, PhotoData, PricingDocument, PricingRule, ResolvedAppInfo, ResolvedConfiguration, SubmitOptions, SubmittedConfiguration, VdEvent, ViewerSettings, VirtualdisplaySelectEvent, VirtualdisplaySelectedEvent, VirtualdisplayUiEventMap, VirtualdisplayUiEventType };
659
+ export { AVAILABILITY, Annotations, Attribute, AttributeValue, Camera, CameraPosition, Configuration, ERROR_CODES, Focus, Photo, Snapshot, VD_EVENTS, VirtualdisplayClient, VirtualdisplayError, enableDebugMode, isDebugMode, logger };
660
+ export type { AnnotationVisibility, AppSubmitSettings, AppType, Availability, CameraLimits, CameraPositionState, ClientAddress, ClientLogger, ClientOptions, ClientOptionsBase, ErrorCode, FocusState, MutationFailedEvent, PhotoData, PricingDocument, PricingRule, ResolvedAppInfo, ResolvedConfiguration, SelectableAttribute, SelectableSnapshot, SubmitOptions, SubmittedConfiguration, VdEvent, ViewerSettings, ViewerSettingsOverride, VirtualdisplaySelectEvent, VirtualdisplaySelectedEvent, VirtualdisplayUiEventMap, VirtualdisplayUiEventType };