@webspatial/core-sdk 1.5.0 → 1.6.1
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 +29 -0
- package/dist/iife/index.d.ts +164 -12
- package/dist/iife/index.global.js +11 -11
- package/dist/iife/index.global.js.map +1 -1
- package/dist/index.d.ts +164 -12
- package/dist/index.js +415 -117
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/JSBCommand.ts +7 -2
- package/src/Spatial.ts +1 -1
- package/src/SpatialSession.ts +4 -2
- package/src/SpatializedElement.ts +10 -10
- package/src/SpatializedElementCreator.ts +5 -2
- package/src/SpatializedStatic3DElement.test.ts +15 -1
- package/src/SpatializedStatic3DElement.ts +167 -8
- package/src/WebMsgCommand.ts +22 -0
- package/src/scene-polyfill.manifest.test.ts +402 -0
- package/src/scene-polyfill.test.ts +168 -18
- package/src/scene-polyfill.ts +290 -46
- package/src/types/global.d.ts +3 -0
- package/src/types/types.ts +71 -0
- package/src/utils.ts +21 -0
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.
|
|
5
|
+
window.__webspatialsdk__['core-sdk-version'] = "1.6.1"
|
|
6
6
|
})()
|
|
7
7
|
|
|
8
8
|
var __defProp = Object.defineProperty;
|
|
@@ -824,6 +824,16 @@ function composeSRT(position, rotation, scale) {
|
|
|
824
824
|
m = m.scale(sx, sy, sz);
|
|
825
825
|
return m;
|
|
826
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
|
+
}
|
|
827
837
|
var init_utils = __esm({
|
|
828
838
|
"src/utils.ts"() {
|
|
829
839
|
"use strict";
|
|
@@ -1015,14 +1025,16 @@ var init_JSBCommand = __esm({
|
|
|
1015
1025
|
}
|
|
1016
1026
|
};
|
|
1017
1027
|
CreateSpatializedStatic3DElementCommand = class extends JSBCommand {
|
|
1018
|
-
constructor(modelURL) {
|
|
1028
|
+
constructor(modelURL, sources) {
|
|
1019
1029
|
super();
|
|
1020
1030
|
this.modelURL = modelURL;
|
|
1031
|
+
this.sources = sources;
|
|
1021
1032
|
this.modelURL = modelURL;
|
|
1033
|
+
this.sources = sources;
|
|
1022
1034
|
}
|
|
1023
1035
|
commandType = "CreateSpatializedStatic3DElement";
|
|
1024
1036
|
getParams() {
|
|
1025
|
-
return { modelURL: this.modelURL };
|
|
1037
|
+
return { modelURL: this.modelURL, sources: this.sources };
|
|
1026
1038
|
}
|
|
1027
1039
|
};
|
|
1028
1040
|
CreateSpatializedDynamic3DElementCommand = class extends JSBCommand {
|
|
@@ -1554,6 +1566,69 @@ var CubeInfo = class {
|
|
|
1554
1566
|
}
|
|
1555
1567
|
};
|
|
1556
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
|
+
|
|
1557
1632
|
// src/scene-polyfill.ts
|
|
1558
1633
|
var defaultSceneConfig = {
|
|
1559
1634
|
defaultSize: {
|
|
@@ -1563,15 +1638,47 @@ var defaultSceneConfig = {
|
|
|
1563
1638
|
};
|
|
1564
1639
|
var defaultSceneConfigVolume = {
|
|
1565
1640
|
defaultSize: {
|
|
1566
|
-
width: 0.
|
|
1567
|
-
height: 0.
|
|
1568
|
-
depth: 0.
|
|
1641
|
+
width: "0.94m",
|
|
1642
|
+
height: "0.94m",
|
|
1643
|
+
depth: "0.94m"
|
|
1569
1644
|
}
|
|
1570
1645
|
};
|
|
1646
|
+
var xr_window_defaults = {
|
|
1647
|
+
...defaultSceneConfig
|
|
1648
|
+
};
|
|
1649
|
+
var xr_volume_defaults = {
|
|
1650
|
+
...defaultSceneConfigVolume
|
|
1651
|
+
};
|
|
1571
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
|
+
}
|
|
1572
1678
|
var SceneManager = class _SceneManager {
|
|
1573
1679
|
originalOpen;
|
|
1574
1680
|
static instance;
|
|
1681
|
+
manifestReady = null;
|
|
1575
1682
|
static getInstance() {
|
|
1576
1683
|
if (!_SceneManager.instance) {
|
|
1577
1684
|
_SceneManager.instance = new _SceneManager();
|
|
@@ -1579,15 +1686,23 @@ var SceneManager = class _SceneManager {
|
|
|
1579
1686
|
return _SceneManager.instance;
|
|
1580
1687
|
}
|
|
1581
1688
|
init(window2) {
|
|
1689
|
+
this.manifestReady = this.setupManifest();
|
|
1582
1690
|
this.originalOpen = window2.open.bind(window2);
|
|
1583
1691
|
window2.open = this.open;
|
|
1584
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.
|
|
1585
1695
|
configMap = {};
|
|
1586
|
-
// name
|
|
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 = {};
|
|
1587
1699
|
getConfig(name) {
|
|
1588
1700
|
if (name === void 0 || !this.configMap[name]) return void 0;
|
|
1589
1701
|
return this.configMap[name];
|
|
1590
1702
|
}
|
|
1703
|
+
waitManifest() {
|
|
1704
|
+
return this.manifestReady ?? Promise.resolve();
|
|
1705
|
+
}
|
|
1591
1706
|
// Ensure URL is absolute; only convert when a relative path is provided
|
|
1592
1707
|
// - Keep external and special schemes untouched (http, https, data, blob, about, file, mailto, etc.)
|
|
1593
1708
|
// - Handle protocol-relative URLs (//example.com/path)
|
|
@@ -1606,14 +1721,41 @@ var SceneManager = class _SceneManager {
|
|
|
1606
1721
|
return raw;
|
|
1607
1722
|
}
|
|
1608
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
|
+
}
|
|
1609
1747
|
open = (url, target, features) => {
|
|
1610
1748
|
if (url?.startsWith(INTERNAL_SCHEMA_PREFIX)) {
|
|
1611
|
-
if (url.includes("createSpatialized2DElement")) {
|
|
1612
|
-
const token =
|
|
1749
|
+
if (url.includes("createSpatialized2DElement") || url.includes("createAttachment")) {
|
|
1750
|
+
const token = (
|
|
1751
|
+
//@ts-ignore
|
|
1752
|
+
(window.webSpatial || window.__webspatialShell__)?.genToken?.()
|
|
1753
|
+
);
|
|
1613
1754
|
if (token) {
|
|
1755
|
+
const command = url.includes("createAttachment") ? "createAttachment" : "createSpatialized2DElement";
|
|
1614
1756
|
const host = window.location.host;
|
|
1615
1757
|
const protocol = window.location.protocol;
|
|
1616
|
-
const finalURL = `${protocol}//${host}/${token}/?command
|
|
1758
|
+
const finalURL = `${protocol}//${host}/${token}/?command=${command}`;
|
|
1617
1759
|
const rid = new URL(url).searchParams.get("rid");
|
|
1618
1760
|
const final = new URL(finalURL);
|
|
1619
1761
|
if (rid) final.searchParams.set("rid", rid);
|
|
@@ -1627,7 +1769,12 @@ var SceneManager = class _SceneManager {
|
|
|
1627
1769
|
const newWindow = this.originalOpen(url, target, features);
|
|
1628
1770
|
return newWindow;
|
|
1629
1771
|
}
|
|
1630
|
-
|
|
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
|
+
}
|
|
1631
1778
|
const cmd = new createSpatialSceneCommand(url, cfg, target, features);
|
|
1632
1779
|
const result = cmd.executeSync();
|
|
1633
1780
|
const id = result.data?.id;
|
|
@@ -1639,23 +1786,116 @@ var SceneManager = class _SceneManager {
|
|
|
1639
1786
|
};
|
|
1640
1787
|
initScene(name, callback, options) {
|
|
1641
1788
|
const sceneType = options?.type ?? "window";
|
|
1642
|
-
const
|
|
1643
|
-
const
|
|
1644
|
-
|
|
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
|
+
);
|
|
1645
1805
|
if (errors.length > 0) {
|
|
1646
1806
|
console.warn(`initScene ${name} with errors: ${errors.join(", ")}`);
|
|
1647
1807
|
}
|
|
1808
|
+
this.callbackReturnMap[name] = sanitizedReturnVal;
|
|
1648
1809
|
this.configMap[name] = {
|
|
1649
1810
|
...formattedConfig,
|
|
1650
1811
|
type: sceneType
|
|
1651
1812
|
};
|
|
1652
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
|
+
}
|
|
1653
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
|
+
}
|
|
1654
1894
|
function pxToMeter(px) {
|
|
1655
|
-
return px
|
|
1895
|
+
return pointToPhysical(px);
|
|
1656
1896
|
}
|
|
1657
1897
|
function meterToPx(meter) {
|
|
1658
|
-
return meter
|
|
1898
|
+
return physicalToPoint(meter);
|
|
1659
1899
|
}
|
|
1660
1900
|
function formatToNumber(str, targetUnit, defaultUnit) {
|
|
1661
1901
|
if (typeof str === "number") {
|
|
@@ -1705,11 +1945,10 @@ function formatSceneConfig(config, sceneType) {
|
|
|
1705
1945
|
config.defaultSize[k] = formatToNumber(
|
|
1706
1946
|
config.defaultSize[k],
|
|
1707
1947
|
isWindow ? "px" : "m",
|
|
1708
|
-
|
|
1948
|
+
"px"
|
|
1709
1949
|
);
|
|
1710
1950
|
} else {
|
|
1711
|
-
;
|
|
1712
|
-
config.defaultSize[k] = defaultSceneConfig2.defaultSize[k];
|
|
1951
|
+
delete config.defaultSize[k];
|
|
1713
1952
|
errors.push(`defaultSize.${k}`);
|
|
1714
1953
|
}
|
|
1715
1954
|
}
|
|
@@ -1723,11 +1962,10 @@ function formatSceneConfig(config, sceneType) {
|
|
|
1723
1962
|
config.resizability[k] = formatToNumber(
|
|
1724
1963
|
config.resizability[k],
|
|
1725
1964
|
"px",
|
|
1726
|
-
|
|
1965
|
+
"px"
|
|
1727
1966
|
);
|
|
1728
1967
|
} else {
|
|
1729
|
-
;
|
|
1730
|
-
config.resizability[k] = void 0;
|
|
1968
|
+
delete config.resizability[k];
|
|
1731
1969
|
errors.push(`resizability.${k}`);
|
|
1732
1970
|
}
|
|
1733
1971
|
}
|
|
@@ -1761,15 +1999,14 @@ function hijackWindowOpen(window2) {
|
|
|
1761
1999
|
function hijackWindowATag(openedWindow) {
|
|
1762
2000
|
openedWindow.document.onclick = function(e) {
|
|
1763
2001
|
let element = e.target;
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
if (handleATag(e)) {
|
|
2002
|
+
while (element) {
|
|
2003
|
+
if (element.tagName == "A") {
|
|
2004
|
+
if (handleATag(e, element)) {
|
|
1768
2005
|
return false;
|
|
1769
2006
|
}
|
|
1770
2007
|
return true;
|
|
1771
2008
|
}
|
|
1772
|
-
if (element
|
|
2009
|
+
if (element.parentElement) {
|
|
1773
2010
|
element = element.parentElement;
|
|
1774
2011
|
} else {
|
|
1775
2012
|
break;
|
|
@@ -1777,21 +2014,18 @@ function hijackWindowATag(openedWindow) {
|
|
|
1777
2014
|
}
|
|
1778
2015
|
};
|
|
1779
2016
|
}
|
|
1780
|
-
function handleATag(event) {
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
window.open(url, target);
|
|
1789
|
-
return true;
|
|
1790
|
-
}
|
|
2017
|
+
function handleATag(event, link) {
|
|
2018
|
+
if (event.defaultPrevented) return false;
|
|
2019
|
+
const target = link.target;
|
|
2020
|
+
const url = link.href;
|
|
2021
|
+
if (target && target !== "_self") {
|
|
2022
|
+
event.preventDefault();
|
|
2023
|
+
window.open(url, target);
|
|
2024
|
+
return true;
|
|
1791
2025
|
}
|
|
1792
2026
|
}
|
|
1793
2027
|
function getSceneDefaultConfig(sceneType) {
|
|
1794
|
-
return sceneType === "window" ? defaultSceneConfig : defaultSceneConfigVolume;
|
|
2028
|
+
return sceneType === "window" ? xr_window_defaults || defaultSceneConfig : xr_volume_defaults || defaultSceneConfigVolume;
|
|
1795
2029
|
}
|
|
1796
2030
|
async function injectScenePolyfill() {
|
|
1797
2031
|
if (!window.opener) return;
|
|
@@ -1805,13 +2039,14 @@ async function injectScenePolyfill() {
|
|
|
1805
2039
|
}
|
|
1806
2040
|
}
|
|
1807
2041
|
onContentLoaded(async () => {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
);
|
|
1811
|
-
|
|
2042
|
+
await SceneManager.getInstance().waitManifest();
|
|
2043
|
+
const sceneType = window.xrCurrentSceneType ?? "window";
|
|
2044
|
+
const rawDefault = getSceneDefaultConfig(sceneType);
|
|
2045
|
+
const pre = deepCloneJSON(rawDefault);
|
|
2046
|
+
let cfg = pre;
|
|
1812
2047
|
if (typeof window.xrCurrentSceneDefaults === "function") {
|
|
1813
2048
|
try {
|
|
1814
|
-
cfg = await window.xrCurrentSceneDefaults?.(
|
|
2049
|
+
cfg = await window.xrCurrentSceneDefaults?.(pre);
|
|
1815
2050
|
} catch (error) {
|
|
1816
2051
|
console.error(error);
|
|
1817
2052
|
}
|
|
@@ -1821,17 +2056,18 @@ async function injectScenePolyfill() {
|
|
|
1821
2056
|
resolve(null);
|
|
1822
2057
|
}, 1e3);
|
|
1823
2058
|
});
|
|
1824
|
-
const
|
|
1825
|
-
const [formattedConfig, errors] = formatSceneConfig(
|
|
2059
|
+
const mergedCfg = deepMergePlain(deepCloneJSON(rawDefault), cfg);
|
|
2060
|
+
const [formattedConfig, errors] = formatSceneConfig(mergedCfg, sceneType);
|
|
1826
2061
|
if (errors.length > 0) {
|
|
1827
2062
|
console.warn(
|
|
1828
2063
|
`window.xrCurrentSceneDefaults with errors: ${errors.join(", ")}`
|
|
1829
2064
|
);
|
|
1830
2065
|
}
|
|
1831
|
-
|
|
2066
|
+
const finalCfg = {
|
|
1832
2067
|
...formattedConfig,
|
|
1833
2068
|
type: sceneType
|
|
1834
|
-
}
|
|
2069
|
+
};
|
|
2070
|
+
await SpatialScene.getInstance().updateSceneCreationConfig(finalCfg);
|
|
1835
2071
|
});
|
|
1836
2072
|
}
|
|
1837
2073
|
function injectSceneHook() {
|
|
@@ -2082,10 +2318,12 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2082
2318
|
* Registers the element to receive spatial events.
|
|
2083
2319
|
* @param id Unique identifier for this element
|
|
2084
2320
|
* @param modelURL URL of the 3D model
|
|
2321
|
+
* @param sources Optional fallback model sources
|
|
2085
2322
|
*/
|
|
2086
|
-
constructor(id, modelURL) {
|
|
2323
|
+
constructor(id, modelURL, sources) {
|
|
2087
2324
|
super(id);
|
|
2088
2325
|
this.modelURL = modelURL;
|
|
2326
|
+
this.sources = sources;
|
|
2089
2327
|
}
|
|
2090
2328
|
/**
|
|
2091
2329
|
* Promise resolver for the ready state.
|
|
@@ -2097,6 +2335,17 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2097
2335
|
* Used to reset the ready promise when the model URL changes.
|
|
2098
2336
|
*/
|
|
2099
2337
|
modelURL;
|
|
2338
|
+
/**
|
|
2339
|
+
* Caches the last sources array to detect changes.
|
|
2340
|
+
*/
|
|
2341
|
+
sources;
|
|
2342
|
+
/**
|
|
2343
|
+
* The model URL that was successfully loaded by the native runtime.
|
|
2344
|
+
*/
|
|
2345
|
+
_currentSrc = "";
|
|
2346
|
+
get currentSrc() {
|
|
2347
|
+
return this._currentSrc;
|
|
2348
|
+
}
|
|
2100
2349
|
/**
|
|
2101
2350
|
* Creates a new promise for tracking the ready state of the model.
|
|
2102
2351
|
* @returns Promise that resolves when the model is loaded (true) or fails to load (false)
|
|
@@ -2119,17 +2368,100 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2119
2368
|
* @returns Promise resolving when the update is complete
|
|
2120
2369
|
*/
|
|
2121
2370
|
async updateProperties(properties) {
|
|
2371
|
+
let needsReadyReset = false;
|
|
2122
2372
|
if (properties.modelURL !== void 0) {
|
|
2123
2373
|
if (this.modelURL !== properties.modelURL) {
|
|
2124
2374
|
this.modelURL = properties.modelURL;
|
|
2125
|
-
|
|
2375
|
+
needsReadyReset = true;
|
|
2126
2376
|
}
|
|
2127
2377
|
}
|
|
2378
|
+
if (properties.sources !== void 0) {
|
|
2379
|
+
const prevJson = JSON.stringify(this.sources);
|
|
2380
|
+
const nextJson = JSON.stringify(properties.sources);
|
|
2381
|
+
if (prevJson !== nextJson) {
|
|
2382
|
+
this.sources = properties.sources;
|
|
2383
|
+
needsReadyReset = true;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
if (needsReadyReset) {
|
|
2387
|
+
this.ready = this.createReadyPromise();
|
|
2388
|
+
}
|
|
2389
|
+
if (properties.autoplay !== void 0) {
|
|
2390
|
+
this._autoplay = properties.autoplay;
|
|
2391
|
+
}
|
|
2392
|
+
if (properties.loop !== void 0) {
|
|
2393
|
+
this._loop = properties.loop;
|
|
2394
|
+
}
|
|
2395
|
+
if (properties.playbackRate !== void 0) {
|
|
2396
|
+
this._playbackRate = properties.playbackRate;
|
|
2397
|
+
}
|
|
2128
2398
|
return new UpdateSpatializedStatic3DElementProperties(
|
|
2129
2399
|
this,
|
|
2130
2400
|
properties
|
|
2131
2401
|
).execute();
|
|
2132
2402
|
}
|
|
2403
|
+
/**
|
|
2404
|
+
* Total animation duration in seconds, synced from native.
|
|
2405
|
+
*/
|
|
2406
|
+
_duration = 0;
|
|
2407
|
+
/**
|
|
2408
|
+
* Returns the total animation duration in seconds.
|
|
2409
|
+
*/
|
|
2410
|
+
get duration() {
|
|
2411
|
+
return this._duration;
|
|
2412
|
+
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Playback speed multiplier.
|
|
2415
|
+
*/
|
|
2416
|
+
_playbackRate = 1;
|
|
2417
|
+
/**
|
|
2418
|
+
* Returns the current playback rate.
|
|
2419
|
+
*/
|
|
2420
|
+
get playbackRate() {
|
|
2421
|
+
return this._playbackRate;
|
|
2422
|
+
}
|
|
2423
|
+
/**
|
|
2424
|
+
* Sets the playback rate and sends it to native.
|
|
2425
|
+
*/
|
|
2426
|
+
set playbackRate(value) {
|
|
2427
|
+
this.updateProperties({ playbackRate: value });
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Whether the animation is currently paused.
|
|
2431
|
+
*/
|
|
2432
|
+
_paused = true;
|
|
2433
|
+
/**
|
|
2434
|
+
* Returns whether the animation is currently paused.
|
|
2435
|
+
*/
|
|
2436
|
+
get paused() {
|
|
2437
|
+
return this._paused;
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Callback for animation state changes.
|
|
2441
|
+
*/
|
|
2442
|
+
_onAnimationStateChangeCallback;
|
|
2443
|
+
/**
|
|
2444
|
+
* Sets the callback for animation state changes.
|
|
2445
|
+
*/
|
|
2446
|
+
set onAnimationStateChangeCallback(callback) {
|
|
2447
|
+
this._onAnimationStateChangeCallback = callback;
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Starts or resumes animation playback.
|
|
2451
|
+
* @returns Promise resolving when the command is sent
|
|
2452
|
+
*/
|
|
2453
|
+
async play() {
|
|
2454
|
+
this._paused = false;
|
|
2455
|
+
await this.updateProperties({ animationPaused: false });
|
|
2456
|
+
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Pauses animation playback.
|
|
2459
|
+
* @returns Promise resolving when the command is sent
|
|
2460
|
+
*/
|
|
2461
|
+
async pause() {
|
|
2462
|
+
this._paused = true;
|
|
2463
|
+
await this.updateProperties({ animationPaused: true });
|
|
2464
|
+
}
|
|
2133
2465
|
/**
|
|
2134
2466
|
* Processes events received from the WebSpatial environment.
|
|
2135
2467
|
* Handles model loading events in addition to base spatial events.
|
|
@@ -2137,15 +2469,40 @@ var SpatializedStatic3DElement = class extends SpatializedElement {
|
|
|
2137
2469
|
*/
|
|
2138
2470
|
onReceiveEvent(data) {
|
|
2139
2471
|
if (data.type === "modelloaded" /* modelloaded */) {
|
|
2472
|
+
this._currentSrc = data.detail?.src ?? this.modelURL ?? "";
|
|
2140
2473
|
this._onLoadCallback?.();
|
|
2141
2474
|
this._readyResolve?.(true);
|
|
2142
2475
|
} else if (data.type === "modelloadfailed" /* modelloadfailed */) {
|
|
2143
2476
|
this._onLoadFailureCallback?.();
|
|
2144
2477
|
this._readyResolve?.(false);
|
|
2478
|
+
} else if (data.type === "animationstatechange" /* animationstatechange */) {
|
|
2479
|
+
this._paused = data.detail.paused;
|
|
2480
|
+
this._duration = data.detail.duration;
|
|
2481
|
+
this._onAnimationStateChangeCallback?.(data.detail);
|
|
2145
2482
|
} else {
|
|
2146
2483
|
super.onReceiveEvent(data);
|
|
2147
2484
|
}
|
|
2148
2485
|
}
|
|
2486
|
+
/**
|
|
2487
|
+
* Whether the model should automatically play its first animation on load.
|
|
2488
|
+
*/
|
|
2489
|
+
_autoplay = false;
|
|
2490
|
+
/**
|
|
2491
|
+
* Returns whether autoplay is enabled for this element.
|
|
2492
|
+
*/
|
|
2493
|
+
get autoplay() {
|
|
2494
|
+
return this._autoplay;
|
|
2495
|
+
}
|
|
2496
|
+
/**
|
|
2497
|
+
* Whether the model animation should loop continuously.
|
|
2498
|
+
*/
|
|
2499
|
+
_loop = false;
|
|
2500
|
+
/**
|
|
2501
|
+
* Returns whether loop is enabled for this element.
|
|
2502
|
+
*/
|
|
2503
|
+
get loop() {
|
|
2504
|
+
return this._loop;
|
|
2505
|
+
}
|
|
2149
2506
|
/**
|
|
2150
2507
|
* Callback function for successful model loading.
|
|
2151
2508
|
*/
|
|
@@ -2219,15 +2576,16 @@ async function createSpatialized2DElement() {
|
|
|
2219
2576
|
return new Spatialized2DElement(id, windowProxy);
|
|
2220
2577
|
}
|
|
2221
2578
|
}
|
|
2222
|
-
async function createSpatializedStatic3DElement(modelURL) {
|
|
2579
|
+
async function createSpatializedStatic3DElement(modelURL, sources) {
|
|
2223
2580
|
const result = await new CreateSpatializedStatic3DElementCommand(
|
|
2224
|
-
modelURL
|
|
2581
|
+
modelURL,
|
|
2582
|
+
sources
|
|
2225
2583
|
).execute();
|
|
2226
2584
|
if (!result.success) {
|
|
2227
2585
|
throw new Error("createSpatializedStatic3DElement failed");
|
|
2228
2586
|
} else {
|
|
2229
2587
|
const { id } = result.data;
|
|
2230
|
-
return new SpatializedStatic3DElement(id, modelURL);
|
|
2588
|
+
return new SpatializedStatic3DElement(id, modelURL, sources);
|
|
2231
2589
|
}
|
|
2232
2590
|
}
|
|
2233
2591
|
async function createSpatializedDynamic3DElement() {
|
|
@@ -2675,8 +3033,8 @@ var SpatialSession = class {
|
|
|
2675
3033
|
* @param modelURL Optional URL to the 3D model to load
|
|
2676
3034
|
* @returns Promise resolving to a new SpatializedStatic3DElement instance
|
|
2677
3035
|
*/
|
|
2678
|
-
createSpatializedStatic3DElement(modelURL) {
|
|
2679
|
-
return createSpatializedStatic3DElement(modelURL);
|
|
3036
|
+
createSpatializedStatic3DElement(modelURL, sources) {
|
|
3037
|
+
return createSpatializedStatic3DElement(modelURL, sources);
|
|
2680
3038
|
}
|
|
2681
3039
|
/**
|
|
2682
3040
|
* Initializes the spatial scene with custom configuration.
|
|
@@ -2811,7 +3169,7 @@ var Spatial = class {
|
|
|
2811
3169
|
* @returns True if running in a spatial web environment, false otherwise
|
|
2812
3170
|
*/
|
|
2813
3171
|
runInSpatialWeb() {
|
|
2814
|
-
if (navigator.userAgent.indexOf("WebSpatial/")
|
|
3172
|
+
if (navigator.userAgent.indexOf("WebSpatial/") >= 0) {
|
|
2815
3173
|
return true;
|
|
2816
3174
|
}
|
|
2817
3175
|
return false;
|
|
@@ -2855,70 +3213,10 @@ var Spatial = class {
|
|
|
2855
3213
|
* @returns Client SDK version string in format "x.x.x"
|
|
2856
3214
|
*/
|
|
2857
3215
|
getClientVersion() {
|
|
2858
|
-
return "1.
|
|
3216
|
+
return "1.6.1";
|
|
2859
3217
|
}
|
|
2860
3218
|
};
|
|
2861
3219
|
|
|
2862
|
-
// src/physicalMetrics.ts
|
|
2863
|
-
var physicalMetrics_exports = {};
|
|
2864
|
-
__export(physicalMetrics_exports, {
|
|
2865
|
-
getValue: () => getValue,
|
|
2866
|
-
physicalToPoint: () => physicalToPoint,
|
|
2867
|
-
pointToPhysical: () => pointToPhysical,
|
|
2868
|
-
subscribe: () => subscribe
|
|
2869
|
-
});
|
|
2870
|
-
init_SpatialWebEvent();
|
|
2871
|
-
var snapshot = {
|
|
2872
|
-
meterToPtUnscaled: 1360,
|
|
2873
|
-
meterToPtScaled: 1360
|
|
2874
|
-
};
|
|
2875
|
-
function getWorldScalingCompensation(options) {
|
|
2876
|
-
return options?.worldScalingCompensation ?? "scaled";
|
|
2877
|
-
}
|
|
2878
|
-
function pointToPhysical(point, options) {
|
|
2879
|
-
updateValue();
|
|
2880
|
-
const compensation = getWorldScalingCompensation(options);
|
|
2881
|
-
if (compensation === "unscaled") {
|
|
2882
|
-
return point / snapshot.meterToPtUnscaled;
|
|
2883
|
-
}
|
|
2884
|
-
return point / snapshot.meterToPtScaled;
|
|
2885
|
-
}
|
|
2886
|
-
function physicalToPoint(physical, options) {
|
|
2887
|
-
updateValue();
|
|
2888
|
-
const compensation = getWorldScalingCompensation(options);
|
|
2889
|
-
if (compensation === "unscaled") {
|
|
2890
|
-
return physical * snapshot.meterToPtUnscaled;
|
|
2891
|
-
}
|
|
2892
|
-
return physical * snapshot.meterToPtScaled;
|
|
2893
|
-
}
|
|
2894
|
-
function updateValue() {
|
|
2895
|
-
if (typeof window === "undefined") return;
|
|
2896
|
-
const src = window.__webspatialsdk__?.physicalMetrics;
|
|
2897
|
-
if (!src) return;
|
|
2898
|
-
const next = {
|
|
2899
|
-
meterToPtScaled: src.meterToPtScaled ?? snapshot.meterToPtScaled,
|
|
2900
|
-
meterToPtUnscaled: src.meterToPtUnscaled ?? snapshot.meterToPtUnscaled
|
|
2901
|
-
};
|
|
2902
|
-
if (next.meterToPtScaled !== snapshot.meterToPtScaled || next.meterToPtUnscaled !== snapshot.meterToPtUnscaled) {
|
|
2903
|
-
snapshot = next;
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
function getValue() {
|
|
2907
|
-
updateValue();
|
|
2908
|
-
return snapshot;
|
|
2909
|
-
}
|
|
2910
|
-
function subscribe(cb) {
|
|
2911
|
-
if (typeof window === "undefined") return () => {
|
|
2912
|
-
};
|
|
2913
|
-
const handler = () => {
|
|
2914
|
-
cb();
|
|
2915
|
-
};
|
|
2916
|
-
SpatialWebEvent.addEventReceiver("window", handler);
|
|
2917
|
-
return () => {
|
|
2918
|
-
SpatialWebEvent.removeEventReceiver("window");
|
|
2919
|
-
};
|
|
2920
|
-
}
|
|
2921
|
-
|
|
2922
3220
|
// src/index.ts
|
|
2923
3221
|
init_ssr_polyfill();
|
|
2924
3222
|
|