@cavegiant/cave-world-r3f 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/dist/core/connector-label/readiness.d.ts +11 -0
- package/dist/core/connector-label/readiness.d.ts.map +1 -0
- package/dist/core/connector-label/readiness.js +31 -0
- package/dist/core/connector-label/readiness.js.map +1 -0
- package/dist/core/connector-label/types.d.ts +64 -0
- package/dist/core/connector-label/types.d.ts.map +1 -0
- package/dist/core/connector-label/types.js +47 -0
- package/dist/core/connector-label/types.js.map +1 -0
- package/dist/core/context/cave-world-context.d.ts +96 -0
- package/dist/core/context/cave-world-context.d.ts.map +1 -0
- package/dist/core/context/cave-world-context.js +100 -0
- package/dist/core/context/cave-world-context.js.map +1 -0
- package/dist/core/errors.d.ts +34 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +17 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/events/event-bus.d.ts +122 -0
- package/dist/core/events/event-bus.d.ts.map +1 -0
- package/dist/core/events/event-bus.js +62 -0
- package/dist/core/events/event-bus.js.map +1 -0
- package/dist/core/hooks/use-cave-event.d.ts +13 -0
- package/dist/core/hooks/use-cave-event.d.ts.map +1 -0
- package/dist/core/hooks/use-cave-event.js +12 -0
- package/dist/core/hooks/use-cave-event.js.map +1 -0
- package/dist/core/i18n/messages.d.ts +82 -0
- package/dist/core/i18n/messages.d.ts.map +1 -0
- package/dist/core/i18n/messages.js +131 -0
- package/dist/core/i18n/messages.js.map +1 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +47 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logging/logger.d.ts +29 -0
- package/dist/core/logging/logger.d.ts.map +1 -0
- package/dist/core/logging/logger.js +47 -0
- package/dist/core/logging/logger.js.map +1 -0
- package/dist/core/registry/component-registry.d.ts +41 -0
- package/dist/core/registry/component-registry.d.ts.map +1 -0
- package/dist/core/registry/component-registry.js +34 -0
- package/dist/core/registry/component-registry.js.map +1 -0
- package/dist/core/registry/object-registry.d.ts +38 -0
- package/dist/core/registry/object-registry.d.ts.map +1 -0
- package/dist/core/registry/object-registry.js +32 -0
- package/dist/core/registry/object-registry.js.map +1 -0
- package/dist/explore/cave-explore.d.ts +85 -0
- package/dist/explore/cave-explore.d.ts.map +1 -0
- package/dist/explore/cave-explore.js +204 -0
- package/dist/explore/cave-explore.js.map +1 -0
- package/dist/explore/cave-orbit-viewer.d.ts +76 -0
- package/dist/explore/cave-orbit-viewer.d.ts.map +1 -0
- package/dist/explore/cave-orbit-viewer.js +123 -0
- package/dist/explore/cave-orbit-viewer.js.map +1 -0
- package/dist/explore/characters/character-configs.d.ts +38 -0
- package/dist/explore/characters/character-configs.d.ts.map +1 -0
- package/dist/explore/characters/character-configs.js +45 -0
- package/dist/explore/characters/character-configs.js.map +1 -0
- package/dist/explore/characters/character-controls.d.ts +44 -0
- package/dist/explore/characters/character-controls.d.ts.map +1 -0
- package/dist/explore/characters/character-pose.d.ts +19 -0
- package/dist/explore/characters/character-pose.d.ts.map +1 -0
- package/dist/explore/characters/character-pose.js +13 -0
- package/dist/explore/characters/character-pose.js.map +1 -0
- package/dist/explore/characters/local-character-model.d.ts +53 -0
- package/dist/explore/characters/local-character-model.d.ts.map +1 -0
- package/dist/explore/characters/local-character-model.js +36 -0
- package/dist/explore/characters/local-character-model.js.map +1 -0
- package/dist/explore/characters/mixamo-style-bone-map.d.ts +9 -0
- package/dist/explore/characters/mixamo-style-bone-map.d.ts.map +1 -0
- package/dist/explore/characters/mixamo-style-bone-map.js +28 -0
- package/dist/explore/characters/mixamo-style-bone-map.js.map +1 -0
- package/dist/explore/characters/remote-character-controller.d.ts +38 -0
- package/dist/explore/characters/remote-character-controller.d.ts.map +1 -0
- package/dist/explore/characters/remote-character-controller.js +135 -0
- package/dist/explore/characters/remote-character-controller.js.map +1 -0
- package/dist/explore/characters/remote-character.d.ts +19 -0
- package/dist/explore/characters/remote-character.d.ts.map +1 -0
- package/dist/explore/characters/remote-character.js +63 -0
- package/dist/explore/characters/remote-character.js.map +1 -0
- package/dist/explore/connected-spaces-renderer.d.ts +16 -0
- package/dist/explore/connected-spaces-renderer.d.ts.map +1 -0
- package/dist/explore/connected-spaces-renderer.js +103 -0
- package/dist/explore/connected-spaces-renderer.js.map +1 -0
- package/dist/explore/hooks/use-action-shortcuts.d.ts +16 -0
- package/dist/explore/hooks/use-action-shortcuts.d.ts.map +1 -0
- package/dist/explore/hooks/use-action-shortcuts.js +23 -0
- package/dist/explore/hooks/use-action-shortcuts.js.map +1 -0
- package/dist/explore/hooks/use-active-space-notify.d.ts +11 -0
- package/dist/explore/hooks/use-active-space-notify.d.ts.map +1 -0
- package/dist/explore/hooks/use-active-space-notify.js +17 -0
- package/dist/explore/hooks/use-active-space-notify.js.map +1 -0
- package/dist/explore/hooks/use-character-sync.d.ts +46 -0
- package/dist/explore/hooks/use-character-sync.d.ts.map +1 -0
- package/dist/explore/hooks/use-character-sync.js +72 -0
- package/dist/explore/hooks/use-character-sync.js.map +1 -0
- package/dist/explore/hooks/use-connector-preloader.d.ts +20 -0
- package/dist/explore/hooks/use-connector-preloader.d.ts.map +1 -0
- package/dist/explore/hooks/use-connector-preloader.js +78 -0
- package/dist/explore/hooks/use-connector-preloader.js.map +1 -0
- package/dist/explore/hooks/use-connector-transitions.d.ts +31 -0
- package/dist/explore/hooks/use-connector-transitions.d.ts.map +1 -0
- package/dist/explore/hooks/use-connector-transitions.js +63 -0
- package/dist/explore/hooks/use-connector-transitions.js.map +1 -0
- package/dist/explore/hooks/use-explore-actions.d.ts +17 -0
- package/dist/explore/hooks/use-explore-actions.d.ts.map +1 -0
- package/dist/explore/hooks/use-explore-actions.js +134 -0
- package/dist/explore/hooks/use-explore-actions.js.map +1 -0
- package/dist/explore/hooks/use-load-error-notify.d.ts +11 -0
- package/dist/explore/hooks/use-load-error-notify.d.ts.map +1 -0
- package/dist/explore/hooks/use-load-error-notify.js +18 -0
- package/dist/explore/hooks/use-load-error-notify.js.map +1 -0
- package/dist/explore/hooks/use-primary-space-loader.d.ts +24 -0
- package/dist/explore/hooks/use-primary-space-loader.d.ts.map +1 -0
- package/dist/explore/hooks/use-primary-space-loader.js +29 -0
- package/dist/explore/hooks/use-primary-space-loader.js.map +1 -0
- package/dist/explore/index.d.ts +37 -0
- package/dist/explore/index.d.ts.map +1 -0
- package/dist/explore/index.js +107 -0
- package/dist/explore/index.js.map +1 -0
- package/dist/explore/loaders/cave-loader.d.ts +58 -0
- package/dist/explore/loaders/cave-loader.d.ts.map +1 -0
- package/dist/explore/loaders/cave-loader.js +79 -0
- package/dist/explore/loaders/cave-loader.js.map +1 -0
- package/dist/explore/math/space-transforms.d.ts +49 -0
- package/dist/explore/math/space-transforms.d.ts.map +1 -0
- package/dist/explore/math/space-transforms.js +54 -0
- package/dist/explore/math/space-transforms.js.map +1 -0
- package/dist/explore/overlays/action-bubble.d.ts +8 -0
- package/dist/explore/overlays/action-bubble.d.ts.map +1 -0
- package/dist/explore/overlays/action-bubble.js +18 -0
- package/dist/explore/overlays/action-bubble.js.map +1 -0
- package/dist/explore/overlays/action-panel.d.ts +12 -0
- package/dist/explore/overlays/action-panel.d.ts.map +1 -0
- package/dist/explore/overlays/action-panel.js +69 -0
- package/dist/explore/overlays/action-panel.js.map +1 -0
- package/dist/explore/overlays/connector-label-beacon.d.ts +11 -0
- package/dist/explore/overlays/connector-label-beacon.d.ts.map +1 -0
- package/dist/explore/overlays/connector-label-beacon.js +38 -0
- package/dist/explore/overlays/connector-label-beacon.js.map +1 -0
- package/dist/explore/overlays/connector-label-tag-shell.d.ts +16 -0
- package/dist/explore/overlays/connector-label-tag-shell.d.ts.map +1 -0
- package/dist/explore/overlays/connector-label-tag-shell.js +18 -0
- package/dist/explore/overlays/connector-label-tag-shell.js.map +1 -0
- package/dist/explore/overlays/detail-window.d.ts +9 -0
- package/dist/explore/overlays/detail-window.d.ts.map +1 -0
- package/dist/explore/overlays/detail-window.js +92 -0
- package/dist/explore/overlays/detail-window.js.map +1 -0
- package/dist/explore/overlays/explore-helper.d.ts +44 -0
- package/dist/explore/overlays/explore-helper.d.ts.map +1 -0
- package/dist/explore/overlays/explore-helper.js +57 -0
- package/dist/explore/overlays/explore-helper.js.map +1 -0
- package/dist/explore/overlays/icons.d.ts +16 -0
- package/dist/explore/overlays/icons.d.ts.map +1 -0
- package/dist/explore/overlays/icons.js +34 -0
- package/dist/explore/overlays/icons.js.map +1 -0
- package/dist/explore/overlays/space-load-error-overlay.d.ts +27 -0
- package/dist/explore/overlays/space-load-error-overlay.d.ts.map +1 -0
- package/dist/explore/overlays/space-load-error-overlay.js +24 -0
- package/dist/explore/overlays/space-load-error-overlay.js.map +1 -0
- package/dist/explore/services/websocket.service.d.ts +180 -0
- package/dist/explore/services/websocket.service.d.ts.map +1 -0
- package/dist/explore/services/websocket.service.js +187 -0
- package/dist/explore/services/websocket.service.js.map +1 -0
- package/dist/explore/simple-character.d.ts +46 -0
- package/dist/explore/simple-character.d.ts.map +1 -0
- package/dist/explore/simple-character.js +119 -0
- package/dist/explore/simple-character.js.map +1 -0
- package/dist/explore/stores/action-prompt.store.d.ts +27 -0
- package/dist/explore/stores/action-prompt.store.d.ts.map +1 -0
- package/dist/explore/stores/action-prompt.store.js +14 -0
- package/dist/explore/stores/action-prompt.store.js.map +1 -0
- package/dist/explore/stores/cave-explore.store.d.ts +77 -0
- package/dist/explore/stores/cave-explore.store.d.ts.map +1 -0
- package/dist/explore/stores/cave-explore.store.js +66 -0
- package/dist/explore/stores/cave-explore.store.js.map +1 -0
- package/dist/explore/stores/explore-context.d.ts +35 -0
- package/dist/explore/stores/explore-context.d.ts.map +1 -0
- package/dist/explore/stores/explore-context.js +43 -0
- package/dist/explore/stores/explore-context.js.map +1 -0
- package/dist/explore/styles/cave-world-css.d.ts +10 -0
- package/dist/explore/styles/cave-world-css.d.ts.map +1 -0
- package/dist/explore/styles/cave-world-css.js +935 -0
- package/dist/explore/styles/cave-world-css.js.map +1 -0
- package/dist/explore/styles/inject.d.ts +32 -0
- package/dist/explore/styles/inject.d.ts.map +1 -0
- package/dist/explore/styles/inject.js +40 -0
- package/dist/explore/styles/inject.js.map +1 -0
- package/dist/explore/utils/schedule-idle.d.ts +12 -0
- package/dist/explore/utils/schedule-idle.d.ts.map +1 -0
- package/dist/explore/utils/schedule-idle.js +16 -0
- package/dist/explore/utils/schedule-idle.js.map +1 -0
- package/dist/explore/viewer-shared.d.ts +56 -0
- package/dist/explore/viewer-shared.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/space/cave-base-layer.d.ts +42 -0
- package/dist/space/cave-base-layer.d.ts.map +1 -0
- package/dist/space/cave-base-layer.js +221 -0
- package/dist/space/cave-base-layer.js.map +1 -0
- package/dist/space/cave-custom-layer.d.ts +12 -0
- package/dist/space/cave-custom-layer.d.ts.map +1 -0
- package/dist/space/cave-custom-layer.js +17 -0
- package/dist/space/cave-custom-layer.js.map +1 -0
- package/dist/space/cave-env.d.ts +6 -0
- package/dist/space/cave-env.d.ts.map +1 -0
- package/dist/space/cave-env.js +23 -0
- package/dist/space/cave-env.js.map +1 -0
- package/dist/space/cave-space.d.ts +46 -0
- package/dist/space/cave-space.d.ts.map +1 -0
- package/dist/space/cave-space.js +37 -0
- package/dist/space/cave-space.js.map +1 -0
- package/dist/space/connector-label-host.d.ts +21 -0
- package/dist/space/connector-label-host.d.ts.map +1 -0
- package/dist/space/connector-label-host.js +65 -0
- package/dist/space/connector-label-host.js.map +1 -0
- package/dist/space/index.d.ts +18 -0
- package/dist/space/index.d.ts.map +1 -0
- package/dist/space/index.js +47 -0
- package/dist/space/index.js.map +1 -0
- package/dist/space/objects/builtin-registrar.d.ts +17 -0
- package/dist/space/objects/builtin-registrar.d.ts.map +1 -0
- package/dist/space/objects/builtin-registrar.js +19 -0
- package/dist/space/objects/builtin-registrar.js.map +1 -0
- package/dist/space/objects/custom-object.d.ts +9 -0
- package/dist/space/objects/custom-object.d.ts.map +1 -0
- package/dist/space/objects/custom-object.js +56 -0
- package/dist/space/objects/custom-object.js.map +1 -0
- package/dist/space/objects/image-object.d.ts +7 -0
- package/dist/space/objects/image-object.d.ts.map +1 -0
- package/dist/space/objects/image-object.js +14 -0
- package/dist/space/objects/image-object.js.map +1 -0
- package/dist/space/objects/model-object/fbx-object.d.ts +8 -0
- package/dist/space/objects/model-object/fbx-object.d.ts.map +1 -0
- package/dist/space/objects/model-object/fbx-object.js +26 -0
- package/dist/space/objects/model-object/fbx-object.js.map +1 -0
- package/dist/space/objects/model-object/gltf-object.d.ts +9 -0
- package/dist/space/objects/model-object/gltf-object.d.ts.map +1 -0
- package/dist/space/objects/model-object/gltf-object.js +66 -0
- package/dist/space/objects/model-object/gltf-object.js.map +1 -0
- package/dist/space/objects/model-object/index.d.ts +9 -0
- package/dist/space/objects/model-object/index.d.ts.map +1 -0
- package/dist/space/objects/model-object/index.js +12 -0
- package/dist/space/objects/model-object/index.js.map +1 -0
- package/dist/space/objects/model-object/splat-object.d.ts +9 -0
- package/dist/space/objects/model-object/splat-object.d.ts.map +1 -0
- package/dist/space/objects/model-object/splat-object.js +19 -0
- package/dist/space/objects/model-object/splat-object.js.map +1 -0
- package/dist/space/objects/object-components/windows/index.d.ts +5 -0
- package/dist/space/objects/object-components/windows/index.d.ts.map +1 -0
- package/dist/space/objects/object-components/windows/index.js +20 -0
- package/dist/space/objects/object-components/windows/index.js.map +1 -0
- package/dist/space/objects/placeholder-object.d.ts +7 -0
- package/dist/space/objects/placeholder-object.d.ts.map +1 -0
- package/dist/space/objects/placeholder-object.js +12 -0
- package/dist/space/objects/placeholder-object.js.map +1 -0
- package/dist/space/objects/poi-object.d.ts +7 -0
- package/dist/space/objects/poi-object.d.ts.map +1 -0
- package/dist/space/objects/poi-object.js +38 -0
- package/dist/space/objects/poi-object.js.map +1 -0
- package/dist/space/objects/portal-object.d.ts +7 -0
- package/dist/space/objects/portal-object.d.ts.map +1 -0
- package/dist/space/objects/portal-object.js +84 -0
- package/dist/space/objects/portal-object.js.map +1 -0
- package/dist/space/objects/video-object.d.ts +7 -0
- package/dist/space/objects/video-object.d.ts.map +1 -0
- package/dist/space/objects/video-object.js +32 -0
- package/dist/space/objects/video-object.js.map +1 -0
- package/dist/space/polygon-boundary.d.ts +10 -0
- package/dist/space/polygon-boundary.d.ts.map +1 -0
- package/dist/space/polygon-boundary.js +205 -0
- package/dist/space/polygon-boundary.js.map +1 -0
- package/dist/space/splats/spark/robust-splat-mesh.d.ts +59 -0
- package/dist/space/splats/spark/robust-splat-mesh.d.ts.map +1 -0
- package/dist/space/splats/spark/robust-splat-mesh.js +105 -0
- package/dist/space/splats/spark/robust-splat-mesh.js.map +1 -0
- package/dist/space/splats/spark/splat-mesh.d.ts +5 -0
- package/dist/space/splats/spark/splat-mesh.d.ts.map +1 -0
- package/dist/space/splats/spark/splat-mesh.js +7 -0
- package/dist/space/splats/spark/splat-mesh.js.map +1 -0
- package/dist/space/splats/spark/splat-spread-reveal.d.ts +14 -0
- package/dist/space/splats/spark/splat-spread-reveal.d.ts.map +1 -0
- package/dist/space/splats/spark/splat-spread-reveal.js +45 -0
- package/dist/space/splats/spark/splat-spread-reveal.js.map +1 -0
- package/dist/space/utils/cave-world-player.d.ts +3 -0
- package/dist/space/utils/cave-world-player.d.ts.map +1 -0
- package/dist/space/utils/cave-world-player.js +5 -0
- package/dist/space/utils/cave-world-player.js.map +1 -0
- package/dist/space/utils/configure-canvas.d.ts +20 -0
- package/dist/space/utils/configure-canvas.d.ts.map +1 -0
- package/dist/space/utils/configure-canvas.js +12 -0
- package/dist/space/utils/configure-canvas.js.map +1 -0
- package/dist/space/utils/polygon-boundary-geometry.d.ts +8 -0
- package/dist/space/utils/polygon-boundary-geometry.d.ts.map +1 -0
- package/dist/space/utils/polygon-boundary-geometry.js +20 -0
- package/dist/space/utils/polygon-boundary-geometry.js.map +1 -0
- package/dist/space/utils/polygon-boundary-segments.d.ts +50 -0
- package/dist/space/utils/polygon-boundary-segments.d.ts.map +1 -0
- package/dist/space/utils/polygon-boundary-segments.js +64 -0
- package/dist/space/utils/polygon-boundary-segments.js.map +1 -0
- package/dist/space/utils/rotation-compat.d.ts +4 -0
- package/dist/space/utils/rotation-compat.d.ts.map +1 -0
- package/dist/space/utils/rotation-compat.js +9 -0
- package/dist/space/utils/rotation-compat.js.map +1 -0
- package/dist/space/utils/select-splat-level.d.ts +52 -0
- package/dist/space/utils/select-splat-level.d.ts.map +1 -0
- package/dist/space/utils/select-splat-level.js +43 -0
- package/dist/space/utils/select-splat-level.js.map +1 -0
- package/dist/space/utils/spark-performance.d.ts +10 -0
- package/dist/space/utils/spark-performance.d.ts.map +1 -0
- package/dist/space/utils/spark-performance.js +10 -0
- package/dist/space/utils/spark-performance.js.map +1 -0
- package/dist/space/utils/splat-collider-helper.d.ts +5 -0
- package/dist/space/utils/splat-collider-helper.d.ts.map +1 -0
- package/dist/space/utils/splat-collider-helper.js +162 -0
- package/dist/space/utils/splat-collider-helper.js.map +1 -0
- package/dist/space/utils/touch-primary-device.d.ts +16 -0
- package/dist/space/utils/touch-primary-device.d.ts.map +1 -0
- package/dist/space/utils/touch-primary-device.js +52 -0
- package/dist/space/utils/touch-primary-device.js.map +1 -0
- package/dist/space/utils/use-visibility-frameloop.d.ts +20 -0
- package/dist/space/utils/use-visibility-frameloop.d.ts.map +1 -0
- package/dist/space/utils/use-visibility-frameloop.js +18 -0
- package/dist/space/utils/use-visibility-frameloop.js.map +1 -0
- package/dist/styles.css +930 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/space-types.d.ts +404 -0
- package/dist/types/space-types.d.ts.map +1 -0
- package/doc/API.md +617 -0
- package/doc/ARCHITECTURE.md +264 -0
- package/doc/multiplayer-protocol.md +757 -0
- package/doc/scene-json-structure.md +567 -0
- package/package.json +131 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@cavegiant/cave-world-r3f` are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
Initial public release.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`CaveExplore`** — first/third-person walkable tour with physics, character controls, connector travel, and interaction overlays.
|
|
15
|
+
- **`CaveOrbitViewer`** — orbit-camera overview for scene pickers and bird's-eye framing (`camera` / `orbit` / `transformSpace`).
|
|
16
|
+
- **Scene graph** — `CaveSpace` renders splat / model base layers, colliders, boundaries, connectors, and custom objects from JSON.
|
|
17
|
+
- **Gaussian splat pipeline** — spark `SplatMesh` wrapper with retry, stall watchdog, optional entry reveal, and device-aware LOD (`splatLevels`).
|
|
18
|
+
- **Extensibility** — `ObjectRegistry` / `ComponentRegistry` with `registerDefault`, typed `CaveEventBus`, and host `render*` / `on*` props for UI and navigation.
|
|
19
|
+
- **Multiplayer (optional)** — WebSocket sync via `multiplayer` config with **required** `url`; avatar catalog must be supplied (or `BUILTIN_AVATARS` for demos).
|
|
20
|
+
- **Localization** — built-in overlay copy via `messages` (zh-CN default, `messagesEn` export); partial overrides deep-merge.
|
|
21
|
+
- **Styles** — `.cw-*` / `--cw-*` stylesheet, runtime inject by default, also published as `./styles.css`.
|
|
22
|
+
- **Loading** — `createHttpCaveSpaceLoader` with timeout and payload validation; terminal failures surface a retry prompt (`CaveWorldError` codes). `DefaultCaveSpaceLoader` targets the public CaveGiant API for demos.
|
|
23
|
+
- **Imperative control** — `CaveExploreHandle` (`getCharacter`, `getActiveSpaceId`, `teleport`).
|
|
24
|
+
- **Sub-path exports** — `.` / `./core` / `./types` / `./space` / `./explore` / `./styles.css` (ESM-only).
|
|
25
|
+
- **Docs & examples** — README, API / ARCHITECTURE / scene-JSON / multiplayer docs (published in the npm `doc/` folder), and a runnable playground (`npm run examples`) mounting `examples/0N-*.tsx` — including multiplayer and product-skin recipes.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CaveGiant
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# @cavegiant/cave-world-r3f
|
|
2
|
+
|
|
3
|
+
Web-based 3D virtual tour SDK. Renders an interactive, walkable 3D scene from a single JSON description — including Gaussian-splat captures, collision, interactive objects, and seamless travel between connected scenes.
|
|
4
|
+
|
|
5
|
+
**ESM-only.** Consumers must use `import` (not `require`).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @cavegiant/cave-world-r3f
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peer dependencies (all required):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install react@">=18 <19.3" react-dom@">=18 <19.3" three @react-three/fiber @react-three/drei @react-three/viverse @sparkjsdev/spark@^0.1.10 zustand
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
> `@react-three/fiber` currently requires React `>=19 <19.3` (or React 18). Do not install React 19.3+ until fiber widens its peer range.
|
|
20
|
+
>
|
|
21
|
+
> `@sparkjsdev/spark` is pinned to `0.1.x` on purpose. Spark 2.x rebuilds its splat accumulator whenever the view changes, which injects frame-time jitter that the character integrator turns into visible stutter while walking. See [`doc/ARCHITECTURE.md`](./doc/ARCHITECTURE.md#spark-version).
|
|
22
|
+
>
|
|
23
|
+
> If you hit `@react-three/timeline` issues with viverse, pin `@react-three/timeline@0.3.9` in the **host** app as well — this package's `overrides` only apply to its own install.
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
The container needs a definite width and height — the viewer fills it. Omitting
|
|
28
|
+
`caveSpaceLoader` uses `DefaultCaveSpaceLoader` (the public CaveGiant content API).
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { CaveExplore } from '@cavegiant/cave-world-r3f/explore'
|
|
32
|
+
|
|
33
|
+
export default function App() {
|
|
34
|
+
return (
|
|
35
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
36
|
+
<CaveExplore spaceId="your-space-id" />
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Loading scenes from your own backend
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { CaveExplore, createHttpCaveSpaceLoader } from '@cavegiant/cave-world-r3f/explore'
|
|
46
|
+
|
|
47
|
+
const loader = createHttpCaveSpaceLoader({
|
|
48
|
+
buildUrl: (spaceId) => `/api/spaces/${spaceId}`,
|
|
49
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
<CaveExplore spaceId={id} caveSpaceLoader={loader} />
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`createHttpCaveSpaceLoader` adds a request timeout, non-2xx detection, and payload validation on top of `fetch`, and reports failures as `CaveWorldError` with a stable `code`. Any `(spaceId) => Promise<CaveSpace>` function works too.
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
- **JSON-driven** — scene content is fully described by one config file
|
|
60
|
+
- **Gaussian splats** — native 3DGS rendering with device-aware LOD selection
|
|
61
|
+
- **Cross-scene travel** — seamless walk-through and portal teleport between scenes, with neighbor preloading
|
|
62
|
+
- **Extensible objects** — register custom 3D object types through `ObjectRegistry` (unregistered types render nothing; enable `setCaveWorldLogLevel('debug')` to see skips)
|
|
63
|
+
- **Extensible behaviors** — attach reusable components to any object through `ComponentRegistry`
|
|
64
|
+
- **Typed event bus** — `object:click`, `connector:enter`, `detail:open`, `custom:*`, and more
|
|
65
|
+
- **Custom avatars** — third-person GLB/VRM with VRM 1.0 bone mapping
|
|
66
|
+
- **Multiplayer** — optional WebSocket character sync (requires explicit `url` + your avatar catalog)
|
|
67
|
+
- **Replaceable UI** — every built-in overlay has a `render*` prop, and all copy is translatable
|
|
68
|
+
- **Load-failure hardening** — automatic splat retry, stall watchdog, and a retry prompt instead of an infinite spinner
|
|
69
|
+
|
|
70
|
+
Wrap the viewer (or your own R3F canvas) in a React error boundary if you need host-level recovery from unexpected WebGL/R3F throws — the SDK's load-error overlay covers the hardened splat/config path only.
|
|
71
|
+
|
|
72
|
+
## Entry points
|
|
73
|
+
|
|
74
|
+
| Import path | Contents |
|
|
75
|
+
| -------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
76
|
+
| `@cavegiant/cave-world-r3f` | Common re-exports from all layers (`CaveSpace` is re-exported as `CaveSpaceRenderer`) |
|
|
77
|
+
| `@cavegiant/cave-world-r3f/types` | Scene JSON types only — zero runtime |
|
|
78
|
+
| `@cavegiant/cave-world-r3f/core` | Event bus, registries, provider, hooks, logger, i18n |
|
|
79
|
+
| `@cavegiant/cave-world-r3f/space` | Scene rendering components (R3F), including `CaveSpace` |
|
|
80
|
+
| `@cavegiant/cave-world-r3f/explore` | Viewers, overlays, character, multiplayer |
|
|
81
|
+
| `@cavegiant/cave-world-r3f/styles.css` | Default overlay stylesheet |
|
|
82
|
+
|
|
83
|
+
## Viewers
|
|
84
|
+
|
|
85
|
+
| Component | Use it for |
|
|
86
|
+
| ----------------- | -------------------------------------------------------------------- |
|
|
87
|
+
| `CaveExplore` | First/third-person walkable tour with physics and cross-scene travel |
|
|
88
|
+
| `CaveOrbitViewer` | Orbit-camera overview of a single scene (no avatar, no physics) |
|
|
89
|
+
|
|
90
|
+
## Styling
|
|
91
|
+
|
|
92
|
+
The built-in overlays ship a small stylesheet, namespaced under `.cw-*` and themed entirely through `--cw-*` custom properties. It is injected at runtime by default, so nothing is required to get a working UI.
|
|
93
|
+
|
|
94
|
+
The defaults are declared on `:root`, which means setting the same property on any ancestor of the viewer overrides them.
|
|
95
|
+
|
|
96
|
+
```css
|
|
97
|
+
/* Re-theme without replacing any component */
|
|
98
|
+
:root {
|
|
99
|
+
--cw-color-accent: #7c9cff;
|
|
100
|
+
--cw-color-surface: rgb(20 20 28 / 88%);
|
|
101
|
+
--cw-radius-md: 4px;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The properties you are most likely to want:
|
|
106
|
+
|
|
107
|
+
| Property | Themes |
|
|
108
|
+
| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
109
|
+
| `--cw-color-accent` / `--cw-color-accent-soft` | Primary gold: eyebrow text, key caps, hairlines, hover rules |
|
|
110
|
+
| `--cw-color-accent-mint` | Secondary accent: hover states and the action-panel status dot |
|
|
111
|
+
| `--cw-color-accent-warm` | Load-error badge and its primary button |
|
|
112
|
+
| `--cw-color-text` / `--cw-color-text-muted` | Overlay copy |
|
|
113
|
+
| `--cw-color-surface` / `--cw-color-surface-solid` / `--cw-color-scrim` | Panel, dialog, and detail-window backgrounds |
|
|
114
|
+
| `--cw-color-label-surface` / `--cw-color-label-border` | In-scene connector tags |
|
|
115
|
+
| `--cw-panel-fill` / `--cw-panel-rim` / `--cw-panel-clip` / `--cw-panel-grain` | Action-panel HUD decoration (set `--cw-panel-clip: none` for square corners) |
|
|
116
|
+
| `--cw-radius-*`, `--cw-blur*`, `--cw-shadow-*`, `--cw-z-*` | Shape, depth, stacking |
|
|
117
|
+
|
|
118
|
+
To bundle the CSS yourself instead, import it and turn the injector off:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import '@cavegiant/cave-world-r3f/styles.css'
|
|
122
|
+
|
|
123
|
+
;<CaveExplore spaceId={id} injectStyles={false} />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Localization
|
|
127
|
+
|
|
128
|
+
All built-in copy defaults to Chinese and is overridable per string. `messages` is deep-merged over the default locale, so partial overrides keep everything else intact.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { messagesEn } from '@cavegiant/cave-world-r3f/core'
|
|
132
|
+
|
|
133
|
+
<CaveExplore spaceId={id} messages={messagesEn} />
|
|
134
|
+
<CaveExplore spaceId={id} messages={{ loadError: { retry: 'Try again' } }} />
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Diagnostics
|
|
138
|
+
|
|
139
|
+
The SDK only logs warnings and errors by default, and never writes to the console outside that.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { setCaveWorldLogLevel, setCaveWorldLogSink } from '@cavegiant/cave-world-r3f/core'
|
|
143
|
+
|
|
144
|
+
setCaveWorldLogLevel('debug') // while integrating (also logs unknown object types)
|
|
145
|
+
setCaveWorldLogLevel('silent') // mute entirely
|
|
146
|
+
setCaveWorldLogSink(myTelemetryAdapter) // forward to your own pipeline
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Documentation
|
|
150
|
+
|
|
151
|
+
| Document | Audience |
|
|
152
|
+
| ------------------------------------------------------------ | ----------------------------------------------------------------- |
|
|
153
|
+
| [doc/API.md](./doc/API.md) | Integration guide, props reference, event list, extension recipes |
|
|
154
|
+
| [doc/ARCHITECTURE.md](./doc/ARCHITECTURE.md) | Module design, data flow, dependency rules |
|
|
155
|
+
| [doc/scene-json-structure.md](./doc/scene-json-structure.md) | Scene JSON schema |
|
|
156
|
+
| [doc/multiplayer-protocol.md](./doc/multiplayer-protocol.md) | WebSocket message protocol |
|
|
157
|
+
| [AGENTS.md](./AGENTS.md) | Invariants and recipes for AI coding agents (repo only) |
|
|
158
|
+
| [examples/](./examples) | Runnable demos — `npm run examples` |
|
|
159
|
+
| [CONTRIBUTING.md](./CONTRIBUTING.md) | Setup, checks, PR expectations |
|
|
160
|
+
| [SECURITY.md](./SECURITY.md) | Vulnerability reporting |
|
|
161
|
+
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
This repository is the standalone package. From the package root:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npm install
|
|
168
|
+
npm run typecheck
|
|
169
|
+
npm run lint
|
|
170
|
+
npm run format:check
|
|
171
|
+
npm run test
|
|
172
|
+
npm run build
|
|
173
|
+
npm run check # typecheck + lint + test
|
|
174
|
+
npm run verify:package # publint + attw
|
|
175
|
+
npm run verify:pack-size # tarball must stay lean (no examples/)
|
|
176
|
+
npm run examples # live playground at https://localhost:5173/
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Nested under a host monorepo
|
|
180
|
+
|
|
181
|
+
If this tree lives inside a larger portal repo, do **not** run `npm install` here (nested `node_modules` duplicates React / Three.js). Use the host's `sdk:*` scripts instead (`npm run sdk:examples`, `sdk:check`, …). See [AGENTS.md](./AGENTS.md).
|
|
182
|
+
|
|
183
|
+
The playground mounts every `examples/0N-*.tsx` against the public demo scene
|
|
184
|
+
id in [`examples/demo-space.ts`](./examples/demo-space.ts). See
|
|
185
|
+
[`examples/README.md`](./examples/README.md).
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CaveSpace, ConnectorDef, ConnectorTarget } from '../../types/index.js';
|
|
2
|
+
/** Whether a connected space has finished base-layer init (splat + colliders). */
|
|
3
|
+
export declare function isSpaceTransferReady(spaceId: string, connectedSpaces: Record<string, CaveSpace>, readySpaceIds: Record<string, true>): boolean;
|
|
4
|
+
export declare function getConnectorPortalTargets(connector: ConnectorDef): ConnectorTarget[];
|
|
5
|
+
export declare function isPortalTargetReady(target: ConnectorTarget, connectedSpaces: Record<string, CaveSpace>, readySpaceIds: Record<string, true>): boolean;
|
|
6
|
+
/** At least one portal destination is loaded and ready to receive the player. */
|
|
7
|
+
export declare function isConnectorPortalNavigable(connector: ConnectorDef, connectedSpaces: Record<string, CaveSpace>, readySpaceIds: Record<string, true>): boolean;
|
|
8
|
+
/** Label / interaction status derived from portal target preload state. */
|
|
9
|
+
export declare function resolveConnectorTransferStatus(connector: ConnectorDef, connectedSpaces: Record<string, CaveSpace>, readySpaceIds: Record<string, true>): 'ready' | 'loading';
|
|
10
|
+
export declare function getReadyPortalTargets(connector: ConnectorDef, connectedSpaces: Record<string, CaveSpace>, readySpaceIds: Record<string, true>): ConnectorTarget[];
|
|
11
|
+
//# sourceMappingURL=readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../../../core/connector-label/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE3E,kFAAkF;AAClF,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAClC,OAAO,CAET;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,YAAY,GAAG,eAAe,EAAE,CAEpF;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,eAAe,EACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAClC,OAAO,CAET;AAED,iFAAiF;AACjF,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,YAAY,EACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAClC,OAAO,CAIT;AAED,2EAA2E;AAC3E,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,YAAY,EACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAClC,OAAO,GAAG,SAAS,CAErB;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,YAAY,EACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAClC,eAAe,EAAE,CAInB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function i(r, t, n) {
|
|
2
|
+
return !!t[r] && !!n[r];
|
|
3
|
+
}
|
|
4
|
+
function o(r) {
|
|
5
|
+
return r.connectorInfo.to.filter((t) => t.transitionType === "portal");
|
|
6
|
+
}
|
|
7
|
+
function a(r, t, n) {
|
|
8
|
+
return i(r.caveWorldId, t, n);
|
|
9
|
+
}
|
|
10
|
+
function u(r, t, n) {
|
|
11
|
+
return o(r).some(
|
|
12
|
+
(e) => a(e, t, n)
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
function c(r, t, n) {
|
|
16
|
+
return u(r, t, n) ? "ready" : "loading";
|
|
17
|
+
}
|
|
18
|
+
function f(r, t, n) {
|
|
19
|
+
return o(r).filter(
|
|
20
|
+
(e) => a(e, t, n)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
o as getConnectorPortalTargets,
|
|
25
|
+
f as getReadyPortalTargets,
|
|
26
|
+
u as isConnectorPortalNavigable,
|
|
27
|
+
a as isPortalTargetReady,
|
|
28
|
+
i as isSpaceTransferReady,
|
|
29
|
+
c as resolveConnectorTransferStatus
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.js","sources":["../../../core/connector-label/readiness.ts"],"sourcesContent":["import type { CaveSpace, ConnectorDef, ConnectorTarget } from '../../types'\n\n/** Whether a connected space has finished base-layer init (splat + colliders). */\nexport function isSpaceTransferReady(\n spaceId: string,\n connectedSpaces: Record<string, CaveSpace>,\n readySpaceIds: Record<string, true>\n): boolean {\n return !!connectedSpaces[spaceId] && !!readySpaceIds[spaceId]\n}\n\nexport function getConnectorPortalTargets(connector: ConnectorDef): ConnectorTarget[] {\n return connector.connectorInfo.to.filter((t) => t.transitionType === 'portal')\n}\n\nexport function isPortalTargetReady(\n target: ConnectorTarget,\n connectedSpaces: Record<string, CaveSpace>,\n readySpaceIds: Record<string, true>\n): boolean {\n return isSpaceTransferReady(target.caveWorldId, connectedSpaces, readySpaceIds)\n}\n\n/** At least one portal destination is loaded and ready to receive the player. */\nexport function isConnectorPortalNavigable(\n connector: ConnectorDef,\n connectedSpaces: Record<string, CaveSpace>,\n readySpaceIds: Record<string, true>\n): boolean {\n return getConnectorPortalTargets(connector).some((target) =>\n isPortalTargetReady(target, connectedSpaces, readySpaceIds)\n )\n}\n\n/** Label / interaction status derived from portal target preload state. */\nexport function resolveConnectorTransferStatus(\n connector: ConnectorDef,\n connectedSpaces: Record<string, CaveSpace>,\n readySpaceIds: Record<string, true>\n): 'ready' | 'loading' {\n return isConnectorPortalNavigable(connector, connectedSpaces, readySpaceIds) ? 'ready' : 'loading'\n}\n\nexport function getReadyPortalTargets(\n connector: ConnectorDef,\n connectedSpaces: Record<string, CaveSpace>,\n readySpaceIds: Record<string, true>\n): ConnectorTarget[] {\n return getConnectorPortalTargets(connector).filter((target) =>\n isPortalTargetReady(target, connectedSpaces, readySpaceIds)\n )\n}\n"],"names":["isSpaceTransferReady","spaceId","connectedSpaces","readySpaceIds","getConnectorPortalTargets","connector","isPortalTargetReady","target","isConnectorPortalNavigable","resolveConnectorTransferStatus","getReadyPortalTargets"],"mappings":"AAGO,SAASA,EACdC,GACAC,GACAC,GACS;AACT,SAAO,CAAC,CAACD,EAAgBD,CAAO,KAAK,CAAC,CAACE,EAAcF,CAAO;AAC9D;AAEO,SAASG,EAA0BC,GAA4C;AACpF,SAAOA,EAAU,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,mBAAmB,QAAQ;AAC/E;AAEO,SAASC,EACdC,GACAL,GACAC,GACS;AACT,SAAOH,EAAqBO,EAAO,aAAaL,GAAiBC,CAAa;AAChF;AAGO,SAASK,EACdH,GACAH,GACAC,GACS;AACT,SAAOC,EAA0BC,CAAS,EAAE;AAAA,IAAK,CAACE,MAChDD,EAAoBC,GAAQL,GAAiBC,CAAa;AAAA,EAAA;AAE9D;AAGO,SAASM,EACdJ,GACAH,GACAC,GACqB;AACrB,SAAOK,EAA2BH,GAAWH,GAAiBC,CAAa,IAAI,UAAU;AAC3F;AAEO,SAASO,EACdL,GACAH,GACAC,GACmB;AACnB,SAAOC,EAA0BC,CAAS,EAAE;AAAA,IAAO,CAACE,MAClDD,EAAoBC,GAAQL,GAAiBC,CAAa;AAAA,EAAA;AAE9D;"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
import type { CaveSpace, ConnectorDef, ConnectorTarget, Vector3 } from '../../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Fallback label text when no target metadata is available yet.
|
|
5
|
+
*
|
|
6
|
+
* @deprecated Prefer `messages.connector.fallbackLabel`, which is what the
|
|
7
|
+
* built-in label host passes to {@link resolveConnectorLabel}.
|
|
8
|
+
*/
|
|
9
|
+
export declare const FALLBACK_LABEL_TEXT = "\u524D\u65B9\u573A\u666F";
|
|
10
|
+
export interface ResolvedConnectorLabel {
|
|
11
|
+
variant: string;
|
|
12
|
+
text: string;
|
|
13
|
+
icon?: string;
|
|
14
|
+
thumbnail?: string;
|
|
15
|
+
anchorOffset: Vector3;
|
|
16
|
+
visibility: {
|
|
17
|
+
maxDistance: number;
|
|
18
|
+
minDistance: number;
|
|
19
|
+
fadeRange: number;
|
|
20
|
+
};
|
|
21
|
+
custom?: Record<string, unknown>;
|
|
22
|
+
/** Whether clicking the label triggers portal navigation. Default true. */
|
|
23
|
+
clickable: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface ConnectorLabelRendererProps {
|
|
26
|
+
connector: ConnectorDef;
|
|
27
|
+
label: ResolvedConnectorLabel;
|
|
28
|
+
targets: Array<{
|
|
29
|
+
target: ConnectorTarget;
|
|
30
|
+
space: CaveSpace | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
distance: number;
|
|
33
|
+
isPlayerInside: boolean;
|
|
34
|
+
/** 0–1 opacity from distance fade + inside-state gating. */
|
|
35
|
+
opacity: number;
|
|
36
|
+
/** 'full' badge with text, 'compact' icon-only, 'hidden' when fully faded. */
|
|
37
|
+
displayMode: 'full' | 'compact' | 'hidden';
|
|
38
|
+
/** Resolved from JSON; default true. When false, `onClick` is omitted. */
|
|
39
|
+
clickable: boolean;
|
|
40
|
+
/** Triggers the same portal navigation as the in-zone action panel. */
|
|
41
|
+
onClick?: () => void;
|
|
42
|
+
/** `loading` while target scene base layer is still preloading. */
|
|
43
|
+
transferStatus: 'ready' | 'loading';
|
|
44
|
+
}
|
|
45
|
+
export type ConnectorLabelRenderer = ComponentType<ConnectorLabelRendererProps>;
|
|
46
|
+
export type ConnectorLabelMode = 'auto' | 'off';
|
|
47
|
+
export declare function shouldShowConnectorLabel(connector: ConnectorDef, connectorLabelDefault: ConnectorLabelMode): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Flatten a connector's label config into concrete render values.
|
|
50
|
+
*
|
|
51
|
+
* Text falls back through `label.text` → first portal target's
|
|
52
|
+
* `metadata.name` → `fallbackText`, so a `caveWorldId` is never shown to users.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveConnectorLabel(connector: ConnectorDef, connectedSpaces: Record<string, CaveSpace>, fallbackText?: string): ResolvedConnectorLabel;
|
|
55
|
+
export declare function getPortalTargetsWithSpaces(connector: ConnectorDef, connectedSpaces: Record<string, CaveSpace>): Array<{
|
|
56
|
+
target: ConnectorTarget;
|
|
57
|
+
space: CaveSpace | undefined;
|
|
58
|
+
}>;
|
|
59
|
+
/** Compute label opacity and display mode from camera distance. */
|
|
60
|
+
export declare function computeLabelVisibility(distance: number, label: ResolvedConnectorLabel, isPlayerInside: boolean): {
|
|
61
|
+
opacity: number;
|
|
62
|
+
displayMode: 'full' | 'compact' | 'hidden';
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../core/connector-label/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EAEZ,eAAe,EACf,OAAO,EACR,MAAM,aAAa,CAAA;AAEpB;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,6BAAS,CAAA;AAEzC,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;IACrB,UAAU,EAAE;QACV,WAAW,EAAE,MAAM,CAAA;QACnB,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,YAAY,CAAA;IACvB,KAAK,EAAE,sBAAsB,CAAA;IAC7B,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;KAAE,CAAC,CAAA;IACzE,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,OAAO,CAAA;IACvB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;IAC1C,0EAA0E;IAC1E,SAAS,EAAE,OAAO,CAAA;IAClB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,mEAAmE;IACnE,cAAc,EAAE,OAAO,GAAG,SAAS,CAAA;CACpC;AAED,MAAM,MAAM,sBAAsB,GAAG,aAAa,CAAC,2BAA2B,CAAC,CAAA;AAE/E,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,KAAK,CAAA;AAE/C,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,YAAY,EACvB,qBAAqB,EAAE,kBAAkB,GACxC,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,YAAY,EACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1C,YAAY,GAAE,MAA4B,GACzC,sBAAsB,CAyBxB;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,YAAY,EACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GACzC,KAAK,CAAC;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,CAAC,CAOlE;AAED,mEAAmE;AACnE,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,sBAAsB,EAC7B,cAAc,EAAE,OAAO,GACtB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;CAAE,CA0BjE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const f = "前方场景";
|
|
2
|
+
function u(t, o) {
|
|
3
|
+
return !t.connectorInfo.to.some((a) => a.transitionType === "portal") || t.label === !1 ? !1 : t.label !== void 0 ? !0 : o === "auto";
|
|
4
|
+
}
|
|
5
|
+
function p(t, o, e = f) {
|
|
6
|
+
const a = t.label === !1 ? void 0 : t.label, n = t.connectorInfo.to.filter((c) => c.transitionType === "portal")[0], i = n ? o[n.caveWorldId] : void 0, s = [0, t.transform.scale[1] / 4 + 0.2, 0];
|
|
7
|
+
return {
|
|
8
|
+
variant: a?.variant ?? "beacon",
|
|
9
|
+
text: a?.text ?? i?.metadata?.name ?? e,
|
|
10
|
+
icon: a?.icon,
|
|
11
|
+
thumbnail: a?.thumbnail ?? i?.metadata?.thumbnail,
|
|
12
|
+
anchorOffset: a?.anchorOffset ?? s,
|
|
13
|
+
visibility: {
|
|
14
|
+
maxDistance: a?.visibility?.maxDistance ?? 30,
|
|
15
|
+
minDistance: a?.visibility?.minDistance ?? 5,
|
|
16
|
+
fadeRange: a?.visibility?.fadeRange ?? 5
|
|
17
|
+
},
|
|
18
|
+
custom: a?.custom,
|
|
19
|
+
clickable: a?.clickable !== !1
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function d(t, o) {
|
|
23
|
+
return t.connectorInfo.to.filter((e) => e.transitionType === "portal").map((e) => ({
|
|
24
|
+
target: e,
|
|
25
|
+
space: o[e.caveWorldId]
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
function m(t, o, e) {
|
|
29
|
+
if (e)
|
|
30
|
+
return { opacity: 0, displayMode: "hidden" };
|
|
31
|
+
const { maxDistance: a, minDistance: l, fadeRange: n } = o.visibility;
|
|
32
|
+
if (t > a + n || t < l - n)
|
|
33
|
+
return { opacity: 0, displayMode: "hidden" };
|
|
34
|
+
let i = 1;
|
|
35
|
+
if (t > a ? i = 1 - (t - a) / n : t < l && (i = 1 - (l - t) / n), i = Math.max(0, Math.min(1, i)), i <= 0)
|
|
36
|
+
return { opacity: 0, displayMode: "hidden" };
|
|
37
|
+
const r = Math.max(a * 0.65, l + 2), s = t > r ? "compact" : "full";
|
|
38
|
+
return { opacity: i, displayMode: s };
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
f as FALLBACK_LABEL_TEXT,
|
|
42
|
+
m as computeLabelVisibility,
|
|
43
|
+
d as getPortalTargetsWithSpaces,
|
|
44
|
+
p as resolveConnectorLabel,
|
|
45
|
+
u as shouldShowConnectorLabel
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../../core/connector-label/types.ts"],"sourcesContent":["import type { ComponentType } from 'react'\nimport type {\n CaveSpace,\n ConnectorDef,\n ConnectorLabelDef,\n ConnectorTarget,\n Vector3\n} from '../../types'\n\n/**\n * Fallback label text when no target metadata is available yet.\n *\n * @deprecated Prefer `messages.connector.fallbackLabel`, which is what the\n * built-in label host passes to {@link resolveConnectorLabel}.\n */\nexport const FALLBACK_LABEL_TEXT = '前方场景'\n\nexport interface ResolvedConnectorLabel {\n variant: string\n text: string\n icon?: string\n thumbnail?: string\n anchorOffset: Vector3\n visibility: {\n maxDistance: number\n minDistance: number\n fadeRange: number\n }\n custom?: Record<string, unknown>\n /** Whether clicking the label triggers portal navigation. Default true. */\n clickable: boolean\n}\n\nexport interface ConnectorLabelRendererProps {\n connector: ConnectorDef\n label: ResolvedConnectorLabel\n targets: Array<{ target: ConnectorTarget; space: CaveSpace | undefined }>\n distance: number\n isPlayerInside: boolean\n /** 0–1 opacity from distance fade + inside-state gating. */\n opacity: number\n /** 'full' badge with text, 'compact' icon-only, 'hidden' when fully faded. */\n displayMode: 'full' | 'compact' | 'hidden'\n /** Resolved from JSON; default true. When false, `onClick` is omitted. */\n clickable: boolean\n /** Triggers the same portal navigation as the in-zone action panel. */\n onClick?: () => void\n /** `loading` while target scene base layer is still preloading. */\n transferStatus: 'ready' | 'loading'\n}\n\nexport type ConnectorLabelRenderer = ComponentType<ConnectorLabelRendererProps>\n\nexport type ConnectorLabelMode = 'auto' | 'off'\n\nexport function shouldShowConnectorLabel(\n connector: ConnectorDef,\n connectorLabelDefault: ConnectorLabelMode\n): boolean {\n const hasPortal = connector.connectorInfo.to.some((t) => t.transitionType === 'portal')\n if (!hasPortal) return false\n if (connector.label === false) return false\n if (connector.label !== undefined) return true\n return connectorLabelDefault === 'auto'\n}\n\n/**\n * Flatten a connector's label config into concrete render values.\n *\n * Text falls back through `label.text` → first portal target's\n * `metadata.name` → `fallbackText`, so a `caveWorldId` is never shown to users.\n */\nexport function resolveConnectorLabel(\n connector: ConnectorDef,\n connectedSpaces: Record<string, CaveSpace>,\n fallbackText: string = FALLBACK_LABEL_TEXT\n): ResolvedConnectorLabel {\n const labelDef: ConnectorLabelDef | undefined =\n connector.label === false ? undefined : connector.label\n\n const portalTargets = connector.connectorInfo.to.filter((t) => t.transitionType === 'portal')\n const firstTarget = portalTargets[0]\n const space = firstTarget ? connectedSpaces[firstTarget.caveWorldId] : undefined\n\n const scaleY = connector.transform.scale[1]\n const defaultAnchor: Vector3 = [0, scaleY / 4 + 0.2, 0]\n\n return {\n variant: labelDef?.variant ?? 'beacon',\n text: labelDef?.text ?? space?.metadata?.name ?? fallbackText,\n icon: labelDef?.icon,\n thumbnail: labelDef?.thumbnail ?? space?.metadata?.thumbnail,\n anchorOffset: labelDef?.anchorOffset ?? defaultAnchor,\n visibility: {\n maxDistance: labelDef?.visibility?.maxDistance ?? 30,\n minDistance: labelDef?.visibility?.minDistance ?? 5,\n fadeRange: labelDef?.visibility?.fadeRange ?? 5\n },\n custom: labelDef?.custom,\n clickable: labelDef?.clickable !== false\n }\n}\n\nexport function getPortalTargetsWithSpaces(\n connector: ConnectorDef,\n connectedSpaces: Record<string, CaveSpace>\n): Array<{ target: ConnectorTarget; space: CaveSpace | undefined }> {\n return connector.connectorInfo.to\n .filter((t) => t.transitionType === 'portal')\n .map((target) => ({\n target,\n space: connectedSpaces[target.caveWorldId]\n }))\n}\n\n/** Compute label opacity and display mode from camera distance. */\nexport function computeLabelVisibility(\n distance: number,\n label: ResolvedConnectorLabel,\n isPlayerInside: boolean\n): { opacity: number; displayMode: 'full' | 'compact' | 'hidden' } {\n if (isPlayerInside) {\n return { opacity: 0, displayMode: 'hidden' }\n }\n\n const { maxDistance, minDistance, fadeRange } = label.visibility\n\n if (distance > maxDistance + fadeRange || distance < minDistance - fadeRange) {\n return { opacity: 0, displayMode: 'hidden' }\n }\n\n let opacity = 1\n if (distance > maxDistance) {\n opacity = 1 - (distance - maxDistance) / fadeRange\n } else if (distance < minDistance) {\n opacity = 1 - (minDistance - distance) / fadeRange\n }\n opacity = Math.max(0, Math.min(1, opacity))\n\n if (opacity <= 0) {\n return { opacity: 0, displayMode: 'hidden' }\n }\n\n const compactThreshold = Math.max(maxDistance * 0.65, minDistance + 2)\n const displayMode = distance > compactThreshold ? 'compact' : 'full'\n return { opacity, displayMode }\n}\n"],"names":["FALLBACK_LABEL_TEXT","shouldShowConnectorLabel","connector","connectorLabelDefault","t","resolveConnectorLabel","connectedSpaces","fallbackText","labelDef","firstTarget","space","defaultAnchor","getPortalTargetsWithSpaces","target","computeLabelVisibility","distance","label","isPlayerInside","maxDistance","minDistance","fadeRange","opacity","compactThreshold","displayMode"],"mappings":"AAeO,MAAMA,IAAsB;AAwC5B,SAASC,EACdC,GACAC,GACS;AAGT,SADI,CADcD,EAAU,cAAc,GAAG,KAAK,CAACE,MAAMA,EAAE,mBAAmB,QAAQ,KAElFF,EAAU,UAAU,KAAc,KAClCA,EAAU,UAAU,SAAkB,KACnCC,MAA0B;AACnC;AAQO,SAASE,EACdH,GACAI,GACAC,IAAuBP,GACC;AACxB,QAAMQ,IACJN,EAAU,UAAU,KAAQ,SAAYA,EAAU,OAG9CO,IADgBP,EAAU,cAAc,GAAG,OAAO,CAACE,MAAMA,EAAE,mBAAmB,QAAQ,EAC1D,CAAC,GAC7BM,IAAQD,IAAcH,EAAgBG,EAAY,WAAW,IAAI,QAGjEE,IAAyB,CAAC,GADjBT,EAAU,UAAU,MAAM,CAAC,IACE,IAAI,KAAK,CAAC;AAEtD,SAAO;AAAA,IACL,SAASM,GAAU,WAAW;AAAA,IAC9B,MAAMA,GAAU,QAAQE,GAAO,UAAU,QAAQH;AAAA,IACjD,MAAMC,GAAU;AAAA,IAChB,WAAWA,GAAU,aAAaE,GAAO,UAAU;AAAA,IACnD,cAAcF,GAAU,gBAAgBG;AAAA,IACxC,YAAY;AAAA,MACV,aAAaH,GAAU,YAAY,eAAe;AAAA,MAClD,aAAaA,GAAU,YAAY,eAAe;AAAA,MAClD,WAAWA,GAAU,YAAY,aAAa;AAAA,IAAA;AAAA,IAEhD,QAAQA,GAAU;AAAA,IAClB,WAAWA,GAAU,cAAc;AAAA,EAAA;AAEvC;AAEO,SAASI,EACdV,GACAI,GACkE;AAClE,SAAOJ,EAAU,cAAc,GAC5B,OAAO,CAACE,MAAMA,EAAE,mBAAmB,QAAQ,EAC3C,IAAI,CAACS,OAAY;AAAA,IAChB,QAAAA;AAAA,IACA,OAAOP,EAAgBO,EAAO,WAAW;AAAA,EAAA,EACzC;AACN;AAGO,SAASC,EACdC,GACAC,GACAC,GACiE;AACjE,MAAIA;AACF,WAAO,EAAE,SAAS,GAAG,aAAa,SAAA;AAGpC,QAAM,EAAE,aAAAC,GAAa,aAAAC,GAAa,WAAAC,EAAA,IAAcJ,EAAM;AAEtD,MAAID,IAAWG,IAAcE,KAAaL,IAAWI,IAAcC;AACjE,WAAO,EAAE,SAAS,GAAG,aAAa,SAAA;AAGpC,MAAIC,IAAU;AAQd,MAPIN,IAAWG,IACbG,IAAU,KAAKN,IAAWG,KAAeE,IAChCL,IAAWI,MACpBE,IAAU,KAAKF,IAAcJ,KAAYK,IAE3CC,IAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGA,CAAO,CAAC,GAEtCA,KAAW;AACb,WAAO,EAAE,SAAS,GAAG,aAAa,SAAA;AAGpC,QAAMC,IAAmB,KAAK,IAAIJ,IAAc,MAAMC,IAAc,CAAC,GAC/DI,IAAcR,IAAWO,IAAmB,YAAY;AAC9D,SAAO,EAAE,SAAAD,GAAS,aAAAE,EAAA;AACpB;"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type Mutate, type StoreApi } from 'zustand';
|
|
3
|
+
import type { CaveSpace, CustomObject } from '../../types/index.js';
|
|
4
|
+
import { CaveEventBus } from '../events/event-bus.js';
|
|
5
|
+
import { type CaveWorldMessages, type PartialCaveWorldMessages } from '../i18n/messages.js';
|
|
6
|
+
import { ObjectRegistry } from '../registry/object-registry.js';
|
|
7
|
+
import { ComponentRegistry } from '../registry/component-registry.js';
|
|
8
|
+
import type { ConnectorLabelMode, ConnectorLabelRenderer } from '../connector-label/types.js';
|
|
9
|
+
export interface CaveWorldInteraction {
|
|
10
|
+
activeObject: CustomObject | undefined;
|
|
11
|
+
detailVisible: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A terminal (retries-exhausted) failure to load a space's assets. Surfaced so
|
|
15
|
+
* a built-in / custom overlay can prompt the user instead of the page hanging
|
|
16
|
+
* on an infinite loading screen.
|
|
17
|
+
*
|
|
18
|
+
* - `stage: 'config'` — the space JSON (`caveSpaceLoader`) failed.
|
|
19
|
+
* - `stage: 'splat'` — the base-layer 3DGS/splat asset failed to download.
|
|
20
|
+
*/
|
|
21
|
+
export interface SpaceLoadError {
|
|
22
|
+
stage: 'config' | 'splat';
|
|
23
|
+
/** Asset URL that failed (present for the splat stage). */
|
|
24
|
+
url?: string;
|
|
25
|
+
/** Space id that failed, when known. */
|
|
26
|
+
spaceId?: string;
|
|
27
|
+
error: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface CaveWorldState extends CaveWorldInteraction {
|
|
30
|
+
spaceConfig: CaveSpace | undefined;
|
|
31
|
+
/** Terminal space/splat load failure, or `undefined` when healthy. */
|
|
32
|
+
spaceLoadError: SpaceLoadError | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Monotonic counter bumped by {@link CaveWorldState.retrySpaceLoad}. Load
|
|
35
|
+
* effects and the robust splat mesh include it in their dependencies so a
|
|
36
|
+
* user-triggered retry forces a fresh config fetch / splat re-download.
|
|
37
|
+
*/
|
|
38
|
+
retryToken: number;
|
|
39
|
+
setSpaceConfig: (config: CaveSpace | undefined) => void;
|
|
40
|
+
setActiveObject: (obj: CustomObject | undefined) => void;
|
|
41
|
+
setDetailVisible: (visible: boolean) => void;
|
|
42
|
+
setSpaceLoadError: (error: SpaceLoadError | undefined) => void;
|
|
43
|
+
/** Clear the error and bump {@link CaveWorldState.retryToken} to retry. */
|
|
44
|
+
retrySpaceLoad: () => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Store handle including the `subscribeWithSelector` overload, so callers can
|
|
48
|
+
* watch one field instead of re-running on every write.
|
|
49
|
+
*/
|
|
50
|
+
export type CaveWorldStoreApi = Mutate<StoreApi<CaveWorldState>, [
|
|
51
|
+
['zustand/subscribeWithSelector', never]
|
|
52
|
+
]>;
|
|
53
|
+
export interface CaveWorldServices {
|
|
54
|
+
eventBus: CaveEventBus;
|
|
55
|
+
objectRegistry: ObjectRegistry;
|
|
56
|
+
componentRegistry: ComponentRegistry;
|
|
57
|
+
store: CaveWorldStoreApi;
|
|
58
|
+
/** Resolved UI copy for the built-in overlays. */
|
|
59
|
+
messages: CaveWorldMessages;
|
|
60
|
+
connectorLabel: ConnectorLabelMode;
|
|
61
|
+
connectorLabelRenderer?: ConnectorLabelRenderer;
|
|
62
|
+
}
|
|
63
|
+
export declare function createCaveWorldStore(): CaveWorldStoreApi;
|
|
64
|
+
export interface CaveWorldProviderProps {
|
|
65
|
+
children: ReactNode;
|
|
66
|
+
/** Reuse an external bus to observe SDK events from outside the tree. */
|
|
67
|
+
eventBus?: CaveEventBus;
|
|
68
|
+
/** Pre-populated registry of object renderers. */
|
|
69
|
+
objectRegistry?: ObjectRegistry;
|
|
70
|
+
/** Pre-populated registry of behavior components. */
|
|
71
|
+
componentRegistry?: ComponentRegistry;
|
|
72
|
+
/** Partial copy overrides, deep-merged over the default locale. */
|
|
73
|
+
messages?: PartialCaveWorldMessages;
|
|
74
|
+
/** Global default for connector wayfinding labels. @default 'auto' */
|
|
75
|
+
connectorLabel?: ConnectorLabelMode;
|
|
76
|
+
connectorLabelRenderer?: ConnectorLabelRenderer;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Provides the per-instance services every SDK hook and component reads:
|
|
80
|
+
* event bus, registries, world store, and resolved copy.
|
|
81
|
+
*
|
|
82
|
+
* `CaveExplore` / `CaveOrbitViewer` mount this for you. Mount it yourself only
|
|
83
|
+
* when composing `CaveSpace` into your own canvas.
|
|
84
|
+
*/
|
|
85
|
+
export declare function CaveWorldProvider({ children, eventBus: externalEventBus, objectRegistry: externalObjectRegistry, componentRegistry: externalComponentRegistry, messages, connectorLabel, connectorLabelRenderer }: CaveWorldProviderProps): import("react").JSX.Element;
|
|
86
|
+
export declare function useCaveWorld(): CaveWorldServices;
|
|
87
|
+
/** Subscribe to a slice of the per-instance world store. */
|
|
88
|
+
export declare function useCaveWorldStore<T>(selector: (state: CaveWorldState) => T): T;
|
|
89
|
+
/** Imperative handle to the world store (`getState` / `setState` / `subscribe`). */
|
|
90
|
+
export declare function useCaveWorldStoreApi(): CaveWorldStoreApi;
|
|
91
|
+
export declare function useCaveEventBus(): CaveEventBus;
|
|
92
|
+
export declare function useObjectRegistry(): ObjectRegistry;
|
|
93
|
+
export declare function useComponentRegistry(): ComponentRegistry;
|
|
94
|
+
/** Resolved UI copy for the current provider. */
|
|
95
|
+
export declare function useCaveWorldMessages(): CaveWorldMessages;
|
|
96
|
+
//# sourceMappingURL=cave-world-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cave-world-context.d.ts","sourceRoot":"","sources":["../../../core/context/cave-world-context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AACpF,OAAO,EAAoB,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEtE,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC9B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AAE1F,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,YAAY,GAAG,SAAS,CAAA;IACtC,aAAa,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAA;IACzB,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,cAAe,SAAQ,oBAAoB;IAC1D,WAAW,EAAE,SAAS,GAAG,SAAS,CAAA;IAElC,sEAAsE;IACtE,cAAc,EAAE,cAAc,GAAG,SAAS,CAAA;IAC1C;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB,cAAc,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,KAAK,IAAI,CAAA;IACvD,eAAe,EAAE,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,KAAK,IAAI,CAAA;IACxD,gBAAgB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAE5C,iBAAiB,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,KAAK,IAAI,CAAA;IAC9D,2EAA2E;IAC3E,cAAc,EAAE,MAAM,IAAI,CAAA;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CACpC,QAAQ,CAAC,cAAc,CAAC,EACxB;IAAC,CAAC,+BAA+B,EAAE,KAAK,CAAC;CAAC,CAC3C,CAAA;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,YAAY,CAAA;IACtB,cAAc,EAAE,cAAc,CAAA;IAC9B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,KAAK,EAAE,iBAAiB,CAAA;IACxB,kDAAkD;IAClD,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,cAAc,EAAE,kBAAkB,CAAA;IAClC,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;CAChD;AAID,wBAAgB,oBAAoB,IAAI,iBAAiB,CAkBxD;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,SAAS,CAAA;IACnB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,kDAAkD;IAClD,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,wBAAwB,CAAA;IACnC,sEAAsE;IACtE,cAAc,CAAC,EAAE,kBAAkB,CAAA;IACnC,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;CAChD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,QAAQ,EAAE,gBAAgB,EAC1B,cAAc,EAAE,sBAAsB,EACtC,iBAAiB,EAAE,yBAAyB,EAC5C,QAAQ,EACR,cAAuB,EACvB,sBAAsB,EACvB,EAAE,sBAAsB,+BAmCxB;AAED,wBAAgB,YAAY,IAAI,iBAAiB,CAShD;AAED,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,CAAC,GAAG,CAAC,CAG9E;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,IAAI,iBAAiB,CAExD;AAED,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAED,wBAAgB,iBAAiB,IAAI,cAAc,CAElD;AAED,wBAAgB,oBAAoB,IAAI,iBAAiB,CAExD;AAED,iDAAiD;AACjD,wBAAgB,oBAAoB,IAAI,iBAAiB,CAExD"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { jsx as g } from "react/jsx-runtime";
|
|
2
|
+
import { useState as t, useMemo as l, useContext as b, createContext as y } from "react";
|
|
3
|
+
import { create as R, useStore as S } from "zustand";
|
|
4
|
+
import { subscribeWithSelector as W } from "zustand/middleware";
|
|
5
|
+
import { CaveEventBus as j } from "../events/event-bus.js";
|
|
6
|
+
import { CaveWorldError as E } from "../errors.js";
|
|
7
|
+
import { resolveMessages as k } from "../i18n/messages.js";
|
|
8
|
+
import { ObjectRegistry as w } from "../registry/object-registry.js";
|
|
9
|
+
import { ComponentRegistry as O } from "../registry/component-registry.js";
|
|
10
|
+
const p = y(null);
|
|
11
|
+
function B() {
|
|
12
|
+
return R(
|
|
13
|
+
W((o) => ({
|
|
14
|
+
spaceConfig: void 0,
|
|
15
|
+
activeObject: void 0,
|
|
16
|
+
detailVisible: !1,
|
|
17
|
+
spaceLoadError: void 0,
|
|
18
|
+
retryToken: 0,
|
|
19
|
+
setSpaceConfig: (e) => o({ spaceConfig: e }),
|
|
20
|
+
setActiveObject: (e) => o({ activeObject: e }),
|
|
21
|
+
setDetailVisible: (e) => o({ detailVisible: e }),
|
|
22
|
+
setSpaceLoadError: (e) => o({ spaceLoadError: e }),
|
|
23
|
+
retrySpaceLoad: () => o((e) => ({ spaceLoadError: void 0, retryToken: e.retryToken + 1 }))
|
|
24
|
+
}))
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function D({
|
|
28
|
+
children: o,
|
|
29
|
+
eventBus: e,
|
|
30
|
+
objectRegistry: s,
|
|
31
|
+
componentRegistry: n,
|
|
32
|
+
messages: i,
|
|
33
|
+
connectorLabel: c = "auto",
|
|
34
|
+
connectorLabelRenderer: a
|
|
35
|
+
}) {
|
|
36
|
+
const [u] = t(B), [v] = t(() => new j()), [f] = t(() => new w()), [m] = t(() => new O()), d = l(() => k(i), [i]), C = l(
|
|
37
|
+
() => ({
|
|
38
|
+
eventBus: e ?? v,
|
|
39
|
+
objectRegistry: s ?? f,
|
|
40
|
+
componentRegistry: n ?? m,
|
|
41
|
+
store: u,
|
|
42
|
+
messages: d,
|
|
43
|
+
connectorLabel: c,
|
|
44
|
+
connectorLabelRenderer: a
|
|
45
|
+
}),
|
|
46
|
+
[
|
|
47
|
+
e,
|
|
48
|
+
s,
|
|
49
|
+
n,
|
|
50
|
+
v,
|
|
51
|
+
f,
|
|
52
|
+
m,
|
|
53
|
+
u,
|
|
54
|
+
d,
|
|
55
|
+
c,
|
|
56
|
+
a
|
|
57
|
+
]
|
|
58
|
+
);
|
|
59
|
+
return /* @__PURE__ */ g(p.Provider, { value: C, children: o });
|
|
60
|
+
}
|
|
61
|
+
function r() {
|
|
62
|
+
const o = b(p);
|
|
63
|
+
if (!o)
|
|
64
|
+
throw new E(
|
|
65
|
+
"MISSING_PROVIDER",
|
|
66
|
+
"useCaveWorld must be used within a <CaveWorldProvider>."
|
|
67
|
+
);
|
|
68
|
+
return o;
|
|
69
|
+
}
|
|
70
|
+
function G(o) {
|
|
71
|
+
const { store: e } = r();
|
|
72
|
+
return S(e, o);
|
|
73
|
+
}
|
|
74
|
+
function N() {
|
|
75
|
+
return r().store;
|
|
76
|
+
}
|
|
77
|
+
function _() {
|
|
78
|
+
return r().eventBus;
|
|
79
|
+
}
|
|
80
|
+
function q() {
|
|
81
|
+
return r().objectRegistry;
|
|
82
|
+
}
|
|
83
|
+
function z() {
|
|
84
|
+
return r().componentRegistry;
|
|
85
|
+
}
|
|
86
|
+
function F() {
|
|
87
|
+
return r().messages;
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
D as CaveWorldProvider,
|
|
91
|
+
B as createCaveWorldStore,
|
|
92
|
+
_ as useCaveEventBus,
|
|
93
|
+
r as useCaveWorld,
|
|
94
|
+
F as useCaveWorldMessages,
|
|
95
|
+
G as useCaveWorldStore,
|
|
96
|
+
N as useCaveWorldStoreApi,
|
|
97
|
+
z as useComponentRegistry,
|
|
98
|
+
q as useObjectRegistry
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=cave-world-context.js.map
|