@webspatial/core-sdk 1.0.3 → 1.0.5
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 +4 -0
- package/README.md +112 -81
- package/dist/iife/index.d.ts +683 -561
- package/dist/iife/index.global.js +3 -4
- package/dist/iife/index.global.js.map +1 -1
- package/dist/index.d.ts +683 -561
- package/dist/index.js +2175 -1291
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
- package/src/JSBCommand.ts +631 -0
- package/src/Spatial.ts +68 -0
- package/src/SpatialObject.ts +46 -0
- package/src/SpatialScene.ts +75 -0
- package/src/SpatialSession.ts +187 -0
- package/src/SpatialWebEvent.ts +23 -0
- package/src/SpatialWebEventCreator.ts +12 -0
- package/src/Spatialized2DElement.ts +51 -0
- package/src/SpatializedDynamic3DElement.ts +30 -0
- package/src/SpatializedElement.ts +331 -0
- package/src/SpatializedElementCreator.ts +45 -0
- package/src/SpatializedStatic3DElement.ts +111 -0
- package/src/WebMsgCommand.ts +88 -0
- package/src/index.ts +23 -1
- package/src/platform-adapter/CommandResultUtils.ts +22 -0
- package/src/platform-adapter/android/AndroidPlatform.ts +133 -0
- package/src/platform-adapter/index.ts +21 -0
- package/src/platform-adapter/interface.ts +36 -0
- package/src/platform-adapter/ssr/SSRPlatform.ts +43 -0
- package/src/platform-adapter/vision-os/VisionOSPlatform.ts +77 -0
- package/src/reality/component/ModelComponent.ts +11 -0
- package/src/reality/component/SpatialComponent.ts +17 -0
- package/src/reality/component/index.ts +2 -0
- package/src/reality/entity/SpatialEntity.ts +255 -0
- package/src/reality/entity/SpatialModelEntity.ts +15 -0
- package/src/reality/entity/index.ts +2 -0
- package/src/reality/geometry/SpatialBoxGeometry.ts +12 -0
- package/src/reality/geometry/SpatialConeGeometry.ts +15 -0
- package/src/reality/geometry/SpatialCylinderGeometry.ts +15 -0
- package/src/reality/geometry/SpatialGeometry.ts +12 -0
- package/src/reality/geometry/SpatialPlaneGeometry.ts +15 -0
- package/src/reality/geometry/SpatialSphereGeometry.ts +15 -0
- package/src/reality/geometry/index.ts +6 -0
- package/src/reality/index.ts +5 -0
- package/src/reality/material/SpatialMaterial.ts +14 -0
- package/src/reality/material/SpatialUnlitMaterial.ts +16 -0
- package/src/reality/material/index.ts +2 -0
- package/src/reality/realityCreator.ts +94 -0
- package/src/reality/resource/SpatialModelAsset.ts +11 -0
- package/src/reality/resource/index.ts +1 -0
- package/src/scene-polyfill.test.ts +376 -0
- package/src/scene-polyfill.ts +359 -0
- package/src/spatial-window-polyfill.ts +182 -0
- package/src/ssr-polyfill.ts +3 -0
- package/src/types/global.d.ts +33 -1
- package/src/types/internal.ts +13 -0
- package/src/types/types.ts +380 -0
- package/src/utils.ts +61 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +8 -0
- package/src/core/Spatial.ts +0 -50
- package/src/core/SpatialEntity.ts +0 -147
- package/src/core/SpatialHelper.ts +0 -230
- package/src/core/SpatialObject.ts +0 -26
- package/src/core/SpatialSession.ts +0 -457
- package/src/core/SpatialTransform.ts +0 -26
- package/src/core/SpatialWindowContainer.ts +0 -59
- package/src/core/component/EventSpatialComponent.ts +0 -32
- package/src/core/component/SpatialComponent.ts +0 -26
- package/src/core/component/SpatialInputComponent.ts +0 -24
- package/src/core/component/SpatialModel3DComponent.ts +0 -223
- package/src/core/component/SpatialModelComponent.ts +0 -39
- package/src/core/component/SpatialViewComponent.ts +0 -32
- package/src/core/component/SpatialWindowComponent.ts +0 -177
- package/src/core/component/index.ts +0 -14
- package/src/core/index.ts +0 -10
- package/src/core/private/WebSpatial.ts +0 -383
- package/src/core/private/remote-command/RemoteCommand.ts +0 -15
- package/src/core/private/remote-command/index.ts +0 -1
- package/src/core/resource/SpatialMeshResource.ts +0 -6
- package/src/core/resource/SpatialPhysicallyBasedMaterialResource.ts +0 -42
- package/src/core/resource/index.ts +0 -2
- package/src/core/types.ts +0 -32
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { createSpatialSceneCommand, FocusScene } from './JSBCommand'
|
|
2
|
+
import { SpatialScene } from './SpatialScene'
|
|
3
|
+
import {
|
|
4
|
+
SpatialSceneCreationOptions,
|
|
5
|
+
SpatialSceneType,
|
|
6
|
+
SpatialSceneState,
|
|
7
|
+
isValidSceneUnit,
|
|
8
|
+
isValidSpatialSceneType,
|
|
9
|
+
isValidWorldScalingType,
|
|
10
|
+
isValidWorldAlignmentType,
|
|
11
|
+
isValidBaseplateVisibilityType,
|
|
12
|
+
} from './types/types'
|
|
13
|
+
import { SpatialSceneCreationOptionsInternal } from './types/internal'
|
|
14
|
+
|
|
15
|
+
const defaultSceneConfig: SpatialSceneCreationOptions = {
|
|
16
|
+
defaultSize: {
|
|
17
|
+
width: 1280,
|
|
18
|
+
height: 720,
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const defaultSceneConfigVolume: SpatialSceneCreationOptions = {
|
|
23
|
+
defaultSize: {
|
|
24
|
+
width: 0.94,
|
|
25
|
+
height: 0.94,
|
|
26
|
+
depth: 0.94,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const INTERNAL_SCHEMA_PREFIX = 'webspatial://'
|
|
31
|
+
|
|
32
|
+
class SceneManager {
|
|
33
|
+
private originalOpen: any
|
|
34
|
+
private static instance: SceneManager
|
|
35
|
+
static getInstance() {
|
|
36
|
+
if (!SceneManager.instance) {
|
|
37
|
+
SceneManager.instance = new SceneManager()
|
|
38
|
+
}
|
|
39
|
+
return SceneManager.instance
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
init(window: WindowProxy) {
|
|
43
|
+
this.originalOpen = window.open.bind(window)
|
|
44
|
+
;(window as any).open = this.open
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private configMap: Record<string, SpatialSceneCreationOptionsInternal> = {} // name=>config
|
|
48
|
+
private getConfig(name?: string) {
|
|
49
|
+
if (name === undefined || !this.configMap[name]) return undefined
|
|
50
|
+
return this.configMap[name]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private open = (url?: string, target?: string, features?: string) => {
|
|
54
|
+
// bypass internal
|
|
55
|
+
if (url?.startsWith(INTERNAL_SCHEMA_PREFIX)) {
|
|
56
|
+
return this.originalOpen(url, target, features)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// absolute url
|
|
60
|
+
const prefix = `${window.location.protocol}//${window.location.host}`
|
|
61
|
+
if (!url?.startsWith(prefix)) {
|
|
62
|
+
url = prefix + url
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// if target is special
|
|
66
|
+
if (target === '_self' || target === '_parent' || target === '_top') {
|
|
67
|
+
const newWindow = this.originalOpen(url, target, features)
|
|
68
|
+
return newWindow
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cfg = target ? this.getConfig(target) : undefined
|
|
72
|
+
const cmd = new createSpatialSceneCommand(url!, cfg, target, features)
|
|
73
|
+
const result = cmd.executeSync()
|
|
74
|
+
|
|
75
|
+
if (typeof target === 'string' && this.configMap[target]) {
|
|
76
|
+
delete this.configMap[target]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const id = result.data?.id
|
|
80
|
+
|
|
81
|
+
if (id) {
|
|
82
|
+
// send JSB to focus
|
|
83
|
+
let focusCmd = new FocusScene(id)
|
|
84
|
+
focusCmd.execute()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result.data?.windowProxy
|
|
88
|
+
}
|
|
89
|
+
initScene(
|
|
90
|
+
name: string,
|
|
91
|
+
callback: (pre: SpatialSceneCreationOptions) => SpatialSceneCreationOptions,
|
|
92
|
+
options?: { type: SpatialSceneType },
|
|
93
|
+
) {
|
|
94
|
+
const sceneType = options?.type ?? 'window'
|
|
95
|
+
const defaultConfig = getSceneDefaultConfig(sceneType)
|
|
96
|
+
const rawReturnVal = callback({ ...defaultConfig })
|
|
97
|
+
const [formattedConfig, errors] = formatSceneConfig(rawReturnVal, sceneType)
|
|
98
|
+
if (errors.length > 0) {
|
|
99
|
+
console.warn(`initScene ${name} with errors: ${errors.join(', ')}`)
|
|
100
|
+
}
|
|
101
|
+
this.configMap[name] = {
|
|
102
|
+
...formattedConfig,
|
|
103
|
+
type: sceneType,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function pxToMeter(px: number): number {
|
|
109
|
+
return px / 1360
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function meterToPx(meter: number): number {
|
|
113
|
+
return meter * 1360
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function formatToNumber(
|
|
117
|
+
str: string | number,
|
|
118
|
+
targetUnit: 'px' | 'm',
|
|
119
|
+
defaultUnit: 'px' | 'm',
|
|
120
|
+
): number {
|
|
121
|
+
if (typeof str === 'number') {
|
|
122
|
+
if (
|
|
123
|
+
(defaultUnit === 'px' && targetUnit === 'px') ||
|
|
124
|
+
(defaultUnit === 'm' && targetUnit === 'm')
|
|
125
|
+
) {
|
|
126
|
+
return str
|
|
127
|
+
}
|
|
128
|
+
// unit not match target
|
|
129
|
+
if (defaultUnit === 'px' && targetUnit === 'm') {
|
|
130
|
+
return pxToMeter(str)
|
|
131
|
+
} else if (defaultUnit === 'm' && targetUnit === 'px') {
|
|
132
|
+
return meterToPx(str)
|
|
133
|
+
}
|
|
134
|
+
// fallback
|
|
135
|
+
return str
|
|
136
|
+
}
|
|
137
|
+
if (targetUnit === 'm') {
|
|
138
|
+
if (str.endsWith('m')) {
|
|
139
|
+
// 1m
|
|
140
|
+
return Number(str.slice(0, -1))
|
|
141
|
+
} else if (str.endsWith('px')) {
|
|
142
|
+
// 100px
|
|
143
|
+
return pxToMeter(Number(str.slice(0, -2)))
|
|
144
|
+
} else {
|
|
145
|
+
throw new Error('formatToNumber: invalid str')
|
|
146
|
+
}
|
|
147
|
+
} else if (targetUnit === 'px') {
|
|
148
|
+
if (str.endsWith('px')) {
|
|
149
|
+
// 100px
|
|
150
|
+
return Number(str.slice(0, -2))
|
|
151
|
+
} else if (str.endsWith('m')) {
|
|
152
|
+
// 1m
|
|
153
|
+
return meterToPx(Number(str.slice(0, -1)))
|
|
154
|
+
} else {
|
|
155
|
+
throw new Error('formatToNumber: invalid str')
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
throw new Error('formatToNumber: invalid targetUnit')
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function formatSceneConfig(
|
|
163
|
+
config: SpatialSceneCreationOptions,
|
|
164
|
+
sceneType: SpatialSceneType,
|
|
165
|
+
): [SpatialSceneCreationOptions, string[]] {
|
|
166
|
+
// defaultSize and resizability's width/height/depth can be 100 or "100px" or "1m"
|
|
167
|
+
// expect:
|
|
168
|
+
// resizability should format into px
|
|
169
|
+
// defaultSize should format into px if window
|
|
170
|
+
// defaultSize should format into m if volume
|
|
171
|
+
|
|
172
|
+
const defaultSceneConfig = getSceneDefaultConfig(sceneType)
|
|
173
|
+
|
|
174
|
+
const errors: string[] = []
|
|
175
|
+
|
|
176
|
+
const isWindow = sceneType === 'window'
|
|
177
|
+
if (!isValidSpatialSceneType(sceneType)) {
|
|
178
|
+
errors.push(`sceneType`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// format defaultSize
|
|
182
|
+
if (config.defaultSize) {
|
|
183
|
+
const iterKeys = ['width', 'height', 'depth']
|
|
184
|
+
for (let k of iterKeys) {
|
|
185
|
+
if (!(k in config.defaultSize)) continue
|
|
186
|
+
if (isValidSceneUnit((config.defaultSize as any)[k])) {
|
|
187
|
+
;(config.defaultSize as any)[k] = formatToNumber(
|
|
188
|
+
(config.defaultSize as any)[k],
|
|
189
|
+
isWindow ? 'px' : 'm',
|
|
190
|
+
isWindow ? 'px' : 'm',
|
|
191
|
+
)
|
|
192
|
+
} else {
|
|
193
|
+
;(config.defaultSize as any)[k] = (
|
|
194
|
+
defaultSceneConfig.defaultSize as any
|
|
195
|
+
)[k]
|
|
196
|
+
errors.push(`defaultSize.${k}`)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// format resizability
|
|
202
|
+
if (config.resizability) {
|
|
203
|
+
const iterKeys = ['minWidth', 'minHeight', 'maxWidth', 'maxHeight']
|
|
204
|
+
for (let k of iterKeys) {
|
|
205
|
+
if (!(k in config.resizability)) continue
|
|
206
|
+
if (isValidSceneUnit((config.resizability as any)[k])) {
|
|
207
|
+
;(config.resizability as any)[k] = formatToNumber(
|
|
208
|
+
(config.resizability as any)[k],
|
|
209
|
+
'px',
|
|
210
|
+
isWindow ? 'px' : 'm',
|
|
211
|
+
)
|
|
212
|
+
} else {
|
|
213
|
+
;(config.resizability as any)[k] = undefined
|
|
214
|
+
errors.push(`resizability.${k}`)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// check value
|
|
220
|
+
if (config.worldScaling) {
|
|
221
|
+
if (!isValidWorldScalingType(config.worldScaling)) {
|
|
222
|
+
config.worldScaling = 'automatic'
|
|
223
|
+
errors.push('worldScaling')
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (config.worldAlignment) {
|
|
228
|
+
if (!isValidWorldAlignmentType(config.worldAlignment)) {
|
|
229
|
+
config.worldAlignment = 'automatic'
|
|
230
|
+
errors.push('worldAlignment')
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (config.baseplateVisibility) {
|
|
235
|
+
if (!isValidBaseplateVisibilityType(config.baseplateVisibility)) {
|
|
236
|
+
config.baseplateVisibility = 'automatic'
|
|
237
|
+
errors.push('baseplateVisibility')
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return [config, errors]
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function initScene(
|
|
245
|
+
name: string,
|
|
246
|
+
callback: (pre: SpatialSceneCreationOptions) => SpatialSceneCreationOptions,
|
|
247
|
+
options?: { type: SpatialSceneType },
|
|
248
|
+
) {
|
|
249
|
+
return SceneManager.getInstance().initScene(name, callback, options)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function hijackWindowOpen(window: WindowProxy) {
|
|
253
|
+
SceneManager.getInstance().init(window)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function hijackWindowATag(openedWindow: WindowProxy) {
|
|
257
|
+
openedWindow!.document.onclick = function (e) {
|
|
258
|
+
let element = e.target as HTMLElement | null
|
|
259
|
+
let found = false
|
|
260
|
+
|
|
261
|
+
// Look for <a> element in the clicked elements parents and if found override navigation behavior if needed
|
|
262
|
+
while (!found) {
|
|
263
|
+
if (element && element.tagName == 'A') {
|
|
264
|
+
// When using libraries like react route's <Link> it sets an onclick event, when this happens we should do nothing and let that occur
|
|
265
|
+
|
|
266
|
+
// if onClick is set for the element, the raw onclick will be noop() trapped so the onclick check is no longer trustable
|
|
267
|
+
// we handle all the scenarios
|
|
268
|
+
|
|
269
|
+
if (handleATag(e)) {
|
|
270
|
+
return false // prevent default action and stop event propagation
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
275
|
+
if (element && element.parentElement) {
|
|
276
|
+
element = element.parentElement
|
|
277
|
+
} else {
|
|
278
|
+
break
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function handleATag(event: MouseEvent) {
|
|
285
|
+
const targetElement = event.target as HTMLElement
|
|
286
|
+
if (targetElement.tagName === 'A') {
|
|
287
|
+
const link = targetElement as HTMLAnchorElement
|
|
288
|
+
const target = link.target
|
|
289
|
+
const url = link.href
|
|
290
|
+
|
|
291
|
+
if (target && target !== '_self') {
|
|
292
|
+
event.preventDefault()
|
|
293
|
+
window.open(url, target)
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function getSceneDefaultConfig(sceneType: SpatialSceneType) {
|
|
300
|
+
return sceneType === 'window' ? defaultSceneConfig : defaultSceneConfigVolume
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function injectScenePolyfill() {
|
|
304
|
+
if (!window.opener) return
|
|
305
|
+
|
|
306
|
+
const state = await SpatialScene.getInstance().getState()
|
|
307
|
+
|
|
308
|
+
// only run this in pending state
|
|
309
|
+
if (state !== SpatialSceneState.pending) return
|
|
310
|
+
|
|
311
|
+
function onContentLoaded(callback: any) {
|
|
312
|
+
if (
|
|
313
|
+
document.readyState === 'interactive' ||
|
|
314
|
+
document.readyState === 'complete'
|
|
315
|
+
) {
|
|
316
|
+
callback()
|
|
317
|
+
} else {
|
|
318
|
+
document.addEventListener('DOMContentLoaded', callback)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
onContentLoaded(async () => {
|
|
323
|
+
let provideDefaultSceneConfig = getSceneDefaultConfig(
|
|
324
|
+
window.xrCurrentSceneType ?? 'window',
|
|
325
|
+
)
|
|
326
|
+
let cfg = provideDefaultSceneConfig
|
|
327
|
+
if (typeof window.xrCurrentSceneDefaults === 'function') {
|
|
328
|
+
try {
|
|
329
|
+
cfg = await window.xrCurrentSceneDefaults?.(provideDefaultSceneConfig)
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error(error)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// fixme: this duration is too short so that hide and show is at racing, so add a little delay to avoid
|
|
335
|
+
await new Promise((resolve, reject) => {
|
|
336
|
+
setTimeout(() => {
|
|
337
|
+
resolve(null)
|
|
338
|
+
}, 1000)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
const sceneType = window.xrCurrentSceneType ?? 'window'
|
|
342
|
+
const [formattedConfig, errors] = formatSceneConfig(cfg, sceneType)
|
|
343
|
+
if (errors.length > 0) {
|
|
344
|
+
console.warn(
|
|
345
|
+
`window.xrCurrentSceneDefaults with errors: ${errors.join(', ')}`,
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
await SpatialScene.getInstance().updateSceneCreationConfig({
|
|
349
|
+
...formattedConfig,
|
|
350
|
+
type: sceneType,
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function injectSceneHook() {
|
|
356
|
+
hijackWindowOpen(window)
|
|
357
|
+
hijackWindowATag(window)
|
|
358
|
+
injectScenePolyfill()
|
|
359
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { parseCornerRadius } from './utils'
|
|
2
|
+
import { Spatial } from './Spatial'
|
|
3
|
+
import { SpatialSession } from './SpatialSession'
|
|
4
|
+
import { BackgroundMaterialType } from './types/types'
|
|
5
|
+
|
|
6
|
+
const spatial = new Spatial()
|
|
7
|
+
let session: SpatialSession | undefined = undefined
|
|
8
|
+
|
|
9
|
+
const SpatialGlobalCustomVars = {
|
|
10
|
+
backgroundMaterial: '--xr-background-material',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// keep track of current html background material
|
|
14
|
+
let htmlBackgroundMaterial = ''
|
|
15
|
+
function setCurrentWindowStyle(backgroundMaterial: string) {
|
|
16
|
+
if (backgroundMaterial !== htmlBackgroundMaterial) {
|
|
17
|
+
session?.getSpatialScene()?.updateSpatialProperties({
|
|
18
|
+
material: backgroundMaterial as BackgroundMaterialType,
|
|
19
|
+
})
|
|
20
|
+
htmlBackgroundMaterial = backgroundMaterial
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function checkHtmlBackgroundMaterial() {
|
|
25
|
+
const computedStyle = getComputedStyle(document.documentElement)
|
|
26
|
+
|
|
27
|
+
const backgroundMaterial = computedStyle.getPropertyValue(
|
|
28
|
+
SpatialGlobalCustomVars.backgroundMaterial,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
setCurrentWindowStyle(backgroundMaterial || 'none')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// keep track of current corner radius
|
|
35
|
+
let htmlCornerRadius = {
|
|
36
|
+
topLeading: 0,
|
|
37
|
+
bottomLeading: 0,
|
|
38
|
+
topTrailing: 0,
|
|
39
|
+
bottomTrailing: 0,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function checkCornerRadius() {
|
|
43
|
+
const computedStyle = getComputedStyle(document.documentElement)
|
|
44
|
+
const cornerRadius = parseCornerRadius(computedStyle)
|
|
45
|
+
setCornerRadius(cornerRadius)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function setCornerRadius(cornerRadius: any) {
|
|
49
|
+
if (
|
|
50
|
+
htmlCornerRadius.topLeading !== cornerRadius.topLeading ||
|
|
51
|
+
htmlCornerRadius.bottomLeading !== cornerRadius.bottomLeading ||
|
|
52
|
+
htmlCornerRadius.topTrailing !== cornerRadius.topTrailing ||
|
|
53
|
+
htmlCornerRadius.bottomTrailing !== cornerRadius.bottomTrailing
|
|
54
|
+
) {
|
|
55
|
+
session?.getSpatialScene()?.updateSpatialProperties({
|
|
56
|
+
cornerRadius,
|
|
57
|
+
})
|
|
58
|
+
htmlCornerRadius.topLeading = cornerRadius.topLeading
|
|
59
|
+
htmlCornerRadius.bottomLeading = cornerRadius.bottomLeading
|
|
60
|
+
htmlCornerRadius.topTrailing = cornerRadius.topTrailing
|
|
61
|
+
htmlCornerRadius.bottomTrailing = cornerRadius.bottomTrailing
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function setOpacity(opacity: number) {
|
|
66
|
+
session?.getSpatialScene().updateSpatialProperties({
|
|
67
|
+
opacity,
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function checkOpacity() {
|
|
72
|
+
const computedStyle = getComputedStyle(document.documentElement)
|
|
73
|
+
const opacity = parseFloat(computedStyle.getPropertyValue('opacity'))
|
|
74
|
+
setOpacity(opacity)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function hijackDocumentElementStyle() {
|
|
78
|
+
const rawDocumentStyle = document.documentElement.style
|
|
79
|
+
const styleProxy = new Proxy(rawDocumentStyle, {
|
|
80
|
+
set: function (target, key, value) {
|
|
81
|
+
const ret = Reflect.set(target, key, value)
|
|
82
|
+
|
|
83
|
+
if (key === SpatialGlobalCustomVars.backgroundMaterial) {
|
|
84
|
+
setCurrentWindowStyle(value)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
key === 'border-radius' ||
|
|
89
|
+
key === 'borderRadius' ||
|
|
90
|
+
key === 'border-top-left-radius' ||
|
|
91
|
+
key === 'borderTopLeftRadius' ||
|
|
92
|
+
key === 'border-top-right-radius' ||
|
|
93
|
+
key === 'borderTopRightRadius' ||
|
|
94
|
+
key === 'border-bottom-left-radius' ||
|
|
95
|
+
key === 'borderBottomLeftRadius' ||
|
|
96
|
+
key === 'border-bottom-right-radius' ||
|
|
97
|
+
key === 'borderBottomRightRadius'
|
|
98
|
+
) {
|
|
99
|
+
checkCornerRadius()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (key === 'opacity') {
|
|
103
|
+
checkOpacity()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return ret
|
|
107
|
+
},
|
|
108
|
+
get: function (target, prop: string) {
|
|
109
|
+
if (typeof target[prop as keyof CSSStyleDeclaration] === 'function') {
|
|
110
|
+
return function (this: any, ...args: any[]) {
|
|
111
|
+
if (prop === 'setProperty') {
|
|
112
|
+
const [property, value] = args
|
|
113
|
+
if (property === SpatialGlobalCustomVars.backgroundMaterial) {
|
|
114
|
+
setCurrentWindowStyle(value)
|
|
115
|
+
}
|
|
116
|
+
} else if (prop === 'removeProperty') {
|
|
117
|
+
const [property] = args
|
|
118
|
+
if (property === SpatialGlobalCustomVars.backgroundMaterial) {
|
|
119
|
+
setCurrentWindowStyle('none')
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return (target[prop as keyof CSSStyleDeclaration] as Function)(
|
|
123
|
+
...args,
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return Reflect.get(target, prop)
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
Object.defineProperty(document.documentElement, 'style', {
|
|
131
|
+
get: function () {
|
|
132
|
+
return styleProxy
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function monitorExternalStyleChange() {
|
|
138
|
+
const headObserver = new MutationObserver(checkCSSProperties)
|
|
139
|
+
|
|
140
|
+
headObserver.observe(document.head, { childList: true, subtree: true })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function checkCSSProperties() {
|
|
144
|
+
checkHtmlBackgroundMaterial()
|
|
145
|
+
checkCornerRadius()
|
|
146
|
+
checkOpacity()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function monitorHTMLAttributeChange() {
|
|
150
|
+
const observer = new MutationObserver(mutations => {
|
|
151
|
+
mutations.forEach(mutation => {
|
|
152
|
+
if (mutation.type === 'attributes' && mutation.attributeName) {
|
|
153
|
+
checkCSSProperties()
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
observer.observe(document.documentElement, {
|
|
159
|
+
attributes: true,
|
|
160
|
+
attributeFilter: ['style', 'class'],
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function spatialWindowPolyfill() {
|
|
165
|
+
if (!spatial.runInSpatialWeb()) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
session = await spatial.requestSession()!
|
|
170
|
+
|
|
171
|
+
if (document.readyState === 'complete') {
|
|
172
|
+
checkCSSProperties()
|
|
173
|
+
} else {
|
|
174
|
+
window.addEventListener('load', () => {
|
|
175
|
+
checkCSSProperties()
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
hijackDocumentElementStyle()
|
|
180
|
+
monitorExternalStyleChange()
|
|
181
|
+
monitorHTMLAttributeChange()
|
|
182
|
+
}
|
package/src/types/global.d.ts
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
declare global {
|
|
2
|
+
declare const __WEBSPATIAL_CORE_SDK_VERSION__: string
|
|
3
|
+
|
|
2
4
|
interface Window {
|
|
5
|
+
xrCurrentSceneType: SpatialSceneType
|
|
6
|
+
xrCurrentSceneDefaults: (
|
|
7
|
+
defaultConfig: SpatialSceneCreationOptions,
|
|
8
|
+
) => Promise<SpatialSceneCreationOptions>
|
|
9
|
+
|
|
10
|
+
// Location for webspatial custom functions
|
|
11
|
+
__WebSpatialData: {
|
|
12
|
+
androidNativeMessage: Function
|
|
13
|
+
getNativeVersion: Function
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Location for webspatial internal callbacks (eg. completion events)
|
|
17
|
+
__SpatialWebEvent: Function
|
|
18
|
+
|
|
19
|
+
// Used to access webkit specific api
|
|
20
|
+
webkit: any
|
|
21
|
+
webspatialBridge: any
|
|
22
|
+
|
|
23
|
+
// Will be removed in favor of __WebSpatialData
|
|
24
|
+
WebSpatailNativeVersion: string
|
|
25
|
+
|
|
3
26
|
__webspatialsdk__?: {
|
|
4
27
|
XR_ENV?: string
|
|
5
28
|
'natvie-version'?: string
|
|
6
29
|
'react-sdk-version'?: string
|
|
7
30
|
'core-sdk-version'?: string
|
|
8
31
|
}
|
|
32
|
+
|
|
33
|
+
innerDepth: number
|
|
34
|
+
outerDepth: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface HTMLElement {
|
|
38
|
+
offsetBack: number
|
|
39
|
+
clientDepth: number
|
|
40
|
+
getBoundingClientCube: () => CubeInfo | undefined
|
|
9
41
|
}
|
|
10
|
-
declare const __WEBSPATIAL_CORE_SDK_VERSION__: string
|
|
11
42
|
}
|
|
43
|
+
|
|
12
44
|
export {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SpatialSceneCreationOptions, SpatialSceneType } from "./types";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export type SpatialSceneCreationOptionsInternal = SpatialSceneCreationOptions & {
|
|
5
|
+
type: SpatialSceneType;
|
|
6
|
+
};
|
|
7
|
+
export enum SpatialSceneState {
|
|
8
|
+
idle = 'idle',
|
|
9
|
+
pending = 'pending',
|
|
10
|
+
willVisible = 'willVisible',
|
|
11
|
+
visible = 'visible',
|
|
12
|
+
fail = 'fail',
|
|
13
|
+
}
|