├── packages ├── cli │ ├── .npmrc │ ├── .gitignore │ ├── bin │ │ └── bundlepwa.js │ ├── src │ │ ├── assets │ │ │ └── icon-default.png │ │ └── lib │ │ │ ├── cmds │ │ │ ├── shutdown.ts │ │ │ ├── launch.ts │ │ │ └── version.ts │ │ │ ├── utils │ │ │ ├── utils.ts │ │ │ ├── CustomError.ts │ │ │ └── FetchUtils-1.ts │ │ │ ├── types.ts │ │ │ └── resource │ │ │ ├── file.ts │ │ │ └── imageHelper.ts │ ├── tsconfig.json │ ├── rollup.config.js │ ├── obfuscate.js │ └── README.md ├── core │ ├── .npmignore │ ├── .gitignore │ ├── src │ │ ├── reality │ │ │ ├── resource │ │ │ │ ├── index.ts │ │ │ │ └── SpatialModelAsset.ts │ │ │ ├── entity │ │ │ │ ├── index.ts │ │ │ │ └── SpatialModelEntity.ts │ │ │ ├── component │ │ │ │ ├── index.ts │ │ │ │ ├── ModelComponent.ts │ │ │ │ └── SpatialComponent.ts │ │ │ ├── material │ │ │ │ ├── index.ts │ │ │ │ ├── SpatialMaterial.ts │ │ │ │ └── SpatialUnlitMaterial.ts │ │ │ ├── index.ts │ │ │ └── geometry │ │ │ │ ├── index.ts │ │ │ │ ├── SpatialGeometry.ts │ │ │ │ ├── SpatialBoxGeometry.ts │ │ │ │ ├── SpatialConeGeometry.ts │ │ │ │ ├── SpatialPlaneGeometry.ts │ │ │ │ ├── SpatialSphereGeometry.ts │ │ │ │ └── SpatialCylinderGeometry.ts │ │ ├── ssr-polyfill.ts │ │ ├── SpatialWebEventCreator.ts │ │ ├── types │ │ │ ├── internal.ts │ │ │ └── global.d.ts │ │ ├── platform-adapter │ │ │ ├── CommandResultUtils.ts │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ └── ssr │ │ │ │ └── SSRPlatform.ts │ │ ├── SpatialWebEvent.ts │ │ ├── index.ts │ │ ├── SpatializedDynamic3DElement.ts │ │ └── SpatialObject.ts │ ├── vitest.config.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── react │ ├── .npmignore │ ├── src │ │ ├── types │ │ │ ├── web.d.ts │ │ │ └── global.d.ts │ │ ├── spatialized-container │ │ │ ├── SpatialID.ts │ │ │ ├── context │ │ │ │ └── SpatialLayerContext.ts │ │ │ └── hooks │ │ │ │ ├── useSync2DFrame.ts │ │ │ │ └── useSpatializedElement.ts │ │ ├── reality │ │ │ ├── context │ │ │ │ ├── index.tsx │ │ │ │ ├── ParentContext.tsx │ │ │ │ └── RealityContext.tsx │ │ │ ├── index.tsx │ │ │ ├── utils │ │ │ │ ├── index.ts │ │ │ │ ├── equal.ts │ │ │ │ ├── AbortResourceManager.ts │ │ │ │ └── ResourceRegistry.ts │ │ │ ├── hooks │ │ │ │ ├── index.tsx │ │ │ │ ├── useForceUpdate.tsx │ │ │ │ ├── useEntityId.tsx │ │ │ │ └── useEntityTransform.tsx │ │ │ └── components │ │ │ │ ├── SceneGraph.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── Entity.tsx │ │ │ │ ├── SphereEntity.tsx │ │ │ │ ├── ConeEntity.tsx │ │ │ │ ├── CylinderEntity.tsx │ │ │ │ ├── PlaneEntity.tsx │ │ │ │ ├── BoxEntity.tsx │ │ │ │ └── UnlitMaterial.tsx │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── getSession.ts │ │ │ └── debugTool.ts │ │ ├── ssr │ │ │ ├── index.tsx │ │ │ ├── SSRContext.tsx │ │ │ └── withSSRSupported.tsx │ │ ├── jsx │ │ │ ├── jsx-runtime.web.ts │ │ │ ├── README.md │ │ │ ├── jsx-dev-runtime.web.ts │ │ │ ├── jsx-runtime.ts │ │ │ ├── jsx-dev-runtime.ts │ │ │ └── xr-css-extension.ts │ │ ├── spatialized-container-monitor │ │ │ ├── index.ts │ │ │ ├── useMonitorDocumentHeaderChange.tsx │ │ │ ├── SpatialMonitor.tsx │ │ │ ├── withSpatialMonitor.tsx │ │ │ └── useMonitorDomChange.tsx │ │ ├── initScene.web.ts │ │ ├── initScene.ts │ │ ├── index.ts │ │ ├── notifyUpdateStandInstanceLayout.ts │ │ ├── Model.tsx │ │ └── noRuntime.ts │ ├── .gitignore │ ├── vitest.setup.ts │ ├── vitest.config.ts │ ├── README.md │ └── tsconfig.json ├── androidXR │ ├── app │ │ ├── .gitignore │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ │ ├── values │ │ │ │ │ │ ├── themes.xml │ │ │ │ │ │ └── strings.xml │ │ │ │ │ ├── mipmap-anydpi │ │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ │ ├── drawable │ │ │ │ │ │ ├── ic_full_space_mode_switch.xml │ │ │ │ │ │ └── ic_home_space_mode_switch.xml │ │ │ │ │ └── xml │ │ │ │ │ │ ├── backup_rules.xml │ │ │ │ │ │ └── data_extraction_rules.xml │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── webspatialandroid │ │ │ │ │ │ └── ui │ │ │ │ │ │ └── theme │ │ │ │ │ │ ├── Color.kt │ │ │ │ │ │ └── Type.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── test │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── webspatialandroid │ │ │ │ │ └── ExampleUnitTest.kt │ │ │ └── androidTest │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── webspatialandroid │ │ │ │ └── ExampleInstrumentedTest.kt │ │ └── proguard-rules.pro │ ├── webspatiallib │ │ ├── .gitignore │ │ ├── consumer-rules.pro │ │ ├── src │ │ │ ├── main │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── webspatiallib │ │ │ │ │ ├── SpatialComponent.kt │ │ │ │ │ ├── Console.kt │ │ │ │ │ └── SpatialWindowComponent.kt │ │ │ ├── test │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── webspatiallib │ │ │ │ │ └── ExampleUnitTest.kt │ │ │ └── androidTest │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── webspatiallib │ │ │ │ └── ExampleInstrumentedTest.kt │ │ └── proguard-rules.pro │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle.kts │ ├── CHANGELOG.md │ ├── package.json │ ├── settings.gradle.kts │ └── gradle.properties └── visionOS │ ├── .gitignore │ ├── Packages │ └── RealityKitContent │ │ ├── README.md │ │ ├── .build │ │ └── workspace-state.json │ │ ├── Sources │ │ └── RealityKitContent │ │ │ └── RealityKitContent.swift │ │ ├── Package.realitycomposerpro │ │ ├── WorkspaceData │ │ │ └── Settings.rcprojectdata │ │ └── ProjectData │ │ │ └── main.json │ │ └── Package.swift │ ├── assets │ ├── back.png │ ├── copy.png │ ├── home.png │ ├── link.png │ ├── more.png │ ├── close.png │ ├── refresh.png │ ├── arrow_left.png │ └── arrow_right.png │ ├── web-spatial │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AppIcon.solidimagestack │ │ │ ├── Back.solidimagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ ├── icon-default.png │ │ │ │ │ └── Contents.json │ │ │ ├── Front.solidimagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ ├── Middle.solidimagestacklayer │ │ │ │ ├── Contents.json │ │ │ │ └── Content.imageset │ │ │ │ │ ├── icon.png │ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── nav.imageset │ │ │ ├── nav.png │ │ │ └── Contents.json │ │ ├── back.imageset │ │ │ ├── back.png │ │ │ └── Contents.json │ │ ├── copy.imageset │ │ │ ├── copy.png │ │ │ └── Contents.json │ │ ├── home.imageset │ │ │ ├── home.png │ │ │ └── Contents.json │ │ ├── link.imageset │ │ │ ├── link.png │ │ │ └── Contents.json │ │ ├── more.imageset │ │ │ ├── more.png │ │ │ └── Contents.json │ │ ├── close.imageset │ │ │ ├── close.png │ │ │ └── Contents.json │ │ ├── browser.imageset │ │ │ ├── browser.png │ │ │ └── Contents.json │ │ ├── logo.imageset │ │ │ ├── icon-default.png │ │ │ └── Contents.json │ │ ├── refresh.imageset │ │ │ ├── refresh.png │ │ │ └── Contents.json │ │ ├── arrow_left.imageset │ │ │ ├── arrow_left.png │ │ │ └── Contents.json │ │ └── arrow_right.imageset │ │ │ ├── arrow_right.png │ │ │ └── Contents.json │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── protocol │ │ ├── ScrollAbleSpatialElementContainer.swift │ │ ├── WebMsgSender.swift │ │ ├── SpatialScrollAble.swift │ │ └── SpatializedElementContainer.swift │ ├── types │ │ ├── Vec3.swift │ │ └── Vec2.swift │ ├── static-web │ │ └── index.html │ ├── model │ │ ├── dynamic3d │ │ │ ├── SpatialTextureResource.swift │ │ │ ├── SpatialMaterial.swift │ │ │ ├── SpatialModelResource.swift │ │ │ └── SpatialModelEntity.swift │ │ ├── SpatializedStatic3DElement.swift │ │ └── SpatializedDynamic3DElement.swift │ ├── view │ │ ├── view-modifier │ │ │ └── HideViewModifier.swift │ │ └── LoadingView.swift │ ├── Utils │ │ ├── Logger.swift │ │ └── ColorExtension.swift │ ├── Info.plist │ ├── webview │ │ └── SpatialWebView.swift │ └── Window.swift │ ├── web-spatial.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist │ └── package.json ├── .prettierignore ├── .swiftformat ├── .gitattributes ├── tests └── ci-test │ ├── src │ ├── vite-env.d.ts │ ├── specs │ │ ├── runtime-info.ts │ │ ├── render.ts │ │ └── core-api.spec.tsx │ ├── main.css │ ├── type.ts │ ├── api.ts │ └── main.ts │ ├── scripts │ ├── dev.ts │ ├── runbuilder.ts │ └── vite-server.ts │ ├── test │ ├── webspatial-builder.test.ts │ ├── websptial-builder-utils.ts │ └── e2e.test.ts │ ├── tsconfig.json │ ├── .mocharc.json │ ├── testPage.html │ ├── README.md │ ├── utils │ └── AsyncPromise.ts │ ├── index.html │ ├── tsconfig.node.json │ ├── tsconfig.app.json │ ├── types │ ├── webspatial.d.ts │ └── runtime.d.ts │ ├── eslint.config.js │ ├── vite.config.ts │ ├── package.json │ └── public │ └── vite.svg ├── .prettierrc ├── assets ├── icon.png ├── logo.png ├── screenshot-desktop.png └── screenshot-spatial.png ├── pnpm-workspace.yaml ├── apps └── test-server │ ├── src │ ├── index.css │ ├── spatialStyleTest │ │ ├── type.d.ts │ │ ├── styles.module.scss │ │ ├── CSSModelSample.tsx │ │ ├── index.html │ │ ├── NestedComponent.tsx │ │ ├── StyledTitleComponent.tsx │ │ ├── SimpleSpatialComponent.tsx │ │ └── index.tsx │ ├── animate │ │ ├── style.scss │ │ ├── index.html │ │ ├── index.tsx │ │ └── GSAPTest.tsx │ ├── reality-sample │ │ └── sample1 │ │ │ ├── src │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ ├── main.tsx │ │ │ └── index.css │ │ │ └── index.html │ ├── canvas-test │ │ ├── AudioTest.tsx │ │ ├── VideoTest.tsx │ │ ├── index.html │ │ ├── EChartPage.tsx │ │ └── index.tsx │ ├── scene │ │ ├── nosdk.html │ │ ├── hook.html │ │ ├── index.html │ │ ├── volume.html │ │ ├── xrapp.html │ │ ├── loading.html │ │ └── volumeHook.html │ ├── reality │ │ ├── low.html │ │ ├── debug.html │ │ ├── empty.html │ │ ├── index.html │ │ ├── issue.html │ │ ├── nested.html │ │ ├── interactable.html │ │ ├── dynamic3d.html │ │ ├── geometryEntity.html │ │ ├── gestures.html │ │ └── spatialDivDynamic.html │ ├── basic-transform │ │ ├── index.html │ │ └── index.tsx │ ├── static-3d-model │ │ ├── index.html │ │ └── index.tsx │ ├── spatial-guesture │ │ └── index.html │ ├── jsapi-test │ │ ├── index.html │ │ └── index.tsx │ ├── memoryStats │ │ └── index.html │ ├── reality-test │ │ └── index.html │ ├── nestedscroll │ │ ├── index.html │ │ └── index.tsx │ ├── FixedPositionTest │ │ └── index.html │ ├── nestedfixposition │ │ └── index.html │ ├── spatialCorner │ │ └── index.html │ ├── spatial-converter │ │ └── index.html │ ├── visibleTest │ │ └── index.html │ ├── androidBringup │ │ └── index.html │ ├── displayTest │ │ └── index.html │ └── model-test │ │ └── index.html │ ├── postcss.config.js │ ├── public │ ├── exampleImg.png │ ├── modelasset │ │ ├── cone.glb │ │ └── cone.usdz │ ├── videos │ │ └── flower.webm │ ├── audios │ │ └── t-rex-roar.mp3 │ ├── assets │ │ └── vehicle-speedster.usdz │ └── vite.svg │ ├── .gitignore │ ├── tailwind.config.js │ ├── .eslintrc.cjs │ ├── index.html │ └── tsconfig.json ├── .changeset ├── lucky-peaches-tease.md ├── tangy-pears-add.md ├── heavy-points-smile.md ├── fair-donkeys-drum.md ├── README.md └── config.json ├── tools └── scripts │ ├── check-filesize.js │ ├── check-valid-characters.js │ └── generateWebsite.sh ├── SECURITY.md ├── .gitignore ├── LICENSE └── .github └── workflows ├── pr-auto-merge.yml ├── preview-release.yml └── ci.yml /packages/cli/.npmrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/react/.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/react/src/types/web.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/androidXR/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ -------------------------------------------------------------------------------- /packages/react/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | pnpm-lock.yaml 3 | CHANGELOG*.md 4 | -------------------------------------------------------------------------------- /packages/visionOS/.gitignore: -------------------------------------------------------------------------------- 1 | web-spatial.xcodeproj/xcuserdata/ -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | # format options 2 | 3 | --stripunusedargs closure-only -------------------------------------------------------------------------------- /packages/cli/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | **/xcuserdata/ 3 | **/*.xcuserstate -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/assets/** filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /tests/ci-test/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/core/src/reality/resource/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SpatialModelAsset'; -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "arrowParens": "avoid" 5 | } -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/assets/logo.png -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'apps/*' 4 | - 'tests/*' 5 | -------------------------------------------------------------------------------- /packages/cli/bin/bundlepwa.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | require('../dist')() 4 | -------------------------------------------------------------------------------- /apps/test-server/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container/SpatialID.ts: -------------------------------------------------------------------------------- 1 | export const SpatialID = 'data-spatial-id' 2 | -------------------------------------------------------------------------------- /packages/core/src/reality/entity/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SpatialEntity' 2 | export * from './SpatialModelEntity' -------------------------------------------------------------------------------- /packages/core/src/reality/component/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SpatialComponent' 2 | export * from './ModelComponent' -------------------------------------------------------------------------------- /packages/react/src/reality/context/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './RealityContext' 2 | export * from './ParentContext' 3 | -------------------------------------------------------------------------------- /packages/visionOS/Packages/RealityKitContent/README.md: -------------------------------------------------------------------------------- 1 | # RealityKitContent 2 | 3 | A description of this package. 4 | -------------------------------------------------------------------------------- /tests/ci-test/scripts/dev.ts: -------------------------------------------------------------------------------- 1 | import { runViteServer } from './vite-server' 2 | 3 | runViteServer({ port: 5173 }) 4 | -------------------------------------------------------------------------------- /assets/screenshot-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/assets/screenshot-desktop.png -------------------------------------------------------------------------------- /assets/screenshot-spatial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/assets/screenshot-spatial.png -------------------------------------------------------------------------------- /packages/core/src/reality/material/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SpatialUnlitMaterial' 2 | export * from './SpatialMaterial' -------------------------------------------------------------------------------- /.changeset/lucky-peaches-tease.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@webspatial/react-sdk': patch 3 | --- 4 | 5 | fix window.open URL cast issue 6 | -------------------------------------------------------------------------------- /packages/core/src/ssr-polyfill.ts: -------------------------------------------------------------------------------- 1 | const isSSR = typeof window === 'undefined' 2 | 3 | export const isSSREnv = () => isSSR 4 | -------------------------------------------------------------------------------- /packages/visionOS/assets/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/back.png -------------------------------------------------------------------------------- /packages/visionOS/assets/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/copy.png -------------------------------------------------------------------------------- /packages/visionOS/assets/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/home.png -------------------------------------------------------------------------------- /packages/visionOS/assets/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/link.png -------------------------------------------------------------------------------- /packages/visionOS/assets/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/more.png -------------------------------------------------------------------------------- /packages/react/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { getSession } from './getSession' 2 | export { enableDebugTool } from './debugTool' 3 | -------------------------------------------------------------------------------- /packages/visionOS/assets/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/close.png -------------------------------------------------------------------------------- /packages/visionOS/assets/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/refresh.png -------------------------------------------------------------------------------- /apps/test-server/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/test-server/public/exampleImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/apps/test-server/public/exampleImg.png -------------------------------------------------------------------------------- /packages/react/src/ssr/index.tsx: -------------------------------------------------------------------------------- 1 | export { SSRProvider } from './SSRContext' 2 | export { withSSRSupported } from './withSSRSupported' 3 | -------------------------------------------------------------------------------- /.changeset/tangy-pears-add.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@webspatial/platform-visionos': minor 3 | --- 4 | 5 | Fix depth property not working on Model tag 6 | -------------------------------------------------------------------------------- /packages/cli/src/assets/icon-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/cli/src/assets/icon-default.png -------------------------------------------------------------------------------- /packages/react/src/jsx/jsx-runtime.web.ts: -------------------------------------------------------------------------------- 1 | export { Fragment } from 'react/jsx-runtime' 2 | export { jsx, jsxs } from 'react/jsx-runtime' 3 | -------------------------------------------------------------------------------- /packages/react/src/reality/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './components' 2 | export type { EntityRef } from './hooks' 3 | export * from './type' 4 | -------------------------------------------------------------------------------- /packages/visionOS/assets/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/arrow_left.png -------------------------------------------------------------------------------- /packages/visionOS/assets/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/assets/arrow_right.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.changeset/heavy-points-smile.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@webspatial/core-sdk': patch 3 | --- 4 | 5 | using correct spatialid name when running on Android 6 | -------------------------------------------------------------------------------- /apps/test-server/public/modelasset/cone.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/apps/test-server/public/modelasset/cone.glb -------------------------------------------------------------------------------- /apps/test-server/public/videos/flower.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/apps/test-server/public/videos/flower.webm -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/type.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.module.scss' { 2 | const content: any 3 | export default content 4 | } 5 | -------------------------------------------------------------------------------- /tests/ci-test/test/webspatial-builder.test.ts: -------------------------------------------------------------------------------- 1 | describe.skip('Test WebSpatial Builder', () => { 2 | it('builder run', async () => {}) 3 | }) 4 | -------------------------------------------------------------------------------- /.changeset/fair-donkeys-drum.md: -------------------------------------------------------------------------------- 1 | --- 2 | '@webspatial/react-sdk': patch 3 | --- 4 | 5 | fixbug when spatialdiv.style.setProperty visibility/transform 6 | -------------------------------------------------------------------------------- /apps/test-server/public/audios/t-rex-roar.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/apps/test-server/public/audios/t-rex-roar.mp3 -------------------------------------------------------------------------------- /apps/test-server/public/modelasset/cone.usdz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/apps/test-server/public/modelasset/cone.usdz -------------------------------------------------------------------------------- /packages/react/src/jsx/README.md: -------------------------------------------------------------------------------- 1 | ## notice 2 | files inside this folder should not use relative path to import files from `src`, use package name instead. -------------------------------------------------------------------------------- /packages/react/src/reality/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ResourceRegistry' 2 | export * from './equal' 3 | export * from './AbortResourceManager' 4 | -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/styles.module.scss: -------------------------------------------------------------------------------- 1 | .localName { 2 | position: relative; 3 | color: yellow; 4 | --xr-back: 100; 5 | } 6 | -------------------------------------------------------------------------------- /packages/react/src/jsx/jsx-dev-runtime.web.ts: -------------------------------------------------------------------------------- 1 | export { Fragment } from 'react/jsx-runtime' 2 | export { jsxDEV, jsxDEV as jsx } from 'react/jsx-dev-runtime' 3 | -------------------------------------------------------------------------------- /apps/test-server/public/assets/vehicle-speedster.usdz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/apps/test-server/public/assets/vehicle-speedster.usdz -------------------------------------------------------------------------------- /packages/androidXR/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/react/vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'vitest' 2 | import * as matchers from '@testing-library/jest-dom/matchers' 3 | 4 | expect.extend(matchers) 5 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container-monitor/index.ts: -------------------------------------------------------------------------------- 1 | export { withSpatialMonitor } from './withSpatialMonitor' 2 | export { SpatialMonitor } from './SpatialMonitor' 3 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/protocol/ScrollAbleSpatialElementContainer.swift: -------------------------------------------------------------------------------- 1 | protocol ScrollAbleSpatialElementContainer: SpatialScrollAble, SpatializedElementContainer {} 2 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/protocol/WebMsgSender.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | protocol WebMsgSender { 4 | func sendWebMsg(_ id: String, _ msg: Encodable) 5 | } 6 | -------------------------------------------------------------------------------- /tests/ci-test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/visionOS/Packages/RealityKitContent/.build/workspace-state.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "artifacts": [], 4 | "dependencies": [] 5 | }, 6 | "version": 6 7 | } 8 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/types/Vec3.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | struct Vec3: Codable { 4 | var x: CGFloat 5 | var y: CGFloat 6 | var z: CGFloat 7 | } 8 | -------------------------------------------------------------------------------- /tests/ci-test/scripts/runbuilder.ts: -------------------------------------------------------------------------------- 1 | import { runWebSpatialBuilder } from '../test/websptial-builder-utils' 2 | const base = 'http://localhost:4000/' 3 | runWebSpatialBuilder(base) 4 | -------------------------------------------------------------------------------- /packages/core/src/reality/index.ts: -------------------------------------------------------------------------------- 1 | export * from './entity' 2 | export * from './component' 3 | export * from './material' 4 | export * from './geometry' 5 | export * from './resource' 6 | -------------------------------------------------------------------------------- /apps/test-server/src/animate/style.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | width: 200px; 3 | height: 100px; 4 | position: relative; 5 | overflow: hidden; 6 | color: blue; 7 | background-color: aqua; 8 | } -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/nav.imageset/nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/nav.imageset/nav.png -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/back.imageset/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/back.imageset/back.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/copy.imageset/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/copy.imageset/copy.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/home.imageset/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/home.imageset/home.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/link.imageset/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/link.imageset/link.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/more.imageset/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/more.imageset/more.png -------------------------------------------------------------------------------- /tests/ci-test/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/mocharc.json", 3 | "require": "tsx", 4 | "extension": ["ts", "tsx"], 5 | "spec": ["test/**/*.test.ts"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/close.imageset/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/close.imageset/close.png -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/androidXR/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/browser.imageset/browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/browser.imageset/browser.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/logo.imageset/icon-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/logo.imageset/icon-default.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/refresh.imageset/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/refresh.imageset/refresh.png -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/arrow_left.imageset/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/arrow_left.imageset/arrow_left.png -------------------------------------------------------------------------------- /packages/visionOS/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Bundle for the RealityKitContent project 4 | public let realityKitContentBundle = Bundle.module 5 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/arrow_right.imageset/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/arrow_right.imageset/arrow_right.png -------------------------------------------------------------------------------- /apps/test-server/src/reality-sample/sample1/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | margin: 0 0; 3 | text-align: center; 4 | } 5 | 6 | .card { 7 | padding: 2em; 8 | } 9 | 10 | .block{ 11 | width:100vw; 12 | height:100vh; 13 | } -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/core/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | environment: 'jsdom', // provide window / document 7 | }, 8 | }) 9 | -------------------------------------------------------------------------------- /apps/test-server/src/reality-sample/sample1/src/App.tsx: -------------------------------------------------------------------------------- 1 | import './App.css' 2 | import DynamicTest from './dynamic3d' 3 | 4 | function App() { 5 | return ( 6 | 7 | ) 8 | } 9 | 10 | export default App 11 | -------------------------------------------------------------------------------- /packages/react/src/jsx/jsx-runtime.ts: -------------------------------------------------------------------------------- 1 | export { Fragment } from 'react/jsx-runtime' 2 | export { jsx, jsxs } from './jsx-shared' 3 | 4 | export type { WebSpatialJSX as JSX } from './jsx-namespace' 5 | export * from './xr-css-extension' 6 | -------------------------------------------------------------------------------- /tests/ci-test/src/specs/runtime-info.ts: -------------------------------------------------------------------------------- 1 | import { enableDebugTool } from '@webspatial/react-sdk' 2 | enableDebugTool() 3 | 4 | declare global { 5 | interface Window { 6 | inspectCurrentSpatialScene: () => Promise 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/test-server/src/canvas-test/AudioTest.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react' 2 | 3 | const AudioTest = () => { 4 | return 5 | } 6 | 7 | export default AudioTest 8 | -------------------------------------------------------------------------------- /apps/test-server/src/reality-sample/sample1/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client' 2 | import './index.css' 3 | import App from './App.tsx' 4 | 5 | createRoot(document.getElementById('root')!).render( 6 | 7 | ) 8 | -------------------------------------------------------------------------------- /packages/cli/src/lib/cmds/shutdown.ts: -------------------------------------------------------------------------------- 1 | import { ParsedArgs } from 'minimist' 2 | import Xcrun from '../xcode/xcrun' 3 | export async function shutdown(args: ParsedArgs): Promise { 4 | Xcrun.shutdownSimulator() 5 | return true 6 | } 7 | -------------------------------------------------------------------------------- /packages/react/src/jsx/jsx-dev-runtime.ts: -------------------------------------------------------------------------------- 1 | export { Fragment } from 'react/jsx-runtime' 2 | export { jsxDEV, jsx } from './jsx-shared' 3 | 4 | export type { WebSpatialJSX as JSX } from './jsx-namespace' 5 | export * from './xr-css-extension' 6 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/react/src/reality/hooks/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './useEntityTransform' 2 | export * from './useEntityEvent' 3 | export * from './useEntityId' 4 | export * from './useEntity' 5 | export * from './useEntityRef' 6 | export * from './useForceUpdate' -------------------------------------------------------------------------------- /tests/ci-test/testPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test page 7 | 8 | 9 | 10 | Hello, Test World! 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/CSSModelSample.tsx: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.scss' 2 | 3 | export const CSSModelSample = () => ( 4 | 5 | this is a CSSModule Sample 6 | 7 | ) 8 | -------------------------------------------------------------------------------- /tests/ci-test/src/main.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | #mocha { 9 | display: absolute; 10 | top: 0; 11 | left: 0; 12 | /* visibility: hidden; */ 13 | } -------------------------------------------------------------------------------- /packages/react/src/initScene.web.ts: -------------------------------------------------------------------------------- 1 | import type { SpatialSceneCreationOptions } from '@webspatial/core-sdk' 2 | 3 | export function initScene( 4 | name: string, 5 | callback: (pre: SpatialSceneCreationOptions) => SpatialSceneCreationOptions, 6 | ) { 7 | return 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/reality/geometry/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SpatialGeometry' 2 | export * from './SpatialBoxGeometry' 3 | export * from './SpatialSphereGeometry' 4 | export * from './SpatialCylinderGeometry' 5 | export * from './SpatialPlaneGeometry' 6 | export * from './SpatialConeGeometry' -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/static-web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebSpatial 5 | 6 | 7 | Hello WebSpatial! 8 | 9 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/protocol/SpatialScrollAble.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | protocol SpatialScrollAble { 4 | func updateDeltaScrollOffset(_ delta: Vec2) 5 | func stopScrolling() 6 | var scrollPageEnabled: Bool { get } 7 | var scrollOffset: Vec2 { get } 8 | } 9 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/react/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | import react from '@vitejs/plugin-react' 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | test: { 7 | environment: 'jsdom', 8 | setupFiles: ['./vitest.setup.ts'], 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/icon.png -------------------------------------------------------------------------------- /packages/react/src/reality/context/ParentContext.tsx: -------------------------------------------------------------------------------- 1 | import { SpatialEntity } from "@webspatial/core-sdk"; 2 | import { createContext, useContext } from "react"; 3 | 4 | export const ParentContext = createContext(null) 5 | export const useParentContext = () => useContext(ParentContext) 6 | -------------------------------------------------------------------------------- /packages/androidXR/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Feb 03 16:19:02 PST 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /packages/react/src/reality/hooks/useForceUpdate.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState } from 'react' 2 | type Props = { 3 | children?: React.ReactNode 4 | } 5 | export const useForceUpdate = () => { 6 | const [, setTick] = useState(0) 7 | return useCallback(() => setTick(tick => tick + 1), []) 8 | } 9 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webspatial/webspatial-sdk/HEAD/packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon-default.png -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "es2018", 5 | "module": "commonjs", 6 | "declaration": true, 7 | "outDir": "dist", 8 | "strict": true 9 | }, 10 | "include": ["src/**/*.ts"], 11 | "exclude": ["node_modules", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "vision", 5 | "scale": "2x" 6 | } 7 | ], 8 | "info": { 9 | "author": "xcode", 10 | "version": 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/androidXR/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /tools/scripts/check-filesize.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | process.argv.slice(1).forEach(file => { 3 | if (fs.existsSync(file)) { 4 | const size = fs.statSync(file).size 5 | if (size >= 1048576) { 6 | console.error(`Error: ${file} is 1MB or larger`) 7 | process.exit(1) 8 | } 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /packages/react/src/jsx/xr-css-extension.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface CSSStyleDeclaration { 3 | '--xr-background-material'?: string 4 | '--xr-back'?: number | string 5 | '--xr-depth'?: number | string 6 | '--xr-z-index'?: number | string 7 | enableXr?: boolean 8 | } 9 | } 10 | 11 | export {} 12 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/types/Vec2.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | 3 | struct Vec2: Codable { 4 | var x: CGFloat 5 | var y: CGFloat 6 | } 7 | 8 | extension Vec2 { 9 | static func + (left: Vec2, right: Vec2) -> Vec2 { 10 | return Vec2(x: left.x + right.x, y: left.y + right.y) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container/context/SpatialLayerContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | 3 | // SpatialLayerContext is used to mark the spatial layer of the spatial div, which is used to help portal instance find the correct layer standard instance div. 4 | export const SpatialLayerContext = createContext(0) 5 | -------------------------------------------------------------------------------- /packages/core/src/SpatialWebEventCreator.ts: -------------------------------------------------------------------------------- 1 | import { SpatialWebMsgType } from './WebMsgCommand' 2 | 3 | export function createSpatialEvent( 4 | type: SpatialWebMsgType, 5 | detail: T, 6 | ): CustomEvent { 7 | return new CustomEvent(type, { 8 | bubbles: true, 9 | cancelable: false, 10 | detail, 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "icon.png", 5 | "idiom": "vision", 6 | "scale": "2x" 7 | } 8 | ], 9 | "info": { 10 | "author": "xcode", 11 | "version": 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/src/main/java/com/example/webspatiallib/SpatialComponent.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatiallib 2 | 3 | open class SpatialComponent : SpatialObject() { 4 | var entity: SpatialEntity? = null 5 | 6 | open fun onAddToEntity() {} 7 | 8 | override fun onDestroy() { 9 | entity = null 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "icon-default.png", 5 | "idiom": "vision", 6 | "scale": "2x" 7 | } 8 | ], 9 | "info": { 10 | "author": "xcode", 11 | "version": 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a Vulnerability 2 | If you happen to discover a potential security issue in this project, please do **not** create a public GitHub issue. 3 | 4 | We kindly ask that you notify us through our [Discord channel](https://discord.gg/S4dnAZqY) via a direct message. 5 | 6 | Your contribution to ensuring the security of this project is greatly appreciated. -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WebSpatialAndroid 3 | Hello Android XR. 4 | Switch to Home Space Mode 5 | Switch to Full Space Mode 6 | -------------------------------------------------------------------------------- /packages/core/src/reality/component/ModelComponent.ts: -------------------------------------------------------------------------------- 1 | import { ModelComponentOptions } from '../../types/types' 2 | import { SpatialComponent } from './SpatialComponent' 3 | 4 | export class ModelComponent extends SpatialComponent { 5 | constructor( 6 | id: string, 7 | public options: ModelComponentOptions, 8 | ) { 9 | super(id) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/reality/resource/SpatialModelAsset.ts: -------------------------------------------------------------------------------- 1 | import { SpatialObject } from '../../SpatialObject' 2 | import { ModelAssetOptions } from '../../types/types' 3 | 4 | export class SpatialModelAsset extends SpatialObject { 5 | constructor( 6 | public id: string, 7 | public options: ModelAssetOptions, 8 | ) { 9 | super(id) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/SceneGraph.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ParentContext } from '../context' 3 | type Props = { 4 | children?: React.ReactNode 5 | } 6 | export const SceneGraph: React.FC = ({ children }) => { 7 | return ( 8 | {children} 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /packages/androidXR/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.android.application) apply false 4 | alias(libs.plugins.kotlin.android) apply false 5 | alias(libs.plugins.kotlin.compose) apply false 6 | alias(libs.plugins.android.library) apply false 7 | } -------------------------------------------------------------------------------- /apps/test-server/src/scene/nosdk.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | NO SDK 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/ci-test/src/specs/render.ts: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client' 2 | import React from 'react' 3 | 4 | const container = document.getElementById('root')! 5 | const root = createRoot(container) 6 | 7 | export async function render(component: React.ReactNode) { 8 | root.render(component) 9 | } 10 | 11 | export function unmount() { 12 | root.unmount() 13 | } 14 | -------------------------------------------------------------------------------- /apps/test-server/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/androidXR/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @webspatial/platform-androidxrapp 2 | 3 | ## 1.0.5 4 | 5 | ## 1.0.4 6 | 7 | ## 1.0.3 8 | 9 | ## 1.0.2 10 | 11 | ## 1.0.1 12 | 13 | ## 1.0.0 14 | 15 | ## 0.1.23 16 | 17 | ## 0.1.22 18 | 19 | ## 0.1.21 20 | 21 | ## 0.1.20 22 | 23 | ## 0.1.19 24 | 25 | ## 0.1.18 26 | 27 | ## 0.1.17 28 | 29 | ## 0.1.16 30 | 31 | ## 0.1.15 32 | 33 | ## 0.1.14 34 | -------------------------------------------------------------------------------- /packages/cli/src/lib/cmds/launch.ts: -------------------------------------------------------------------------------- 1 | import { ParsedArgs } from 'minimist' 2 | import Xcrun from '../xcode/xcrun' 3 | export async function launch(args: ParsedArgs): Promise { 4 | const bundleId = args['bundleId'] ?? 'com.webspatial.test' 5 | const appInfo = { 6 | name: '', 7 | id: bundleId, 8 | } 9 | Xcrun.launchWithSimulator(appInfo) 10 | return true 11 | } 12 | -------------------------------------------------------------------------------- /packages/react/src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | __webspatialsdk__?: { 4 | XR_ENV?: string 5 | 'react-sdk-version'?: string 6 | 'core-sdk-version'?: string 7 | } 8 | } 9 | interface ImportMeta { 10 | readonly XR_ENV: string 11 | } 12 | 13 | declare const __WEBSPATIAL_REACT_SDK_VERSION__: string 14 | } 15 | 16 | export {} 17 | -------------------------------------------------------------------------------- /tests/ci-test/src/type.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | type PassedTestResult = { 3 | title: string 4 | fullTitle: string 5 | duration?: number 6 | } 7 | 8 | type FailedTestResult = { 9 | title: string 10 | fullTitle: string 11 | err: string 12 | } 13 | 14 | type TestResults = { 15 | passes: PassedTestResult[] 16 | failures: FailedTestResult[] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/java/com/example/webspatialandroid/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatialandroid.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Entity' 2 | export * from './BoxEntity' 3 | export * from './UnlitMaterial' 4 | export * from './SphereEntity' 5 | export * from './ConeEntity' 6 | export * from './CylinderEntity' 7 | export * from './PlaneEntity' 8 | export * from './SceneGraph' 9 | export * from './ModelAsset' 10 | export * from './ModelEntity' 11 | export * from './Reality' 12 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | }, 6 | "layers": [ 7 | { 8 | "filename": "Front.solidimagestacklayer" 9 | }, 10 | { 11 | "filename": "Middle.solidimagestacklayer" 12 | }, 13 | { 14 | "filename": "Back.solidimagestacklayer" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /apps/test-server/src/scene/hook.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/scene/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/test-server/src/scene/volume.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/core/src/types/internal.ts: -------------------------------------------------------------------------------- 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 | } 14 | -------------------------------------------------------------------------------- /tools/scripts/check-valid-characters.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const f = process.argv[1] 3 | 4 | if (!f.endsWith('check-valid-characters.js')) { 5 | const c = fs 6 | .readFileSync(f, 'utf8') 7 | .split('\\n') 8 | .filter(l => /[\\u4E00-\\u9FFF]/.test(l)) 9 | if (c.length) { 10 | console.error('Non english characters detected:\\n' + c.join('\\n')) 11 | process.exit(1) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/low.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/scene/xrapp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/issue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/nested.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/scene/loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/core/src/reality/geometry/SpatialGeometry.ts: -------------------------------------------------------------------------------- 1 | import { SpatialObject } from '../../SpatialObject' 2 | import { SpatialGeometryOptions, SpatialGeometryType } from '../../types/types' 3 | 4 | export class SpatialGeometry extends SpatialObject { 5 | static type: SpatialGeometryType 6 | constructor( 7 | public id: string, 8 | public options: SpatialGeometryOptions, 9 | ) { 10 | super(id) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/visionOS/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata: -------------------------------------------------------------------------------- 1 | { 2 | "cameraPresets" : { 3 | 4 | }, 5 | "secondaryToolbarData" : { 6 | "isGridVisible" : true, 7 | "sceneReverbPreset" : -1 8 | }, 9 | "unitDefaults" : { 10 | "°" : "°", 11 | "kg" : "g", 12 | "m" : "cm", 13 | "m\/s" : "m\/s", 14 | "m\/s²" : "m\/s²", 15 | "s" : "s" 16 | } 17 | } -------------------------------------------------------------------------------- /apps/test-server/src/scene/volumeHook.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/interactable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/cli/src/lib/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import { ConsoleLog } from './Log' 2 | 3 | export function parseRouter(url: string): string { 4 | let urlParts = url.split('/') 5 | urlParts.pop() 6 | let pathUrl: string = urlParts.join() 7 | while (pathUrl.indexOf(',') >= 0) { 8 | pathUrl = pathUrl.replace(',', '/') 9 | } 10 | return pathUrl 11 | } 12 | 13 | export const CliLog: ConsoleLog = new ConsoleLog('webspatial-builder') 14 | -------------------------------------------------------------------------------- /packages/react/src/initScene.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpatialSceneCreationOptions, 3 | SpatialSceneType, 4 | } from '@webspatial/core-sdk' 5 | import { getSession } from './utils' 6 | 7 | export function initScene( 8 | name: string, 9 | callback: (pre: SpatialSceneCreationOptions) => SpatialSceneCreationOptions, 10 | options?: { type: SpatialSceneType }, 11 | ) { 12 | return getSession()?.initScene(name, callback, options) 13 | } 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality-sample/sample1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to WebSpatial 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/dynamic3d.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial Dynamic3D 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/geometryEntity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/gestures.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial Gestures 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/cli/src/lib/cmds/version.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import * as path from 'path' 3 | 4 | export async function getVersion(): Promise { 5 | const packageJsonFile = path.join(__dirname, '../../../package.json') 6 | const packageJsonContents = await ( 7 | await fs.promises.readFile(packageJsonFile) 8 | ).toString() 9 | const packageJson = JSON.parse(packageJsonContents) 10 | return packageJson.version 11 | } 12 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/drawable/ic_full_space_mode_switch.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/drawable/ic_home_space_mode_switch.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/nav.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "nav.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/test-server/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import daisyui from 'daisyui' 2 | import typography from '@tailwindcss/typography' 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: ['./index.html', './index.tsx', './src/**/*.{js,jsx,ts,tsx}'], 7 | theme: { 8 | extend: {}, 9 | }, 10 | daisyui: { 11 | base: false, 12 | themes: false, // Disable all themes 13 | }, 14 | plugins: [daisyui, typography], 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/reality/material/SpatialMaterial.ts: -------------------------------------------------------------------------------- 1 | import { SpatialObject } from '../../SpatialObject' 2 | import { SpatialMaterialType } from '../../types/types' 3 | 4 | export abstract class SpatialMaterial extends SpatialObject { 5 | constructor( 6 | public id: string, 7 | public type: SpatialMaterialType, 8 | ) { 9 | super(id) 10 | this.type = type 11 | } 12 | 13 | abstract updateProperties(properties: any): void 14 | } 15 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "back.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "close.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/copy.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "copy.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/home.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "home.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/link.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "link.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/more.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "more.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/browser.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "browser.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "icon-default.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/refresh.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "refresh.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/test-server/src/reality/spatialDivDynamic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSpatial SpatialDiv Dynamic 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/core/src/reality/geometry/SpatialBoxGeometry.ts: -------------------------------------------------------------------------------- 1 | import { SpatialBoxGeometryOptions, SpatialGeometryType } from '../../types/types' 2 | import { SpatialGeometry } from './SpatialGeometry' 3 | 4 | export class SpatialBoxGeometry extends SpatialGeometry { 5 | static type: SpatialGeometryType = 'BoxGeometry' 6 | constructor( 7 | public id: string, 8 | public options: SpatialBoxGeometryOptions, 9 | ) { 10 | super(id, options) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/arrow_left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "arrow_left.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/test-server/src/basic-transform/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | basic-transform 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/test-server/src/canvas-test/VideoTest.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react' 2 | 3 | const VideoTest = () => { 4 | return ( 5 | 6 | 7 | 8 | Download the 9 | WEBM 10 | video. 11 | 12 | 13 | ) 14 | } 15 | 16 | export default VideoTest 17 | -------------------------------------------------------------------------------- /apps/test-server/src/static-3d-model/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Static 3D model 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Assets.xcassets/arrow_right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "arrow_right.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "author": "xcode", 19 | "version": 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/ci-test/README.md: -------------------------------------------------------------------------------- 1 | ## **Introduction** 2 | 3 | ci-test is a tool for testing WebSpatial project. 4 | It will run the project on Apple Vision Pro simulator using mocha and chai. 5 | 6 | ## **Usage** 7 | 8 | start e2e test 9 | `npm run test` 10 | 11 | ## **How to add test cases ** 12 | 1. npm run dev 13 | 2. open xcode project and run 14 | 3. write test cases in `src/specs` folder 15 | 4. see the result in window page 16 | 17 | ## **How to run test cases ** 18 | -------------------------------------------------------------------------------- /apps/test-server/src/spatial-guesture/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | spatial-guesture 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/protocol/SpatializedElementContainer.swift: -------------------------------------------------------------------------------- 1 | protocol SpatializedElementContainer { 2 | var id: String { get } 3 | var parent: ScrollAbleSpatialElementContainer? { get } 4 | func addChild(_ spatializedElement: SpatializedElement) 5 | func removeChild(_ spatializedElement: SpatializedElement) 6 | func getChildren() -> [String: SpatializedElement] 7 | func getChildrenOfType(_ type: SpatializedElementType) -> [String: SpatializedElement] 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/reality/entity/SpatialModelEntity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpatialEntityUserData, 3 | SpatialModelEntityCreationOptions, 4 | } from '../../types/types' 5 | import { SpatialEntity } from './SpatialEntity' 6 | 7 | export class SpatialModelEntity extends SpatialEntity { 8 | constructor( 9 | public id: string, 10 | public options?: SpatialModelEntityCreationOptions, 11 | public userData?: SpatialEntityUserData, 12 | ) { 13 | super(id, userData) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/reality/geometry/SpatialConeGeometry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpatialGeometryType, 3 | SpatialConeGeometryOptions, 4 | } from '../../types/types' 5 | import { SpatialGeometry } from './SpatialGeometry' 6 | 7 | export class SpatialConeGeometry extends SpatialGeometry { 8 | static type: SpatialGeometryType = 'ConeGeometry' 9 | constructor( 10 | public id: string, 11 | public options: SpatialConeGeometryOptions, 12 | ) { 13 | super(id, options) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/test-server/src/jsapi-test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spatial Scene Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/core/src/reality/geometry/SpatialPlaneGeometry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpatialGeometryType, 3 | SpatialPlaneGeometryOptions, 4 | } from '../../types/types' 5 | import { SpatialGeometry } from './SpatialGeometry' 6 | 7 | export class SpatialPlaneGeometry extends SpatialGeometry { 8 | static type: SpatialGeometryType = 'PlaneGeometry' 9 | constructor( 10 | public id: string, 11 | public options: SpatialPlaneGeometryOptions, 12 | ) { 13 | super(id, options) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/model/dynamic3d/SpatialTextureResource.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import RealityKit 3 | 4 | @Observable 5 | class SpatialTextureResource:SpatialObject { 6 | internal var _resource:TextureResource? = nil 7 | var resource:TextureResource? { 8 | _resource 9 | } 10 | 11 | override init(_ url:String){ 12 | super.init() 13 | } 14 | 15 | override internal func onDestroy() { 16 | _resource = nil 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/cli/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript' 2 | import terser from '@rollup/plugin-terser' 3 | 4 | export default { 5 | input: 'src/index.ts', 6 | output: { 7 | dir: 'dist', 8 | format: 'cjs', 9 | }, 10 | plugins: [ 11 | typescript(), 12 | terser({ 13 | format: { 14 | comments: true, 15 | }, 16 | compress: { 17 | drop_console: true, 18 | }, 19 | }), 20 | ], 21 | external: ['tslib'], 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/reality/geometry/SpatialSphereGeometry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpatialGeometryType, 3 | SpatialSphereGeometryOptions, 4 | } from '../../types/types' 5 | import { SpatialGeometry } from './SpatialGeometry' 6 | 7 | export class SpatialSphereGeometry extends SpatialGeometry { 8 | static type: SpatialGeometryType = 'SphereGeometry' 9 | constructor( 10 | public id: string, 11 | public options: SpatialSphereGeometryOptions, 12 | ) { 13 | super(id, options) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | import { initPolyfill } from './spatialized-container' 2 | 3 | export { enableDebugTool } from './utils' 4 | export * from './initScene' 5 | export * from './spatialized-container' 6 | export * from './spatialized-container-monitor' 7 | export * from './reality' 8 | export * from './Model' 9 | export { SSRProvider } from './ssr' 10 | 11 | export const version = __WEBSPATIAL_REACT_SDK_VERSION__ 12 | 13 | if (typeof window !== 'undefined') { 14 | initPolyfill() 15 | } 16 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/test/java/com/example/webspatialandroid/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatialandroid 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/src/main/java/com/example/webspatiallib/Console.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatiallib 2 | 3 | import android.util.Log 4 | 5 | class Console { 6 | fun log(message:String){ 7 | Log.d("WebSpatialLogs", message) 8 | } 9 | 10 | fun warn(message:String){ 11 | Log.d("WebSpatialLogs", "Warn: $message") 12 | } 13 | 14 | fun error(message:String){ 15 | Log.d("WebSpatialLogs", "Error: $message") 16 | } 17 | } 18 | var console = Console() -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/src/test/java/com/example/webspatiallib/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatiallib 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /packages/core/src/reality/geometry/SpatialCylinderGeometry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpatialGeometryType, 3 | SpatialCylinderGeometryOptions, 4 | } from '../../types/types' 5 | import { SpatialGeometry } from './SpatialGeometry' 6 | 7 | export class SpatialCylinderGeometry extends SpatialGeometry { 8 | static type: SpatialGeometryType = 'CylinderGeometry' 9 | constructor( 10 | public id: string, 11 | public options: SpatialCylinderGeometryOptions, 12 | ) { 13 | super(id, options) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Xcode 3 | **/xcuserdata 4 | **/visionOS/web-spatial/web-project 5 | **/visionOS/icon.* 6 | **/visionOS/build/** 7 | 8 | # We only allow pnpm lock files 9 | **/yarn.lock 10 | **/package-lock.json 11 | 12 | # No node_modules anywhere 13 | **/node_modules/ 14 | node_modules 15 | 16 | # System files 17 | .DS_Store 18 | *.log 19 | 20 | # Package managers 21 | .pnpm-store 22 | 23 | # IDEs and editors 24 | .vscode 25 | **/.idea 26 | 27 | # Build and test output 28 | coverage 29 | **/dist 30 | temp 31 | -------------------------------------------------------------------------------- /apps/test-server/src/memoryStats/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebSpatial 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /apps/test-server/src/reality-test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | reality-test 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/androidXR/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webspatial/platform-androidxrapp", 3 | "version": "1.0.5", 4 | "private": true, 5 | "files": [ 6 | "webspatiallib", 7 | "package.json" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/webspatial/webspatial-sdk.git", 12 | "directory": "packages/androidXR" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "MIT", 17 | "description": "Used to publish WebSpatial projects to AndroidXRApp" 18 | } 19 | -------------------------------------------------------------------------------- /tests/ci-test/src/api.ts: -------------------------------------------------------------------------------- 1 | export const MOCHA_RESULT_API = '/api/mockresult' 2 | 3 | export function postMochaResult(data: TestResults) { 4 | const requestOptions = { 5 | method: 'POST', 6 | headers: { 7 | 'Content-Type': 'application/json', 8 | }, 9 | body: JSON.stringify(data), 10 | } 11 | return fetch(MOCHA_RESULT_API, requestOptions).then(response => { 12 | if (!response.ok) { 13 | throw new Error('Network response was not ok') 14 | } 15 | return response.json() 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /apps/test-server/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/platform-adapter/CommandResultUtils.ts: -------------------------------------------------------------------------------- 1 | import { CommandResult } from './interface' 2 | 3 | export function CommandResultSuccess(data: any): CommandResult { 4 | return { 5 | success: true, 6 | data, 7 | errorCode: '', 8 | errorMessage: '', 9 | } 10 | } 11 | 12 | export function CommandResultFailure( 13 | errorCode: string, 14 | errorMessage = '', 15 | ): CommandResult { 16 | return { 17 | success: false, 18 | data: undefined, 19 | errorCode, 20 | errorMessage, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/view/view-modifier/HideViewModifier.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct HideViewModifier: ViewModifier { 4 | let isHidden: Bool 5 | @ViewBuilder func body(content: Content) -> some View { 6 | content 7 | .opacity(isHidden ? 0 : 1) 8 | .disabled(isHidden) 9 | } 10 | } 11 | 12 | // Extending on View to apply to all Views 13 | extension View { 14 | func hidden(_ isHidden: Bool) -> some View { 15 | modifier(HideViewModifier(isHidden: isHidden)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/test-server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebSpatial 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/test-server/src/nestedscroll/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | nested scroll 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/react/src/reality/context/RealityContext.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | SpatializedDynamic3DElement, 3 | SpatialSession, 4 | } from '@webspatial/core-sdk' 5 | import { createContext, useContext } from 'react' 6 | import { ResourceRegistry } from '../utils' 7 | 8 | export type RealityContextValue = { 9 | session: SpatialSession 10 | reality: SpatializedDynamic3DElement 11 | resourceRegistry: ResourceRegistry 12 | } | null 13 | export const RealityContext = createContext(null) 14 | export const useRealityContext = () => useContext(RealityContext) 15 | -------------------------------------------------------------------------------- /apps/test-server/src/FixedPositionTest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | FixedPositionTest 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/test-server/src/nestedfixposition/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | nestedfixposition 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /packages/react/src/reality/utils/equal.ts: -------------------------------------------------------------------------------- 1 | export function shallowEqualVec3( 2 | a?: { x: number; y: number; z: number }, 3 | b?: { x: number; y: number; z: number }, 4 | ) { 5 | if (a === b) return true 6 | if (!a || !b) return false 7 | return a.x === b.x && a.y === b.y && a.z === b.z 8 | } 9 | 10 | export function shallowEqualRotation(a?: any, b?: any) { 11 | if (a === b) return true 12 | if (!a || !b) return false 13 | // support Vec3 / Vec4 14 | return ( 15 | a.x === b.x && a.y === b.y && a.z === b.z && ('w' in a ? a.w === b.w : true) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /packages/react/src/ssr/SSRContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useState, useEffect } from 'react' 2 | 3 | export const SSRContext = createContext(false) 4 | 5 | export const SSRProvider = ({ 6 | isSSR: initialIsSSR = true, 7 | children, 8 | }: { 9 | isSSR?: boolean 10 | children: React.ReactNode 11 | }) => { 12 | const [isSSR, setIsSSR] = useState(initialIsSSR) 13 | 14 | useEffect(() => { 15 | if (isSSR) { 16 | setIsSSR(false) 17 | } 18 | }, []) 19 | 20 | return {children} 21 | } 22 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # React SDK for WebSpatial 7 | 8 | The React SDK from the WebSpatial SDK makes the WebSpatial API immediately available inside React. 9 | 10 | ## Documentation 11 | 12 | - [Introduction](https://webspatial.dev/docs/introduction) 13 | - [Quick Example](https://webspatial.dev/docs/quick-example) 14 | - [Core Concepts](https://webspatial.dev/docs/core-concepts) 15 | - [Development Guide](https://webspatial.dev/docs/development-guide) 16 | -------------------------------------------------------------------------------- /apps/test-server/src/spatialCorner/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Corner Test 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | -------------------------------------------------------------------------------- /apps/test-server/src/spatial-converter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | spatial-convertor 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/test-server/src/visibleTest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Visible Test 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | -------------------------------------------------------------------------------- /tests/ci-test/utils/AsyncPromise.ts: -------------------------------------------------------------------------------- 1 | export class AsyncPromise { 2 | private promise: Promise 3 | private promiseResolve?: (value: T | PromiseLike) => void 4 | private promiseReject?: (reason?: any) => void 5 | 6 | constructor() { 7 | this.promise = new Promise((res, rej) => { 8 | this.promiseResolve = res 9 | this.promiseReject = rej 10 | }) 11 | } 12 | 13 | waitFinish() { 14 | return this.promise 15 | } 16 | 17 | resolve(data: T) { 18 | this.promiseResolve!(data) 19 | } 20 | 21 | reject(error: any) { 22 | this.promiseReject!(error) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [ 6 | [ 7 | "@webspatial/core-sdk", 8 | "@webspatial/react-sdk", 9 | "@webspatial/builder", 10 | "@webspatial/platform-visionos", 11 | "@webspatial/platform-androidxrapp" 12 | ] 13 | ], 14 | "linked": [], 15 | "access": "public", 16 | "baseBranch": "stable", 17 | "updateInternalDependencies": "patch", 18 | "ignore": [], 19 | "privatePackages": { "version": true, "tag": false } 20 | } 21 | -------------------------------------------------------------------------------- /apps/test-server/src/androidBringup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebSpatial 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /apps/test-server/src/animate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Animation Test 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | -------------------------------------------------------------------------------- /apps/test-server/src/basic-transform/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | 3 | import { enableDebugTool, Model } from '@webspatial/react-sdk' 4 | 5 | enableDebugTool() 6 | 7 | function App() { 8 | const style = { 9 | width: '300px', 10 | height: '300px', 11 | // '--xr-back': 100, 12 | // transform: 'rotateX(30deg)', 13 | backgroundColor: 'green', 14 | } 15 | return ( 16 | 17 | hello basic-transform 18 | 19 | tail end 20 | 21 | ) 22 | } 23 | 24 | ReactDOM.createRoot(document.getElementById('root')!).render() 25 | -------------------------------------------------------------------------------- /packages/core/src/reality/material/SpatialUnlitMaterial.ts: -------------------------------------------------------------------------------- 1 | import { UpdateUnlitMaterialProperties } from '../../JSBCommand' 2 | import { SpatialUnlitMaterialOptions } from '../../types/types' 3 | import { SpatialMaterial } from './SpatialMaterial' 4 | 5 | export class SpatialUnlitMaterial extends SpatialMaterial { 6 | constructor( 7 | public id: string, 8 | public options: SpatialUnlitMaterialOptions, 9 | ) { 10 | super(id, 'unlit') 11 | } 12 | 13 | updateProperties(properties: Partial) { 14 | return new UpdateUnlitMaterialProperties(this, properties).execute() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/test-server/src/displayTest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Visible Test 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | -------------------------------------------------------------------------------- /packages/core/src/reality/component/SpatialComponent.ts: -------------------------------------------------------------------------------- 1 | import { SpatialObject } from '../../SpatialObject' 2 | import { SpatialWebEvent } from '../../SpatialWebEvent' 3 | import { ObjectDestroyMsg, SpatialWebMsgType } from '../../WebMsgCommand' 4 | 5 | export class SpatialComponent extends SpatialObject { 6 | constructor(id: string) { 7 | super(id) 8 | SpatialWebEvent.addEventReceiver(id, this.onReceiveEvent) 9 | } 10 | 11 | private onReceiveEvent = (data: ObjectDestroyMsg) => { 12 | const { type } = data 13 | if (type === SpatialWebMsgType.objectdestroy) { 14 | this.isDestroyed = true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/test-server/src/static-3d-model/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | 3 | import { enableDebugTool, Model } from '@webspatial/react-sdk' 4 | 5 | enableDebugTool() 6 | 7 | function App() { 8 | return ( 9 | 10 | Static 3D model test 11 | 16 | this is place holder when failure 17 | 18 | 19 | ) 20 | } 21 | 22 | ReactDOM.createRoot(document.getElementById('root')!).render() 23 | -------------------------------------------------------------------------------- /packages/react/src/reality/hooks/useEntityId.tsx: -------------------------------------------------------------------------------- 1 | import { SpatialEntity } from '@webspatial/core-sdk' 2 | import React, { useEffect } from 'react' 3 | import { useRealityContext } from '../context' 4 | type Props = { 5 | id?: string 6 | entity?: SpatialEntity | null 7 | } 8 | export const useEntityId: React.FC = ({ id, entity }) => { 9 | const ctx = useRealityContext() 10 | useEffect(() => { 11 | if (!id || !entity || !ctx) return 12 | ctx.resourceRegistry.add(id, Promise.resolve(entity)) 13 | return () => { 14 | ctx.resourceRegistry.remove(id) 15 | } 16 | }, [id, entity, ctx]) 17 | 18 | return null 19 | } 20 | -------------------------------------------------------------------------------- /packages/cli/src/lib/utils/CustomError.ts: -------------------------------------------------------------------------------- 1 | type ErrorType = 'info' | 'warning' | 'error' 2 | type ErrorMessageItem = { 3 | code: number 4 | message: string 5 | tag?: string 6 | message_staring_params: Record 7 | } 8 | class CustomError extends Error { 9 | type: ErrorType 10 | customMessage: ErrorMessageItem | Array 11 | constructor( 12 | customMessage: ErrorMessageItem | Array, 13 | type?: ErrorType, 14 | ) { 15 | super(JSON.stringify(customMessage)) 16 | this.customMessage = customMessage 17 | this.type = type || 'info' 18 | } 19 | } 20 | 21 | export { CustomError } 22 | -------------------------------------------------------------------------------- /packages/react/src/utils/getSession.ts: -------------------------------------------------------------------------------- 1 | import { isSSREnv, Spatial, SpatialSession } from '@webspatial/core-sdk' 2 | // Create the default Spatial session for the app 3 | let spatial: Spatial | null = null 4 | let _currentSession: SpatialSession | null = null 5 | /** @hidden */ 6 | export function getSession() { 7 | if (isSSREnv()) return null 8 | if (!spatial) { 9 | spatial = new Spatial() 10 | } 11 | if (!spatial.isSupported()) { 12 | return null 13 | } 14 | if (_currentSession) { 15 | return _currentSession 16 | } 17 | _currentSession = spatial.requestSession() 18 | return _currentSession 19 | } 20 | 21 | export { spatial } 22 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Utils/Logger.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Logger { 4 | static func getLogger() -> Logger { 5 | return Logger() 6 | } 7 | 8 | static func initLogger() {} 9 | 10 | func error(_ str: String) { 11 | print("error: " + str) 12 | } 13 | 14 | func verbose(_ str: String) { 15 | print("verbose: " + str) 16 | } 17 | 18 | func debug(_ str: String) { 19 | print("debug: " + str) 20 | } 21 | 22 | func info(_ str: String) { 23 | print("info: " + str) 24 | } 25 | 26 | func warning(_ str: String) { 27 | print("warning: " + str) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container-monitor/useMonitorDocumentHeaderChange.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { notifyUpdateStandInstanceLayout } from '../notifyUpdateStandInstanceLayout' 3 | 4 | export function useMonitorDocumentHeaderChange() { 5 | useEffect(() => { 6 | const observer = new MutationObserver(mutationsList => { 7 | notifyUpdateStandInstanceLayout() 8 | }) 9 | 10 | const config = { 11 | childList: true, 12 | subtree: true, 13 | attributes: true, 14 | } 15 | 16 | observer.observe(document.head, config) 17 | 18 | return () => { 19 | observer.disconnect() 20 | } 21 | }, []) 22 | } 23 | -------------------------------------------------------------------------------- /apps/test-server/src/canvas-test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | spatial-model-test 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /apps/test-server/src/model-test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | spatial-model-test 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /packages/androidXR/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "WebSpatialAndroid" 23 | include(":app") 24 | include(":webspatiallib") 25 | -------------------------------------------------------------------------------- /packages/cli/src/lib/utils/FetchUtils-1.ts: -------------------------------------------------------------------------------- 1 | class FetchUtils { 2 | private fetchEngine = null 3 | public fetch: any = null 4 | public downloadFile: any = null 5 | public decompressResponseBuffer: any = null 6 | setFetchEngine(newFetchEngine: any): void { 7 | this.fetchEngine = newFetchEngine 8 | } 9 | setFetch(newFetch: any): void { 10 | this.fetch = newFetch 11 | } 12 | setDownloadFile(newDownloadFile: any): void { 13 | this.downloadFile = newDownloadFile 14 | } 15 | setDecompressResponseBuffer(newDecompressResponseBuffer: any): void { 16 | this.decompressResponseBuffer = newDecompressResponseBuffer 17 | } 18 | } 19 | const fetchUtils = new FetchUtils() 20 | export { fetchUtils } 21 | -------------------------------------------------------------------------------- /tests/ci-test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebSpatial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/ci-test/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedSideEffectImports": true 22 | }, 23 | "include": ["vite.config.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /apps/test-server/src/canvas-test/EChartPage.tsx: -------------------------------------------------------------------------------- 1 | import ReactECharts from 'echarts-for-react' 2 | 3 | const EChartPage: React.FC = () => { 4 | const options = { 5 | grid: { top: 8, right: 8, bottom: 24, left: 36 }, 6 | xAxis: { 7 | type: 'category', 8 | data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], 9 | }, 10 | yAxis: { 11 | type: 'value', 12 | }, 13 | series: [ 14 | { 15 | data: [820, 932, 901, 934, 1290, 1330, 1320], 16 | type: 'line', 17 | smooth: true, 18 | }, 19 | ], 20 | tooltip: { 21 | trigger: 'axis', 22 | }, 23 | } 24 | 25 | return 26 | } 27 | 28 | export default EChartPage 29 | -------------------------------------------------------------------------------- /packages/core/src/SpatialWebEvent.ts: -------------------------------------------------------------------------------- 1 | interface SpatialWebEventData { 2 | id: string 3 | data: any 4 | } 5 | 6 | export class SpatialWebEvent { 7 | static eventReceiver: Record void> = {} 8 | static init() { 9 | // inject __SpatialWebEvent 10 | window.__SpatialWebEvent = ({ id, data }: SpatialWebEventData) => { 11 | // console.log('__SpatialWebEvent', id, data) 12 | SpatialWebEvent.eventReceiver[id]?.(data) 13 | } 14 | } 15 | 16 | static addEventReceiver(id: string, callback: (data: any) => void) { 17 | SpatialWebEvent.eventReceiver[id] = callback 18 | } 19 | 20 | static removeEventReceiver(id: string) { 21 | delete SpatialWebEvent.eventReceiver[id] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/platform-adapter/index.ts: -------------------------------------------------------------------------------- 1 | import { isSSREnv } from '../ssr-polyfill' 2 | import { PlatformAbility } from './interface' 3 | import { SSRPlatform } from './ssr/SSRPlatform' 4 | 5 | export function createPlatform(): PlatformAbility { 6 | if (isSSREnv()) { 7 | return new SSRPlatform() 8 | } 9 | 10 | if ( 11 | window.navigator.userAgent.includes('Android') || 12 | window.navigator.userAgent.includes('Linux') 13 | ) { 14 | const AndroidPlatform = require('./android/AndroidPlatform').AndroidPlatform 15 | return new AndroidPlatform() 16 | } else { 17 | const VisionOSPlatform = 18 | require('./vision-os/VisionOSPlatform').VisionOSPlatform 19 | return new VisionOSPlatform() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/react/src/utils/debugTool.ts: -------------------------------------------------------------------------------- 1 | import { isSSREnv, Spatialized2DElement } from '@webspatial/core-sdk' 2 | import { getSession } from './getSession' 3 | 4 | async function inspectCurrentSpatialScene() { 5 | const spatialScene = getSession()!.getSpatialScene() 6 | return spatialScene.inspect() 7 | } 8 | 9 | function getSpatialized2DElement( 10 | spatialized2DElement: HTMLDivElement, 11 | ): Spatialized2DElement { 12 | return ( 13 | spatialized2DElement as any 14 | ).__innerSpatializedElement?.() as Spatialized2DElement 15 | } 16 | 17 | export function enableDebugTool() { 18 | if (isSSREnv()) return 19 | 20 | Object.assign(window, { 21 | inspectCurrentSpatialScene, 22 | getSpatialized2DElement, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/model/SpatializedStatic3DElement.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | @Observable 5 | class SpatializedStatic3DElement: SpatializedElement { 6 | var modelURL: String = "" 7 | var modelTransform: AffineTransform3D = AffineTransform3D.identity 8 | 9 | enum CodingKeys: String, CodingKey { 10 | case modelURL, type 11 | } 12 | 13 | override func encode(to encoder: Encoder) throws { 14 | try super.encode(to: encoder) 15 | var container = encoder.container(keyedBy: CodingKeys.self) 16 | try container.encode(modelURL, forKey: .modelURL) 17 | try container.encode(SpatializedElementType.SpatializedStatic3DElement, forKey: .type) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/cli/obfuscate.js: -------------------------------------------------------------------------------- 1 | const obfuscator = require('javascript-obfuscator') 2 | const fs = require('fs-extra') 3 | const glob = require('glob') 4 | const obfuscate = async () => { 5 | try { 6 | const jsfiles = glob.sync('dist/**/*.js', { nodir: true }) 7 | 8 | const tsfiles = [] // glob.sync('dist/**/*.ts', {nodir: true}); 9 | const files = [...jsfiles, ...tsfiles] 10 | for (const file of files) { 11 | const code = await fs.readFile(file, 'utf-8') 12 | const obfuscatedCode = obfuscator.obfuscate(code).getObfuscatedCode() 13 | await fs.writeFile(file, obfuscatedCode, 'utf-8') 14 | } 15 | } catch (error) { 16 | console.error('Error while obfuscating files:', error) 17 | } 18 | } 19 | 20 | obfuscate() 21 | -------------------------------------------------------------------------------- /packages/cli/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface PWAInitArgs { 2 | manifestUrl?: string // remote manifest url 3 | manifest?: string // local manifest path 4 | project?: string // local web project path 5 | base: string // url root 6 | bundleId?: string // bundle id 7 | } 8 | 9 | export interface ManifestInfo { 10 | json: Record 11 | url: string 12 | fromNet: boolean 13 | } 14 | 15 | export interface HistoryInfo { 16 | cmd: string 17 | manifest: Record 18 | appInfo: BasicAppInfo 19 | simulator: string 20 | } 21 | 22 | export interface SimulatorInfo { 23 | name: string 24 | deviceId: string 25 | state: string 26 | } 27 | 28 | export interface BasicAppInfo { 29 | name: string 30 | id: string 31 | } 32 | -------------------------------------------------------------------------------- /packages/visionOS/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webspatial/platform-visionos", 3 | "version": "1.0.5", 4 | "description": "Used to publish WebSpatial projects to Apple Vision Pro", 5 | "type": "commonjs", 6 | "engines": { 7 | "node": ">=14.15.0" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/webspatial/webspatial-sdk.git", 12 | "directory": "packages/visionOS" 13 | }, 14 | "files": [ 15 | "Packages", 16 | "web-spatial", 17 | "web-spatialTests", 18 | "web-spatial.xcodeproj", 19 | "package.json" 20 | ], 21 | "author": "", 22 | "license": "MIT", 23 | "dependencies": {}, 24 | "devDependencies": {}, 25 | "publishConfig": { 26 | "registry": "https://registry.npmjs.org" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2023", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2023", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "allowJs": false, 9 | /* Bundler mode */ 10 | "moduleResolution": "node", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "noEmit": false, 14 | "declaration": true, 15 | "declarationMap": true, 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": false, 19 | "noUnusedParameters": false, 20 | "noFallthroughCasesInSwitch": true, 21 | "outDir": "./dist", 22 | "allowSyntheticDefaultImports": true 23 | }, 24 | "include": ["src/**/*.ts", "src/types/*.d.ts"], 25 | "references": [] 26 | } 27 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/view/LoadingView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct LoadingView: View { 4 | var body: some View { 5 | GeometryReader { proxy3D in 6 | let width = proxy3D.size.width 7 | let height = proxy3D.size.height 8 | ZStack { 9 | VStack { 10 | ProgressView() 11 | .progressViewStyle(CircularProgressViewStyle(tint: .white)) 12 | .scaleEffect(2) 13 | Text("Loading...") 14 | .foregroundColor(.white) 15 | .font(.headline) 16 | .padding(.top, 16) 17 | } 18 | }.frame(width: width, height: height).glassBackgroundEffect() 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tools/scripts/generateWebsite.sh: -------------------------------------------------------------------------------- 1 | echo 'creating folders...' 2 | mkdir ../webspatial.github.io/dist ; 3 | mkdir ../webspatial.github.io/dist/src; 4 | mkdir ../webspatial.github.io/src ; 5 | 6 | echo 'Copying homepage files...' 7 | cp apps/test-server/index.html ../webspatial.github.io/index.html; 8 | cp apps/test-server/dist/index.js ../webspatial.github.io/dist/index.js; 9 | cp apps/test-server/dist/src/index.css ../webspatial.github.io/dist/src/index.css; 10 | 11 | echo 'Copying docs website...' 12 | cp -r apps/test-server/dist/src/docsWebsite ../webspatial.github.io/dist/src; 13 | cp -r apps/test-server/src/docsWebsite ../webspatial.github.io/src/; 14 | 15 | echo 'Copying assets' 16 | cp -r apps/test-server/public ../webspatial.github.io/; 17 | 18 | echo 'done, run: [cd ../webspatial.github.io/; npx serve .] to test locally' -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | SpatialStyleTest 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # WebSpatial Builder 7 | 8 | The build tool transforms websites into Packaged WebSpatial Apps for debugging and distributing on spatial computing platforms. 9 | 10 | ## Documentation 11 | 12 | For WebSpatial Builder: 13 | - [Add Build Tool for Packaged WebSpatial Apps](https://webspatial.dev/docs/development-guide/enabling-webspatial-in-web-projects/step-2-add-build-tool-for-packaged-webspatial-apps) 14 | 15 | For WebSpatial: 16 | - [Introduction](https://webspatial.dev/docs/introduction) 17 | - [Quick Example](https://webspatial.dev/docs/quick-example) 18 | - [Core Concepts](https://webspatial.dev/docs/core-concepts) 19 | - [Development Guide](https://webspatial.dev/docs/development-guide) 20 | -------------------------------------------------------------------------------- /tests/ci-test/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "isolatedModules": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noUncheckedSideEffectImports": true 24 | }, 25 | "include": ["src", "test", "types", "scripts", "utils"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/androidXR/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /packages/androidXR/app/src/androidTest/java/com/example/webspatialandroid/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatialandroid 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.webspatialandroid", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /tests/ci-test/types/webspatial.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface CSSStyleDeclaration { 3 | '--xr-background-material'?: string 4 | '--xr-back'?: number | string 5 | '--xr-z-index'?: number | string 6 | } 7 | } 8 | 9 | declare module 'react' { 10 | interface CSSProperties { 11 | '--xr-background-material'?: string 12 | '--xr-back'?: number | string 13 | '--xr-z-index'?: number | string 14 | } 15 | } 16 | 17 | declare global { 18 | interface CSSProperties { 19 | '--xr-background-material'?: string 20 | '--xr-z-index'?: number | string 21 | } 22 | } 23 | 24 | export namespace JSX { 25 | // export type IntrinsicElements = { 26 | // [K in keyof ReactJSXIntrinsicElements]: ReactJSXIntrinsicElements[K] & { 27 | // style?: React.CSSProperties 28 | // 'enable-xr'?: boolean 29 | // } 30 | // } 31 | } 32 | -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/src/androidTest/java/com/example/webspatiallib/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatiallib 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.webspatiallib.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /tests/ci-test/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import reactHooks from 'eslint-plugin-react-hooks' 4 | import reactRefresh from 'eslint-plugin-react-refresh' 5 | import tseslint from 'typescript-eslint' 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': [ 23 | 'warn', 24 | { allowConstantExport: true }, 25 | ], 26 | }, 27 | }, 28 | ) 29 | -------------------------------------------------------------------------------- /apps/test-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "allowJs": false, 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | "jsxImportSource": "@webspatial/react-sdk", 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": false, 20 | "noUnusedParameters": false, 21 | "noFallthroughCasesInSwitch": true, 22 | "paths": { 23 | "web-spatial": ["../npmLib/src"] 24 | } 25 | }, 26 | "include": ["src"], 27 | "exclude": ["node_modules"] 28 | // "references": [{ "path": "./tsconfig.node.json" }] 29 | } 30 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2023", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2023", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "allowJs": false, 9 | /* Bundler mode */ 10 | "moduleResolution": "node", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "noEmit": false, 14 | "declaration": true, 15 | "declarationMap": true, 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": false, 19 | "noUnusedParameters": false, 20 | "noFallthroughCasesInSwitch": true, 21 | "outDir": "./dist", 22 | "allowSyntheticDefaultImports": true, 23 | "jsx": "react-jsx", 24 | "paths": { 25 | "@webspatial/react-sdk": ["./src"] 26 | } 27 | }, 28 | "include": ["src/index.ts", "esbuild.mjs", "src/types/*.d.ts"], 29 | "references": [] 30 | } 31 | -------------------------------------------------------------------------------- /tests/ci-test/test/websptial-builder-utils.ts: -------------------------------------------------------------------------------- 1 | import { exec, execSync } from 'child_process' 2 | 3 | export async function runWebSpatialBuilder( 4 | base: string, 5 | logBuilder: boolean = true, 6 | ) { 7 | return new Promise((resolve, reject) => { 8 | exec( 9 | `node ../../packages/cli/bin/bundlepwa.js run --base=${base}`, 10 | (error: any, stdout: any, stderr: any) => { 11 | if (logBuilder) { 12 | if (stdout) { 13 | console.log('stdout:' + stdout) 14 | } 15 | if (stderr) { 16 | console.log('stderr:' + stderr) 17 | } 18 | if (error) { 19 | console.log('error:' + error) 20 | } 21 | } 22 | 23 | if (error) { 24 | reject(error) 25 | } else { 26 | console.log('Builder finished') 27 | resolve(null) 28 | } 29 | }, 30 | ) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container/hooks/useSync2DFrame.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { SpatializedContainerObject } from '../context/SpatializedContainerContext' 3 | import { PortalInstanceObject } from '../context/PortalInstanceContext' 4 | 5 | function useForceUpdate() { 6 | const [, setToggle] = useState(false) 7 | return () => setToggle(toggle => !toggle) 8 | } 9 | 10 | export function useSync2DFrame( 11 | spatialId: string, 12 | portalInstanceObject: PortalInstanceObject, 13 | spatializedContainerObject: SpatializedContainerObject, 14 | ) { 15 | const forceUpdate = useForceUpdate() 16 | 17 | useEffect(() => { 18 | spatializedContainerObject.on2DFrameChange(spatialId, () => { 19 | portalInstanceObject.notify2DFrameChange() 20 | forceUpdate() 21 | }) 22 | 23 | return () => { 24 | spatializedContainerObject.off2DFrameChange(spatialId) 25 | } 26 | }, []) 27 | } 28 | -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/NestedComponent.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | export const NestedComponent = () => { 4 | const [primary, setPrimary] = useState(true) 5 | 6 | const styleOuter = { 7 | '--xr-back': 121, 8 | width: '200px', 9 | height: '78px', 10 | 11 | backgroundColor: 'red', 12 | } 13 | 14 | const styleInner = { 15 | backgroundColor: 'blue', 16 | } 17 | 18 | const styleInner2 = { 19 | '--xr-back': primary ? 66 : 89, 20 | backgroundColor: primary ? 'green' : 'grey', 21 | } 22 | 23 | return ( 24 | { 28 | setPrimary(!primary) 29 | }} 30 | > 31 | OuterDiv 32 | 33 | Inner Div!! 34 | 35 | 36 | Inner Div 2!! 37 | 38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { SpatialObject } from './SpatialObject' 2 | export { Spatial } from './Spatial' 3 | export { SpatialSession } from './SpatialSession' 4 | export { SpatialScene } from './SpatialScene' 5 | export { SpatializedElement } from './SpatializedElement' 6 | export { Spatialized2DElement } from './Spatialized2DElement' 7 | export { SpatializedStatic3DElement } from './SpatializedStatic3DElement' 8 | export { SpatializedDynamic3DElement } from './SpatializedDynamic3DElement' 9 | export * from './reality' 10 | export * from './types/types' 11 | export * from './types/global.d' 12 | 13 | // side effects 14 | import { injectSceneHook } from './scene-polyfill' 15 | import { isSSREnv } from './ssr-polyfill' 16 | import { spatialWindowPolyfill } from './spatial-window-polyfill' 17 | 18 | export { isSSREnv } 19 | 20 | if (!isSSREnv() && navigator.userAgent.indexOf('WebSpatial/') > 0) { 21 | injectSceneHook() 22 | spatialWindowPolyfill() 23 | } 24 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/Entity.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { ParentContext, useRealityContext } from '../context' 3 | import { EntityProps, EntityEventHandler } from '../type' 4 | import { EntityRefShape, useEntity, useEntityRef } from '../hooks' 5 | 6 | type Props = EntityProps & EntityEventHandler & { children?: React.ReactNode } 7 | 8 | export const Entity = forwardRef( 9 | ({ id, children, position, rotation, scale, onSpatialTap, name }, ref) => { 10 | const ctx = useRealityContext() 11 | const entity = useEntity({ 12 | ref, 13 | id, 14 | position, 15 | rotation, 16 | scale, 17 | onSpatialTap, 18 | createEntity: async () => ctx!.session.createEntity({ id, name }), 19 | }) 20 | 21 | if (!entity) return null 22 | return ( 23 | {children} 24 | ) 25 | }, 26 | ) 27 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container-monitor/SpatialMonitor.tsx: -------------------------------------------------------------------------------- 1 | import { useMonitorDomChange } from './useMonitorDomChange' 2 | import { useMonitorDocumentHeaderChange } from './useMonitorDocumentHeaderChange' 3 | import { ElementType, ForwardedRef, forwardRef } from 'react' 4 | 5 | type SpatialMonitorProps = { 6 | El?: ElementType 7 | } 8 | 9 | /** 10 | * Component that add MutationObserver to monitor all dom changes including its children. 11 | * If any dom changes, it will notify all SpatialDiv to render again for the purpose of sync standInstance layout to portalInstance. 12 | */ 13 | function SpatialMonitorBase( 14 | inProps: SpatialMonitorProps, 15 | inRef: ForwardedRef, 16 | ) { 17 | const { El = 'div', ...props } = inProps 18 | const ref = useMonitorDomChange(inRef) 19 | useMonitorDocumentHeaderChange() 20 | 21 | return 22 | } 23 | 24 | export const SpatialMonitor = forwardRef(SpatialMonitorBase) 25 | -------------------------------------------------------------------------------- /packages/react/src/notifyUpdateStandInstanceLayout.ts: -------------------------------------------------------------------------------- 1 | export enum SpatialStyleInfoUpdateEvent { 2 | standInstanceLayout = 'standInstanceLayout', 3 | domUpdated = 'domUpdated', 4 | } 5 | 6 | /** 7 | * External-developers can call this function to sync the standardInstance layout to PortalInstance. 8 | * 9 | * Currently: notifyUpdateStandInstanceLayout is called when the document head changed 10 | * or when the monitored div changed (in both cases spatialDiv's layout may be changed, so we need to update the layout) 11 | */ 12 | export function notifyUpdateStandInstanceLayout() { 13 | document.dispatchEvent( 14 | new CustomEvent(SpatialStyleInfoUpdateEvent.standInstanceLayout, { 15 | detail: {}, 16 | }), 17 | ) 18 | } 19 | 20 | export function notifyDOMUpdate(mutationsList: MutationRecord[]) { 21 | document.dispatchEvent( 22 | new CustomEvent(SpatialStyleInfoUpdateEvent.domUpdated, { 23 | detail: mutationsList, 24 | }), 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /packages/androidXR/webspatiallib/src/main/java/com/example/webspatiallib/SpatialWindowComponent.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatiallib 2 | 3 | import android.content.Context 4 | import android.util.MutableFloat 5 | import android.util.MutableInt 6 | import androidx.compose.runtime.getValue 7 | import java.lang.ref.WeakReference 8 | import androidx.compose.runtime.mutableIntStateOf 9 | import androidx.compose.runtime.mutableStateOf 10 | import androidx.compose.runtime.setValue 11 | 12 | class SpatialWindowComponent(context: Context) : SpatialComponent() { 13 | companion object { 14 | var mountIdCounter = 1 15 | } 16 | 17 | val nativeWebView = NativeWebView(context) 18 | 19 | var mountedId by mutableIntStateOf(0) 20 | var backgroundStyle by mutableStateOf("none") 21 | 22 | init { 23 | nativeWebView.windowComponent = WeakReference(this) 24 | } 25 | 26 | fun loadURL(url: String) { 27 | nativeWebView.navigateToURL(url) 28 | } 29 | } -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ITSAppUsesNonExemptEncryption 6 | 7 | CFBundleURLTypes 8 | 9 | 10 | CFBundleURLName 11 | web-spatial 12 | CFBundleURLSchemes 13 | 14 | web+spatial 15 | 16 | 17 | 18 | NSAppTransportSecurity 19 | 20 | NSAllowsArbitraryLoads 21 | 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationPreferredDefaultSceneSessionRole 26 | UIWindowSceneSessionRoleApplication 27 | UIApplicationSupportsMultipleScenes 28 | 29 | UISceneConfigurations 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /apps/test-server/src/reality-sample/sample1/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | /* color-scheme: light dark; */ 7 | /* color: rgba(255, 255, 255, 0.87); */ 8 | 9 | font-synthesis: none; 10 | text-rendering: optimizeLegibility; 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | } 14 | 15 | 16 | body { 17 | margin: 0; 18 | display: flex; 19 | place-items: center; 20 | background: transparent; 21 | } 22 | 23 | 24 | button { 25 | border-radius: 8px; 26 | border: 1px solid transparent; 27 | padding: 0.6em 1.2em; 28 | font-size: 1em; 29 | font-weight: 500; 30 | font-family: inherit; 31 | background-color: #1a1a1a; 32 | cursor: pointer; 33 | transition: border-color 0.25s; 34 | } 35 | button:hover { 36 | border-color: #646cff; 37 | } 38 | button:focus, 39 | button:focus-visible { 40 | outline: 4px auto -webkit-focus-ring-color; 41 | } 42 | -------------------------------------------------------------------------------- /apps/test-server/src/canvas-test/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | 3 | import { enableDebugTool } from '@webspatial/react-sdk' 4 | import EChartPage from './EChartPage' 5 | import ThreeScene from './ThreeScene' 6 | import ThreeSceneOutsideDivWithCanvas from './ThreeSceneOutsideDivWithJSXCanva' 7 | import VideoTest from './VideoTest' 8 | import CanvasPop from './CanvasPop' 9 | import AudioTest from './AudioTest' 10 | 11 | enableDebugTool() 12 | 13 | function App() { 14 | return ( 15 | <> 16 | 27 | 28 | 29 | 30 | {/* */} 31 | > 32 | ) 33 | } 34 | 35 | ReactDOM.createRoot(document.getElementById('root')!).render() 36 | -------------------------------------------------------------------------------- /apps/test-server/src/nestedscroll/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | 3 | import { enableDebugTool } from '@webspatial/react-sdk' 4 | import { CSSProperties } from 'react' 5 | 6 | enableDebugTool() 7 | 8 | function App() { 9 | const styleContainer: CSSProperties = { 10 | enableXr: true, 11 | } 12 | 13 | const styleHead = { 14 | height: '200px', 15 | fontSize: '50px', 16 | color: 'white', 17 | background: 'linear-gradient(to bottom, yellow, green)', 18 | } 19 | 20 | const styleLongContent = { 21 | height: '200vh', 22 | fontSize: '50px', 23 | color: 'white', 24 | background: 'linear-gradient(to bottom, blue, green)', 25 | '--xr-back': 121, 26 | } 27 | 28 | return ( 29 | 30 | head 31 | 32 | long content 33 | 34 | 35 | ) 36 | } 37 | 38 | ReactDOM.createRoot(document.getElementById('root')!).render() 39 | -------------------------------------------------------------------------------- /packages/core/src/platform-adapter/interface.ts: -------------------------------------------------------------------------------- 1 | export interface CommandResult { 2 | success: boolean 3 | data: any 4 | errorCode: string | undefined 5 | errorMessage: string | undefined 6 | } 7 | 8 | export interface WebSpatialProtocolResult extends CommandResult { 9 | success: boolean 10 | data: 11 | | { 12 | windowProxy: WindowProxy 13 | id: string 14 | } 15 | | undefined 16 | errorCode: string | undefined 17 | errorMessage: string | undefined 18 | } 19 | 20 | export interface PlatformAbility { 21 | callJSB(cmd: string, msg: string): Promise 22 | callWebSpatialProtocol( 23 | schema: string, 24 | query?: string, 25 | target?: string, 26 | features?: string, 27 | ): Promise 28 | 29 | callWebSpatialProtocolSync( 30 | schema: string, 31 | query?: string, 32 | target?: string, 33 | features?: string, 34 | resultCallback?: (result: CommandResult) => void, 35 | ): WebSpatialProtocolResult 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/src/SpatializedDynamic3DElement.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AddEntityToDynamic3DCommand, 3 | SetParentForEntityCommand, 4 | UpdateSpatializedDynamic3DElementProperties, 5 | } from './JSBCommand' 6 | import { SpatialEntity } from './reality' 7 | import { SpatializedElement } from './SpatializedElement' 8 | import { 9 | SpatialEntityOrReality, 10 | SpatializedElementProperties, 11 | } from './types/types' 12 | export class SpatializedDynamic3DElement extends SpatializedElement { 13 | children: SpatialEntityOrReality[] = [] 14 | constructor(id: string) { 15 | super(id) 16 | } 17 | 18 | async addEntity(entity: SpatialEntity) { 19 | const ans = new SetParentForEntityCommand(entity.id, this.id).execute() 20 | this.children.push(entity) 21 | entity.parent = this 22 | return ans 23 | } 24 | async updateProperties(properties: Partial) { 25 | return new UpdateSpatializedDynamic3DElementProperties( 26 | this, 27 | properties, 28 | ).execute() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/SphereEntity.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { EntityProps, EntityEventHandler } from '../type' 3 | import { EntityRefShape } from '../hooks' 4 | import { SpatialSphereGeometryOptions } from '@webspatial/core-sdk' 5 | import { GeometryEntity } from './GeometryEntity' 6 | import { useRealityContext } from '../context' 7 | 8 | type SphereEntityProps = EntityProps & 9 | EntityEventHandler & { 10 | children?: React.ReactNode 11 | materials?: string[] 12 | } & SpatialSphereGeometryOptions 13 | 14 | export const SphereEntity = forwardRef( 15 | ({ children, ...props }, ref) => { 16 | const ctx = useRealityContext() 17 | return ( 18 | ctx!.session.createSphereGeometry(options)} 22 | geometryOptions={{ 23 | radius: props.radius, 24 | }} 25 | > 26 | {children} 27 | 28 | ) 29 | }, 30 | ) 31 | -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/StyledTitleComponent.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import styled from 'styled-components' 3 | 4 | export const StyledTitle = styled.h1<{ $primary?: boolean }>` 5 | font-size: 1.5em; 6 | text-align: center; 7 | position: relative; 8 | color: ${props => (props.$primary ? 'blue' : 'red')}; 9 | --xr-back: ${props => (props.$primary ? 60 : 120)}; 10 | background: #fff; 11 | ` 12 | 13 | export const StyledTitleComponent = () => { 14 | const [isPrimary, setIsPrimary] = useState(true) 15 | 16 | const onClick = () => { 17 | setIsPrimary(v => !v) 18 | } 19 | 20 | const [showText, setShowText] = useState(true) 21 | const onToggleHelloworld = () => { 22 | setShowText(v => !v) 23 | } 24 | 25 | return ( 26 | 27 | toggle helloworld 28 | {showText && helloworld} 29 | 30 | 31 | this is style component 32 | 33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /packages/visionOS/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "pathsToIds": { 3 | "/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/GridMaterial.usda": "CB766F92-EE55-4A63-9401-E7B8C009764D", 4 | "/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda": "65F6F990-A780-4474-B78B-572E0E4E273D", 5 | "/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda": "0A9B4653-B11E-4D6A-850E-C6FCB621626C", 6 | "/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Untitled Scene.usda": "D560BB77-AAF3-4BDE-B7C4-989332A4688B", 7 | "RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/GridMaterial.usda": "66168B71-AB05-424E-8B6C-D33D6E61B08F", 8 | "RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda": "AF09ED6F-1707-48FD-8720-65B998362C09", 9 | "RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda": "D66134B1-3681-4A8E-AFE5-29F257229F3B" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/model/SpatializedDynamic3DElement.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @Observable 4 | class SpatializedDynamic3DElement: SpatializedElement { 5 | private var rootEntity = SpatialEntity() 6 | 7 | func getRoot() -> SpatialEntity { 8 | return rootEntity 9 | } 10 | 11 | func addEntity(_ entity: SpatialEntity) { 12 | rootEntity.addChild(entity) 13 | } 14 | 15 | func removeEntity(_ entity: SpatialEntity) { 16 | rootEntity.removeChild(entity) 17 | } 18 | 19 | enum CodingKeys: String, CodingKey { 20 | case type, root 21 | } 22 | 23 | override func encode(to encoder: Encoder) throws { 24 | try super.encode(to: encoder) 25 | var container = encoder.container(keyedBy: CodingKeys.self) 26 | try container.encode(SpatializedElementType.SpatializedDynamic3DElement, forKey: .type) 27 | try container.encode(rootEntity, forKey: .root) 28 | } 29 | 30 | override func onDestroy() { 31 | rootEntity.destroy() 32 | super.onDestroy() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/ConeEntity.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { EntityProps, EntityEventHandler } from '../type' 3 | import { EntityRefShape } from '../hooks' 4 | import { SpatialConeGeometryOptions } from '@webspatial/core-sdk' 5 | import { GeometryEntity } from './GeometryEntity' 6 | import { useRealityContext } from '../context' 7 | 8 | type ConeEntityProps = EntityProps & 9 | EntityEventHandler & { 10 | children?: React.ReactNode 11 | materials?: string[] 12 | } & SpatialConeGeometryOptions 13 | 14 | export const ConeEntity = forwardRef( 15 | ({ children, ...props }, ref) => { 16 | const ctx = useRealityContext() 17 | return ( 18 | ctx!.session.createConeGeometry(options)} 22 | geometryOptions={{ 23 | radius: props.radius, 24 | height: props.height, 25 | }} 26 | > 27 | {children} 28 | 29 | ) 30 | }, 31 | ) 32 | -------------------------------------------------------------------------------- /packages/cli/src/lib/resource/file.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | export function copyDir(from: string, to: string) { 3 | const paths = fs.readdirSync(from) 4 | paths.forEach(path => { 5 | const _from = from + '/' + path 6 | const _to = to + '/' + path 7 | const stat = fs.statSync(_from) 8 | if (stat.isFile()) { 9 | try { 10 | fs.writeFileSync(_to, fs.readFileSync(_from)) 11 | } catch (err) { 12 | console.log(err) 13 | } 14 | } else if (stat.isDirectory()) { 15 | if (!fs.existsSync(_to)) { 16 | fs.mkdirSync(_to, { recursive: true }) 17 | } 18 | copyDir(_from, _to) 19 | } 20 | }) 21 | } 22 | 23 | export function clearDir(from: string) { 24 | const paths = fs.readdirSync(from) 25 | paths.forEach(path => { 26 | const _from = from + '/' + path 27 | const stat = fs.statSync(_from) 28 | if (stat.isFile()) { 29 | try { 30 | fs.unlinkSync(_from) 31 | } catch (err) { 32 | console.log(err) 33 | } 34 | } else if (stat.isDirectory()) { 35 | clearDir(_from) 36 | } 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/CylinderEntity.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { EntityProps, EntityEventHandler } from '../type' 3 | import { EntityRefShape } from '../hooks' 4 | import { SpatialCylinderGeometryOptions } from '@webspatial/core-sdk' 5 | import { GeometryEntity } from './GeometryEntity' 6 | import { useRealityContext } from '../context' 7 | 8 | type CylinderEntityProps = EntityProps & 9 | EntityEventHandler & { 10 | children?: React.ReactNode 11 | materials?: string[] 12 | } & SpatialCylinderGeometryOptions 13 | 14 | export const CylinderEntity = forwardRef( 15 | ({ children, ...props }, ref) => { 16 | const ctx = useRealityContext() 17 | return ( 18 | ctx!.session.createCylinderGeometry(options)} 22 | geometryOptions={{ 23 | radius: props.radius, 24 | height: props.height, 25 | }} 26 | > 27 | {children} 28 | 29 | ) 30 | }, 31 | ) 32 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container-monitor/withSpatialMonitor.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef, Ref } from 'react' 2 | import { SpatialMonitor } from './SpatialMonitor' 3 | 4 | const cachedWithSpatialMonitorType = new Map() 5 | 6 | export function withSpatialMonitor(El: React.ElementType) { 7 | if (cachedWithSpatialMonitorType.has(El)) { 8 | return cachedWithSpatialMonitorType.get(El) 9 | } else { 10 | const WithSpatialMonitorComponent = forwardRef( 11 | (givenProps: any, givenRef: Ref) => { 12 | const { 13 | El: _, 14 | 15 | ...props 16 | } = givenProps 17 | 18 | return 19 | }, 20 | ) 21 | WithSpatialMonitorComponent.displayName = `WithSpatialMonitor(${typeof El === 'string' ? El : El.displayName || El.name})` 22 | 23 | cachedWithSpatialMonitorType.set(El, WithSpatialMonitorComponent) 24 | cachedWithSpatialMonitorType.set( 25 | cachedWithSpatialMonitorType, 26 | cachedWithSpatialMonitorType, 27 | ) 28 | return WithSpatialMonitorComponent 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/visionOS/Packages/RealityKitContent/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "RealityKitContent", 8 | products: [ 9 | // Products define the executables and libraries a package produces, and make them visible to other packages. 10 | .library( 11 | name: "RealityKitContent", 12 | targets: ["RealityKitContent"] 13 | ), 14 | ], 15 | dependencies: [ 16 | // Dependencies declare other packages that this package depends on. 17 | // .package(url: /* package url */, from: "1.0.0"), 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 21 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 22 | .target( 23 | name: "RealityKitContent", 24 | dependencies: [] 25 | ), 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /packages/react/src/reality/utils/AbortResourceManager.ts: -------------------------------------------------------------------------------- 1 | export class AbortResourceManager { 2 | private resources: { destroy: () => Promise | void }[] = [] 3 | private aborted = false 4 | 5 | constructor(private signal: AbortSignal) { 6 | signal.addEventListener('abort', () => { 7 | this.aborted = true 8 | void this.dispose() 9 | }) 10 | } 11 | 12 | async addResource Promise | void }>( 13 | factory: () => Promise, 14 | ): Promise { 15 | if (this.aborted) throw new DOMException('Aborted', 'AbortError') 16 | const resource = await factory() 17 | if (this.aborted) { 18 | await resource.destroy() 19 | throw new DOMException('Aborted', 'AbortError') 20 | } 21 | this.resources.push(resource) 22 | return resource 23 | } 24 | 25 | async dispose() { 26 | const resources = this.resources.splice(0) 27 | for (const r of resources) { 28 | try { 29 | await r.destroy() 30 | } catch (e) { 31 | console.error('AbortResourceManager dispose error:', e, r) 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/SimpleSpatialComponent.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | export const SimpleSpatialComponent = () => { 3 | const [isPrimary, setIsPrimary] = useState(true) 4 | 5 | const onClick = () => { 6 | setIsPrimary(v => !v) 7 | } 8 | 9 | const style = isPrimary 10 | ? { 11 | // '--xr-back': 180, 12 | width: '200px', 13 | // transform: 'rotate3d(0, 1, 0, -30deg) scale(1.5)', 14 | color: 'blue', 15 | } 16 | : { 17 | // '--xr-back': 10, 18 | color: 'red', 19 | } 20 | 21 | const style2 = { 22 | // '--xr-back': 0, 23 | transformOrigin: 'left top', 24 | // transform: 'rotate3d(0, 1, 0, -30deg)', 25 | color: 'red', 26 | } 27 | 28 | return ( 29 | 30 | 31 | Outter SimpleSpatialComponent 32 | 33 | Inner SimpleSpatialComponent 34 | 35 | 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/SpatialObject.ts: -------------------------------------------------------------------------------- 1 | import { DestroyCommand, InspectCommand } from './JSBCommand' 2 | 3 | /** 4 | * @hidden 5 | * Parent class of spatial objects, should not be used directly 6 | */ 7 | export class SpatialObject { 8 | /** @hidden */ 9 | constructor( 10 | /** @hidden */ 11 | public readonly id: string, 12 | ) {} 13 | 14 | name?: string 15 | 16 | isDestroyed = false 17 | 18 | async inspect() { 19 | const ret = await new InspectCommand(this.id).execute() 20 | if (ret.success) { 21 | return ret.data 22 | } 23 | throw new Error(ret.errorMessage) 24 | } 25 | 26 | async destroy() { 27 | if (this.isDestroyed) { 28 | return 29 | } 30 | 31 | const ret = await new DestroyCommand(this.id).execute() 32 | if (ret.success) { 33 | this.onDestroy() 34 | this.isDestroyed = true 35 | return ret.data 36 | } else if (this.isDestroyed) { 37 | // already destroyed 38 | return 39 | } 40 | 41 | throw new Error(ret.errorMessage) 42 | } 43 | 44 | // override this method to do some cleanup 45 | protected onDestroy() {} 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Spatial Web 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/PlaneEntity.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { EntityProps, EntityEventHandler } from '../type' 3 | import { EntityRefShape } from '../hooks' 4 | import { SpatialPlaneGeometryOptions } from '@webspatial/core-sdk' 5 | import { GeometryEntity } from './GeometryEntity' 6 | import { useRealityContext } from '../context' 7 | 8 | type PlaneEntityProps = EntityProps & 9 | EntityEventHandler & { 10 | children?: React.ReactNode 11 | materials?: string[] 12 | } & SpatialPlaneGeometryOptions 13 | 14 | export const PlaneEntity = forwardRef( 15 | ({ children, ...props }, ref) => { 16 | const ctx = useRealityContext() 17 | return ( 18 | ctx!.session.createPlaneGeometry(options)} 22 | geometryOptions={{ 23 | width: props.width, 24 | height: props.height, 25 | cornerRadius: props.cornerRadius, 26 | }} 27 | > 28 | {children} 29 | 30 | ) 31 | }, 32 | ) 33 | -------------------------------------------------------------------------------- /apps/test-server/src/animate/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | import { enableDebugTool } from '@webspatial/react-sdk' 3 | import './style.scss' 4 | import { PopmotionTest } from './PopmotionTest' 5 | import { TeenjsTest } from './TeenjsTest' 6 | import { GSAPTest } from './GSAPTest' 7 | import { AnimeTest } from './AnimeTest' 8 | import { ReactSpringTest } from './ReactSpringTest' 9 | import { ReactSpringModel3DTest } from './ReactSpringModel3DTest' 10 | 11 | enableDebugTool() 12 | 13 | function App() { 14 | return ( 15 | 16 | 17 | history.go(-1)}> 18 | Go Back 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | ReactDOM.createRoot(document.getElementById('root')!).render() 40 | -------------------------------------------------------------------------------- /tests/ci-test/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import path from 'path' 3 | import react from '@vitejs/plugin-react' 4 | 5 | const packagesBasePath = '../../packages' 6 | const XRSDKBaseDir = path.join(__dirname, packagesBasePath) 7 | 8 | const corePkg = require(`../../packages/core/package.json`) 9 | const reactPkg = require(`../../packages/react/package.json`) 10 | 11 | const tsconfig: any = { 12 | compilerOptions: { 13 | jsx: 'react-jsx', 14 | jsxImportSource: '@webspatial/react-sdk/jsx', 15 | }, 16 | } 17 | 18 | export default defineConfig({ 19 | // root: './', 20 | // logLevel: 'silent', 21 | server: { 22 | port: 4000, 23 | open: false, 24 | }, 25 | 26 | plugins: [react()], 27 | 28 | resolve: { 29 | alias: { 30 | '@webspatial/react-sdk': path.join(XRSDKBaseDir, 'react/src'), 31 | '@webspatial/core-sdk': path.join(XRSDKBaseDir, 'core/src'), 32 | }, 33 | }, 34 | define: { 35 | __WEBSPATIAL_CORE_SDK_VERSION__: JSON.stringify(corePkg.version), 36 | __WEBSPATIAL_REACT_SDK_VERSION__: JSON.stringify(reactPkg.version), 37 | }, 38 | esbuild: { 39 | tsconfigRaw: tsconfig, 40 | }, 41 | }) 42 | -------------------------------------------------------------------------------- /.github/workflows/pr-auto-merge.yml: -------------------------------------------------------------------------------- 1 | # Used to merge main/stable branches with each other 2 | name: PR Auto Merge Bot 3 | 4 | on: 5 | schedule: 6 | - cron: '*/5 * * * *' 7 | 8 | jobs: 9 | pr-check-ci: 10 | if: github.repository == 'webspatial/webspatial-sdk' 11 | permissions: 12 | checks: read # for actions-cool/check-pr-ci to get check reference 13 | contents: write # for actions-cool/check-pr-ci to merge PRs 14 | issues: write # for actions-cool/check-pr-ci to update issues 15 | pull-requests: write # for actions-cool/check-pr-ci to update PRs 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions-cool/check-pr-ci@v1 19 | with: 20 | filter-label: BranchAutoMerge 21 | filter-creator-authority: write 22 | filter-head-ref: 'main, stable, stable-merge-main' 23 | filter-support-fork: false 24 | skip-run-names: 'pr-check-ci' 25 | conflict-review-body: 😅 This branch has conflicts that must be resolved! 26 | success-review: true 27 | success-merge: true 28 | merge-method: merge 29 | merge-title: 'chore: auto merge branches (#${number})' 30 | -------------------------------------------------------------------------------- /.github/workflows/preview-release.yml: -------------------------------------------------------------------------------- 1 | name: Preview release 2 | 3 | permissions: 4 | pull-requests: write 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | - stable 11 | pull_request: 12 | types: [opened, synchronize, labeled] 13 | 14 | jobs: 15 | preview: 16 | if: > 17 | github.repository == 'webspatial/webspatial-sdk' && 18 | (github.event_name == 'push' || 19 | (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'trigger: preview'))) 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout code repository 23 | uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 # Use full git history to enable tagging 26 | 27 | - name: Setup pnpm 28 | uses: pnpm/action-setup@v4 29 | 30 | - name: Setup node.js 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: 20 34 | cache: 'pnpm' 35 | 36 | - name: Install dependencies and build 37 | run: pnpm run setup 38 | 39 | - run: pnpm dlx pkg-pr-new@0.0 publish --compact --pnpm --comment=create --no-template ./packages/* --packageManager=pnpm 40 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/java/com/example/webspatialandroid/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.example.webspatialandroid.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /packages/core/src/platform-adapter/ssr/SSRPlatform.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CommandResult, 3 | PlatformAbility, 4 | WebSpatialProtocolResult, 5 | } from '../interface' 6 | 7 | export class SSRPlatform implements PlatformAbility { 8 | callJSB(cmd: string, msg: string): Promise { 9 | return Promise.resolve({ 10 | success: true, 11 | data: undefined, 12 | errorCode: undefined, 13 | errorMessage: undefined, 14 | }) 15 | } 16 | callWebSpatialProtocol( 17 | schema: string, 18 | query?: string, 19 | target?: string, 20 | features?: string, 21 | ): Promise { 22 | return Promise.resolve({ 23 | success: true, 24 | data: undefined, 25 | errorCode: undefined, 26 | errorMessage: undefined, 27 | }) 28 | } 29 | callWebSpatialProtocolSync( 30 | schema: string, 31 | query?: string, 32 | target?: string, 33 | features?: string, 34 | resultCallback?: (result: CommandResult) => void, 35 | ): WebSpatialProtocolResult { 36 | return { 37 | success: true, 38 | data: undefined, 39 | errorCode: undefined, 40 | errorMessage: undefined, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/BoxEntity.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { EntityProps, EntityEventHandler } from '../type' 3 | import { EntityRefShape } from '../hooks' 4 | import { SpatialBoxGeometryOptions } from '@webspatial/core-sdk' 5 | import { GeometryEntity } from './GeometryEntity' 6 | import { useRealityContext } from '../context' 7 | 8 | type BoxEntityProps = EntityProps & 9 | EntityEventHandler & { 10 | children?: React.ReactNode 11 | materials?: string[] 12 | } & SpatialBoxGeometryOptions 13 | 14 | export const BoxEntity = forwardRef( 15 | ({ children, ...props }, ref) => { 16 | const ctx = useRealityContext() 17 | return ( 18 | ctx!.session.createBoxGeometry(options)} 22 | geometryOptions={{ 23 | width: props.width, 24 | height: props.height, 25 | depth: props.depth, 26 | cornerRadius: props.cornerRadius, 27 | splitFaces: props.splitFaces, 28 | }} 29 | > 30 | {children} 31 | 32 | ) 33 | }, 34 | ) 35 | -------------------------------------------------------------------------------- /packages/cli/src/lib/resource/imageHelper.ts: -------------------------------------------------------------------------------- 1 | import Jimp = require('jimp') 2 | import sharp = require('sharp') 3 | import { Resvg, ResvgRenderOptions } from '@resvg/resvg-js' 4 | 5 | export class ImageHelper { 6 | public static createImg(size: number): Jimp { 7 | return new Jimp(size, size, 0x00000000) 8 | } 9 | public static async webp2PngBuffer(buffer: Buffer): Promise { 10 | return await sharp(buffer).toFormat('png').toBuffer() 11 | } 12 | 13 | public static async svg2img(svg: string): Promise { 14 | const opt = { 15 | fitTo: { 16 | mode: 'width', 17 | value: 1024, // Generate the SVG with 1024px width, for larger icons. 18 | }, 19 | } as ResvgRenderOptions 20 | const resvg = new Resvg(svg, opt) 21 | const pngData = resvg.render() 22 | const pngBuffer = pngData.asPng() 23 | return pngBuffer 24 | } 25 | 26 | public static isFullyOpaque(image: Jimp): boolean { 27 | const pixelNum = image.getWidth() * image.getHeight() 28 | for (var i = 0; i < pixelNum; i++) { 29 | const idx = i * 4 + 3 30 | if (image.bitmap.data[idx] < 255) { 31 | return false 32 | } 33 | } 34 | return true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Utils/ColorExtension.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | extension Color { 4 | init(hex: String) { 5 | let hex = hex.trimmingCharacters(in: .whitespacesAndNewlines) 6 | .replacingOccurrences(of: "#", with: "") 7 | 8 | guard hex.count == 6 || hex.count == 8 else { 9 | self.init(.sRGB, red: 0, green: 0, blue: 0, opacity: 1) 10 | return 11 | } 12 | 13 | let scanner = Scanner(string: hex) 14 | var hexNumber: UInt64 = 0 15 | scanner.scanHexInt64(&hexNumber) 16 | 17 | let r, g, b, a: Double 18 | if hex.count == 8 { 19 | r = Double((hexNumber & 0xFF00_0000) >> 24) / 255 20 | g = Double((hexNumber & 0x00FF_0000) >> 16) / 255 21 | b = Double((hexNumber & 0x0000_FF00) >> 8) / 255 22 | a = Double(hexNumber & 0x0000_00FF) / 255 23 | } else { 24 | a = 1.0 25 | r = Double((hexNumber & 0xFF0000) >> 16) / 255 26 | g = Double((hexNumber & 0x00FF00) >> 8) / 255 27 | b = Double(hexNumber & 0x0000FF) / 255 28 | } 29 | 30 | self.init(.sRGB, red: r, green: g, blue: b, opacity: a) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/ci-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ci-test", 3 | "private": true, 4 | "version": "0.0.16", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "tsx scripts/dev.ts", 8 | "test": "mocha", 9 | "runbuilder": "tsx scripts/runbuilder.ts", 10 | "lint": "eslint .", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "chai": "^5.2.0", 15 | "express": "^4.21.2", 16 | "mocha": "^11.1.0", 17 | "react": "^19.0.0", 18 | "react-dom": "^19.0.0", 19 | "tsx": "^4.19.3", 20 | "@webspatial/core-sdk": "workspace:*", 21 | "@webspatial/react-sdk": "workspace:*", 22 | "@webspatial/builder": "workspace:^" 23 | }, 24 | "devDependencies": { 25 | "@eslint/js": "^9.22.0", 26 | "@types/chai": "^5.2.1", 27 | "@types/mocha": "^10.0.10", 28 | "@types/react": "^19.0.10", 29 | "@types/react-dom": "^19.0.4", 30 | "@types/express": "^5.0.1", 31 | "@vitejs/plugin-react": "^4.3.4", 32 | "eslint": "^9.22.0", 33 | "eslint-plugin-react-hooks": "^5.2.0", 34 | "eslint-plugin-react-refresh": "^0.4.19", 35 | "globals": "^16.0.0", 36 | "typescript": "~5.7.2", 37 | "typescript-eslint": "^8.26.1", 38 | "vite": "^6.3.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/webview/SpatialWebView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | @preconcurrency import WebKit 3 | 4 | struct SpatialWebView: UIViewRepresentable { 5 | weak var model: SpatialWebViewModel? = nil 6 | var url: URL = .init(filePath: "/") 7 | private var webviewStateChangeInvoke: ((_ type: SpatialWebViewState) -> Void)? 8 | 9 | func makeUIView(context: Context) -> WKWebView { 10 | webviewStateChangeInvoke?(.didMakeView) 11 | return model!.getController().webview! 12 | } 13 | 14 | func makeCoordinator() -> SpatialWebController { 15 | return model!.getController() 16 | } 17 | 18 | func updateUIView(_ webView: WKWebView, context: Context) { 19 | webviewStateChangeInvoke?(.didUpdateView) 20 | } 21 | 22 | mutating func registerWebviewStateChangeInvoke(invoke: @escaping (_ type: SpatialWebViewState) -> Void) { 23 | webviewStateChangeInvoke = invoke 24 | } 25 | 26 | mutating func destroy() { 27 | webviewStateChangeInvoke = nil 28 | model = nil 29 | } 30 | 31 | static func dismantleUIView(_ uiView: WKWebView, coordinator: SpatialWebController) { 32 | // print("dismantleUIView", coordinator.model?.id) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/react/src/reality/hooks/useEntityTransform.tsx: -------------------------------------------------------------------------------- 1 | import { SpatialEntity } from '@webspatial/core-sdk' 2 | import { useEffect, useRef } from 'react' 3 | import { EntityProps } from '../type' 4 | import { shallowEqualRotation, shallowEqualVec3 } from '../utils' 5 | 6 | export function useEntityTransform( 7 | entity: SpatialEntity | null, 8 | { position, rotation, scale }: EntityProps, 9 | ) { 10 | const last = useRef<{ position?: any; rotation?: any; scale?: any }>({}) 11 | 12 | useEffect(() => { 13 | if (!entity) return 14 | 15 | const shouldUpdate = 16 | !shallowEqualVec3(last.current.position, position) || 17 | !shallowEqualRotation(last.current.rotation, rotation) || 18 | !shallowEqualVec3(last.current.scale, scale) 19 | 20 | if (!shouldUpdate) return 21 | 22 | last.current = { position, rotation, scale } 23 | 24 | const updateTransform = async () => { 25 | try { 26 | await entity.updateTransform({ position, rotation, scale }) 27 | } catch (err) { 28 | console.error('[useEntityTransform] Failed to update transform:', err) 29 | } 30 | } 31 | 32 | updateTransform() 33 | 34 | return () => {} 35 | }, [entity, position, rotation, scale]) 36 | } 37 | -------------------------------------------------------------------------------- /packages/androidXR/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/model/dynamic3d/SpatialMaterial.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import RealityKit 3 | 4 | @Observable 5 | class SpatialMaterial: SpatialObject { 6 | let type: SpatialMaterialType 7 | 8 | internal var _resource:RealityKit.Material? = nil 9 | var resource:RealityKit.Material? { 10 | _resource 11 | } 12 | 13 | init(_ _type:SpatialMaterialType){ 14 | type = _type 15 | super.init() 16 | } 17 | 18 | override func onDestroy() { 19 | _resource = nil 20 | } 21 | } 22 | 23 | @Observable 24 | class SpatialUnlitMaterial: SpatialMaterial{ 25 | let color:UIColor 26 | 27 | init(_ color:String, _ texture:TextureResource? = nil, _ transparent:Bool = true, _ opacity:Float = 1){ 28 | self.color = UIColor.init(Color(hex: color)) 29 | super.init(.UnlitMaterial) 30 | var mat = UnlitMaterial() 31 | mat.color = .init(tint:UIColor(Color.init(hex: color)), texture: texture != nil ? .init(texture!) : nil) 32 | mat.blending = transparent ? .transparent(opacity: .init(scale: opacity)) : .opaque 33 | _resource = mat 34 | } 35 | } 36 | 37 | enum SpatialMaterialType: String{ 38 | case UnlitMaterial = "UnlitMaterial" 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | declare const __WEBSPATIAL_CORE_SDK_VERSION__: string 3 | 4 | interface Window { 5 | xrCurrentSceneType: SpatialSceneType 6 | xrCurrentSceneDefaults: ( 7 | defaultConfig: SpatialSceneCreationOptions, 8 | ) => Promise 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 | 26 | __webspatialsdk__?: { 27 | XR_ENV?: string 28 | 'natvie-version'?: string 29 | 'react-sdk-version'?: string 30 | 'core-sdk-version'?: string 31 | } 32 | 33 | innerDepth: number 34 | outerDepth: number 35 | } 36 | 37 | interface HTMLElement { 38 | offsetBack: number 39 | clientDepth: number 40 | getBoundingClientCube: () => CubeInfo | undefined 41 | } 42 | } 43 | 44 | export {} 45 | -------------------------------------------------------------------------------- /packages/react/src/reality/components/UnlitMaterial.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react' 2 | import { 3 | SpatialUnlitMaterial, 4 | SpatialUnlitMaterialOptions, 5 | } from '@webspatial/core-sdk' 6 | import { useRealityContext } from '../context' 7 | type Props = { 8 | children?: React.ReactNode 9 | id: string // user id 10 | } & SpatialUnlitMaterialOptions 11 | export const UnlitMaterial: React.FC = ({ children, ...options }) => { 12 | const ctx = useRealityContext() 13 | const materialRef = useRef() 14 | useEffect(() => { 15 | if (!ctx) return 16 | const { session, reality, resourceRegistry } = ctx 17 | const init = async () => { 18 | const materialPromise = session.createUnlitMaterial(options) 19 | resourceRegistry.add(options.id, materialPromise) 20 | try { 21 | const mat = await materialPromise 22 | materialRef.current = mat 23 | } catch (error) { 24 | console.error(' ~ UnlitMaterial ~ error:', error) 25 | } 26 | } 27 | init() 28 | 29 | return () => { 30 | // Use registry to schedule destruction after promise resolves 31 | resourceRegistry.removeAndDestroy(options.id) 32 | } 33 | }, [ctx]) 34 | 35 | return null 36 | } 37 | -------------------------------------------------------------------------------- /tests/ci-test/src/specs/core-api.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Spatial } from '@webspatial/core-sdk' 2 | import { expect, assert } from 'chai' 3 | 4 | const fail = assert.fail 5 | 6 | const session = new Spatial().requestSession() 7 | 8 | describe('Core API', function () { 9 | it('SpatialSession exist', function () { 10 | expect(session).to.be.true 11 | }) 12 | 13 | it('setBackgroundStyle', async function () { 14 | const prevColor = document.documentElement.style.color 15 | const prevPadding = document.documentElement.style.padding 16 | document.documentElement.style.color = 'white' 17 | document.documentElement.style.padding = '50px' 18 | 19 | try { 20 | session?.getSpatialScene().updateSpatialProperties({ 21 | material: 'translucent', 22 | }) 23 | } catch (error) { 24 | fail('setBackgroundStyle failed') 25 | } 26 | 27 | document.documentElement.style.color = prevColor 28 | document.documentElement.style.padding = prevPadding 29 | }) 30 | 31 | it.only('window.open', async function () { 32 | this.timeout(150000 * 360000) 33 | try { 34 | const windowProxy = window.open('/testPage.html') 35 | windowProxy?.close() 36 | } catch (error) { 37 | fail('failed') 38 | } 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /apps/test-server/src/animate/GSAPTest.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react' 2 | import gsap from 'gsap' 3 | import { useGSAP } from '@gsap/react' 4 | 5 | gsap.registerPlugin(useGSAP) 6 | 7 | export function GSAPTest() { 8 | const ref = useRef(null) 9 | 10 | ;(window as any).ref = ref 11 | 12 | const { contextSafe } = useGSAP({ scope: ref }) 13 | 14 | const onChangeBack = contextSafe(() => { 15 | gsap.to(ref.current, { 16 | rotation: 200, 17 | '--xr-back': 200, 18 | duration: 1, 19 | clearProps: 'all', 20 | }) 21 | }) 22 | 23 | const onChangeOpacity = contextSafe(() => { 24 | gsap.to(ref.current, { 25 | opacity: 0.5, 26 | x: 200, 27 | duration: 1, 28 | clearProps: 'all', 29 | }) 30 | }) 31 | 32 | return ( 33 | 34 | this is GSAP test 35 | 36 | this is parent 37 | 38 | 39 | 40 | 41 | start animate xr-back 42 | 43 | 44 | start animate opacity 45 | 46 | 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /apps/test-server/src/spatialStyleTest/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | import { CSSModelSample } from './CSSModelSample' 3 | import { enableDebugTool } from '@webspatial/react-sdk' 4 | import { SpatialTagComponent } from './SpatialTagComponent' 5 | import { NestedComponent } from './NestedComponent' 6 | import { CubeComponent } from './CubeComponent' 7 | import { SimpleSpatialComponent } from './SimpleSpatialComponent' 8 | import { StyledTitleComponent } from './StyledTitleComponent' 9 | 10 | enableDebugTool() 11 | 12 | function App() { 13 | return ( 14 | 15 | 16 | history.go(-1)}> 17 | Go Back 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | } 34 | 35 | ReactDOM.createRoot(document.getElementById('root')!).render() 36 | -------------------------------------------------------------------------------- /packages/core/tsup.config.ts: -------------------------------------------------------------------------------- 1 | // tsup.config.ts 2 | import { defineConfig, Options } from 'tsup' 3 | import { version } from './package.json' 4 | const baseConfig: Options = { 5 | splitting: false, 6 | sourcemap: true, 7 | clean: true, 8 | dts: true, 9 | banner: { 10 | js: ` 11 | (function(){ 12 | if(typeof window === 'undefined') return; 13 | if(!window.__webspatialsdk__) window.__webspatialsdk__ = {} 14 | window.__webspatialsdk__['core-sdk-version'] = ${JSON.stringify(version)} 15 | })() 16 | `, 17 | }, 18 | } 19 | 20 | const versionDefine = { 21 | __WEBSPATIAL_CORE_SDK_VERSION__: JSON.stringify(version), 22 | } 23 | 24 | export default defineConfig([ 25 | { 26 | ...baseConfig, 27 | entry: ['src/index.ts'], 28 | format: ['esm'], 29 | outDir: 'dist', 30 | esbuildOptions(options) { 31 | options.define = { 32 | ...options.define, 33 | ...versionDefine, 34 | } 35 | }, 36 | }, 37 | { 38 | ...baseConfig, 39 | entry: ['src/index.ts'], 40 | format: ['iife'], 41 | outDir: 'dist/iife', 42 | globalName: 'webspatialCore', 43 | esbuildOptions(options) { 44 | options.define = { 45 | ...options.define, 46 | ...versionDefine, 47 | } 48 | }, 49 | minify: true, 50 | }, 51 | ]) 52 | -------------------------------------------------------------------------------- /apps/test-server/src/jsapi-test/index.tsx: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | // import React from 'react' 4 | // import ReactDOM from 'react-dom/client' 5 | 6 | import { 7 | testCreateSpatialized2DElement, 8 | testSpatialSceneCorner, 9 | testSpatialSceneMaterial, 10 | testSpatialInspect, 11 | testSpatialSceneInspect, 12 | testAddMultipleSpatialized2DElement, 13 | testAddMultipleSpatializedStatic3DElement, 14 | } from './jsapi' 15 | 16 | // testSpatialSceneMaterial() 17 | // testSpatialSceneCorner() 18 | testCreateSpatialized2DElement().then(spatialized2DElement => { 19 | testAddMultipleSpatialized2DElement(spatialized2DElement) 20 | // testAddMultipleSpatializedStatic3DElement(spatialized2DElement) 21 | // .then(() => 22 | // testSpatialSceneInspect(), 23 | // ) 24 | }) 25 | 26 | window.testSpatialSceneInspect = testSpatialSceneInspect 27 | 28 | // function App() { 29 | // const style = { 30 | // width: '100vw', 31 | // height: '150vh', 32 | // } 33 | // return ( 34 | // 35 | // this is jsapi test page 36 | // 37 | // ) 38 | // } 39 | 40 | // // Initialize react 41 | // var root = document.createElement('div') 42 | // document.body.appendChild(root) 43 | // ReactDOM.createRoot(root).render() 44 | 45 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container-monitor/useMonitorDomChange.tsx: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect, ForwardedRef, useMemo } from 'react' 2 | import { notifyDOMUpdate } from '../notifyUpdateStandInstanceLayout' 3 | 4 | export function useMonitorDomChange(inRef: ForwardedRef) { 5 | const ref = useRef(null) 6 | 7 | useEffect(() => { 8 | const observer = new MutationObserver(mutationsList => { 9 | notifyDOMUpdate(mutationsList) 10 | }) 11 | 12 | const config = { 13 | childList: true, 14 | subtree: true, 15 | attributes: true, 16 | attributeFilter: ['style', 'class'], 17 | } 18 | 19 | ref.current && observer.observe(ref.current, config) 20 | 21 | return () => { 22 | observer.disconnect() 23 | } 24 | }, []) 25 | 26 | const proxyRef = useMemo( 27 | () => 28 | new Proxy(ref, { 29 | set: function (target, key, value) { 30 | if (key === 'current') { 31 | if (inRef) { 32 | if (typeof inRef === 'function') { 33 | inRef(value) 34 | } else if (inRef) { 35 | inRef.current = value 36 | } 37 | } 38 | } 39 | return Reflect.set(target, key, value) 40 | }, 41 | }), 42 | [], 43 | ) 44 | 45 | return proxyRef 46 | } 47 | -------------------------------------------------------------------------------- /tests/ci-test/types/runtime.d.ts: -------------------------------------------------------------------------------- 1 | interface SpatialObjectInfo { 2 | id: string 3 | name: string 4 | isDestroyed: boolean 5 | } 6 | 7 | interface SpatialComponentInfo extends SpatialObjectInfo { 8 | type: string 9 | entity: string 10 | } 11 | 12 | interface SpatialEntityInfo extends SpatialObjectInfo { 13 | position: string 14 | scale: string 15 | zIndex: number 16 | visible: boolean 17 | childEntities: Record 18 | coordinateSpace: 'App' | 'Dom' | 'Root' 19 | parent: string 20 | parentWindowContainer: string 21 | components: Array 22 | } 23 | 24 | interface SpatialWindowComponentInfo extends SpatialComponentInfo { 25 | scrollWithParent: boolean 26 | resolutionX: number 27 | resolutionY: number 28 | parentWebviewID: string 29 | parentWindowContainerID: string 30 | childWindowContainers: Array 31 | spawnedNativeWebviewsCount: number 32 | childResources: Record 33 | cornerRadius: { 34 | topLeading: number 35 | bottomLeading: number 36 | topTrailing: number 37 | bottomTrailing: number 38 | } 39 | backgroundMaterial: string 40 | isScrollEnabled: boolean 41 | isOpaque: boolean 42 | } 43 | 44 | interface WindowContainerInfo extends SpatialObjectInfo { 45 | childEntities: Record 46 | } 47 | -------------------------------------------------------------------------------- /packages/react/src/reality/utils/ResourceRegistry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpatializedDynamic3DElement, 3 | SpatialObject, 4 | SpatialSession, 5 | } from '@webspatial/core-sdk' 6 | export class ResourceRegistry { 7 | private resources: Map> = new Map() 8 | add(id: string, resource: Promise) { 9 | this.resources.set(id, resource) 10 | } 11 | remove(id: string) { 12 | this.resources.delete(id) 13 | } 14 | 15 | // Remove the resource by id and destroy it once resolved 16 | // This does not cancel in-flight creation; it schedules destruction after resolution 17 | removeAndDestroy(id: string) { 18 | const p = this.resources.get(id) 19 | if (p) { 20 | // Schedule destruction when the resource becomes available 21 | p.then(spatialObj => spatialObj.destroy()) 22 | } 23 | this.resources.delete(id) 24 | } 25 | get(id: string) { 26 | return this.resources.get(id) as Promise 27 | } 28 | destroy() { 29 | // Collect pending resources and clear registry immediately 30 | const pending = Array.from(this.resources.values()) 31 | this.resources.clear() 32 | 33 | // Best-effort destroy for all resolved and future-resolving resources 34 | pending.forEach(promise => promise.then(spatialObj => spatialObj.destroy())) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/react/src/ssr/withSSRSupported.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentType, forwardRef, PropsWithoutRef } from 'react' 2 | import { useSSRPhase } from './useSSRPhase' 3 | 4 | export type SSRPhase = 'ssr' | 'hydrate' | 'after-hydrate' 5 | 6 | /** 7 | * A HOC that ensures a component is only rendered on the client side. 8 | * It returns null during server-side rendering. 9 | * @param Component The component to be rendered only on the client. 10 | */ 11 | export function withSSRSupported(Component: ComponentType) { 12 | const ClientOnlyComponent = ( 13 | props: PropsWithoutRef, 14 | ref: React.ForwardedRef, 15 | ) => { 16 | const phase = useSSRPhase() 17 | 18 | let renderType: 'fake' | 'real' = 'real' 19 | 20 | if (phase === 'ssr' || phase === 'hydrate') { 21 | renderType = 'fake' 22 | } 23 | 24 | if (renderType === 'fake') { 25 | const { style, className } = props as any 26 | // keep style and className for SSR, prevent style flicker on hydration 27 | return 28 | } else { 29 | return 30 | } 31 | } 32 | 33 | ClientOnlyComponent.displayName = `withClientOnly(${Component.displayName || Component.name || 'Component'})` 34 | 35 | return forwardRef(ClientOnlyComponent) 36 | } 37 | -------------------------------------------------------------------------------- /tests/ci-test/scripts/vite-server.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from 'vite' 2 | import express from 'express' 3 | import { MOCHA_RESULT_API } from '../src/api' 4 | import { Server } from 'http' 5 | 6 | export async function runViteServer( 7 | option: { port?: number; mochaResultCb?: any } = {}, 8 | ): Promise { 9 | const app = express() 10 | 11 | const vite = await createServer({ 12 | configFile: './vite.config.ts', 13 | server: { middlewareMode: true }, 14 | appType: 'mpa', 15 | }) 16 | 17 | const port = option.port || vite.config.server.port 18 | 19 | app.use(express.json()) 20 | 21 | app.post(MOCHA_RESULT_API, (req, res) => { 22 | const data = req.body 23 | res.status(200).json({ message: 'Data received successfully', data }) 24 | option.mochaResultCb && option.mochaResultCb(data) 25 | }) 26 | 27 | app.use(vite.middlewares) 28 | 29 | return new Promise((resolve, reject) => { 30 | const server = app.listen(port, err => { 31 | if (err) { 32 | reject(err) 33 | } else { 34 | server.on('close', () => { 35 | console.log('Express server has been closed.') 36 | vite.close() 37 | }) 38 | 39 | resolve(server) 40 | 41 | const url = `http://localhost:${port}` 42 | console.log(`Vite server running at ${url}`) 43 | } 44 | }) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /packages/react/src/spatialized-container/hooks/useSpatializedElement.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { PortalInstanceObject } from '../context/PortalInstanceContext' 3 | import { SpatializedElement } from '@webspatial/core-sdk' 4 | 5 | export function useSpatializedElement( 6 | createSpatializedElement: () => Promise, 7 | portalInstanceObject: PortalInstanceObject, 8 | ) { 9 | const [spatializedElement, setSpatializedElement] = 10 | useState() 11 | 12 | useEffect(() => { 13 | let isDestroyed = false 14 | let spatializedElement: SpatializedElement | undefined 15 | createSpatializedElement().then( 16 | (inSpatializedElement: SpatializedElement) => { 17 | if (!isDestroyed) { 18 | spatializedElement = inSpatializedElement 19 | portalInstanceObject.attachSpatializedElement(spatializedElement) 20 | 21 | setSpatializedElement(spatializedElement) 22 | } else { 23 | inSpatializedElement?.destroy() 24 | } 25 | }, 26 | ) 27 | 28 | return () => { 29 | isDestroyed = true 30 | if (spatializedElement) { 31 | spatializedElement.destroy() 32 | spatializedElement = undefined 33 | } 34 | } 35 | }, [createSpatializedElement, portalInstanceObject]) 36 | 37 | return spatializedElement 38 | } 39 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/model/dynamic3d/SpatialModelResource.swift: -------------------------------------------------------------------------------- 1 | import RealityKit 2 | import SwiftUI 3 | 4 | @Observable 5 | class SpatialModelResource: SpatialObject { 6 | var _resource: Entity? = nil 7 | var resource: Entity? { 8 | _resource 9 | } 10 | 11 | init(_ urlString: String, _ onload: @escaping (Result) -> Void) { 12 | super.init() 13 | Dynamic3DManager.loadResourceToLocal(urlString) { result in 14 | switch result { 15 | case let .success(url): 16 | DispatchQueue.main.async { 17 | do { 18 | let entity = try Entity.load(contentsOf: url) 19 | self._resource = entity 20 | onload(.success(self)) 21 | } catch { 22 | print("Failed to load entity from URL: \(error)") 23 | onload(.failure(error)) 24 | self.destroy() 25 | } 26 | } 27 | case let .failure(error): 28 | print("Failed to download model: \(error)") 29 | onload(.failure(error)) 30 | self.destroy() 31 | } 32 | } 33 | } 34 | 35 | override func onDestroy() { 36 | _resource = nil 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | - stable 9 | pull_request: 10 | 11 | env: 12 | CI: true 13 | SKIP_SIMPLE_GIT_HOOKS: 1 14 | 15 | jobs: 16 | version: 17 | timeout-minutes: 15 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout code repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup pnpm 24 | uses: pnpm/action-setup@v4 25 | 26 | - name: Setup node.js 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: 20 30 | cache: 'pnpm' 31 | 32 | - name: Install dependencies and build 33 | run: pnpm run setup 34 | 35 | - name: Test 36 | run: pnpm run test 37 | 38 | - name: Test changeset 39 | run: npx changeset version 40 | 41 | - name: Report legacy status 42 | uses: actions/github-script@v6 43 | with: 44 | github-token: ${{ secrets.GITHUB_TOKEN }} 45 | script: | 46 | await github.rest.repos.createCommitStatus({ 47 | owner: context.repo.owner, 48 | repo: context.repo.repo, 49 | sha: context.sha, 50 | state: 'success', 51 | context: 'ci/github-actions', 52 | description: 'CI passed' 53 | }); 54 | -------------------------------------------------------------------------------- /packages/react/src/Model.tsx: -------------------------------------------------------------------------------- 1 | import { ForwardedRef, forwardRef } from 'react' 2 | import { 3 | SpatializedStatic3DContainerProps, 4 | SpatializedStatic3DElementContainer, 5 | SpatializedStatic3DElementRef, 6 | } from './spatialized-container' 7 | import { withSSRSupported } from './ssr' 8 | 9 | import { Spatial } from '@webspatial/core-sdk' 10 | 11 | export type ModelProps = SpatializedStatic3DContainerProps & { 12 | 'enable-xr'?: boolean 13 | } 14 | 15 | export type ModelRef = SpatializedStatic3DElementRef 16 | 17 | const spatial = new Spatial() 18 | 19 | function ModelBase(props: ModelProps, ref: ForwardedRef) { 20 | const { 'enable-xr': enableXR, ...restProps } = props 21 | if (!enableXR || !spatial.runInSpatialWeb()) { 22 | const { 23 | onSpatialTap, 24 | onSpatialDragStart, 25 | onSpatialDrag, 26 | onSpatialDragEnd, 27 | onSpatialRotateStart, 28 | onSpatialRotate, 29 | onSpatialRotateEnd, 30 | onSpatialMagnifyStart, 31 | onSpatialMagnify, 32 | onSpatialMagnifyEnd, 33 | ...modelProps 34 | } = restProps 35 | // map to VisionOS26 model tag 36 | // @ts-ignore 37 | return 38 | } 39 | 40 | return 41 | } 42 | 43 | export const Model = withSSRSupported(forwardRef(ModelBase)) 44 | Model.displayName = 'Model' 45 | -------------------------------------------------------------------------------- /packages/react/src/noRuntime.ts: -------------------------------------------------------------------------------- 1 | // Redirect to empty module for treeshaking 2 | export default {} 3 | 4 | export const SpatialHelper = {} 5 | 6 | export class Spatial { 7 | /** 8 | * Requests a session object from the browser 9 | * @returns The session or null if not availible in the current browser 10 | * [TODO] discuss implications of this not being async 11 | */ 12 | requestSession() { 13 | return null 14 | } 15 | /** 16 | * Checks if the current page is running in a spatial web environment. 17 | * This method detects if the application is running in a WebSpatial-compatible browser. 18 | * @returns True if running in a spatial web environment, false otherwise 19 | */ 20 | runInSpatialWeb() { 21 | return false 22 | } 23 | /** 24 | * @returns true if web spatial is supported by this webpage 25 | */ 26 | isSupported() { 27 | return false 28 | } 29 | /** 30 | * Gets the native version, format is "x.x.x" 31 | * @returns native version string 32 | */ 33 | getNativeVersion() { 34 | return null 35 | } 36 | /** 37 | * Gets the client version, format is "x.x.x" 38 | * @returns client version string 39 | */ 40 | getClientVersion() { 41 | return null 42 | } 43 | } 44 | 45 | export const version = undefined // no runtime so this should set undefined 46 | 47 | export class SpatialScene {} 48 | 49 | export function isSSREnv() { 50 | return false 51 | } 52 | -------------------------------------------------------------------------------- /packages/androidXR/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /tests/ci-test/test/e2e.test.ts: -------------------------------------------------------------------------------- 1 | import { runViteServer } from '../scripts/vite-server' 2 | import { runWebSpatialBuilder } from './websptial-builder-utils' 3 | import { AddressInfo } from 'node:net' 4 | import { AsyncPromise } from '../utils/AsyncPromise' 5 | import { assert } from 'chai' 6 | const fail = assert.fail 7 | 8 | describe('E2E Test For Webspatial SDK', function () { 9 | this.timeout(1000 * 60 * 5) // 5 minutes 10 | 11 | it('should pass', async () => { 12 | const promise = new AsyncPromise() 13 | 14 | try { 15 | const server = await runViteServer({ 16 | mochaResultCb: (data: any) => { 17 | promise.resolve(data) 18 | }, 19 | }) 20 | 21 | const address = server.address() as AddressInfo 22 | const entryURL = `http://localhost:${address.port}/` 23 | try { 24 | await runWebSpatialBuilder(entryURL) 25 | } catch (error) { 26 | console.error(error) 27 | fail() 28 | } 29 | 30 | // wait for mocha result finished 31 | const result = await promise.waitFinish() 32 | const hasError = result.failures.length > 0 33 | if (hasError) { 34 | console.error('Failures', result.failures) 35 | fail() 36 | } else { 37 | // close server 38 | server.close() 39 | } 40 | } catch (error) { 41 | console.log('error', error) 42 | fail(error as string) 43 | } 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/Window.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | // Access window (https://stackoverflow.com/questions/60359808/how-to-access-own-window-within-swiftui-view/60359809#60359809) 5 | class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate { 6 | var window: UIWindow? // << contract of `UIWindowSceneDelegate` 7 | 8 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 9 | guard let windowScene = scene as? UIWindowScene else { return } 10 | window = windowScene.keyWindow // << store !!! 11 | window?.overrideUserInterfaceStyle = .light 12 | } 13 | 14 | // do memory cleanup after scene removed, otherwise windowContainer cannot destroy content after being dismissed 15 | func sceneDidDisconnect(_ scene: UIScene) { 16 | window = nil 17 | } 18 | } 19 | 20 | class AppDelegate: NSObject, UIApplicationDelegate { 21 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 22 | let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role) 23 | if connectingSceneSession.role == .windowApplication { 24 | configuration.delegateClass = SceneDelegate.self 25 | } 26 | return configuration 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/visionOS/web-spatial/model/dynamic3d/SpatialModelEntity.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import RealityKit 3 | 4 | @Observable 5 | class SpatialModelEntity: SpatialEntity{ 6 | private var modelEntity:Entity? = nil 7 | required init(_ modelResource:SpatialModelResource, _ _name:String = ""){ 8 | super.init(_name) 9 | modelEntity = modelResource.resource 10 | addChild(modelEntity!) 11 | generateCollisionShapes(recursive: true) 12 | } 13 | 14 | required init() { 15 | super.init() 16 | } 17 | 18 | override internal func onDestroy(){ 19 | super.onDestroy() 20 | if let modelEntity = self.modelEntity{ 21 | removeChild(modelEntity) 22 | } 23 | modelEntity = nil 24 | } 25 | 26 | enum CodingKeys: String, CodingKey { 27 | case id, name, isDestroyed, children, components, model 28 | } 29 | 30 | override func encode(to encoder: any Encoder) throws { 31 | var container = encoder.container(keyedBy: CodingKeys.self) 32 | try container.encode(spatialId, forKey: .id) 33 | try container.encode(name, forKey: .name) 34 | try container.encode(isDestroyed, forKey: .isDestroyed) 35 | try container.encode(spatialChildren, forKey: .children) 36 | try container.encode(spatialComponents, forKey: .components) 37 | try container.encode(modelEntity?.id, forKey: .model) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/ci-test/src/main.ts: -------------------------------------------------------------------------------- 1 | import { postMochaResult } from './api' 2 | import './main.css' 3 | 4 | import { enableDebugTool } from '@webspatial/react-sdk' 5 | 6 | enableDebugTool() 7 | 8 | async function runMocha(): Promise { 9 | const modules = import.meta.glob('./specs/*.spec.tsx', { eager: true }) 10 | for (const path in modules) { 11 | modules[path] 12 | } 13 | const runner = mocha.run() 14 | 15 | const results: TestResults = { 16 | passes: [], 17 | failures: [], 18 | } 19 | 20 | runner.on('pass', function (test) { 21 | results.passes.push({ 22 | title: test.title, 23 | fullTitle: test.fullTitle(), 24 | duration: test.duration, 25 | }) 26 | }) 27 | 28 | runner.on('fail', function (test, err) { 29 | results.failures.push({ 30 | title: test.title, 31 | fullTitle: test.fullTitle(), 32 | err: err.message, 33 | }) 34 | }) 35 | 36 | return new Promise((resolve, _) => { 37 | runner.on('end', function () { 38 | resolve(results) 39 | }) 40 | }) 41 | } 42 | 43 | async function waitWindowLoaded() { 44 | return new Promise((resolve, _) => { 45 | window.onload = () => { 46 | resolve(null) 47 | } 48 | }) 49 | } 50 | 51 | async function main() { 52 | const p1 = waitWindowLoaded() 53 | const p2 = runMocha() 54 | await Promise.allSettled([p1, p2]) 55 | const results = await p2 56 | postMochaResult(results) 57 | } 58 | 59 | main() 60 | -------------------------------------------------------------------------------- /tests/ci-test/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/test-server/public/vite.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------