@virtualdisplay.io/client 3.5.0 → 3.6.0
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 +12 -0
- package/dist/index.d.ts +120 -2
- package/dist/virtualdisplay.client.cjs.js +3 -3
- package/dist/virtualdisplay.client.cjs.js.map +1 -1
- package/dist/virtualdisplay.client.es.js +235 -31
- package/dist/virtualdisplay.client.es.js.map +1 -1
- package/dist/virtualdisplay.client.umd.js +3 -3
- package/dist/virtualdisplay.client.umd.js.map +1 -1
- package/package.json +10 -10
|
@@ -4,7 +4,8 @@ const ERROR_CODES = {
|
|
|
4
4
|
VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
|
|
5
5
|
INVALID_MAPPING: "INVALID_MAPPING",
|
|
6
6
|
PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
|
|
7
|
-
INVALID_MUTATIONS: "INVALID_MUTATIONS"
|
|
7
|
+
INVALID_MUTATIONS: "INVALID_MUTATIONS",
|
|
8
|
+
INVALID_PARAMETER: "INVALID_PARAMETER"
|
|
8
9
|
};
|
|
9
10
|
class VirtualdisplayError extends Error {
|
|
10
11
|
code;
|
|
@@ -61,7 +62,10 @@ const EVENT_NAMES = {
|
|
|
61
62
|
// State flow (Server → Client)
|
|
62
63
|
MESSAGE_RECEIVED: "message:received",
|
|
63
64
|
STATE_CHANGED: "state:changed",
|
|
64
|
-
INITIAL_STATE_RECEIVED: "state:initial-received"
|
|
65
|
+
INITIAL_STATE_RECEIVED: "state:initial-received",
|
|
66
|
+
// Snapshot flow
|
|
67
|
+
SNAPSHOT_MESSAGE: "snapshot:take",
|
|
68
|
+
SNAPSHOT_DEVELOPED: "snapshot:developed"
|
|
65
69
|
};
|
|
66
70
|
const g = Object.freeze({
|
|
67
71
|
debug: 0,
|
|
@@ -6931,7 +6935,8 @@ const MESSAGE_TYPES = {
|
|
|
6931
6935
|
MUTATION: "mutation",
|
|
6932
6936
|
STATE: "state",
|
|
6933
6937
|
CONFIG: "config",
|
|
6934
|
-
CAMERA: "camera"
|
|
6938
|
+
CAMERA: "camera",
|
|
6939
|
+
SNAPSHOT: "snapshot"
|
|
6935
6940
|
};
|
|
6936
6941
|
function createMutationMessage(mutations) {
|
|
6937
6942
|
if (!Array.isArray(mutations)) {
|
|
@@ -6943,14 +6948,7 @@ function createMutationMessage(mutations) {
|
|
|
6943
6948
|
};
|
|
6944
6949
|
}
|
|
6945
6950
|
function isValidMessage(data) {
|
|
6946
|
-
return
|
|
6947
|
-
}
|
|
6948
|
-
function isMutationMessage(data) {
|
|
6949
|
-
if (typeof data !== "object" || data === null) {
|
|
6950
|
-
return false;
|
|
6951
|
-
}
|
|
6952
|
-
const obj = data;
|
|
6953
|
-
return obj.type === MESSAGE_TYPES.MUTATION && Array.isArray(obj.mutations);
|
|
6951
|
+
return isStateMessage(data) || isSnapshotDevelopedMessage(data);
|
|
6954
6952
|
}
|
|
6955
6953
|
function isStateMessage(data) {
|
|
6956
6954
|
if (typeof data !== "object" || data === null) {
|
|
@@ -6959,12 +6957,12 @@ function isStateMessage(data) {
|
|
|
6959
6957
|
const obj = data;
|
|
6960
6958
|
return obj.type === MESSAGE_TYPES.STATE && Array.isArray(obj.nodes);
|
|
6961
6959
|
}
|
|
6962
|
-
function
|
|
6960
|
+
function isSnapshotDevelopedMessage(data) {
|
|
6963
6961
|
if (typeof data !== "object" || data === null) {
|
|
6964
6962
|
return false;
|
|
6965
6963
|
}
|
|
6966
6964
|
const obj = data;
|
|
6967
|
-
return obj.type === MESSAGE_TYPES.
|
|
6965
|
+
return obj.type === MESSAGE_TYPES.SNAPSHOT && typeof obj.filename === "string" && typeof obj.data === "string";
|
|
6968
6966
|
}
|
|
6969
6967
|
class AttributeService {
|
|
6970
6968
|
attributes = /* @__PURE__ */ new Map();
|
|
@@ -7227,11 +7225,13 @@ class MessageHandler {
|
|
|
7227
7225
|
const outgoingEvents = [
|
|
7228
7226
|
EVENT_NAMES.MUTATION_MESSAGE,
|
|
7229
7227
|
EVENT_NAMES.CONFIG_MESSAGE,
|
|
7230
|
-
EVENT_NAMES.CAMERA_MESSAGE
|
|
7228
|
+
EVENT_NAMES.CAMERA_MESSAGE,
|
|
7229
|
+
EVENT_NAMES.SNAPSHOT_MESSAGE
|
|
7231
7230
|
];
|
|
7232
7231
|
outgoingEvents.forEach((eventName) => {
|
|
7233
7232
|
this.eventBus.on(eventName, (event) => {
|
|
7234
|
-
|
|
7233
|
+
const messageEvent = event;
|
|
7234
|
+
this.queueOrSend(messageEvent.message);
|
|
7235
7235
|
});
|
|
7236
7236
|
});
|
|
7237
7237
|
}
|
|
@@ -7419,6 +7419,156 @@ class StateService {
|
|
|
7419
7419
|
this.hasReceivedInitialSync = false;
|
|
7420
7420
|
}
|
|
7421
7421
|
}
|
|
7422
|
+
class Photo {
|
|
7423
|
+
constructor(filename) {
|
|
7424
|
+
this.filename = filename;
|
|
7425
|
+
}
|
|
7426
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
7427
|
+
developed = false;
|
|
7428
|
+
photoData = null;
|
|
7429
|
+
/**
|
|
7430
|
+
* Register a callback for when the photo is developed
|
|
7431
|
+
* If the photo is already developed, the callback is called immediately
|
|
7432
|
+
*
|
|
7433
|
+
* @param callback Function to call with photo data
|
|
7434
|
+
* @returns This Photo instance for chaining
|
|
7435
|
+
*/
|
|
7436
|
+
onDeveloped(callback) {
|
|
7437
|
+
if (this.developed && this.photoData !== null) {
|
|
7438
|
+
callback(this.photoData);
|
|
7439
|
+
return this;
|
|
7440
|
+
}
|
|
7441
|
+
this.callbacks.add(callback);
|
|
7442
|
+
return this;
|
|
7443
|
+
}
|
|
7444
|
+
/**
|
|
7445
|
+
* Get the filename for this photo
|
|
7446
|
+
*/
|
|
7447
|
+
getFilename() {
|
|
7448
|
+
return this.filename;
|
|
7449
|
+
}
|
|
7450
|
+
/**
|
|
7451
|
+
* Mark the photo as developed and notify all callbacks
|
|
7452
|
+
* This method is called by the SnapshotMessageHandler when the photo data arrives
|
|
7453
|
+
*/
|
|
7454
|
+
develop(photoData) {
|
|
7455
|
+
if (this.developed) {
|
|
7456
|
+
return;
|
|
7457
|
+
}
|
|
7458
|
+
this.photoData = photoData;
|
|
7459
|
+
this.developed = true;
|
|
7460
|
+
this.callbacks.forEach((callback) => {
|
|
7461
|
+
callback(this.photoData);
|
|
7462
|
+
});
|
|
7463
|
+
this.callbacks.clear();
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
class SnapshotMessageHandler {
|
|
7467
|
+
constructor(eventBus) {
|
|
7468
|
+
this.eventBus = eventBus;
|
|
7469
|
+
logger.debug("SnapshotMessageHandler initialized");
|
|
7470
|
+
this.setupMessageListener();
|
|
7471
|
+
}
|
|
7472
|
+
pendingPhotos = /* @__PURE__ */ new Map();
|
|
7473
|
+
/**
|
|
7474
|
+
* Register a photo to be developed when its data arrives
|
|
7475
|
+
*/
|
|
7476
|
+
registerPhoto(photo) {
|
|
7477
|
+
logger.debug("Registering photo for development", { filename: photo.getFilename() });
|
|
7478
|
+
this.pendingPhotos.set(photo.getFilename(), photo);
|
|
7479
|
+
logger.debug("Pending photos count", { count: this.pendingPhotos.size });
|
|
7480
|
+
}
|
|
7481
|
+
setupMessageListener() {
|
|
7482
|
+
logger.debug("Setting up message listener for snapshot messages");
|
|
7483
|
+
this.eventBus.on(EVENT_NAMES.MESSAGE_RECEIVED, (event) => {
|
|
7484
|
+
const { message } = event;
|
|
7485
|
+
if (message.type === MESSAGE_TYPES.SNAPSHOT) {
|
|
7486
|
+
logger.debug("Processing snapshot message");
|
|
7487
|
+
this.handleSnapshotDeveloped(message);
|
|
7488
|
+
}
|
|
7489
|
+
});
|
|
7490
|
+
}
|
|
7491
|
+
handleSnapshotDeveloped(message) {
|
|
7492
|
+
logger.debug("Handling snapshot developed message", {
|
|
7493
|
+
filename: message.filename,
|
|
7494
|
+
dataLength: message.data.length,
|
|
7495
|
+
pendingCount: this.pendingPhotos.size
|
|
7496
|
+
});
|
|
7497
|
+
const photo = this.pendingPhotos.get(message.filename);
|
|
7498
|
+
if (photo !== void 0) {
|
|
7499
|
+
logger.debug("Found pending photo, developing...", { filename: message.filename });
|
|
7500
|
+
const photoData = {
|
|
7501
|
+
filename: message.filename,
|
|
7502
|
+
data: message.data
|
|
7503
|
+
};
|
|
7504
|
+
photo.develop(photoData);
|
|
7505
|
+
this.pendingPhotos.delete(message.filename);
|
|
7506
|
+
logger.debug("Photo developed and removed from pending", {
|
|
7507
|
+
filename: message.filename,
|
|
7508
|
+
remainingCount: this.pendingPhotos.size
|
|
7509
|
+
});
|
|
7510
|
+
return;
|
|
7511
|
+
}
|
|
7512
|
+
logger.warn("No pending photo found for filename", {
|
|
7513
|
+
filename: message.filename,
|
|
7514
|
+
pendingFilenames: Array.from(this.pendingPhotos.keys())
|
|
7515
|
+
});
|
|
7516
|
+
}
|
|
7517
|
+
}
|
|
7518
|
+
class Snapshot {
|
|
7519
|
+
constructor(eventBus) {
|
|
7520
|
+
this.eventBus = eventBus;
|
|
7521
|
+
this.messageHandler = new SnapshotMessageHandler(eventBus);
|
|
7522
|
+
}
|
|
7523
|
+
validExtensions = [".png", ".jpg", ".jpeg", ".webp"];
|
|
7524
|
+
messageHandler;
|
|
7525
|
+
/**
|
|
7526
|
+
* Take a snapshot of the current 3D model view
|
|
7527
|
+
*
|
|
7528
|
+
* @param filename The filename for the snapshot (must include valid extension)
|
|
7529
|
+
* @returns Photo object for registering callbacks
|
|
7530
|
+
* @throws VirtualdisplayError if filename is invalid
|
|
7531
|
+
*/
|
|
7532
|
+
take(filename) {
|
|
7533
|
+
this.validateFilename(filename);
|
|
7534
|
+
logger.debug("Taking snapshot", { filename });
|
|
7535
|
+
const photo = new Photo(filename);
|
|
7536
|
+
this.messageHandler.registerPhoto(photo);
|
|
7537
|
+
this.eventBus.emit(EVENT_NAMES.SNAPSHOT_MESSAGE, {
|
|
7538
|
+
message: {
|
|
7539
|
+
type: MESSAGE_TYPES.SNAPSHOT,
|
|
7540
|
+
filename
|
|
7541
|
+
}
|
|
7542
|
+
});
|
|
7543
|
+
return photo;
|
|
7544
|
+
}
|
|
7545
|
+
/**
|
|
7546
|
+
* Validate filename has a valid image extension
|
|
7547
|
+
*/
|
|
7548
|
+
validateFilename(filename) {
|
|
7549
|
+
if (filename === "" || typeof filename !== "string") {
|
|
7550
|
+
throw new VirtualdisplayError(
|
|
7551
|
+
"Filename must be a non-empty string",
|
|
7552
|
+
ERROR_CODES.INVALID_PARAMETER
|
|
7553
|
+
);
|
|
7554
|
+
}
|
|
7555
|
+
const hasValidExtension = this.validExtensions.some(
|
|
7556
|
+
(ext) => filename.toLowerCase().endsWith(ext)
|
|
7557
|
+
);
|
|
7558
|
+
if (!hasValidExtension) {
|
|
7559
|
+
throw new VirtualdisplayError(
|
|
7560
|
+
`Invalid filename: "${filename}". Filename must end with one of: ${this.validExtensions.join(", ")}`,
|
|
7561
|
+
ERROR_CODES.INVALID_PARAMETER
|
|
7562
|
+
);
|
|
7563
|
+
}
|
|
7564
|
+
if (filename.includes("..") || filename.includes("/") || filename.includes("\\")) {
|
|
7565
|
+
throw new VirtualdisplayError(
|
|
7566
|
+
'Filename cannot contain path separators or ".."',
|
|
7567
|
+
ERROR_CODES.INVALID_PARAMETER
|
|
7568
|
+
);
|
|
7569
|
+
}
|
|
7570
|
+
}
|
|
7571
|
+
}
|
|
7422
7572
|
class VirtualdisplayViewerService {
|
|
7423
7573
|
eventBus;
|
|
7424
7574
|
constructor(eventBus) {
|
|
@@ -7426,50 +7576,88 @@ class VirtualdisplayViewerService {
|
|
|
7426
7576
|
}
|
|
7427
7577
|
/**
|
|
7428
7578
|
* Send initial configuration based on client options
|
|
7429
|
-
*
|
|
7579
|
+
* Supports both individual properties and ui object
|
|
7430
7580
|
*/
|
|
7431
7581
|
sendInitialConfig(options) {
|
|
7432
|
-
const
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
if (options.fullscreenEnabled === false) {
|
|
7437
|
-
config.fullscreenEnabled = false;
|
|
7582
|
+
const uiConfig = this.buildUIConfig(options);
|
|
7583
|
+
const viewerConfig = this.buildViewerConfig(uiConfig, options.camera);
|
|
7584
|
+
if (viewerConfig !== void 0) {
|
|
7585
|
+
this.sendConfig(viewerConfig);
|
|
7438
7586
|
}
|
|
7439
|
-
|
|
7440
|
-
|
|
7587
|
+
}
|
|
7588
|
+
/**
|
|
7589
|
+
* Build UI configuration from client options
|
|
7590
|
+
*
|
|
7591
|
+
* Supports both legacy properties (options.arEnabled) and the newer ui object
|
|
7592
|
+
* for backward compatibility. Legacy properties take precedence when both exist.
|
|
7593
|
+
*/
|
|
7594
|
+
buildUIConfig(options) {
|
|
7595
|
+
const uiValues = this.extractUIValues(options);
|
|
7596
|
+
const hasUIConfig = Object.values(uiValues).some((value) => value !== void 0);
|
|
7597
|
+
if (!hasUIConfig) {
|
|
7598
|
+
return void 0;
|
|
7441
7599
|
}
|
|
7442
|
-
|
|
7443
|
-
|
|
7600
|
+
return {
|
|
7601
|
+
...uiValues.arEnabled !== void 0 && { arEnabled: uiValues.arEnabled },
|
|
7602
|
+
...uiValues.fullscreenEnabled !== void 0 && {
|
|
7603
|
+
fullscreenEnabled: uiValues.fullscreenEnabled
|
|
7604
|
+
},
|
|
7605
|
+
...uiValues.loadingIndicatorEnabled !== void 0 && {
|
|
7606
|
+
loadingIndicatorEnabled: uiValues.loadingIndicatorEnabled
|
|
7607
|
+
}
|
|
7608
|
+
};
|
|
7609
|
+
}
|
|
7610
|
+
/**
|
|
7611
|
+
* Extract UI values from options, with legacy properties taking precedence
|
|
7612
|
+
*/
|
|
7613
|
+
extractUIValues(options) {
|
|
7614
|
+
return {
|
|
7615
|
+
arEnabled: options.arEnabled ?? options.ui?.arEnabled,
|
|
7616
|
+
fullscreenEnabled: options.fullscreenEnabled ?? options.ui?.fullscreenEnabled,
|
|
7617
|
+
loadingIndicatorEnabled: options.loadingIndicatorEnabled ?? options.ui?.loadingIndicatorEnabled
|
|
7618
|
+
};
|
|
7619
|
+
}
|
|
7620
|
+
/**
|
|
7621
|
+
* Build viewer configuration from UI config and camera config
|
|
7622
|
+
*/
|
|
7623
|
+
buildViewerConfig(uiConfig, cameraConfig) {
|
|
7624
|
+
if (uiConfig === void 0 && cameraConfig === void 0) {
|
|
7625
|
+
return void 0;
|
|
7444
7626
|
}
|
|
7627
|
+
return {
|
|
7628
|
+
...uiConfig !== void 0 && { ui: uiConfig },
|
|
7629
|
+
...cameraConfig !== void 0 && { camera: cameraConfig }
|
|
7630
|
+
};
|
|
7445
7631
|
}
|
|
7446
7632
|
/**
|
|
7447
7633
|
* Update AR button visibility
|
|
7448
7634
|
*/
|
|
7449
7635
|
setArEnabled(enabled) {
|
|
7450
7636
|
logger.debug(`Setting AR button enabled: ${enabled}`);
|
|
7451
|
-
this.
|
|
7637
|
+
this.updateUIConfig({ arEnabled: enabled });
|
|
7452
7638
|
}
|
|
7453
7639
|
/**
|
|
7454
7640
|
* Update fullscreen button visibility
|
|
7455
7641
|
*/
|
|
7456
7642
|
setFullscreenEnabled(enabled) {
|
|
7457
7643
|
logger.debug(`Setting fullscreen button enabled: ${enabled}`);
|
|
7458
|
-
this.
|
|
7644
|
+
this.updateUIConfig({ fullscreenEnabled: enabled });
|
|
7459
7645
|
}
|
|
7460
7646
|
/**
|
|
7461
7647
|
* Update loading indicator visibility
|
|
7462
7648
|
*/
|
|
7463
7649
|
setLoadingIndicatorEnabled(enabled) {
|
|
7464
7650
|
logger.debug(`Setting loading indicator enabled: ${enabled}`);
|
|
7465
|
-
this.
|
|
7651
|
+
this.updateUIConfig({ loadingIndicatorEnabled: enabled });
|
|
7466
7652
|
}
|
|
7467
7653
|
/**
|
|
7468
7654
|
* Update multiple UI settings at once
|
|
7469
7655
|
*/
|
|
7470
7656
|
updateUIConfig(config) {
|
|
7471
7657
|
logger.debug("Updating UI configuration", config);
|
|
7472
|
-
|
|
7658
|
+
if (Object.keys(config).length > 0) {
|
|
7659
|
+
this.sendConfig({ ui: config });
|
|
7660
|
+
}
|
|
7473
7661
|
}
|
|
7474
7662
|
/**
|
|
7475
7663
|
* Hide all UI elements
|
|
@@ -7497,11 +7685,17 @@ class VirtualdisplayViewerService {
|
|
|
7497
7685
|
* Send UI configuration to server
|
|
7498
7686
|
*/
|
|
7499
7687
|
sendUIConfig(uiConfig) {
|
|
7500
|
-
|
|
7688
|
+
this.sendConfig({ ui: uiConfig });
|
|
7689
|
+
}
|
|
7690
|
+
/**
|
|
7691
|
+
* Send full viewer configuration to server
|
|
7692
|
+
*/
|
|
7693
|
+
sendConfig(config) {
|
|
7501
7694
|
const message = {
|
|
7502
7695
|
type: MESSAGE_TYPES.CONFIG,
|
|
7503
7696
|
config
|
|
7504
7697
|
};
|
|
7698
|
+
logger.debug("Sending config", message);
|
|
7505
7699
|
this.eventBus.emit(EVENT_NAMES.CONFIG_MESSAGE, { message });
|
|
7506
7700
|
}
|
|
7507
7701
|
}
|
|
@@ -7512,6 +7706,7 @@ class VirtualdisplayClient {
|
|
|
7512
7706
|
messageHandler;
|
|
7513
7707
|
viewerService;
|
|
7514
7708
|
cameraApi;
|
|
7709
|
+
snapshotApi;
|
|
7515
7710
|
options;
|
|
7516
7711
|
iframe;
|
|
7517
7712
|
constructor(options) {
|
|
@@ -7522,6 +7717,7 @@ class VirtualdisplayClient {
|
|
|
7522
7717
|
this.messageHandler = new MessageHandler(this.eventBus);
|
|
7523
7718
|
this.viewerService = new VirtualdisplayViewerService(this.eventBus);
|
|
7524
7719
|
this.cameraApi = new Camera(this.eventBus);
|
|
7720
|
+
this.snapshotApi = new Snapshot(this.eventBus);
|
|
7525
7721
|
this.initializeLogging();
|
|
7526
7722
|
this.createAndConnectIframe();
|
|
7527
7723
|
this.setupInitialConfig();
|
|
@@ -7621,6 +7817,12 @@ class VirtualdisplayClient {
|
|
|
7621
7817
|
get camera() {
|
|
7622
7818
|
return this.cameraApi;
|
|
7623
7819
|
}
|
|
7820
|
+
/**
|
|
7821
|
+
* Get the snapshot API for taking screenshots
|
|
7822
|
+
*/
|
|
7823
|
+
get snapshot() {
|
|
7824
|
+
return this.snapshotApi;
|
|
7825
|
+
}
|
|
7624
7826
|
destroy() {
|
|
7625
7827
|
logger.info("Destroying Virtualdisplay client");
|
|
7626
7828
|
this.attributeService.clear();
|
|
@@ -7645,6 +7847,8 @@ export {
|
|
|
7645
7847
|
Mutation,
|
|
7646
7848
|
NODE_TYPES,
|
|
7647
7849
|
NodeSelector,
|
|
7850
|
+
Photo,
|
|
7851
|
+
Snapshot,
|
|
7648
7852
|
VirtualdisplayClient,
|
|
7649
7853
|
VirtualdisplayError,
|
|
7650
7854
|
VirtualdisplayViewerService,
|