@webspatial/core-sdk 1.2.1 → 1.4.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/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  (function(){
3
3
  if(typeof window === 'undefined') return;
4
4
  if(!window.__webspatialsdk__) window.__webspatialsdk__ = {}
5
- window.__webspatialsdk__['core-sdk-version'] = "1.2.1"
5
+ window.__webspatialsdk__['core-sdk-version'] = "1.4.0"
6
6
  })()
7
7
 
8
8
  var __defProp = Object.defineProperty;
@@ -93,6 +93,368 @@ var init_CommandResultUtils = __esm({
93
93
  }
94
94
  });
95
95
 
96
+ // src/platform-adapter/puppeteer/PuppeteerPlatform.ts
97
+ var PuppeteerPlatform_exports = {};
98
+ __export(PuppeteerPlatform_exports, {
99
+ PuppeteerPlatform: () => PuppeteerPlatform
100
+ });
101
+ var PuppeteerPlatform;
102
+ var init_PuppeteerPlatform = __esm({
103
+ "src/platform-adapter/puppeteer/PuppeteerPlatform.ts"() {
104
+ "use strict";
105
+ init_CommandResultUtils();
106
+ console.log("PuppeteerPlatform");
107
+ PuppeteerPlatform = class {
108
+ // store iframe instance
109
+ iframeRegistry = /* @__PURE__ */ new Map();
110
+ constructor() {
111
+ }
112
+ callJSB(cmd, msg) {
113
+ return new Promise((resolve) => {
114
+ try {
115
+ if (window.__handleJSBMessage) {
116
+ try {
117
+ console.log(` core-sdk Puppeteer Platform: callJSB: ${cmd}::${msg}`);
118
+ const result = window.__handleJSBMessage(`${cmd}::${msg}`);
119
+ console.log(
120
+ ` core-sdk Puppeteer Platform callJSB result: ${result}`
121
+ );
122
+ resolve(CommandResultSuccess(result));
123
+ } catch (err) {
124
+ resolve(CommandResultFailure("500", "JSB execution error"));
125
+ }
126
+ } else {
127
+ resolve(CommandResultSuccess("ok"));
128
+ }
129
+ } catch (error) {
130
+ console.error(
131
+ `PuppeteerPlatform cmd Error: ${cmd}, msg: ${msg} error: ${error}`
132
+ );
133
+ resolve(CommandResultFailure("500", "Internal error"));
134
+ }
135
+ });
136
+ }
137
+ /**
138
+ * Synchronously create Spatialized2DElement to Puppeteer Runner
139
+ */
140
+ createSpatializedElementSync(spatialId, webspatialUrl) {
141
+ try {
142
+ console.log(
143
+ `[Puppeteer Platform] Creating spatialized element sync with id: ${spatialId}, url: ${webspatialUrl}`
144
+ );
145
+ const win = window;
146
+ if (win.__handleJSBMessage) {
147
+ const createCommand = {
148
+ id: spatialId,
149
+ url: webspatialUrl
150
+ };
151
+ win.__handleJSBMessage(
152
+ `CreateSpatialized2DElement::${JSON.stringify(createCommand)}`
153
+ );
154
+ }
155
+ } catch (error) {
156
+ console.error("Error creating spatialized element sync:", error);
157
+ }
158
+ }
159
+ callWebSpatialProtocol(command, query, target, features) {
160
+ console.log(
161
+ `PuppeteerPlatform: Calling webspatial protocol: webspatial://${command}${query ? `?${query}` : ""}`
162
+ );
163
+ return new Promise((resolve) => {
164
+ try {
165
+ const webspatialUrl = `webspatial://${command}${query ? `?${query}` : ""}`;
166
+ const { spatialId, iframe, windowProxy } = this.createIframeWindow(
167
+ webspatialUrl,
168
+ target,
169
+ features
170
+ );
171
+ if (command === "createSpatialized2DElement") {
172
+ this.createSpatializedElementSync(spatialId, webspatialUrl);
173
+ }
174
+ console.log(
175
+ `[Puppeteer Platform] iframe created with spatialId: ${spatialId}`
176
+ );
177
+ this.iframeRegistry.set(spatialId, iframe);
178
+ resolve(CommandResultSuccess({ windowProxy, id: spatialId }));
179
+ } catch (error) {
180
+ console.error("Error calling webspatial protocol:", error);
181
+ resolve(
182
+ CommandResultFailure("500", "Failed to call webspatial protocol")
183
+ );
184
+ }
185
+ });
186
+ }
187
+ callWebSpatialProtocolSync(command, query, target, features) {
188
+ try {
189
+ const webspatialUrl = `webspatial://${command}${query ? `?${query}` : ""}`;
190
+ console.log(`Calling webspatial protocol sync: ${webspatialUrl}`);
191
+ const { spatialId, iframe, windowProxy } = this.createIframeWindow(
192
+ webspatialUrl,
193
+ target,
194
+ features
195
+ );
196
+ if (command === "createSpatialized2DElement") {
197
+ this.createSpatializedElementSync(spatialId, webspatialUrl);
198
+ }
199
+ this.iframeRegistry.set(spatialId, iframe);
200
+ return CommandResultSuccess({ windowProxy, id: spatialId });
201
+ } catch (error) {
202
+ console.error("Error calling webspatial protocol sync:", error);
203
+ return CommandResultFailure(
204
+ "500",
205
+ "Failed to call webspatial protocol sync"
206
+ );
207
+ }
208
+ }
209
+ /**
210
+ * Synchronously create iframe-based window
211
+ */
212
+ createIframeWindow(url, target, features) {
213
+ const iframe = document.createElement("iframe");
214
+ iframe.style.border = "none";
215
+ iframe.style.display = "none";
216
+ iframe.style.width = "100%";
217
+ iframe.style.height = "100%";
218
+ const spatialId = this.generateUUID();
219
+ iframe.spatialId = spatialId;
220
+ iframe.id = `spatial-iframe-${spatialId}`;
221
+ const featuresObj = this.parseFeatures(features || "");
222
+ if (featuresObj.width) {
223
+ iframe.style.width = featuresObj.width;
224
+ }
225
+ if (featuresObj.height) {
226
+ iframe.style.height = featuresObj.height;
227
+ }
228
+ if (featuresObj.left) {
229
+ iframe.style.left = featuresObj.left;
230
+ iframe.style.position = "absolute";
231
+ }
232
+ if (featuresObj.top) {
233
+ iframe.style.top = featuresObj.top;
234
+ iframe.style.position = "absolute";
235
+ }
236
+ document.body.appendChild(iframe);
237
+ const windowProxy = this.createEnhancedWindowProxy(iframe, url, spatialId);
238
+ iframe.src = "about:blank";
239
+ console.log(
240
+ `PuppeteerPlatform created iframe window with spatialId: ${spatialId}, URL: ${url}`
241
+ );
242
+ this.initializeIframeContent(iframe, url, spatialId);
243
+ return { spatialId, iframe, windowProxy };
244
+ }
245
+ /**
246
+ * create enhanced windowProxy object
247
+ */
248
+ createEnhancedWindowProxy(iframe, url, spatialId) {
249
+ return {
250
+ // basic properties
251
+ location: {
252
+ href: url,
253
+ toString: () => url,
254
+ reload: () => {
255
+ if (iframe.contentWindow) {
256
+ iframe.contentWindow.location.reload();
257
+ }
258
+ }
259
+ },
260
+ navigator: {
261
+ userAgent: `Mozilla/5.0 (WebKit) SpatialId/${spatialId}`
262
+ },
263
+ // methods
264
+ close: () => {
265
+ console.log(`Closing iframe with spatialId: ${spatialId}`);
266
+ iframe.remove();
267
+ this.iframeRegistry.delete(spatialId);
268
+ },
269
+ // document access
270
+ document: iframe.contentDocument || {},
271
+ contentWindow: iframe.contentWindow || {},
272
+ // add message communication method
273
+ postMessage: (message, targetOrigin) => {
274
+ if (iframe.contentWindow) {
275
+ iframe.contentWindow.postMessage(message, targetOrigin || "*");
276
+ }
277
+ },
278
+ // add event listener method
279
+ addEventListener: (type, listener) => {
280
+ if (iframe.contentWindow) {
281
+ iframe.contentWindow.addEventListener(type, listener);
282
+ }
283
+ },
284
+ removeEventListener: (type, listener) => {
285
+ if (iframe.contentWindow) {
286
+ iframe.contentWindow.removeEventListener(type, listener);
287
+ }
288
+ },
289
+ // execute JavaScript
290
+ executeScript: (code) => {
291
+ if (iframe.contentWindow) {
292
+ try {
293
+ const win = iframe.contentWindow;
294
+ return win.eval(code);
295
+ } catch (error) {
296
+ console.error(
297
+ `Error executing script in iframe ${spatialId}:`,
298
+ error
299
+ );
300
+ return null;
301
+ }
302
+ }
303
+ return null;
304
+ },
305
+ // get iframe reference
306
+ getIframe: () => iframe,
307
+ // get spatialId
308
+ getSpatialId: () => spatialId
309
+ };
310
+ }
311
+ /**
312
+ * initialize iframe content
313
+ */
314
+ initializeIframeContent(iframe, url, spatialId) {
315
+ try {
316
+ iframe.onload = () => {
317
+ try {
318
+ const iframeContent = `
319
+ // inject communication script
320
+ window.webSpatialId = '${spatialId}';
321
+ window.SpatialId = '${spatialId}';
322
+
323
+ // override window.open to support webspatial protocol
324
+ const originalOpen = window.open;
325
+ window.open = function(url, target, features) {
326
+ if (url && url.startsWith('webspatial://')) {
327
+ // handle webspatial protocol through windowProxy
328
+ const windowProxy = new Proxy({}, {
329
+ get: function(target, prop) {
330
+ if (prop === 'toString') {
331
+ return function() { return url; };
332
+ }
333
+ return undefined;
334
+ }
335
+ });
336
+ return windowProxy;
337
+ }
338
+ return originalOpen.call(window, url, target, features);
339
+ };
340
+
341
+ // set navigator.userAgent to identify webspatial environment
342
+ Object.defineProperty(navigator, 'userAgent', {
343
+ value: 'WebSpatial/1.0 ' + navigator.userAgent,
344
+ configurable: true
345
+ });
346
+
347
+ // send loaded message
348
+ window.parent.postMessage({
349
+ type: 'iframe_loaded',
350
+ spatialId: '${spatialId}',
351
+ url: '${url}'
352
+ }, '${window.location.origin}');
353
+
354
+ // set message handler
355
+ window.addEventListener('message', (event) => {
356
+ if (event.origin !== window.parent.location.origin) return;
357
+
358
+ const data = event.data;
359
+ if (data && data.type === 'webspatial_command') {
360
+ // handle command from parent window
361
+ console.log('Received command in iframe from parent:', data.command);
362
+ // add command handling logic here
363
+ }
364
+ });
365
+ `;
366
+ const doc = iframe.contentDocument;
367
+ if (doc) {
368
+ doc.open();
369
+ doc.write(`
370
+ <!DOCTYPE html>
371
+ <html>
372
+ <head>
373
+ <title>Spatial Iframe - ${spatialId}</title>
374
+ <meta charset="UTF-8">
375
+ <style>
376
+ body {
377
+ margin: 0;
378
+ padding: 0;
379
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
380
+ }
381
+ </style>
382
+ </head>
383
+ <body>
384
+ <script>${iframeContent}</script>
385
+ </body>
386
+ </html>
387
+ `);
388
+ doc.close();
389
+ }
390
+ } catch (error) {
391
+ console.error("Error initializing iframe content:", error);
392
+ }
393
+ };
394
+ } catch (error) {
395
+ console.error("Error setting up iframe:", error);
396
+ }
397
+ }
398
+ /**
399
+ * parse features string to object
400
+ */
401
+ parseFeatures(features) {
402
+ const result = {};
403
+ const pairs = features.split(",");
404
+ pairs.forEach((pair) => {
405
+ const [key, value] = pair.split("=").map((s) => s.trim());
406
+ if (key && value) {
407
+ result[key] = value;
408
+ }
409
+ });
410
+ return result;
411
+ }
412
+ /**
413
+ * send message to iframe with specified spatialId
414
+ */
415
+ sendMessageToIframe(spatialId, message) {
416
+ const iframe = this.iframeRegistry.get(spatialId);
417
+ if (iframe && iframe.contentWindow) {
418
+ iframe.contentWindow.postMessage(message, window.location.origin);
419
+ return true;
420
+ }
421
+ return false;
422
+ }
423
+ /**
424
+ * get all active iframes
425
+ */
426
+ getAllActiveIframes() {
427
+ const result = [];
428
+ this.iframeRegistry.forEach((iframe, spatialId) => {
429
+ result.push({ spatialId, iframe });
430
+ });
431
+ return result;
432
+ }
433
+ /**
434
+ * dispose all active iframes
435
+ */
436
+ dispose() {
437
+ this.iframeRegistry.forEach((iframe, spatialId) => {
438
+ console.log(`Disposing iframe with spatialId: ${spatialId}`);
439
+ iframe.remove();
440
+ });
441
+ this.iframeRegistry.clear();
442
+ }
443
+ // generate UUID function
444
+ generateUUID() {
445
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
446
+ /[xy]/g,
447
+ function(c) {
448
+ const r = Math.random() * 16 | 0;
449
+ const v = c === "x" ? r : r & 3 | 8;
450
+ return v.toString(16).toUpperCase();
451
+ }
452
+ );
453
+ }
454
+ };
455
+ }
456
+ });
457
+
96
458
  // src/SpatialWebEvent.ts
