@webspatial/core-sdk 1.4.0 → 1.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/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.4.0"
5
+ window.__webspatialsdk__['core-sdk-version'] = "1.6.0"
6
6
  })()
7
7
 
8
8
  var __defProp = Object.defineProperty;
@@ -477,24 +477,24 @@ var init_SpatialWebEvent = __esm({
477
477
  }
478
478
  });
479
479
 
480
- // src/platform-adapter/xr/XRPlatform.ts
481
- var XRPlatform_exports = {};
482
- __export(XRPlatform_exports, {
483
- XRPlatform: () => XRPlatform
480
+ // src/platform-adapter/pico-os/PicoOSPlatform.ts
481
+ var PicoOSPlatform_exports = {};
482
+ __export(PicoOSPlatform_exports, {
483
+ PicoOSPlatform: () => PicoOSPlatform
484
484
  });
485
485
  function nextRequestId() {
486
486
  requestId = (requestId + 1) % MAX_ID;
487
487
  return `rId_${requestId}`;
488
488
  }
489
- var requestId, MAX_ID, XRPlatform;
490
- var init_XRPlatform = __esm({
491
- "src/platform-adapter/xr/XRPlatform.ts"() {
489
+ var requestId, MAX_ID, PicoOSPlatform;
490
+ var init_PicoOSPlatform = __esm({
491
+ "src/platform-adapter/pico-os/PicoOSPlatform.ts"() {
492
492
  "use strict";
493
493
  init_CommandResultUtils();
494
494
  init_SpatialWebEvent();
495
495
  requestId = 0;
496
496
  MAX_ID = 1e5;
497
- XRPlatform = class {
497
+ PicoOSPlatform = class {
498
498
  async callJSB(cmd, msg) {
499
499
  return new Promise((resolve, reject) => {
500
500
  try {
@@ -520,7 +520,7 @@ var init_XRPlatform = __esm({
520
520
  }
521
521
  }
522
522
  } catch (error) {
523
- console.error(`XRPlatform cmd: ${cmd}, msg: ${msg} error: ${error}`);
523
+ console.error(`SwanPlatform cmd: ${cmd}, msg: ${msg} error: ${error}`);
524
524
  const { code, message } = error;
525
525
  resolve(CommandResultFailure(code, message));
526
526
  }
@@ -534,7 +534,6 @@ var init_XRPlatform = __esm({
534
534
  SpatialWebEvent.addEventReceiver(
535
535
  createdId,
536
536
  (result) => {
537
- console.log("createdId", createdId, result.spatialId);
538
537
  resolve(
539
538
  CommandResultSuccess({
540
539
  windowProxy,
@@ -546,13 +545,11 @@ var init_XRPlatform = __esm({
546
545
  );
547
546
  windowProxy = this.openWindow(
548
547
  command,
549
- query,
548
+ "rid=" + createdId,
550
549
  target,
551
550
  features
552
551
  ).windowProxy;
553
- windowProxy?.open(`about:blank?rid=${createdId}`, "_self");
554
552
  } catch (error) {
555
- console.error(`open window error: ${error}`);
556
553
  const { code, message } = error;
557
554
  SpatialWebEvent.removeEventReceiver(createdId);
558
555
  resolve(CommandResultFailure(code, message));
@@ -767,8 +764,8 @@ function createPlatform() {
767
764
  const PuppeteerPlatform2 = (init_PuppeteerPlatform(), __toCommonJS(PuppeteerPlatform_exports)).PuppeteerPlatform;
768
765
  return new PuppeteerPlatform2();
769
766
  } else if (userAgent.includes("PicoWebApp") && isVersionGreater(webSpatialVersion, [0, 0, 1])) {
770
- const XRPlatform2 = (init_XRPlatform(), __toCommonJS(XRPlatform_exports)).XRPlatform;
771
- return new XRPlatform2();
767
+ const PicoOSPlatform2 = (init_PicoOSPlatform(), __toCommonJS(PicoOSPlatform_exports)).PicoOSPlatform;
768
+ return new PicoOSPlatform2();
772
769
  } else if (userAgent.includes("Android") || userAgent.includes("Linux")) {
773
770
  const AndroidPlatform2 = (init_AndroidPlatform(), __toCommonJS(AndroidPlatform_exports)).AndroidPlatform;
774
771
  return new AndroidPlatform2();
@@ -827,6 +824,16 @@ function composeSRT(position, rotation, scale) {
827
824
  m = m.scale(sx, sy, sz);
828
825
  return m;
829
826
  }
827
+ function deepCloneJSON(value) {
828
+ const sc = globalThis.structuredClone;
829
+ if (typeof sc === "function") {
830
+ try {
831
+ return sc(value);
832
+ } catch {
833
+ }
834
+ }
835
+ return JSON.parse(JSON.stringify(value));
836
+ }
830
837
  var init_utils = __esm({
831
838
  "src/utils.ts"() {
832
839
  "use strict";
@@ -834,7 +841,7 @@ var init_utils = __esm({
834
841
  });
835
842
 
836
843
  // src/JSBCommand.ts
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;
844
+ 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, RemoveComponentFromEntityCommand, SetMaterialsOnEntityCommand, SetParentForEntityCommand, ConvertFromEntityToEntityCommand, ConvertFromEntityToSceneCommand, ConvertFromSceneToEntityCommand, ConvertCoordinateCommand, InspectCommand, DestroyCommand, CheckWebViewCanCreateCommand, WebSpatialProtocolCommand, createSpatialized2DElementCommand, createSpatialSceneCommand, CreateAttachmentEntityCommand, InitializeAttachmentCommand, UpdateAttachmentEntityCommand;
838
845
  var init_JSBCommand = __esm({
839
846
  "src/JSBCommand.ts"() {
840
847
  "use strict";
@@ -1018,14 +1025,16 @@ var init_JSBCommand = __esm({
1018
1025
  }
1019
1026
  };
1020
1027
  CreateSpatializedStatic3DElementCommand = class extends JSBCommand {
1021
- constructor(modelURL) {
1028
+ constructor(modelURL, sources) {
1022
1029
  super();
1023
1030
  this.modelURL = modelURL;
1031
+ this.sources = sources;
1024
1032
  this.modelURL = modelURL;
1033
+ this.sources = sources;
1025
1034
  }
1026
1035
  commandType = "CreateSpatializedStatic3DElement";
1027
1036
  getParams() {
1028
- return { modelURL: this.modelURL };
1037
+ return { modelURL: this.modelURL, sources: this.sources };
1029
1038
  }
1030
1039
  };
1031
1040
  CreateSpatializedDynamic3DElementCommand = class extends JSBCommand {
@@ -1111,6 +1120,34 @@ var init_JSBCommand = __esm({
1111
1120
  }
1112
1121
  commandType = "AddComponentToEntity";
1113
1122
  };
1123
+ RemoveComponentFromEntityCommand = class extends JSBCommand {
1124
+ constructor(entity, comp) {
1125
+ super();
1126
+ this.entity = entity;
1127
+ this.comp = comp;
1128
+ }
1129
+ getParams() {
1130
+ return {
1131
+ entityId: this.entity.id,
1132
+ componentId: this.comp.id
1133
+ };
1134
+ }
1135
+ commandType = "RemoveComponentFromEntity";
1136
+ };
1137
+ SetMaterialsOnEntityCommand = class extends JSBCommand {
1138
+ constructor(entityId, materials) {
1139
+ super();
1140
+ this.entityId = entityId;
1141
+ this.materials = materials;
1142
+ }
1143
+ getParams() {
1144
+ return {
1145
+ entityId: this.entityId,
1146
+ materialIds: this.materials.map((m) => m.id)
1147
+ };
1148
+ }
1149
+ commandType = "SetMaterialsOnEntity";
1150
+ };
1114
1151
  SetParentForEntityCommand = class extends JSBCommand {
1115
1152
  // childId, parentId
1116
1153
  constructor(childId, parentId) {
@@ -1529,6 +1566,69 @@ var CubeInfo = class {
1529
1566
  }
1530
1567
  };
1531
1568
 
1569
+ // src/scene-polyfill.ts
1570
+ init_utils();
1571
+
1572
+ // src/physicalMetrics.ts
1573
+ var physicalMetrics_exports = {};
1574
+ __export(physicalMetrics_exports, {
1575
+ getValue: () => getValue,
1576
+ physicalToPoint: () => physicalToPoint,
1577
+ pointToPhysical: () => pointToPhysical,
1578
+ subscribe: () => subscribe
1579
+ });
1580
+ init_SpatialWebEvent();
1581
+ var snapshot = {
1582
+ meterToPtUnscaled: 1360,
1583
+ meterToPtScaled: 1360
1584
+ };
1585
+ function getWorldScalingCompensation(options) {
1586
+ return options?.worldScalingCompensation ?? "scaled";
1587
+ }
1588
+ function pointToPhysical(point, options) {
1589
+ updateValue();
1590
+ const compensation = getWorldScalingCompensation(options);
1591
+ if (compensation === "unscaled") {
1592
+ return point / snapshot.meterToPtUnscaled;
1593
+ }
1594
+ return point / snapshot.meterToPtScaled;
1595
+ }
1596
+ function physicalToPoint(physical, options) {
1597
+ updateValue();
1598
+ const compensation = getWorldScalingCompensation(options);
1599
+ if (compensation === "unscaled") {
1600
+ return physical * snapshot.meterToPtUnscaled;
1601
+ }
1602
+ return physical * snapshot.meterToPtScaled;
1603
+ }
1604
+ function updateValue() {
1605
+ if (typeof window === "undefined") return;
1606
+ const src = window.__webspatialsdk__?.physicalMetrics;
1607
+ if (!src) return;
1608
+ const next = {
1609
+ meterToPtScaled: src.meterToPtScaled ?? snapshot.meterToPtScaled,
1610
+ meterToPtUnscaled: src.meterToPtUnscaled ?? snapshot.meterToPtUnscaled
1611
+ };
1612
+ if (next.meterToPtScaled !== snapshot.meterToPtScaled || next.meterToPtUnscaled !== snapshot.meterToPtUnscaled) {
1613
+ snapshot = next;
1614
+ }
1615
+ }
1616
+ function getValue() {
1617
+ updateValue();
1618
+ return snapshot;
1619
+ }
1620
+ function subscribe(cb) {
1621
+ if (typeof window === "undefined") return () => {
1622
+ };
1623
+ const handler = () => {
1624
+ cb();
1625
+ };
1626
+ SpatialWebEvent.addEventReceiver("window", handler);
1627
+ return () => {
1628
+ SpatialWebEvent.removeEventReceiver("window");
1629
+ };
1630
+ }
1631
+
1532
1632
  // src/scene-polyfill.ts
1533
1633
  var defaultSceneConfig = {
1534
1634
  defaultSize: {
@@ -1538,15 +1638,47 @@ var defaultSceneConfig = {
1538
1638
  };
1539
1639
  var defaultSceneConfigVolume = {
1540
1640
  defaultSize: {
1541
- width: 0.94,
1542
- height: 0.94,
1543
- depth: 0.94
1641
+ width: "0.94m",
1642
+ height: "0.94m",
1643
+ depth: "0.94m"
1544
1644
  }
1545
1645
  };
1646
+ var xr_window_defaults = {
1647
+ ...defaultSceneConfig
1648
+ };
1649
+ var xr_volume_defaults = {
1650
+ ...defaultSceneConfigVolume
1651
+ };
1546
1652
  var INTERNAL_SCHEMA_PREFIX = "webspatial://";
1653
+ function deepMergePlain(base, over) {
1654
+ if (!over) return { ...base || {} };
1655
+ const out = { ...base || {} };
1656
+ for (const k of Object.keys(over)) {
1657
+ const bv = out[k];
1658
+ const ov = over[k];
1659
+ if (ov && typeof ov === "object" && !Array.isArray(ov) && bv && typeof bv === "object" && !Array.isArray(bv)) {
1660
+ out[k] = deepMergePlain(bv, ov);
1661
+ } else {
1662
+ out[k] = ov;
1663
+ }
1664
+ }
1665
+ return out;
1666
+ }
1667
+ function normalizeXRDefaultsToSceneOptions(src) {
1668
+ const out = { ...src || {} };
1669
+ const ds = src.defaultSize !== void 0 ? src.defaultSize : src.default_size;
1670
+ if (ds !== void 0) {
1671
+ out.defaultSize = ds;
1672
+ }
1673
+ if ("default_size" in out) {
1674
+ delete out.default_size;
1675
+ }
1676
+ return out;
1677
+ }
1547
1678
  var SceneManager = class _SceneManager {
1548
1679
  originalOpen;
1549
1680
  static instance;
1681
+ manifestReady = null;
1550
1682
  static getInstance() {
1551
1683
  if (!_SceneManager.instance) {
1552
1684
  _SceneManager.instance = new _SceneManager();
@@ -1554,15 +1686,23 @@ var SceneManager = class _SceneManager {
1554
1686
  return _SceneManager.instance;
1555
1687
  }
1556
1688
  init(window2) {
1689
+ this.manifestReady = this.setupManifest();
1557
1690
  this.originalOpen = window2.open.bind(window2);
1558
1691
  window2.open = this.open;
1559
1692
  }
1693
+ // Stores the latest formatted config used by the platform (per scene name).
1694
+ // This object contains normalized values and is safe for internal consumption.
1560
1695
  configMap = {};
1561
- // name=>config
1696
+ // Stores the raw callback return value (per scene name) to feed into the next initScene call as `pre`.
1697
+ // We keep this unformatted so developers receive exactly what they last returned.
1698
+ callbackReturnMap = {};
1562
1699
  getConfig(name) {
1563
1700
  if (name === void 0 || !this.configMap[name]) return void 0;
1564
1701
  return this.configMap[name];
1565
1702
  }
1703
+ waitManifest() {
1704
+ return this.manifestReady ?? Promise.resolve();
1705
+ }
1566
1706
  // Ensure URL is absolute; only convert when a relative path is provided
1567
1707
  // - Keep external and special schemes untouched (http, https, data, blob, about, file, mailto, etc.)
1568
1708
  // - Handle protocol-relative URLs (//example.com/path)
@@ -1581,8 +1721,47 @@ var SceneManager = class _SceneManager {
1581
1721
  return raw;
1582
1722
  }
1583
1723
  }
1724
+ async setupManifest() {
1725
+ const manifest = await this.getPWAManifest();
1726
+ try {
1727
+ const xr = manifest?.xr_spatial_scene;
1728
+ if (!xr || typeof xr !== "object") return;
1729
+ const { overrides, ...topLevel } = xr;
1730
+ const windowRaw = deepMergePlain(topLevel, overrides?.window_scene);
1731
+ const volumeRaw = deepMergePlain(topLevel, overrides?.volume_scene);
1732
+ const windowNext = normalizeXRDefaultsToSceneOptions(windowRaw);
1733
+ const volumeNext = normalizeXRDefaultsToSceneOptions(volumeRaw);
1734
+ if (windowNext && Object.keys(windowNext).length > 0) {
1735
+ xr_window_defaults = windowNext;
1736
+ }
1737
+ if (volumeNext && Object.keys(volumeNext).length > 0) {
1738
+ xr_volume_defaults = volumeNext;
1739
+ }
1740
+ } catch (error) {
1741
+ console.warn(
1742
+ "SceneManager.setupManifest failed; using built-in defaults.",
1743
+ error?.message || error
1744
+ );
1745
+ }
1746
+ }
1584
1747
  open = (url, target, features) => {
1585
1748
  if (url?.startsWith(INTERNAL_SCHEMA_PREFIX)) {
1749
+ if (url.includes("createSpatialized2DElement") || url.includes("createAttachment")) {
1750
+ const token = (
1751
+ //@ts-ignore
1752
+ (window.webSpatial || window.__webspatialShell__)?.genToken?.()
1753
+ );
1754
+ if (token) {
1755
+ const command = url.includes("createAttachment") ? "createAttachment" : "createSpatialized2DElement";
1756
+ const host = window.location.host;
1757
+ const protocol = window.location.protocol;
1758
+ const finalURL = `${protocol}//${host}/${token}/?command=${command}`;
1759
+ const rid = new URL(url).searchParams.get("rid");
1760
+ const final = new URL(finalURL);
1761
+ if (rid) final.searchParams.set("rid", rid);
1762
+ return this.originalOpen(final.toString(), target, features);
1763
+ }
1764
+ }
1586
1765
  return this.originalOpen(url, target, features);
1587
1766
  }
1588
1767
  url = this.ensureAbsoluteUrl(url);
@@ -1590,7 +1769,12 @@ var SceneManager = class _SceneManager {
1590
1769
  const newWindow = this.originalOpen(url, target, features);
1591
1770
  return newWindow;
1592
1771
  }
1593
- const cfg = target ? this.getConfig(target) : void 0;
1772
+ let cfg = target ? this.getConfig(target) : void 0;
1773
+ if (cfg === void 0) {
1774
+ const preFormatted = deepCloneJSON(getSceneDefaultConfig("window"));
1775
+ const [ans] = formatSceneConfig(preFormatted, "window");
1776
+ cfg = { ...ans, type: "window" };
1777
+ }
1594
1778
  const cmd = new createSpatialSceneCommand(url, cfg, target, features);
1595
1779
  const result = cmd.executeSync();
1596
1780
  const id = result.data?.id;
@@ -1602,23 +1786,116 @@ var SceneManager = class _SceneManager {
1602
1786
  };
1603
1787
  initScene(name, callback, options) {
1604
1788
  const sceneType = options?.type ?? "window";
1605
- const defaultConfig = getSceneDefaultConfig(sceneType);
1606
- const rawReturnVal = callback({ ...defaultConfig });
1607
- const [formattedConfig, errors] = formatSceneConfig(rawReturnVal, sceneType);
1789
+ const defaultConfigRaw = getSceneDefaultConfig(sceneType);
1790
+ const previousOrDefault = this.callbackReturnMap[name] ?? (() => {
1791
+ const cloned = deepCloneJSON(defaultConfigRaw);
1792
+ return cloned;
1793
+ })();
1794
+ const rawReturnVal = callback(previousOrDefault);
1795
+ const sanitizedReturnVal = sanitizeSceneOptionsUnits(
1796
+ deepCloneJSON(rawReturnVal)
1797
+ );
1798
+ const clonedForFormat = deepCloneJSON(sanitizedReturnVal);
1799
+ const baseDefaults = deepCloneJSON(getSceneDefaultConfig(sceneType));
1800
+ const mergedForFormat = deepMergePlain(baseDefaults, clonedForFormat);
1801
+ const [formattedConfig, errors] = formatSceneConfig(
1802
+ mergedForFormat,
1803
+ sceneType
1804
+ );
1608
1805
  if (errors.length > 0) {
1609
1806
  console.warn(`initScene ${name} with errors: ${errors.join(", ")}`);
1610
1807
  }
1808
+ this.callbackReturnMap[name] = sanitizedReturnVal;
1611
1809
  this.configMap[name] = {
1612
1810
  ...formattedConfig,
1613
1811
  type: sceneType
1614
1812
  };
1615
1813
  }
1814
+ /**
1815
+ * Resolve and load a PWA manifest as JSON:
1816
+ * 1) Determine href:
1817
+ * - Prefer explicit manifestUrl if provided;
1818
+ * - Fallback to <link rel="manifest">, preferring the raw attribute over computed href.
1819
+ * 2) Normalize href to absolute using ensureAbsoluteUrl (respects <base href>).
1820
+ * 3) Handle data URLs inline:
1821
+ * - data:...;base64,... → atob then JSON.parse
1822
+ * - data:...,... → decodeURIComponent then JSON.parse
1823
+ * 4) Fetch with credentials same-origin first; if that fails (e.g., CORS), attempt unauthenticated fetch.
1824
+ * 5) Parse as JSON; if response body is text, parse the text as JSON.
1825
+ */
1826
+ async getPWAManifest(manifestUrl) {
1827
+ let href = manifestUrl;
1828
+ if (!href) {
1829
+ const el = document.querySelector(
1830
+ 'link[rel="manifest"]'
1831
+ );
1832
+ href = el?.getAttribute("href") || el?.href;
1833
+ }
1834
+ if (!href) return;
1835
+ href = this.ensureAbsoluteUrl(href);
1836
+ if (!href) return;
1837
+ if (href.startsWith("data:")) {
1838
+ try {
1839
+ const comma = href.indexOf(",");
1840
+ if (comma < 0) return;
1841
+ const meta = href.slice(5, comma);
1842
+ const data = href.slice(comma + 1);
1843
+ const isBase64 = /;base64/i.test(meta);
1844
+ const decoded = isBase64 ? atob(data) : decodeURIComponent(data);
1845
+ return JSON.parse(decoded);
1846
+ } catch {
1847
+ return;
1848
+ }
1849
+ }
1850
+ try {
1851
+ const res = await fetch(href, { credentials: "same-origin" });
1852
+ if (!res.ok) throw new Error(String(res.status));
1853
+ try {
1854
+ return await res.json();
1855
+ } catch {
1856
+ const t = await res.text();
1857
+ return JSON.parse(t);
1858
+ }
1859
+ } catch {
1860
+ try {
1861
+ const res = await fetch(href);
1862
+ if (!res.ok) return;
1863
+ try {
1864
+ return await res.json();
1865
+ } catch {
1866
+ const t = await res.text();
1867
+ return JSON.parse(t);
1868
+ }
1869
+ } catch {
1870
+ return;
1871
+ }
1872
+ }
1873
+ }
1616
1874
  };
1875
+ function sanitizeSceneOptionsUnits(val) {
1876
+ if (val?.defaultSize) {
1877
+ const keys = ["width", "height", "depth"];
1878
+ for (const k of keys) {
1879
+ if (k in val.defaultSize && !isValidSceneUnit(val.defaultSize[k])) {
1880
+ delete val.defaultSize[k];
1881
+ }
1882
+ }
1883
+ }
1884
+ if (val?.resizability) {
1885
+ const keys = ["minWidth", "minHeight", "maxWidth", "maxHeight"];
1886
+ for (const k of keys) {
1887
+ if (k in val.resizability && !isValidSceneUnit(val.resizability[k])) {
1888
+ delete val.resizability[k];
1889
+ }
1890
+ }
1891
+ }
1892
+ return val;
1893
+ }
1617
1894
  function pxToMeter(px) {
1618
- return px / 1360;
1895
+ return pointToPhysical(px);
1619
1896
  }
1620
1897
  function meterToPx(meter) {
1621
- return meter * 1360;
1898
+ return physicalToPoint(meter);
1622
1899
  }
1623
1900
  function formatToNumber(str, targetUnit, defaultUnit) {
1624
1901
  if (typeof str === "number") {
@@ -1668,11 +1945,10 @@ function formatSceneConfig(config, sceneType) {
1668
1945
  config.defaultSize[k] = formatToNumber(
1669
1946
  config.defaultSize[k],
1670
1947
  isWindow ? "px" : "m",
1671
- isWindow ? "px" : "m"
1948
+ "px"
1672
1949
  );
1673
1950
  } else {
1674
- ;
1675
- config.defaultSize[k] = defaultSceneConfig2.defaultSize[k];
1951
+ delete config.defaultSize[k];
1676
1952
  errors.push(`defaultSize.${k}`);
1677
1953
  }
1678
1954
  }
@@ -1686,11 +1962,10 @@ function formatSceneConfig(config, sceneType) {
1686
1962
  config.resizability[k] = formatToNumber(
1687
1963
  config.resizability[k],
1688
1964
  "px",
1689
- isWindow ? "px" : "m"
1965
+ "px"
1690
1966
  );
1691
1967
  } else {
1692
- ;
1693
- config.resizability[k] = void 0;
1968
+ delete config.resizability[k];
1694
1969
  errors.push(`resizability.${k}`);
1695
1970
  }
1696
1971
  }
@@ -1754,7 +2029,7 @@ function handleATag(event) {
1754
2029
  }
1755
2030
  }
1756
2031
  function getSceneDefaultConfig(sceneType) {
1757
- return sceneType === "window" ? defaultSceneConfig : defaultSceneConfigVolume;
2032
+ return sceneType === "window" ? xr_window_defaults || defaultSceneConfig : xr_volume_defaults || defaultSceneConfigVolume;
1758
2033
  }
1759
2034
  async function injectScenePolyfill() {
1760
2035
  if (!window.opener) return;
@@ -1768,13 +2043,14 @@ async function injectScenePolyfill() {
1768
2043
  }
1769
2044
  }
1770
2045
  onContentLoaded(async () => {
1771
- let provideDefaultSceneConfig = getSceneDefaultConfig(
1772
- window.xrCurrentSceneType ?? "window"
1773
- );
1774
- let cfg = provideDefaultSceneConfig;
2046
+ await SceneManager.getInstance().waitManifest();
2047
+ const sceneType = window.xrCurrentSceneType ?? "window";
2048
+ const rawDefault = getSceneDefaultConfig(sceneType);
2049
+ const pre = deepCloneJSON(rawDefault);
2050
+ let cfg = pre;
1775
2051
  if (typeof window.xrCurrentSceneDefaults === "function") {
1776
2052
  try {
1777
- cfg = await window.xrCurrentSceneDefaults?.(provideDefaultSceneConfig);
2053
+ cfg = await window.xrCurrentSceneDefaults?.(pre);
1778
2054
  } catch (error) {
1779
2055
  console.error(error);
1780
2056
  }
@@ -1784,17 +2060,18 @@ async function injectScenePolyfill() {
1784
2060
  resolve(null);
1785
2061
  }, 1e3);
1786
2062
  });
1787
- const sceneType = window.xrCurrentSceneType ?? "window";
1788
- const [formattedConfig, errors] = formatSceneConfig(cfg, sceneType);
2063
+ const mergedCfg = deepMergePlain(deepCloneJSON(rawDefault), cfg);
2064
+ const [formattedConfig, errors] = formatSceneConfig(mergedCfg, sceneType);
1789
2065
  if (errors.length > 0) {
1790
2066
  console.warn(
1791
2067
  `window.xrCurrentSceneDefaults with errors: ${errors.join(", ")}`
1792
2068
  );
1793
2069
  }
1794
- await SpatialScene.getInstance().updateSceneCreationConfig({
2070
+ const finalCfg = {
1795
2071
  ...formattedConfig,
1796
2072
  type: sceneType
1797
- });
2073
+ };
2074
+ await SpatialScene.getInstance().updateSceneCreationConfig(finalCfg);
1798
2075
  });
1799
2076
  }
1800
2077
  function injectSceneHook() {
@@ -2045,10 +2322,12 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
2045
2322
  * Registers the element to receive spatial events.
2046
2323
  * @param id Unique identifier for this element
2047
2324
  * @param modelURL URL of the 3D model
2325
+ * @param sources Optional fallback model sources
2048
2326
  */
2049
- constructor(id, modelURL) {
2327
+ constructor(id, modelURL, sources) {
2050
2328
  super(id);
2051
2329
  this.modelURL = modelURL;
2330
+ this.sources = sources;
2052
2331
  }
2053
2332
  /**
2054
2333
  * Promise resolver for the ready state.
@@ -2060,6 +2339,17 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
2060
2339
  * Used to reset the ready promise when the model URL changes.
2061
2340
  */
2062
2341
  modelURL;
2342
+ /**
2343
+ * Caches the last sources array to detect changes.
2344
+ */
2345
+ sources;
2346
+ /**
2347
+ * The model URL that was successfully loaded by the native runtime.
2348
+ */
2349
+ _currentSrc = "";
2350
+ get currentSrc() {
2351
+ return this._currentSrc;
2352
+ }
2063
2353
  /**
2064
2354
  * Creates a new promise for tracking the ready state of the model.
2065
2355
  * @returns Promise that resolves when the model is loaded (true) or fails to load (false)
@@ -2082,17 +2372,100 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
2082
2372
  * @returns Promise resolving when the update is complete
2083
2373
  */
2084
2374
  async updateProperties(properties) {
2375
+ let needsReadyReset = false;
2085
2376
  if (properties.modelURL !== void 0) {
2086
2377
  if (this.modelURL !== properties.modelURL) {
2087
2378
  this.modelURL = properties.modelURL;
2088
- this.ready = this.createReadyPromise();
2379
+ needsReadyReset = true;
2089
2380
  }
2090
2381
  }
2382
+ if (properties.sources !== void 0) {
2383
+ const prevJson = JSON.stringify(this.sources);
2384
+ const nextJson = JSON.stringify(properties.sources);
2385
+ if (prevJson !== nextJson) {
2386
+ this.sources = properties.sources;
2387
+ needsReadyReset = true;
2388
+ }
2389
+ }
2390
+ if (needsReadyReset) {
2391
+ this.ready = this.createReadyPromise();
2392
+ }
2393
+ if (properties.autoplay !== void 0) {
2394
+ this._autoplay = properties.autoplay;
2395
+ }
2396
+ if (properties.loop !== void 0) {
2397
+ this._loop = properties.loop;
2398
+ }
2399
+ if (properties.playbackRate !== void 0) {
2400
+ this._playbackRate = properties.playbackRate;
2401
+ }
2091
2402
  return new UpdateSpatializedStatic3DElementProperties(
2092
2403
  this,
2093
2404
  properties
2094
2405
  ).execute();
2095
2406
  }
2407
+ /**
2408
+ * Total animation duration in seconds, synced from native.
2409
+ */
2410
+ _duration = 0;
2411
+ /**
2412
+ * Returns the total animation duration in seconds.
2413
+ */
2414
+ get duration() {
2415
+ return this._duration;
2416
+ }
2417
+ /**
2418
+ * Playback speed multiplier.
2419
+ */
2420
+ _playbackRate = 1;
2421
+ /**
2422
+ * Returns the current playback rate.
2423
+ */
2424
+ get playbackRate() {
2425
+ return this._playbackRate;
2426
+ }
2427
+ /**
2428
+ * Sets the playback rate and sends it to native.
2429
+ */
2430
+ set playbackRate(value) {
2431
+ this.updateProperties({ playbackRate: value });
2432
+ }
2433
+ /**
2434
+ * Whether the animation is currently paused.
2435
+ */
2436
+ _paused = true;
2437
+ /**
2438
+ * Returns whether the animation is currently paused.
2439
+ */
2440
+ get paused() {
2441
+ return this._paused;
2442
+ }
2443
+ /**
2444
+ * Callback for animation state changes.
2445
+ */
2446
+ _onAnimationStateChangeCallback;
2447
+ /**
2448
+ * Sets the callback for animation state changes.
2449
+ */
2450
+ set onAnimationStateChangeCallback(callback) {
2451
+ this._onAnimationStateChangeCallback = callback;
2452
+ }
2453
+ /**
2454
+ * Starts or resumes animation playback.
2455
+ * @returns Promise resolving when the command is sent
2456
+ */
2457
+ async play() {
2458
+ this._paused = false;
2459
+ await this.updateProperties({ animationPaused: false });
2460
+ }
2461
+ /**
2462
+ * Pauses animation playback.
2463
+ * @returns Promise resolving when the command is sent
2464
+ */
2465
+ async pause() {
2466
+ this._paused = true;
2467
+ await this.updateProperties({ animationPaused: true });
2468
+ }
2096
2469
  /**
2097
2470
  * Processes events received from the WebSpatial environment.
2098
2471
  * Handles model loading events in addition to base spatial events.
@@ -2100,15 +2473,40 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
2100
2473
  */
2101
2474
  onReceiveEvent(data) {
2102
2475
  if (data.type === "modelloaded" /* modelloaded */) {
2476
+ this._currentSrc = data.detail?.src ?? this.modelURL ?? "";
2103
2477
  this._onLoadCallback?.();
2104
2478
  this._readyResolve?.(true);
2105
2479
  } else if (data.type === "modelloadfailed" /* modelloadfailed */) {
2106
2480
  this._onLoadFailureCallback?.();
2107
2481
  this._readyResolve?.(false);
2482
+ } else if (data.type === "animationstatechange" /* animationstatechange */) {
2483
+ this._paused = data.detail.paused;
2484
+ this._duration = data.detail.duration;
2485
+ this._onAnimationStateChangeCallback?.(data.detail);
2108
2486
  } else {
2109
2487
  super.onReceiveEvent(data);
2110
2488
  }
2111
2489
  }
2490
+ /**
2491
+ * Whether the model should automatically play its first animation on load.
2492
+ */
2493
+ _autoplay = false;
2494
+ /**
2495
+ * Returns whether autoplay is enabled for this element.
2496
+ */
2497
+ get autoplay() {
2498
+ return this._autoplay;
2499
+ }
2500
+ /**
2501
+ * Whether the model animation should loop continuously.
2502
+ */
2503
+ _loop = false;
2504
+ /**
2505
+ * Returns whether loop is enabled for this element.
2506
+ */
2507
+ get loop() {
2508
+ return this._loop;
2509
+ }
2112
2510
  /**
2113
2511
  * Callback function for successful model loading.
2114
2512
  */
@@ -2182,15 +2580,16 @@ async function createSpatialized2DElement() {
2182
2580
  return new Spatialized2DElement(id, windowProxy);
2183
2581
  }
2184
2582
  }
2185
- async function createSpatializedStatic3DElement(modelURL) {
2583
+ async function createSpatializedStatic3DElement(modelURL, sources) {
2186
2584
  const result = await new CreateSpatializedStatic3DElementCommand(
2187
- modelURL
2585
+ modelURL,
2586
+ sources
2188
2587
  ).execute();
2189
2588
  if (!result.success) {
2190
2589
  throw new Error("createSpatializedStatic3DElement failed");
2191
2590
  } else {
2192
2591
  const { id } = result.data;
2193
- return new SpatializedStatic3DElement(id, modelURL);
2592
+ return new SpatializedStatic3DElement(id, modelURL, sources);
2194
2593
  }
2195
2594
  }
2196
2595
  async function createSpatializedDynamic3DElement() {
@@ -2270,6 +2669,9 @@ var SpatialEntity = class extends SpatialObject {
2270
2669
  async addComponent(component) {
2271
2670
  return new AddComponentToEntityCommand(this, component).execute();
2272
2671
  }
2672
+ async removeComponent(component) {
2673
+ return new RemoveComponentFromEntityCommand(this, component).execute();
2674
+ }
2273
2675
  async setPosition(position) {
2274
2676
  return this.updateTransform({ position });
2275
2677
  }
@@ -2422,6 +2824,7 @@ var SpatialEntity = class extends SpatialObject {
2422
2824
  };
2423
2825
 
2424
2826
  // src/reality/entity/SpatialModelEntity.ts
2827
+ init_JSBCommand();
2425
2828
  var SpatialModelEntity = class extends SpatialEntity {
2426
2829
  constructor(id, options, userData) {
2427
2830
  super(id, userData);
@@ -2429,6 +2832,9 @@ var SpatialModelEntity = class extends SpatialEntity {
2429
2832
  this.options = options;
2430
2833
  this.userData = userData;
2431
2834
  }
2835
+ async setMaterials(materials) {
2836
+ return new SetMaterialsOnEntityCommand(this.id, materials).execute();
2837
+ }
2432
2838
  };
2433
2839
 
2434
2840
  // src/reality/component/SpatialComponent.ts
@@ -2631,8 +3037,8 @@ var SpatialSession = class {
2631
3037
  * @param modelURL Optional URL to the 3D model to load
2632
3038
  * @returns Promise resolving to a new SpatializedStatic3DElement instance
2633
3039
  */
2634
- createSpatializedStatic3DElement(modelURL) {
2635
- return createSpatializedStatic3DElement(modelURL);
3040
+ createSpatializedStatic3DElement(modelURL, sources) {
3041
+ return createSpatializedStatic3DElement(modelURL, sources);
2636
3042
  }
2637
3043
  /**
2638
3044
  * Initializes the spatial scene with custom configuration.
@@ -2767,7 +3173,7 @@ var Spatial = class {
2767
3173
  * @returns True if running in a spatial web environment, false otherwise
2768
3174
  */
2769
3175
  runInSpatialWeb() {
2770
- if (navigator.userAgent.indexOf("WebSpatial/") > 0) {
3176
+ if (navigator.userAgent.indexOf("WebSpatial/") >= 0) {
2771
3177
  return true;
2772
3178
  }
2773
3179
  return false;
@@ -2811,70 +3217,10 @@ var Spatial = class {
2811
3217
  * @returns Client SDK version string in format "x.x.x"
2812
3218
  */
2813
3219
  getClientVersion() {
2814
- return "1.4.0";
3220
+ return "1.6.0";
2815
3221
  }
2816
3222
  };
2817
3223
 
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
-
2878
3224
  // src/index.ts
2879
3225
  init_ssr_polyfill();
2880
3226