@virtualdisplay.io/client 4.0.0-alpha.20 → 4.0.0-alpha.22
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 +18 -0
- package/dist/index.d.ts +135 -3
- package/dist/virtualdisplay.client.cjs.js +64 -17
- package/dist/virtualdisplay.client.es.js +2096 -1882
- package/dist/virtualdisplay.client.umd.js +95 -48
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.0-alpha.22
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
## 4.0.0-alpha.21
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
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.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
3
21
|
## 4.0.0-alpha.20
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ declare const MESSAGE_TYPES: {
|
|
|
19
19
|
readonly ANNOTATION_TOGGLE: "annotation_toggle";
|
|
20
20
|
readonly FOCUS: "focus";
|
|
21
21
|
readonly PRICING: "pricing";
|
|
22
|
+
readonly QR_SHOWN: "qr_shown";
|
|
23
|
+
readonly SYNC: "sync";
|
|
22
24
|
};
|
|
23
25
|
interface MutationDto {
|
|
24
26
|
readonly name: string;
|
|
@@ -30,10 +32,50 @@ interface MutationResult {
|
|
|
30
32
|
readonly success: boolean;
|
|
31
33
|
readonly error?: string;
|
|
32
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The folded family state riding along on a live sync channel broadcast
|
|
37
|
+
* (ar-session-handoff, sync-channel.md): the same shape the public
|
|
38
|
+
* session-state read returns. Authoritative self-heal input; `mutations`
|
|
39
|
+
* on the same message is only the delta that triggered the broadcast.
|
|
40
|
+
*/
|
|
41
|
+
interface SyncStateSnapshot {
|
|
42
|
+
readonly license: string;
|
|
43
|
+
readonly model: string;
|
|
44
|
+
readonly attributes: Record<string, string>;
|
|
45
|
+
}
|
|
33
46
|
interface MutationMessage {
|
|
34
47
|
readonly type: typeof MESSAGE_TYPES.MUTATION;
|
|
35
48
|
readonly mutations: MutationDto[];
|
|
36
49
|
readonly isInitial: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Live sync channel extras (ar-session-handoff, sync-channel.md), present
|
|
52
|
+
* only on a mutation the viewer engine's sync module received over the SSE
|
|
53
|
+
* live-state channel; a MutationMessage posted locally by the client never
|
|
54
|
+
* carries these. `origin` is the sender's page session id: its presence
|
|
55
|
+
* marks the mutation as remote, so the tracker must never record it again
|
|
56
|
+
* (the no-re-track loop guard, decision 15), and the sync module drops it
|
|
57
|
+
* when it equals the receiving engine's own session id (the echo filter).
|
|
58
|
+
* `seq` is the sender's per-session sequence number, used by the sync
|
|
59
|
+
* module to track how far the channel has progressed. `state` is the
|
|
60
|
+
* authoritative folded family state: the sync module diffs it against the
|
|
61
|
+
* channel's last-seen state and applies the difference, so a catch-up
|
|
62
|
+
* (empty `mutations`) or a broadcast the hub dropped still converges the
|
|
63
|
+
* subscriber. None of these fields are read by `MutationRouter`.
|
|
64
|
+
*/
|
|
65
|
+
readonly origin?: string;
|
|
66
|
+
readonly seq?: number;
|
|
67
|
+
readonly state?: SyncStateSnapshot;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Sync channel status (server → client, ar-session-handoff, sync-channel.md):
|
|
71
|
+
* whether the viewer engine's live-state channel is currently connected to its
|
|
72
|
+
* hand-off family. One-way, latest-wins: no history, no buffering. The public
|
|
73
|
+
* client exposes it as `onSyncChange`; the studio composes nothing, so its
|
|
74
|
+
* engine never sends this (it never opens the channel).
|
|
75
|
+
*/
|
|
76
|
+
interface SyncMessage {
|
|
77
|
+
readonly type: typeof MESSAGE_TYPES.SYNC;
|
|
78
|
+
readonly connected: boolean;
|
|
37
79
|
}
|
|
38
80
|
interface MutationResultMessage {
|
|
39
81
|
readonly type: typeof MESSAGE_TYPES.MUTATION;
|
|
@@ -127,6 +169,16 @@ interface ArActivateMessage {
|
|
|
127
169
|
interface FullscreenMessage {
|
|
128
170
|
readonly type: typeof MESSAGE_TYPES.FULLSCREEN;
|
|
129
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* QR shown (client → server): the desktop QR hand-off modal (ar-session-handoff)
|
|
174
|
+
* was displayed to the shopper. The QR itself is generated client-side; this
|
|
175
|
+
* only lets the server record the engagement (`qr_shown`, the funnel's
|
|
176
|
+
* denominator) against the tracking session it owns. One-way, fire-and-forget,
|
|
177
|
+
* nothing to act on, same shape as FULLSCREEN.
|
|
178
|
+
*/
|
|
179
|
+
interface QrShownMessage {
|
|
180
|
+
readonly type: typeof MESSAGE_TYPES.QR_SHOWN;
|
|
181
|
+
}
|
|
130
182
|
/**
|
|
131
183
|
* Config (server → client): viewer settings and camera limits from model metadata.
|
|
132
184
|
* Sent early — before MAPPING — so the client can show/hide controls and disable
|
|
@@ -247,7 +299,7 @@ interface PricingMessage {
|
|
|
247
299
|
readonly type: typeof MESSAGE_TYPES.PRICING;
|
|
248
300
|
readonly pricing: PricingDocument$1;
|
|
249
301
|
}
|
|
250
|
-
type Message = HandshakeMessage | SessionMessage | MutationMessage | MutationResultMessage | CameraCommandMessage | CameraPositionMessage | SnapshotRequestMessage | SnapshotResultMessage | MappingMessage | LoadingIndicatorMessage | ArActivateMessage | FullscreenMessage | ConfigMessage | AnnotationToggleMessage | FocusMessage | FocusStateMessage | PricingMessage;
|
|
302
|
+
type Message = HandshakeMessage | SessionMessage | MutationMessage | MutationResultMessage | CameraCommandMessage | CameraPositionMessage | SnapshotRequestMessage | SnapshotResultMessage | MappingMessage | LoadingIndicatorMessage | ArActivateMessage | FullscreenMessage | ConfigMessage | AnnotationToggleMessage | FocusMessage | FocusStateMessage | PricingMessage | QrShownMessage | SyncMessage;
|
|
251
303
|
type MessageCallback = (message: Message) => void | Promise<void>;
|
|
252
304
|
|
|
253
305
|
/**
|
|
@@ -348,6 +400,16 @@ declare class IframeMessenger {
|
|
|
348
400
|
* Queued until markReady() is called
|
|
349
401
|
*/
|
|
350
402
|
send(message: Message): void;
|
|
403
|
+
/**
|
|
404
|
+
* Feed a message through the same routing path as one that just arrived via
|
|
405
|
+
* postMessage, without an actual postMessage round-trip: still validated
|
|
406
|
+
* against `isValid`, and still respects the consumer queue's buffering and
|
|
407
|
+
* the `shouldBuffer` bypass. For a transport that delivers messages some
|
|
408
|
+
* other way than the iframe channel this messenger already listens on (the
|
|
409
|
+
* viewer engine's SSE-sourced live sync channel, ar-session-handoff) and
|
|
410
|
+
* needs them to land exactly where a postMessage would.
|
|
411
|
+
*/
|
|
412
|
+
receive(message: Message): void;
|
|
351
413
|
/**
|
|
352
414
|
* Enable sending and flush queued outgoing messages
|
|
353
415
|
*/
|
|
@@ -808,6 +870,13 @@ interface ClientOptions {
|
|
|
808
870
|
readonly language?: string;
|
|
809
871
|
/** W3C traceparent string to propagate to the viewer iframe for end-to-end tracing. */
|
|
810
872
|
readonly traceparent?: string;
|
|
873
|
+
/**
|
|
874
|
+
* A source viewer session id to restore into this mount (ar-session-handoff): the
|
|
875
|
+
* iframe's metadata fetch merges that session's latest configuration into the
|
|
876
|
+
* attribute defaults, so the viewer boots straight into it. Internal option, used
|
|
877
|
+
* by the AR landing, not a customer-facing setting.
|
|
878
|
+
*/
|
|
879
|
+
readonly session?: string;
|
|
811
880
|
/**
|
|
812
881
|
* Override viewer settings from metadata. Omitted fields use the metadata value.
|
|
813
882
|
* A warning is logged when an override differs from the metadata value.
|
|
@@ -857,6 +926,31 @@ type VirtualdisplayUiEventMap = {
|
|
|
857
926
|
type VirtualdisplayUiEventType = keyof VirtualdisplayUiEventMap;
|
|
858
927
|
|
|
859
928
|
type SendFn = (message: Message) => void;
|
|
929
|
+
/**
|
|
930
|
+
* The seam for the AR flow behind the standard interface's AR button. The base
|
|
931
|
+
* only routes to it: the implementation (mobile AR_ACTIVATE, desktop hand-off
|
|
932
|
+
* QR) lives in the public client package (`@virtualdisplay.io/client`), which
|
|
933
|
+
* composes it via {@link BaseClientExtensions.createArActivation}.
|
|
934
|
+
*/
|
|
935
|
+
interface ArFlow {
|
|
936
|
+
/** The AR button handler. */
|
|
937
|
+
activate(): void;
|
|
938
|
+
/** The engine-announced tracking session id (SESSION), which the flow may wait on. */
|
|
939
|
+
onSession(sessionId: string): void;
|
|
940
|
+
destroy(): void;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* The seam for the live sync channel's connection status (ar-session-handoff,
|
|
944
|
+
* sync-channel.md). The base only routes SYNC messages to it; the concrete
|
|
945
|
+
* tracker (callback list + `onSyncChange`) lives in the public client package,
|
|
946
|
+
* composed via {@link BaseClientExtensions.createSyncTracker}. The studio
|
|
947
|
+
* composes nothing, so `onSyncChange` does not exist on the studio client
|
|
948
|
+
* (its engine never opens the channel, so it never sends SYNC either).
|
|
949
|
+
*/
|
|
950
|
+
interface SyncTracker {
|
|
951
|
+
/** Latest-wins, no buffering: the channel's current connection state. */
|
|
952
|
+
handleSync(connected: boolean): void;
|
|
953
|
+
}
|
|
860
954
|
/**
|
|
861
955
|
* Extensions a subclass (the studio client) layers onto the base composition without
|
|
862
956
|
* re-implementing it: a custom incoming-message validator and extra message handlers.
|
|
@@ -872,6 +966,19 @@ interface BaseClientExtensions {
|
|
|
872
966
|
* so the host cannot be a single build-time env shared by both.
|
|
873
967
|
*/
|
|
874
968
|
readonly serverHost?: string;
|
|
969
|
+
/**
|
|
970
|
+
* The AR flow behind the standard interface's AR button. The public client
|
|
971
|
+
* composes its `ArActivation` here; the studio composes nothing (the editor
|
|
972
|
+
* has no AR use case), so the base wires no AR button at all.
|
|
973
|
+
*/
|
|
974
|
+
readonly createArActivation?: (send: SendFn) => ArFlow;
|
|
975
|
+
/**
|
|
976
|
+
* The live sync channel's connection tracker (ar-session-handoff). The
|
|
977
|
+
* public client composes a tracker and exposes `onSyncChange`; the studio
|
|
978
|
+
* composes nothing, so SYNC messages (which its own engine never sends
|
|
979
|
+
* anyway) are simply dropped.
|
|
980
|
+
*/
|
|
981
|
+
readonly createSyncTracker?: () => SyncTracker;
|
|
875
982
|
}
|
|
876
983
|
/**
|
|
877
984
|
* The shared client composition root. Wires the engine (transport, facades, UI) and exposes
|
|
@@ -890,9 +997,12 @@ declare class BaseClient {
|
|
|
890
997
|
private readonly annotationsApi;
|
|
891
998
|
private readonly eventBus;
|
|
892
999
|
private readonly ui;
|
|
893
|
-
private readonly qrModal;
|
|
894
1000
|
private readonly options;
|
|
895
1001
|
private readonly serverHost;
|
|
1002
|
+
/** The composed AR flow, or undefined in builds without one (the studio). */
|
|
1003
|
+
private readonly arActivation;
|
|
1004
|
+
/** The composed sync tracker, or undefined in builds without one (the studio). */
|
|
1005
|
+
private readonly syncTracker;
|
|
896
1006
|
private iframe;
|
|
897
1007
|
private container;
|
|
898
1008
|
private readyCallbacks;
|
|
@@ -902,6 +1012,12 @@ declare class BaseClient {
|
|
|
902
1012
|
constructor(options: ClientOptions, extensions?: BaseClientExtensions);
|
|
903
1013
|
/** Wire the inbound viewer protocol to the facades. */
|
|
904
1014
|
private wireIncomingMessages;
|
|
1015
|
+
/** Store the tracking session id and hand it to the AR flow, which waits on it for the QR. */
|
|
1016
|
+
private handleSession;
|
|
1017
|
+
/** Forward a failed mutation to every registered `onMutationFailed` callback. */
|
|
1018
|
+
private notifyMutationFailed;
|
|
1019
|
+
/** Flush the queued `onReady` callbacks once, then let the standard interface know. */
|
|
1020
|
+
private notifyReady;
|
|
905
1021
|
/**
|
|
906
1022
|
* Dispatch a bubbling `vd:focus-change` event on the container so a page can
|
|
907
1023
|
* react to focus without registering an `onChange` callback.
|
|
@@ -915,7 +1031,6 @@ declare class BaseClient {
|
|
|
915
1031
|
/** Dispatch a bubbling `vd:configuration-submit` event when the CTA is used. */
|
|
916
1032
|
private dispatchConfigurationSubmit;
|
|
917
1033
|
private handleConfig;
|
|
918
|
-
private activateAr;
|
|
919
1034
|
/** Tell the viewer the user entered fullscreen, so it records the engagement. */
|
|
920
1035
|
private trackFullscreen;
|
|
921
1036
|
private initializeLogging;
|
|
@@ -928,6 +1043,14 @@ declare class BaseClient {
|
|
|
928
1043
|
get isReady(): boolean;
|
|
929
1044
|
onReady(callback: (client: this) => void): void;
|
|
930
1045
|
onMutationFailed(callback: (event: MutationFailedEvent) => void): void;
|
|
1046
|
+
/**
|
|
1047
|
+
* Trigger the composed AR flow directly (ar-session-handoff task 035): a host
|
|
1048
|
+
* page that hides the built-in corner button (`viewerSettings.ar = false`,
|
|
1049
|
+
* e.g. the AR landing, which owns a single custom AR affordance) still needs
|
|
1050
|
+
* an entry point to the same flow that button would have called. A no-op
|
|
1051
|
+
* where no AR flow is composed (the studio client has none).
|
|
1052
|
+
*/
|
|
1053
|
+
activateAr(): void;
|
|
931
1054
|
on<T extends VirtualdisplayUiEventType>(type: T, callback: (event: VirtualdisplayUiEventMap[T]) => void): void;
|
|
932
1055
|
get annotations(): Annotations;
|
|
933
1056
|
get camera(): Camera;
|
|
@@ -956,8 +1079,17 @@ declare function isDebugMode(): boolean;
|
|
|
956
1079
|
* standard interface) with no studio capability. This is the buyer-facing SDK published as
|
|
957
1080
|
* `@virtualdisplay.io/client`. The internal studio client adds the editing facades over the
|
|
958
1081
|
* same base in `@virtualdisplay.io/client-studio`.
|
|
1082
|
+
*
|
|
1083
|
+
* The AR flow (mobile AR_ACTIVATE, desktop hand-off QR) is composed here: it is a shopper
|
|
1084
|
+
* feature, so the studio client does not compose it and renders no AR button. The live sync
|
|
1085
|
+
* channel's connection status (`onSyncChange`) is composed the same way: the studio client
|
|
1086
|
+
* has no such method, its engine never opens the channel.
|
|
959
1087
|
*/
|
|
960
1088
|
declare class VirtualdisplayClient extends BaseClient {
|
|
1089
|
+
private readonly syncStatusTracker;
|
|
1090
|
+
constructor(options: ClientOptions);
|
|
1091
|
+
/** Subscribe to the live sync channel's connection state (ar-session-handoff). Latest-wins, no buffering. */
|
|
1092
|
+
onSyncChange(callback: (connected: boolean) => void): void;
|
|
961
1093
|
}
|
|
962
1094
|
|
|
963
1095
|
/**
|