97
459
  var SpatialWebEvent;
98
460
  var init_SpatialWebEvent = __esm({
@@ -401,7 +763,10 @@ function createPlatform() {
401
763
  }
402
764
  const userAgent = window.navigator.userAgent;
403
765
  const webSpatialVersion = getWebSpatialVersion(userAgent);
404
- if (userAgent.includes("PicoWebApp") && isVersionGreater(webSpatialVersion, [0, 0, 1])) {
766
+ if (window.navigator.userAgent.includes("Puppeteer")) {
767
+ const PuppeteerPlatform2 = (init_PuppeteerPlatform(), __toCommonJS(PuppeteerPlatform_exports)).PuppeteerPlatform;
768
+ return new PuppeteerPlatform2();
769
+ } else if (userAgent.includes("PicoWebApp") && isVersionGreater(webSpatialVersion, [0, 0, 1])) {
405
770
  const XRPlatform2 = (init_XRPlatform(), __toCommonJS(XRPlatform_exports)).XRPlatform;
406
771
  return new XRPlatform2();
407
772
  } else if (userAgent.includes("Android") || userAgent.includes("Linux")) {
@@ -469,7 +834,7 @@ var init_utils = __esm({
469
834
  });
470
835
 
471
836
  // src/JSBCommand.ts
472
- var platform, JSBCommand, UpdateEntityPropertiesCommand, UpdateEntityEventCommand, UpdateSpatialSceneProperties, UpdateSceneConfig, FocusScene, GetSpatialSceneState, SpatializedElementCommand, UpdateSpatialized2DElementProperties, UpdateSpatializedDynamic3DElementProperties, UpdateUnlitMaterialProperties, UpdateSpatializedElementTransform, UpdateSpatializedStatic3DElementProperties, AddSpatializedElementToSpatialized2DElement, AddSpatializedElementToSpatialScene, CreateSpatializedStatic3DElementCommand, CreateSpatializedDynamic3DElementCommand, CreateSpatialEntityCommand, CreateModelComponentCommand, CreateSpatialModelEntityCommand, CreateModelAssetCommand, CreateSpatialGeometryCommand, CreateSpatialUnlitMaterialCommand, AddComponentToEntityCommand, SetParentForEntityCommand, ConvertFromEntityToEntityCommand, ConvertFromEntityToSceneCommand, ConvertFromSceneToEntityCommand, InspectCommand, DestroyCommand, CheckWebViewCanCreateCommand, WebSpatialProtocolCommand, createSpatialized2DElementCommand, createSpatialSceneCommand;
837
+ var platform, JSBCommand, UpdateEntityPropertiesCommand, UpdateEntityEventCommand, UpdateSpatialSceneProperties, UpdateSceneConfig, FocusScene, GetSpatialSceneState, SpatializedElementCommand, UpdateSpatialized2DElementProperties, UpdateSpatializedDynamic3DElementProperties, UpdateUnlitMaterialProperties, UpdateSpatializedElementTransform, UpdateSpatializedStatic3DElementProperties, AddSpatializedElementToSpatialized2DElement, AddSpatializedElementToSpatialScene, CreateSpatializedStatic3DElementCommand, CreateSpatializedDynamic3DElementCommand, CreateSpatialEntityCommand, CreateModelComponentCommand, CreateSpatialModelEntityCommand, CreateModelAssetCommand, CreateSpatialGeometryCommand, CreateSpatialUnlitMaterialCommand, AddComponentToEntityCommand, SetParentForEntityCommand, ConvertFromEntityToEntityCommand, ConvertFromEntityToSceneCommand, ConvertFromSceneToEntityCommand, ConvertCoordinateCommand, InspectCommand, DestroyCommand, CheckWebViewCanCreateCommand, WebSpatialProtocolCommand, createSpatialized2DElementCommand, createSpatialSceneCommand, CreateAttachmentEntityCommand, InitializeAttachmentCommand, UpdateAttachmentEntityCommand;
473
838
  var init_JSBCommand = __esm({
474
839
  "src/JSBCommand.ts"() {
475
840
  "use strict";
@@ -807,6 +1172,22 @@ var init_JSBCommand = __esm({
807
1172
  }
808
1173
  commandType = "ConvertFromSceneToEntity";
809
1174
  };
1175
+ ConvertCoordinateCommand = class extends JSBCommand {
1176
+ constructor(position, fromId, toId) {
1177
+ super();
1178
+ this.position = position;
1179
+ this.fromId = fromId;
1180
+ this.toId = toId;
1181
+ }
1182
+ getParams() {
1183
+ return {
1184
+ position: this.position,
1185
+ fromId: this.fromId,
1186
+ toId: this.toId
1187
+ };
1188
+ }
1189
+ commandType = "ConvertCoordinate";
1190
+ };
810
1191
  InspectCommand = class extends JSBCommand {
811
1192
  constructor(id = "") {
812
1193
  super();
@@ -896,6 +1277,47 @@ var init_JSBCommand = __esm({
896
1277
  };
897
1278
  }
898
1279
  };
1280
+ CreateAttachmentEntityCommand = class extends WebSpatialProtocolCommand {
1281
+ constructor(options) {
1282
+ super();
1283
+ this.options = options;
1284
+ }
1285
+ commandType = "createAttachment";
1286
+ getParams() {
1287
+ return {};
1288
+ }
1289
+ };
1290
+ InitializeAttachmentCommand = class extends JSBCommand {
1291
+ constructor(attachmentId, options) {
1292
+ super();
1293
+ this.attachmentId = attachmentId;
1294
+ this.options = options;
1295
+ }
1296
+ commandType = "InitializeAttachment";
1297
+ getParams() {
1298
+ return {
1299
+ id: this.attachmentId,
1300
+ parentEntityId: this.options.parentEntityId,
1301
+ position: this.options.position ?? [0, 0, 0],
1302
+ size: this.options.size,
1303
+ ownerViewId: this.options.ownerViewId
1304
+ };
1305
+ }
1306
+ };
1307
+ UpdateAttachmentEntityCommand = class extends JSBCommand {
1308
+ constructor(attachmentId, options) {
1309
+ super();
1310
+ this.attachmentId = attachmentId;
1311
+ this.options = options;
1312
+ }
1313
+ commandType = "UpdateAttachmentEntity";
1314
+ getParams() {
1315
+ return {
1316
+ id: this.attachmentId,
1317
+ ...this.options
1318
+ };
1319
+ }
1320
+ };
899
1321
  }
900
1322
  });
901
1323
 
@@ -939,6 +1361,7 @@ init_JSBCommand();
939
1361
 
940
1362
  // src/SpatialScene.ts
941
1363
  init_JSBCommand();
1364
+ init_JSBCommand();
942
1365
  var instance;
943
1366
  var SpatialScene = class _SpatialScene extends SpatialObject {
944
1367
  /**
@@ -952,6 +1375,19 @@ var SpatialScene = class _SpatialScene extends SpatialObject {
952
1375
  }
953
1376
  return instance;
954
1377
  }
1378
+ async convertCoordinate(position, fromId, toId) {
1379
+ try {
1380
+ const ret = await new ConvertCoordinateCommand(
1381
+ position,
1382
+ fromId,
1383
+ toId
1384
+ ).execute();
1385
+ return ret?.data ?? position;
1386
+ } catch (error) {
1387
+ console.warn("SpatialScene.convertCoordinate error:", error);
1388
+ throw error;
1389
+ }
1390
+ }
955
1391
  /**
956
1392
  * Updates the properties of the spatial scene.
957
1393
  * This can include background settings, lighting, and other scene-wide properties.
@@ -1451,29 +1887,6 @@ var SpatializedElement = class extends SpatialObject {
1451
1887
  const { type } = data;
1452
1888
  if (type === "objectdestroy" /* objectdestroy */) {
1453
1889
  this.isDestroyed = true;
1454
- } else if (type === "cubeInfo" /* cubeInfo */) {
1455
- const cubeInfoMsg = data;
1456
- this._cubeInfo = new CubeInfo(cubeInfoMsg.size, cubeInfoMsg.origin);
1457
- } else if (type === "transform" /* transform */) {
1458
- this._transform = new DOMMatrix([
1459
- data.detail.column0[0],
1460
- data.detail.column0[1],
1461
- data.detail.column0[2],
1462
- 0,
1463
- data.detail.column1[0],
1464
- data.detail.column1[1],
1465
- data.detail.column1[2],
1466
- 0,
1467
- data.detail.column2[0],
1468
- data.detail.column2[1],
1469
- data.detail.column2[2],
1470
- 0,
1471
- data.detail.column3[0],
1472
- data.detail.column3[1],
1473
- data.detail.column3[2],
1474
- 1
1475
- ]);
1476
- this._transformInv = this._transform.inverse();
1477
1890
  } else if (type === "spatialtap" /* spatialtap */) {
1478
1891
  const event = createSpatialEvent(
1479
1892
  "spatialtap" /* spatialtap */,
@@ -1627,6 +2040,16 @@ var Spatialized2DElement = class extends SpatializedElement {
1627
2040
  // src/SpatializedStatic3DElement.ts
1628
2041
  init_JSBCommand();
1629
2042
  var SpatializedStatic3DElement = class extends SpatializedElement {
2043
+ /**
2044
+ * Creates a new spatialized static 3D element with the specified ID and URL.
2045
+ * Registers the element to receive spatial events.
2046
+ * @param id Unique identifier for this element
2047
+ * @param modelURL URL of the 3D model
2048
+ */
2049
+ constructor(id, modelURL) {
2050
+ super(id);
2051
+ this.modelURL = modelURL;
2052
+ }
1630
2053
  /**
1631
2054
  * Promise resolver for the ready state.
1632
2055
  * Used to resolve the ready promise when the model is loaded.
@@ -1636,12 +2059,13 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
1636
2059
  * Caches the last model URL to detect changes.
1637
2060
  * Used to reset the ready promise when the model URL changes.
1638
2061
  */
1639
- modelURL = "";
2062
+ modelURL;
1640
2063
  /**
1641
2064
  * Creates a new promise for tracking the ready state of the model.
1642
2065
  * @returns Promise that resolves when the model is loaded (true) or fails to load (false)
1643
2066
  */
1644
2067
  createReadyPromise() {
2068
+ this._readyResolve?.(false);
1645
2069
  return new Promise((resolve) => {
1646
2070
  this._readyResolve = resolve;
1647
2071
  });
@@ -1717,6 +2141,7 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
1717
2141
  init_JSBCommand();
1718
2142
  var SpatializedDynamic3DElement = class extends SpatializedElement {
1719
2143
  children = [];
2144
+ events = {};
1720
2145
  constructor(id) {
1721
2146
  super(id);
1722
2147
  }
@@ -1726,6 +2151,17 @@ var SpatializedDynamic3DElement = class extends SpatializedElement {
1726
2151
  entity.parent = this;
1727
2152
  return ans;
1728
2153
  }
2154
+ addEvent(type, callback) {
2155
+ this.events[type] = callback;
2156
+ }
2157
+ removeEvent(eventName) {
2158
+ if (this.events[eventName]) {
2159
+ delete this.events[eventName];
2160
+ }
2161
+ }
2162
+ dispatchEvent(evt) {
2163
+ this.events[evt.type]?.(evt);
2164
+ }
1729
2165
  async updateProperties(properties) {
1730
2166
  return new UpdateSpatializedDynamic3DElementProperties(
1731
2167
  this,
@@ -1754,7 +2190,7 @@ async function createSpatializedStatic3DElement(modelURL) {
1754
2190
  throw new Error("createSpatializedStatic3DElement failed");
1755
2191
  } else {
1756
2192
  const { id } = result.data;
1757
- return new SpatializedStatic3DElement(id);
2193
+ return new SpatializedStatic3DElement(id, modelURL);
1758
2194
  }
1759
2195
  }
1760
2196
  async function createSpatializedDynamic3DElement() {
@@ -1767,6 +2203,37 @@ async function createSpatializedDynamic3DElement() {
1767
2203
  }
1768
2204
  }
1769
2205
 
2206
+ // src/reality/Attachment.ts
2207
+ init_JSBCommand();
2208
+ var Attachment = class extends SpatialObject {
2209
+ constructor(id, windowProxy, options) {
2210
+ super(id);
2211
+ this.windowProxy = windowProxy;
2212
+ this.options = options;
2213
+ }
2214
+ getContainer() {
2215
+ return this.windowProxy.document.body;
2216
+ }
2217
+ getWindowProxy() {
2218
+ return this.windowProxy;
2219
+ }
2220
+ async update(options) {
2221
+ if (this.isDestroyed) return;
2222
+ if (options.position) this.options.position = options.position;
2223
+ if (options.size) this.options.size = options.size;
2224
+ return new UpdateAttachmentEntityCommand(this.id, options).execute();
2225
+ }
2226
+ };
2227
+ async function createAttachmentEntity(options) {
2228
+ const result = await new CreateAttachmentEntityCommand(options).execute();
2229
+ if (!result.success) {
2230
+ throw new Error("createAttachmentEntity failed: " + result?.errorMessage);
2231
+ }
2232
+ const { id, windowProxy } = result.data;
2233
+ await new InitializeAttachmentCommand(id, options).execute();
2234
+ return new Attachment(id, windowProxy, options);
2235
+ }
2236
+
1770
2237
  // src/reality/realityCreator.ts
1771
2238
  init_JSBCommand();
1772
2239
 
@@ -1774,7 +2241,7 @@ init_JSBCommand();
1774
2241
  init_JSBCommand();
1775
2242
  init_JSBCommand();
1776
2243
  init_SpatialWebEvent();
1777
- var SpatialEntity = class _SpatialEntity extends SpatialObject {
2244
+ var SpatialEntity = class extends SpatialObject {
1778
2245
  constructor(id, userData) {
1779
2246
  super(id);
1780
2247
  this.userData = userData;
@@ -1786,6 +2253,20 @@ var SpatialEntity = class _SpatialEntity extends SpatialObject {
1786
2253
  events = {};
1787
2254
  children = [];
1788
2255
  parent = null;
2256
+ _enableInput = false;
2257
+ get enableInput() {
2258
+ return this._enableInput;
2259
+ }
2260
+ set enableInput(value) {
2261
+ if (this._enableInput === value) return;
2262
+ this._enableInput = value;
2263
+ void this.updateEntityEvent("spatialtap", value).catch((err) => {
2264
+ console.error("enableInput updateEntityEvent failed", "spatialtap", err);
2265
+ if (this._enableInput === value) {
2266
+ this._enableInput = !value;
2267
+ }
2268
+ });
2269
+ }
1789
2270
  async addComponent(component) {
1790
2271
  return new AddComponentToEntityCommand(this, component).execute();
1791
2272
  }
@@ -1853,10 +2334,7 @@ var SpatialEntity = class _SpatialEntity extends SpatialObject {
1853
2334
  if (type === "objectdestroy" /* objectdestroy */) {
1854
2335
  this.isDestroyed = true;
1855
2336
  } else if (type === "spatialtap" /* spatialtap */) {
1856
- const evt = createSpatialEvent(
1857
- "spatialtap" /* spatialtap */,
1858
- data.detail
1859
- );
2337
+ const evt = createSpatialEvent("spatialtap" /* spatialtap */, data.detail);
1860
2338
  this.dispatchEvent(evt);
1861
2339
  } else if (type === "spatialdragstart" /* spatialdragstart */) {
1862
2340
  const evt = createSpatialEvent(
@@ -1865,10 +2343,7 @@ var SpatialEntity = class _SpatialEntity extends SpatialObject {
1865
2343
  );
1866
2344
  this.dispatchEvent(evt);
1867
2345
  } else if (type === "spatialdrag" /* spatialdrag */) {
1868
- const evt = createSpatialEvent(
1869
- "spatialdrag" /* spatialdrag */,
1870
- data.detail
1871
- );
2346
+ const evt = createSpatialEvent("spatialdrag" /* spatialdrag */, data.detail);
1872
2347
  this.dispatchEvent(evt);
1873
2348
  } else if (type === "spatialdragend" /* spatialdragend */) {
1874
2349
  const evt = createSpatialEvent(
@@ -1908,7 +2383,7 @@ var SpatialEntity = class _SpatialEntity extends SpatialObject {
1908
2383
  }
1909
2384
  this.events[evt.type]?.(evt);
1910
2385
  if (evt.bubbles && !evt.cancelBubble) {
1911
- if (this.parent && this.parent instanceof _SpatialEntity) {
2386
+ if (this.parent) {
1912
2387
  this.parent.dispatchEvent(evt);
1913
2388
  }
1914
2389
  }
@@ -2156,7 +2631,7 @@ var SpatialSession = class {
2156
2631
  * @param modelURL Optional URL to the 3D model to load
2157
2632
  * @returns Promise resolving to a new SpatializedStatic3DElement instance
2158
2633
  */
2159
- createSpatializedStatic3DElement(modelURL = "") {
2634
+ createSpatializedStatic3DElement(modelURL) {
2160
2635
  return createSpatializedStatic3DElement(modelURL);
2161
2636
  }
2162
2637
  /**
@@ -2257,11 +2732,21 @@ var SpatialSession = class {
2257
2732
  createSpatialModelEntity(options, userData) {
2258
2733
  return createSpatialModelEntity(options, userData);
2259
2734
  }
2735
+ /**
2736
+ * Creates an attachment entity that renders 2D HTML content as a child
2737
+ * of a 3D entity in the scene graph.
2738
+ * @param options Configuration options including parent entity ID, position, and size
2739
+ * @returns Promise resolving to a new Attachment instance
2740
+ */
2741
+ createAttachmentEntity(options) {
2742
+ return createAttachmentEntity(options);
2743
+ }
2260
2744
  };
2261
2745
 
2262
2746
  // src/Spatial.ts
2263
2747
  init_SpatialWebEvent();
2264
2748
  var Spatial = class {
2749
+ wsAppShellVersionFromUA;
2265
2750
  /**
2266
2751
  * Requests a spatial session object from the browser.
2267
2752
  * This is the primary method to initialize spatial functionality.
@@ -2287,6 +2772,20 @@ var Spatial = class {
2287
2772
  }
2288
2773
  return false;
2289
2774
  }
2775
+ getShellVersionFromUA() {
2776
+ if (this.wsAppShellVersionFromUA !== void 0) {
2777
+ return this.wsAppShellVersionFromUA;
2778
+ }
2779
+ if (typeof navigator === "undefined" || typeof navigator.userAgent !== "string") {
2780
+ this.wsAppShellVersionFromUA = null;
2781
+ return null;
2782
+ }
2783
+ const match = navigator.userAgent.match(
2784
+ /WSAppShell\/(\d+(?:\.\d+){2}(?:[-+][0-9A-Za-z.-]+)*)/
2785
+ );
2786
+ this.wsAppShellVersionFromUA = match ? match[1] : "1.3.0";
2787
+ return this.wsAppShellVersionFromUA;
2788
+ }
2290
2789
  /** @deprecated
2291
2790
  * Checks if WebSpatial is supported in the current environment.
2292
2791
  * Verifies compatibility between native and client versions.
@@ -2304,7 +2803,7 @@ var Spatial = class {
2304
2803
  if (window.__WebSpatialData && window.__WebSpatialData.getNativeVersion) {
2305
2804
  return window.__WebSpatialData.getNativeVersion();
2306
2805
  }
2307
- return window.WebSpatailNativeVersion === "PACKAGE_VERSION" ? this.getClientVersion() : window.WebSpatailNativeVersion;
2806
+ return window.WebSpatailNativeVersion === "WS_SHELL_VERSION" ? this.getClientVersion() : window.WebSpatailNativeVersion;
2308
2807
  }
2309
2808
  /** @deprecated
2310
2809
  * Gets the client SDK version.
@@ -2312,10 +2811,70 @@ var Spatial = class {
2312
2811
  * @returns Client SDK version string in format "x.x.x"
2313
2812
  */
2314
2813
  getClientVersion() {
2315
- return "1.2.1";
2814
+ return "1.4.0";
2316
2815
  }
2317
2816
  };
2318
2817
 
2818
+ // src/physicalMetrics.ts
2819
+ var physicalMetrics_exports = {};
2820
+ __export(physicalMetrics_exports, {
2821
+ getValue: () => getValue,
2822
+ physicalToPoint: () => physicalToPoint,
2823
+ pointToPhysical: () => pointToPhysical,
2824
+ subscribe: () => subscribe
2825
+ });
2826
+ init_SpatialWebEvent();
2827
+ var snapshot = {
2828
+ meterToPtUnscaled: 1360,
2829
+ meterToPtScaled: 1360
2830
+ };
2831
+ function getWorldScalingCompensation(options) {
2832
+ return options?.worldScalingCompensation ?? "scaled";
2833
+ }
2834
+ function pointToPhysical(point, options) {
2835
+ updateValue();
2836
+ const compensation = getWorldScalingCompensation(options);
2837
+ if (compensation === "unscaled") {
2838
+ return point / snapshot.meterToPtUnscaled;
2839
+ }
2840
+ return point / snapshot.meterToPtScaled;
2841
+ }
2842
+ function physicalToPoint(physical, options) {
2843
+ updateValue();
2844
+ const compensation = getWorldScalingCompensation(options);
2845
+ if (compensation === "unscaled") {
2846
+ return physical * snapshot.meterToPtUnscaled;
2847
+ }
2848
+ return physical * snapshot.meterToPtScaled;
2849
+ }
2850
+ function updateValue() {
2851
+ if (typeof window === "undefined") return;
2852
+ const src = window.__webspatialsdk__?.physicalMetrics;
2853
+ if (!src) return;
2854
+ const next = {
2855
+ meterToPtScaled: src.meterToPtScaled ?? snapshot.meterToPtScaled,
2856
+ meterToPtUnscaled: src.meterToPtUnscaled ?? snapshot.meterToPtUnscaled
2857
+ };
2858
+ if (next.meterToPtScaled !== snapshot.meterToPtScaled || next.meterToPtUnscaled !== snapshot.meterToPtUnscaled) {
2859
+ snapshot = next;
2860
+ }
2861
+ }
2862
+ function getValue() {
2863
+ updateValue();
2864
+ return snapshot;
2865
+ }
2866
+ function subscribe(cb) {
2867
+ if (typeof window === "undefined") return () => {
2868
+ };
2869
+ const handler = () => {
2870
+ cb();
2871
+ };
2872
+ SpatialWebEvent.addEventReceiver("window", handler);
2873
+ return () => {
2874
+ SpatialWebEvent.removeEventReceiver("window");
2875
+ };
2876
+ }
2877
+
2319
2878
  // src/index.ts
2320
2879
  init_ssr_polyfill();
2321
2880
 
@@ -2463,9 +3022,11 @@ if (!isSSREnv() && navigator.userAgent.indexOf("WebSpatial/") > 0) {
2463
3022
  spatialWindowPolyfill();
2464
3023
  }
2465
3024
  export {
3025
+ Attachment,
2466
3026
  BaseplateVisibilityValues,
2467
3027
  CubeInfo,
2468
3028
  ModelComponent,
3029
+ physicalMetrics_exports as PhysicalMetrics,
2469
3030
  Spatial,
2470
3031
  SpatialBoxGeometry,
2471
3032
  SpatialComponent,
@@ -2491,6 +3052,7 @@ export {
2491
3052
  SpatializedStatic3DElement,
2492
3053
  WorldAlignmentValues,
2493
3054
  WorldScalingValues,
3055
+ createAttachmentEntity,
2494
3056
  isSSREnv,
2495
3057
  isValidBaseplateVisibilityType,
2496
3058
  isValidSceneUnit,