@volter/editor-game 0.5.65
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/LICENSE +661 -0
- package/NOTICE +23 -0
- package/contributions/asset-budget-asset.menu.ts +26 -0
- package/contributions/asset-budget.action.ts +18 -0
- package/contributions/asset-budget.document.tsx +21 -0
- package/contributions/asset-budget.menu.ts +22 -0
- package/contributions/audio-unlock.service.ts +17 -0
- package/contributions/audio.utility.tsx +15 -0
- package/contributions/autoplay.service.ts +48 -0
- package/contributions/bridge.command.ts +172 -0
- package/contributions/build-profiles.document.tsx +21 -0
- package/contributions/build-progress.status.tsx +45 -0
- package/contributions/build.action.ts +26 -0
- package/contributions/build.command.ts +27 -0
- package/contributions/build.header.tsx +37 -0
- package/contributions/build.menu.ts +31 -0
- package/contributions/build.service.ts +31 -0
- package/contributions/connection.status.tsx +43 -0
- package/contributions/coverage.service.ts +118 -0
- package/contributions/edit-mode-audio.service.ts +23 -0
- package/contributions/edit-mode-networking.service.ts +30 -0
- package/contributions/game-document.service.ts +25 -0
- package/contributions/game-eval.command.ts +210 -0
- package/contributions/game.layout.ts +19 -0
- package/contributions/gameplay.command.ts +255 -0
- package/contributions/generation.service.ts +96 -0
- package/contributions/generations.status.tsx +53 -0
- package/contributions/ingest.service.ts +80 -0
- package/contributions/instances.command.ts +75 -0
- package/contributions/navmesh.menu.ts +39 -0
- package/contributions/navmesh.service.ts +19 -0
- package/contributions/network.utility.tsx +17 -0
- package/contributions/play.command.ts +355 -0
- package/contributions/profiler.action.ts +16 -0
- package/contributions/profiler.menu.ts +22 -0
- package/contributions/profiler.utility.tsx +17 -0
- package/contributions/react/component-board.service.ts +21 -0
- package/contributions/react/design-time-mount.service.ts +60 -0
- package/contributions/react/pasteboard.action.ts +41 -0
- package/contributions/react/react-inspector.service.ts +85 -0
- package/contributions/react/story-documents.service.ts +44 -0
- package/contributions/scene-document.service.ts +32 -0
- package/contributions/state-watch.action.ts +17 -0
- package/contributions/state-watch.menu.ts +23 -0
- package/contributions/state-watch.utility.tsx +20 -0
- package/contributions/team-playtest.service.ts +124 -0
- package/contributions/three/camera-runtime.inspector.tsx +34 -0
- package/contributions/three/component-board.service.ts +24 -0
- package/contributions/three/component-verbs.command.ts +110 -0
- package/contributions/three/component-verbs.service.ts +92 -0
- package/contributions/three/constraints.inspector.tsx +34 -0
- package/contributions/three/model-asset-sections.service.ts +26 -0
- package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
- package/contributions/three/story-documents.service.ts +31 -0
- package/contributions/three/three-authoring.service.ts +66 -0
- package/contributions/transport.header.tsx +19 -0
- package/contributions/xstate-behavior.action.ts +42 -0
- package/contributions/xstate-behavior.document.tsx +73 -0
- package/contributions/xstate-behavior.inspector.tsx +28 -0
- package/contributions/xstate-behavior.menu.ts +23 -0
- package/package.json +144 -0
- package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
- package/src/asset-budget/asset-budget-model.ts +799 -0
- package/src/asset-budget/basis-encoder.ts +165 -0
- package/src/asset-budget/gltf-io.ts +154 -0
- package/src/asset-budget/gltf-optimize.ts +384 -0
- package/src/asset-budget/image-dims.ts +78 -0
- package/src/asset-budget/optimize-apply.ts +221 -0
- package/src/audio/AudioDebuggerPanel.tsx +457 -0
- package/src/audio/audio-debugger-model.ts +87 -0
- package/src/bridge/call.ts +60 -0
- package/src/bridge/dispatch.ts +459 -0
- package/src/bridge/live-frames.ts +25 -0
- package/src/bridge/screenshot.ts +316 -0
- package/src/build/BuildProfilesPanel.tsx +476 -0
- package/src/build/build-session.ts +247 -0
- package/src/build/format-bytes.ts +14 -0
- package/src/command-results.ts +36 -0
- package/src/coverage/live-authoring-surface.ts +30 -0
- package/src/coverage/live-project-verbs.ts +162 -0
- package/src/coverage/native-system-coverage.ts +143 -0
- package/src/coverage/root-coverage.ts +79 -0
- package/src/coverage/session-coverage.ts +193 -0
- package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
- package/src/edit-mode/edit-mode-audio.ts +56 -0
- package/src/edit-mode/edit-mode-networking.ts +112 -0
- package/src/game-document/DevicePresetPicker.tsx +84 -0
- package/src/game-document/GameCaptureFrameButton.tsx +45 -0
- package/src/game-document/GameDocument.tsx +215 -0
- package/src/game-document/GamePanel.tsx +662 -0
- package/src/game-document/InstanceInspectorPicker.tsx +183 -0
- package/src/game-document/crowd-debug.ts +183 -0
- package/src/game-document/device-preview.ts +336 -0
- package/src/game-document/game-view-store.ts +107 -0
- package/src/game-document/physics-debug.ts +187 -0
- package/src/generation/GenerationActivity.tsx +421 -0
- package/src/generation/GenerationGallery.css +231 -0
- package/src/generation/generation-documents.tsx +338 -0
- package/src/generation/generation-jobs.ts +128 -0
- package/src/generation/generation-presentation.ts +257 -0
- package/src/host/adapter-reach.ts +445 -0
- package/src/host/adapter-runtime-bindings.ts +303 -0
- package/src/host/after-paint.ts +112 -0
- package/src/host/api/configurations.ts +66 -0
- package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
- package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
- package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
- package/src/host/authoring/contract-scenes-stories.ts +204 -0
- package/src/host/authoring/creation-site-related.ts +55 -0
- package/src/host/authoring/ephemeral-persistence.ts +29 -0
- package/src/host/authoring/gesture-persist.ts +84 -0
- package/src/host/authoring/ingest-data-writer.ts +232 -0
- package/src/host/authoring/ingest-source-persistence.ts +826 -0
- package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
- package/src/host/authoring/mounted-authoring.ts +41 -0
- package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
- package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
- package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
- package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
- package/src/host/authoring/pixi-isolation-assets.ts +25 -0
- package/src/host/authoring/pixi-live-write-target.ts +979 -0
- package/src/host/authoring/pixi-source-identity.ts +141 -0
- package/src/host/authoring/pixi-still-presentation.ts +71 -0
- package/src/host/authoring/pixi-structure-history.ts +237 -0
- package/src/host/authoring/pixi-transform-channels.ts +205 -0
- package/src/host/authoring/selection-remount-handoff.ts +23 -0
- package/src/host/authoring/source-persistence-backend.ts +373 -0
- package/src/host/authoring/source-refresh-revisions.ts +81 -0
- package/src/host/authoring/struct-write-pipe.ts +143 -0
- package/src/host/auto-frame-window.ts +89 -0
- package/src/host/binding-resolver.ts +393 -0
- package/src/host/browser-transpile.ts +631 -0
- package/src/host/canvas-entry-runtime.ts +95 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
- package/src/host/components/HeaderTelemetry.tsx +341 -0
- package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
- package/src/host/components/ResolutionPicker.tsx +89 -0
- package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
- package/src/host/components/frame-debugger-model.ts +579 -0
- package/src/host/components/header-telemetry-model.ts +74 -0
- package/src/host/components/scene-document.tsx +447 -0
- package/src/host/components/utility-view-state.ts +87 -0
- package/src/host/components/world-root-stage-binding.tsx +133 -0
- package/src/host/components/world-root-stage.ts +1046 -0
- package/src/host/coverage/authoring-read-probe.ts +583 -0
- package/src/host/coverage/capability-coverage.ts +1587 -0
- package/src/host/coverage/coverage-accounting.ts +261 -0
- package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
- package/src/host/coverage/project-verb-coverage.ts +148 -0
- package/src/host/coverage/system-adapter-coverage.ts +373 -0
- package/src/host/design-system-stories/StoryLayout.tsx +104 -0
- package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
- package/src/host/document-preview-three.ts +110 -0
- package/src/host/entry-adjudication.ts +89 -0
- package/src/host/game-location-guard.ts +150 -0
- package/src/host/game-module-access.ts +196 -0
- package/src/host/game-realm-page.ts +358 -0
- package/src/host/game-realm-reclaim.ts +55 -0
- package/src/host/game-realm-storage.ts +104 -0
- package/src/host/gameplay-export.ts +288 -0
- package/src/host/gameplay-recording.ts +717 -0
- package/src/host/gated-globals.ts +1511 -0
- package/src/host/history/json-history-resource.ts +254 -0
- package/src/host/ingest/registry.ts +261 -0
- package/src/host/instance-extract-actions.ts +120 -0
- package/src/host/instance-fork-actions.ts +120 -0
- package/src/host/play-control-hook.ts +26 -0
- package/src/host/playwright-shim.ts +479 -0
- package/src/host/projection/dom.ts +295 -0
- package/src/host/projection/pixi.ts +288 -0
- package/src/host/r3f-entry-runtime.ts +77 -0
- package/src/host/react-mount-runtime.ts +162 -0
- package/src/host/realm-services.ts +148 -0
- package/src/host/recording-preview.ts +106 -0
- package/src/host/roots/module-root.ts +203 -0
- package/src/host/roots/react-root.ts +181 -0
- package/src/host/same-realm-loop-gate.ts +544 -0
- package/src/host/scene-view-drawability.ts +69 -0
- package/src/host/sdk/tools.ts +31 -0
- package/src/host/served-bundle-runtime-modules.ts +331 -0
- package/src/host/server-log-bridge.ts +60 -0
- package/src/host/staged-projects.ts +24 -0
- package/src/host/stories/mounted-story-viewport-source.ts +64 -0
- package/src/host/stories/story-arg-descriptors.ts +50 -0
- package/src/host/stories/story-media-presence.ts +152 -0
- package/src/host/surface-content.ts +87 -0
- package/src/host/take-named-export.ts +23 -0
- package/src/host/three-ingest-runtime.ts +76 -0
- package/src/host/types-fastnoise-lite.d.ts +7 -0
- package/src/host/types-mikktspace.d.ts +20 -0
- package/src/host/types-troika-three-text.d.ts +7 -0
- package/src/host/use-active-performance-source.ts +53 -0
- package/src/host/viewport-pose-memory.ts +48 -0
- package/src/host/viewport-root-presentation.ts +40 -0
- package/src/ingest/active-ingest.ts +251 -0
- package/src/ingest/active-scene-navigation.ts +43 -0
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
- package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
- package/src/ingest/capture-wait-report.ts +122 -0
- package/src/ingest/deferred-ingest-play.ts +216 -0
- package/src/ingest/deferred-ingest-session.ts +23 -0
- package/src/ingest/discovery-public-ingest.ts +242 -0
- package/src/ingest/dom-stub-mark.ts +7 -0
- package/src/ingest/entry-load.ts +56 -0
- package/src/ingest/game-contract-realm.ts +34 -0
- package/src/ingest/game-pointer-lock.ts +89 -0
- package/src/ingest/held-scene-repaint.ts +73 -0
- package/src/ingest/host-surface-box.ts +174 -0
- package/src/ingest/ingest-boot-viewport.ts +54 -0
- package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
- package/src/ingest/ingest-canvas-scene.ts +50 -0
- package/src/ingest/ingest-evidence-hook.ts +131 -0
- package/src/ingest/ingest-frame-snapshot.ts +183 -0
- package/src/ingest/ingest-play-commands.ts +134 -0
- package/src/ingest/ingest-play-control.ts +299 -0
- package/src/ingest/ingest-render-debug.ts +320 -0
- package/src/ingest/ingest-siblings.ts +487 -0
- package/src/ingest/ingest-status.ts +62 -0
- package/src/ingest/live-ingest-facet.ts +46 -0
- package/src/ingest/module-mode.ts +229 -0
- package/src/ingest/mount-canvas-ingest-root.ts +899 -0
- package/src/ingest/mount-coverage.ts +296 -0
- package/src/ingest/mount-dom-ingest-root.ts +282 -0
- package/src/ingest/mount-ingest-root.ts +744 -0
- package/src/ingest/mount-three-ingest-root.ts +366 -0
- package/src/ingest/resolve-canvas.ts +47 -0
- package/src/ingest/resolve-three.ts +123 -0
- package/src/ingest/served-bundle.ts +109 -0
- package/src/ingest/served-html-boot.ts +292 -0
- package/src/ingest/surface-canvas.ts +84 -0
- package/src/ingest/surface-dom.ts +84 -0
- package/src/ingest/surface-three.ts +128 -0
- package/src/ingest/types.ts +76 -0
- package/src/ingest/unmount-ingest-root.ts +224 -0
- package/src/navmesh/navmesh-actions.ts +27 -0
- package/src/navmesh/navmesh-handler.ts +237 -0
- package/src/navmesh/navmesh-workflow-store.ts +78 -0
- package/src/network/NetworkInspectorPanel.tsx +644 -0
- package/src/network/network-inspector-model.ts +225 -0
- package/src/play/play-boot-stall.ts +118 -0
- package/src/play/play-log-events.ts +26 -0
- package/src/play/play-mode.ts +2553 -0
- package/src/play/play-recording.ts +335 -0
- package/src/play/react-play-live-authoring.ts +165 -0
- package/src/play-bar/PlayBar.tsx +488 -0
- package/src/play-bar/PlayerCountPicker.tsx +100 -0
- package/src/profiler/FrameDebuggerPanel.tsx +458 -0
- package/src/profiler/PerformancePanel.tsx +1068 -0
- package/src/profiler/ProfilerPanel.tsx +53 -0
- package/src/profiler/frame-debugger-store.ts +97 -0
- package/src/profiler/main-thread-busy.ts +90 -0
- package/src/react/design-time-react-mount.ts +782 -0
- package/src/react/dom-authoring-adapter.ts +764 -0
- package/src/react/pasteboard-materialize.ts +154 -0
- package/src/react/react-inspector-section.tsx +2496 -0
- package/src/react/react-world-authoring-adapter.ts +3795 -0
- package/src/react/story-documents/story-args-section.ts +19 -0
- package/src/react/story-documents/story-documents.tsx +1380 -0
- package/src/react/story-paint-bounds.ts +94 -0
- package/src/react/ui-board-document.tsx +101 -0
- package/src/react/ui-board-title.ts +9 -0
- package/src/react/ui-component-board.ts +72 -0
- package/src/services/audio-pose-guard.ts +81 -0
- package/src/services/game-audio-unlock.ts +48 -0
- package/src/state-watch/StateWatchPanel.tsx +535 -0
- package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
- package/src/three/authoring/constraint-inspector-section.tsx +189 -0
- package/src/three/authoring/design-time-renderer.ts +189 -0
- package/src/three/authoring/model-asset-inspector-section.css +41 -0
- package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
- package/src/three/authoring/oid-source-persistence.ts +557 -0
- package/src/three/authoring/r3f-design-session.ts +1174 -0
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
- package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
- package/src/three/authoring/spatial-audio-handles.ts +215 -0
- package/src/three/authoring/spatial-collider-handles.ts +229 -0
- package/src/three/authoring/spatial-joint-handles.ts +114 -0
- package/src/three/authoring/spatial-light-handles.ts +178 -0
- package/src/three/authoring/spatial-lod-handles.ts +93 -0
- package/src/three/authoring/spatial-particle-handles.ts +368 -0
- package/src/three/authoring/three-authoring-adapter.ts +1953 -0
- package/src/three/authoring/three-scene-identity.ts +19 -0
- package/src/three/authoring/three-spatial-handles.ts +161 -0
- package/src/three/authoring/typed-three-inspector.ts +528 -0
- package/src/three/component-verbs/extract-menu.ts +71 -0
- package/src/three/component-verbs/fork-menu.ts +78 -0
- package/src/three/component-verbs/internals-menu.ts +91 -0
- package/src/three/story-documents/three-story-documents.tsx +607 -0
- package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
- package/src/three/three-board/board-framing.ts +412 -0
- package/src/three/three-board/board-layout.ts +401 -0
- package/src/three/three-board/board-scene.ts +901 -0
- package/src/three/three-board/three-component-board.ts +62 -0
- package/src/xstate/XStateBehaviorSection.tsx +130 -0
- package/src/xstate/XStateMachineInspector.tsx +566 -0
- package/src/xstate/character-animation-machine.fixture.ts +74 -0
- package/src/xstate/live-behaviors.ts +106 -0
- package/src/xstate/use-live-actor-state.ts +44 -0
- package/src/xstate/xstate-graph.ts +235 -0
- package/src/xstate/xstate-layout.ts +76 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuthoringAdapter,
|
|
3
|
+
EditorNode,
|
|
4
|
+
PropertyDescriptor,
|
|
5
|
+
Transform,
|
|
6
|
+
} from '@volter/editor-project/adapter';
|
|
7
|
+
|
|
8
|
+
const hierarchyNodes: Readonly<Record<string, Omit<EditorNode, 'label'>>> = {
|
|
9
|
+
scene: {
|
|
10
|
+
id: 'scene',
|
|
11
|
+
role: 'document',
|
|
12
|
+
secondaryLabel: 'src/scenes/AuroraScene.tsx',
|
|
13
|
+
kind: 'scene',
|
|
14
|
+
parentId: null,
|
|
15
|
+
childIds: ['environment', 'camera', 'player', 'enemy-bravo', 'turret'],
|
|
16
|
+
},
|
|
17
|
+
environment: {
|
|
18
|
+
id: 'environment',
|
|
19
|
+
role: 'folder',
|
|
20
|
+
kind: 'group',
|
|
21
|
+
parentId: 'scene',
|
|
22
|
+
childIds: ['key-light', 'fill-light', 'ground'],
|
|
23
|
+
},
|
|
24
|
+
'key-light': {
|
|
25
|
+
id: 'key-light',
|
|
26
|
+
role: 'entity',
|
|
27
|
+
kind: 'light',
|
|
28
|
+
parentId: 'environment',
|
|
29
|
+
childIds: [],
|
|
30
|
+
},
|
|
31
|
+
'fill-light': {
|
|
32
|
+
id: 'fill-light',
|
|
33
|
+
role: 'entity',
|
|
34
|
+
kind: 'light',
|
|
35
|
+
parentId: 'environment',
|
|
36
|
+
childIds: [],
|
|
37
|
+
},
|
|
38
|
+
ground: {
|
|
39
|
+
id: 'ground',
|
|
40
|
+
role: 'entity',
|
|
41
|
+
kind: 'mesh',
|
|
42
|
+
parentId: 'environment',
|
|
43
|
+
childIds: [],
|
|
44
|
+
},
|
|
45
|
+
camera: {
|
|
46
|
+
id: 'camera',
|
|
47
|
+
role: 'entity',
|
|
48
|
+
kind: 'camera',
|
|
49
|
+
parentId: 'scene',
|
|
50
|
+
childIds: [],
|
|
51
|
+
},
|
|
52
|
+
player: {
|
|
53
|
+
id: 'player',
|
|
54
|
+
role: 'entity',
|
|
55
|
+
kind: 'mesh',
|
|
56
|
+
parentId: 'scene',
|
|
57
|
+
childIds: ['weapon'],
|
|
58
|
+
},
|
|
59
|
+
weapon: {
|
|
60
|
+
id: 'weapon',
|
|
61
|
+
role: 'entity',
|
|
62
|
+
kind: 'mesh',
|
|
63
|
+
parentId: 'player',
|
|
64
|
+
childIds: [],
|
|
65
|
+
},
|
|
66
|
+
// The two instance states the hierarchy row now has to say out loud: a
|
|
67
|
+
// component instance carrying its type, and one whose three transform
|
|
68
|
+
// channels are all refused. Every other node above stays a plain entity,
|
|
69
|
+
// which is the third state.
|
|
70
|
+
'enemy-bravo': {
|
|
71
|
+
id: 'enemy-bravo',
|
|
72
|
+
role: 'component',
|
|
73
|
+
kind: 'group',
|
|
74
|
+
typeLabel: 'Enemy',
|
|
75
|
+
parentId: 'scene',
|
|
76
|
+
childIds: [],
|
|
77
|
+
},
|
|
78
|
+
turret: {
|
|
79
|
+
id: 'turret',
|
|
80
|
+
role: 'component',
|
|
81
|
+
kind: 'group',
|
|
82
|
+
typeLabel: 'Turret',
|
|
83
|
+
parentId: 'scene',
|
|
84
|
+
childIds: [],
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const initialLabels: Readonly<Record<string, string>> = {
|
|
89
|
+
scene: 'Aurora',
|
|
90
|
+
environment: 'Environment',
|
|
91
|
+
'key-light': 'Key Light',
|
|
92
|
+
'fill-light': 'Fill Light',
|
|
93
|
+
ground: 'Ground',
|
|
94
|
+
camera: 'Main Camera',
|
|
95
|
+
player: 'Player',
|
|
96
|
+
weapon: 'Pulse Blade',
|
|
97
|
+
'enemy-bravo': 'EnemyBravo',
|
|
98
|
+
turret: 'WatchTurret',
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const commonProperties: readonly PropertyDescriptor[] = [
|
|
102
|
+
{ path: 'name', label: 'Name', type: 'string' },
|
|
103
|
+
{ path: 'visible', label: 'Visible', type: 'boolean' },
|
|
104
|
+
{ path: 'locked', label: 'Locked', type: 'boolean' },
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const propertiesByKind: Readonly<Record<string, readonly PropertyDescriptor[]>> = {
|
|
108
|
+
mesh: [
|
|
109
|
+
...commonProperties,
|
|
110
|
+
{ path: 'material.color', label: 'Tint', type: 'color', group: 'Material' },
|
|
111
|
+
{
|
|
112
|
+
path: 'material.finish',
|
|
113
|
+
label: 'Finish',
|
|
114
|
+
type: 'enum',
|
|
115
|
+
options: ['Matte', 'Metal', 'Glass'],
|
|
116
|
+
group: 'Material',
|
|
117
|
+
},
|
|
118
|
+
{ path: 'gameplay.health', label: 'Health', type: 'number', group: 'Gameplay' },
|
|
119
|
+
{ path: 'gameplay.respawn', label: 'Respawn', type: 'boolean', group: 'Gameplay' },
|
|
120
|
+
],
|
|
121
|
+
light: [
|
|
122
|
+
...commonProperties,
|
|
123
|
+
{ path: 'light.color', label: 'Color', type: 'color', group: 'Light' },
|
|
124
|
+
{ path: 'light.intensity', label: 'Intensity', type: 'number', group: 'Light' },
|
|
125
|
+
{ path: 'light.shadows', label: 'Shadows', type: 'boolean', group: 'Light' },
|
|
126
|
+
],
|
|
127
|
+
camera: [
|
|
128
|
+
...commonProperties,
|
|
129
|
+
{
|
|
130
|
+
path: 'camera.projection',
|
|
131
|
+
label: 'Projection',
|
|
132
|
+
type: 'enum',
|
|
133
|
+
options: ['Perspective', 'Orthographic'],
|
|
134
|
+
group: 'Camera',
|
|
135
|
+
},
|
|
136
|
+
{ path: 'camera.fov', label: 'Field of view', type: 'number', group: 'Camera' },
|
|
137
|
+
],
|
|
138
|
+
group: commonProperties,
|
|
139
|
+
scene: commonProperties,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/** Mutable adapter fixture shared by every authoring-system story. */
|
|
143
|
+
export function createAuthoringFixture(): AuthoringAdapter {
|
|
144
|
+
const listeners = new Set<() => void>();
|
|
145
|
+
const labels = new Map(Object.entries(initialLabels));
|
|
146
|
+
const values = new Map<string, unknown>();
|
|
147
|
+
const transforms = new Map<string, Transform>();
|
|
148
|
+
let selection = ['player'];
|
|
149
|
+
|
|
150
|
+
for (const id of Object.keys(hierarchyNodes)) {
|
|
151
|
+
values.set(`${id}:visible`, true);
|
|
152
|
+
values.set(`${id}:locked`, id === 'ground');
|
|
153
|
+
transforms.set(id, {
|
|
154
|
+
position: id === 'player' ? [1.5, 0, -2] : [0, 0, 0],
|
|
155
|
+
rotation: [0, 0, 0, 1],
|
|
156
|
+
scale: [1, 1, 1],
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
values.set('player:material.color', '#6ea8ff');
|
|
160
|
+
values.set('player:material.finish', 'Metal');
|
|
161
|
+
values.set('player:gameplay.health', 100);
|
|
162
|
+
values.set('player:gameplay.respawn', true);
|
|
163
|
+
values.set('weapon:material.color', '#a9e8ff');
|
|
164
|
+
values.set('weapon:material.finish', 'Glass');
|
|
165
|
+
values.set('weapon:gameplay.health', 24);
|
|
166
|
+
values.set('weapon:gameplay.respawn', false);
|
|
167
|
+
values.set('key-light:light.color', '#ffe3b0');
|
|
168
|
+
values.set('key-light:light.intensity', 3.2);
|
|
169
|
+
values.set('key-light:light.shadows', true);
|
|
170
|
+
values.set('fill-light:light.color', '#8cbcff');
|
|
171
|
+
values.set('fill-light:light.intensity', 1.4);
|
|
172
|
+
values.set('fill-light:light.shadows', false);
|
|
173
|
+
values.set('camera:camera.projection', 'Perspective');
|
|
174
|
+
values.set('camera:camera.fov', 52);
|
|
175
|
+
|
|
176
|
+
const notify = () => {
|
|
177
|
+
for (const listener of listeners) listener();
|
|
178
|
+
};
|
|
179
|
+
const node = (id: string): EditorNode | null => {
|
|
180
|
+
const fixture = hierarchyNodes[id];
|
|
181
|
+
return fixture ? { ...fixture, label: labels.get(id) ?? id } : null;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
capabilities: {
|
|
186
|
+
transform: true,
|
|
187
|
+
inspectorFields: true,
|
|
188
|
+
persist: false,
|
|
189
|
+
},
|
|
190
|
+
provenance: {
|
|
191
|
+
source: 'document',
|
|
192
|
+
label: 'scene',
|
|
193
|
+
detail: 'Fixture data exercises the same adapter contract as an authored scene document.',
|
|
194
|
+
},
|
|
195
|
+
hierarchy: {
|
|
196
|
+
roots: () => [node('scene')!],
|
|
197
|
+
node,
|
|
198
|
+
object3D: () => null,
|
|
199
|
+
idForObject3D: () => null,
|
|
200
|
+
},
|
|
201
|
+
selection: {
|
|
202
|
+
get: () => selection,
|
|
203
|
+
set: (ids) => {
|
|
204
|
+
selection = ids;
|
|
205
|
+
notify();
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
transforms: {
|
|
209
|
+
dimensions: () => '3d',
|
|
210
|
+
get: (id) => transforms.get(id) ?? transforms.get('scene')!,
|
|
211
|
+
// H2 — one fixture row is fully refused, with the kind of sentence a
|
|
212
|
+
// real adapter produces. The row shows THIS string and nothing else.
|
|
213
|
+
editability: (id, channel) =>
|
|
214
|
+
id === 'turret'
|
|
215
|
+
? {
|
|
216
|
+
writable: false,
|
|
217
|
+
reason: `Turret does not forward ${channel} to its group root.`,
|
|
218
|
+
}
|
|
219
|
+
: { writable: true },
|
|
220
|
+
beginEdit: () => undefined,
|
|
221
|
+
apply: (id, transform) => {
|
|
222
|
+
transforms.set(id, transform);
|
|
223
|
+
notify();
|
|
224
|
+
},
|
|
225
|
+
endEdit: () => undefined,
|
|
226
|
+
},
|
|
227
|
+
inspector: {
|
|
228
|
+
properties: (id) => {
|
|
229
|
+
const kind = node(id)?.kind ?? 'object';
|
|
230
|
+
return [...(propertiesByKind[kind] ?? commonProperties)];
|
|
231
|
+
},
|
|
232
|
+
get: (id, path) => {
|
|
233
|
+
if (path === 'name') return labels.get(id);
|
|
234
|
+
return values.get(`${id}:${path}`);
|
|
235
|
+
},
|
|
236
|
+
set: (id, path, value) => {
|
|
237
|
+
if (path === 'name') labels.set(id, String(value));
|
|
238
|
+
else values.set(`${id}:${path}`, value);
|
|
239
|
+
notify();
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
subscribe: (listener) => {
|
|
243
|
+
listeners.add(listener);
|
|
244
|
+
return () => listeners.delete(listener);
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { type ReactNode, useEffect, useLayoutEffect, useState } from 'react';
|
|
2
|
+
import { resetProjectThumbnailManifestCache } from '@volter/editor-core/asset-workflow/thumbnail-system';
|
|
3
|
+
import { type EditorRuntime, EditorRuntimeProvider, type EditorStats } from '@volter/editor-core/editor-runtime';
|
|
4
|
+
import { EditorShellStore } from '@volter/editor-core/editor-shell-store';
|
|
5
|
+
import { EditorSession } from '@volter/editor-core/history/editor-session';
|
|
6
|
+
import { getStorageBackend, MemStorage, setStorageBackend } from '@volter/editor-core/storage/index';
|
|
7
|
+
import {
|
|
8
|
+
activeWorkspaceUtility,
|
|
9
|
+
installWorkspaceHostCommands,
|
|
10
|
+
setActiveWorkspaceUtility,
|
|
11
|
+
setWorkspaceUtilityTabs,
|
|
12
|
+
workspaceUtilityTabs,
|
|
13
|
+
} from '@volter/editor-core/workspace-host-commands';
|
|
14
|
+
import { availableWorkspaceUtilities } from '@volter/editor-core/workspace-utility-registry';
|
|
15
|
+
|
|
16
|
+
const ZERO_STATS: EditorStats = {
|
|
17
|
+
fps: 60,
|
|
18
|
+
frameTime: 16.7,
|
|
19
|
+
drawCalls: 42,
|
|
20
|
+
triangles: 12_480,
|
|
21
|
+
cameraPosition: { x: 4, y: 3, z: 8 },
|
|
22
|
+
cameraTarget: { x: 0, y: 1, z: 0 },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** A real, bounded editor runtime for production component stories. */
|
|
26
|
+
export function createStoryEditorRuntime(): EditorRuntime {
|
|
27
|
+
const store = new EditorShellStore();
|
|
28
|
+
const session = new EditorSession('storybook-fixture');
|
|
29
|
+
store.attachHistory(session.history);
|
|
30
|
+
return {
|
|
31
|
+
store,
|
|
32
|
+
session,
|
|
33
|
+
stats: ZERO_STATS,
|
|
34
|
+
initPromise: Promise.resolve(),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Installs the smallest honest workspace controller needed by shell stories.
|
|
40
|
+
* The production toolbar still talks through its normal semantic command
|
|
41
|
+
* boundary; the fixture only supplies the host state that Storybook does not
|
|
42
|
+
* mount. This keeps toolbar behavior testable without cloning its UI.
|
|
43
|
+
*/
|
|
44
|
+
export function installStoryWorkspaceUtilityController(): () => void {
|
|
45
|
+
const previousTabs = workspaceUtilityTabs();
|
|
46
|
+
const previousActive = activeWorkspaceUtility();
|
|
47
|
+
let tabs = availableWorkspaceUtilities()
|
|
48
|
+
.filter((utility) => utility.visibleByDefault !== false)
|
|
49
|
+
.map((utility) => utility.id);
|
|
50
|
+
|
|
51
|
+
const publishTabs = () => setWorkspaceUtilityTabs(tabs);
|
|
52
|
+
const showUtility = (id: string) => {
|
|
53
|
+
if (!tabs.includes(id)) {
|
|
54
|
+
tabs = [...tabs, id];
|
|
55
|
+
publishTabs();
|
|
56
|
+
}
|
|
57
|
+
setActiveWorkspaceUtility(id);
|
|
58
|
+
};
|
|
59
|
+
const uninstall = installWorkspaceHostCommands({
|
|
60
|
+
showStaticPanel: () => undefined,
|
|
61
|
+
showUtility,
|
|
62
|
+
toggleUtility: (id) => {
|
|
63
|
+
if (activeWorkspaceUtility() === id) setActiveWorkspaceUtility(null);
|
|
64
|
+
else showUtility(id);
|
|
65
|
+
},
|
|
66
|
+
closeUtility: (id) => {
|
|
67
|
+
tabs = tabs.filter((tabId) => tabId !== id);
|
|
68
|
+
publishTabs();
|
|
69
|
+
if (activeWorkspaceUtility() === id) setActiveWorkspaceUtility(null);
|
|
70
|
+
},
|
|
71
|
+
showAuxiliary: () => undefined,
|
|
72
|
+
toggleFocus: () => undefined,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
publishTabs();
|
|
76
|
+
setActiveWorkspaceUtility(null);
|
|
77
|
+
return () => {
|
|
78
|
+
uninstall();
|
|
79
|
+
setWorkspaceUtilityTabs(previousTabs);
|
|
80
|
+
setActiveWorkspaceUtility(previousActive);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Shared Storybook host for components that normally consume EditorContext.
|
|
86
|
+
* It deliberately provides runtime state only: project discovery, network
|
|
87
|
+
* listeners, and filesystem initialization remain application lifecycles.
|
|
88
|
+
*/
|
|
89
|
+
export function StoryEditorRuntime({
|
|
90
|
+
children,
|
|
91
|
+
onRuntime,
|
|
92
|
+
}: {
|
|
93
|
+
readonly children: ReactNode | ((runtime: EditorRuntime) => ReactNode);
|
|
94
|
+
readonly onRuntime?: (runtime: EditorRuntime) => void;
|
|
95
|
+
}) {
|
|
96
|
+
const [runtime] = useState(createStoryEditorRuntime);
|
|
97
|
+
useLayoutEffect(() => {
|
|
98
|
+
const previousStorage = getStorageBackend();
|
|
99
|
+
setStorageBackend(new MemStorage());
|
|
100
|
+
resetProjectThumbnailManifestCache();
|
|
101
|
+
return () => {
|
|
102
|
+
setStorageBackend(previousStorage);
|
|
103
|
+
resetProjectThumbnailManifestCache();
|
|
104
|
+
};
|
|
105
|
+
}, []);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
onRuntime?.(runtime);
|
|
108
|
+
return () => runtime.session.dispose();
|
|
109
|
+
}, [onRuntime, runtime]);
|
|
110
|
+
return (
|
|
111
|
+
<EditorRuntimeProvider runtime={runtime}>
|
|
112
|
+
{typeof children === 'function' ? children(runtime) : children}
|
|
113
|
+
</EditorRuntimeProvider>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { collectContentNodeRecords } from '@volter/editor-threejs/viewport/content-bounds';
|
|
2
|
+
import { isEditorOwnedObject } from '@volter/editor-threejs/viewport/editor-layers';
|
|
3
|
+
import { markHostRenderer } from '@volter/editor-threejs/viewport/renderer-ownership';
|
|
4
|
+
import * as THREE from 'three';
|
|
5
|
+
import type { DocumentPreviewCaptureRequest } from '@volter/editor-core/document-preview-source';
|
|
6
|
+
import { frameableContentBounds } from '@volter/editor-core/scene-framing';
|
|
7
|
+
|
|
8
|
+
function authoredCameras(root: THREE.Object3D): THREE.Camera[] {
|
|
9
|
+
const cameras: THREE.Camera[] = [];
|
|
10
|
+
root.traverse((object) => {
|
|
11
|
+
if (!isEditorOwnedObject(object) && (object as THREE.Camera).isCamera)
|
|
12
|
+
cameras.push(object as THREE.Camera);
|
|
13
|
+
});
|
|
14
|
+
return cameras;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function fallbackCamera(root: THREE.Object3D, aspect: number): THREE.PerspectiveCamera {
|
|
18
|
+
// The shared collector includes furniture for other callers. Capture fits
|
|
19
|
+
// only authored content, so remove isolated helper subtrees for this pass.
|
|
20
|
+
const helpers: { object: THREE.Object3D; parent: THREE.Object3D }[] = [];
|
|
21
|
+
root.traverse((object) => {
|
|
22
|
+
if (object.parent && isEditorOwnedObject(object))
|
|
23
|
+
helpers.push({ object, parent: object.parent });
|
|
24
|
+
});
|
|
25
|
+
for (const { object } of helpers) object.removeFromParent();
|
|
26
|
+
let records: ReturnType<typeof collectContentNodeRecords>;
|
|
27
|
+
try {
|
|
28
|
+
records = collectContentNodeRecords(root);
|
|
29
|
+
} finally {
|
|
30
|
+
for (const { object, parent } of helpers) parent.add(object);
|
|
31
|
+
}
|
|
32
|
+
const meaningful = records.filter((record) => !record.overlay && !record.enclosure);
|
|
33
|
+
const candidates = meaningful;
|
|
34
|
+
const bounds = frameableContentBounds(candidates.map((record) => record.box));
|
|
35
|
+
const camera = new THREE.PerspectiveCamera(42, aspect, 0.01, 100_000);
|
|
36
|
+
const center = bounds.isEmpty() ? new THREE.Vector3() : bounds.getCenter(new THREE.Vector3());
|
|
37
|
+
const radius = bounds.isEmpty()
|
|
38
|
+
? 1
|
|
39
|
+
: Math.max(0.01, bounds.getBoundingSphere(new THREE.Sphere()).radius);
|
|
40
|
+
const distance = radius / Math.sin(THREE.MathUtils.degToRad(camera.fov / 2));
|
|
41
|
+
camera.position
|
|
42
|
+
.copy(center)
|
|
43
|
+
.addScaledVector(new THREE.Vector3(1, 0.75, 1).normalize(), distance * 1.15);
|
|
44
|
+
camera.lookAt(center);
|
|
45
|
+
camera.near = Math.max(0.001, distance - radius * 2.5);
|
|
46
|
+
camera.far = distance + radius * 3;
|
|
47
|
+
camera.updateProjectionMatrix();
|
|
48
|
+
camera.updateMatrixWorld(true);
|
|
49
|
+
return camera;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function captureCamera(root: THREE.Object3D, request: DocumentPreviewCaptureRequest): THREE.Camera {
|
|
53
|
+
if (request.camera) {
|
|
54
|
+
const camera = new THREE.PerspectiveCamera(
|
|
55
|
+
request.camera.fov ?? 42,
|
|
56
|
+
request.width / request.height,
|
|
57
|
+
0.01,
|
|
58
|
+
100_000,
|
|
59
|
+
);
|
|
60
|
+
camera.position.set(
|
|
61
|
+
request.camera.position.x,
|
|
62
|
+
request.camera.position.y,
|
|
63
|
+
request.camera.position.z,
|
|
64
|
+
);
|
|
65
|
+
camera.lookAt(request.camera.target.x, request.camera.target.y, request.camera.target.z);
|
|
66
|
+
camera.updateMatrixWorld(true);
|
|
67
|
+
return camera;
|
|
68
|
+
}
|
|
69
|
+
const declared = authoredCameras(root);
|
|
70
|
+
if (declared.length === 1) {
|
|
71
|
+
const camera = declared[0]!.clone();
|
|
72
|
+
camera.matrixWorld.copy(declared[0]!.matrixWorld);
|
|
73
|
+
camera.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale);
|
|
74
|
+
if (camera instanceof THREE.PerspectiveCamera) {
|
|
75
|
+
camera.aspect = request.width / request.height;
|
|
76
|
+
camera.updateProjectionMatrix();
|
|
77
|
+
}
|
|
78
|
+
return camera;
|
|
79
|
+
}
|
|
80
|
+
return fallbackCamera(root, request.width / request.height);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** One chrome-free frame of the authored scene. No studio dressing is added. */
|
|
84
|
+
export function captureAuthoredThreeScenePreview(
|
|
85
|
+
root: THREE.Object3D,
|
|
86
|
+
scene: THREE.Scene,
|
|
87
|
+
request: DocumentPreviewCaptureRequest,
|
|
88
|
+
prepare?: (renderer: THREE.WebGLRenderer) => () => void,
|
|
89
|
+
): string {
|
|
90
|
+
scene.updateMatrixWorld(true);
|
|
91
|
+
const camera = captureCamera(root, request);
|
|
92
|
+
const renderer = markHostRenderer(
|
|
93
|
+
new THREE.WebGLRenderer({ antialias: true, alpha: true, preserveDrawingBuffer: true }),
|
|
94
|
+
);
|
|
95
|
+
let cleanup: (() => void) | undefined;
|
|
96
|
+
try {
|
|
97
|
+
cleanup = prepare?.(renderer);
|
|
98
|
+
renderer.setPixelRatio(1);
|
|
99
|
+
renderer.setSize(request.width, request.height, false);
|
|
100
|
+
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
|
101
|
+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
102
|
+
renderer.setClearColor(0x20242b, scene.background === null ? 0 : 1);
|
|
103
|
+
renderer.render(scene, camera);
|
|
104
|
+
return renderer.domElement.toDataURL('image/png');
|
|
105
|
+
} finally {
|
|
106
|
+
cleanup?.();
|
|
107
|
+
renderer.dispose();
|
|
108
|
+
renderer.forceContextLoss();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHAT DOES THIS ENTRY MODULE MEAN? — the ONE editor-side answer.
|
|
3
|
+
*
|
|
4
|
+
* The engine owns the answer itself (`resolveR3FEntryAdapter`, and the
|
|
5
|
+
* packaged-aware `resolveR3FEntryAdapterForEditor` delegate that routes it
|
|
6
|
+
* through the project's own module graph). The editor's job is to ASK, once.
|
|
7
|
+
* Before this module there were three askers, and they had already drifted:
|
|
8
|
+
*
|
|
9
|
+
* - the dev-server lane called the packaged-aware `…ForEditor`;
|
|
10
|
+
* - the browser/hosted lane called the BARE `resolveR3FEntryAdapter`, so a
|
|
11
|
+
* packaged run built a project component's adapter with the shell bundle's
|
|
12
|
+
* Fiber and crashed hooks on a null React dispatcher — a divergence
|
|
13
|
+
* invisible in dev and reachable only on the packaged lane;
|
|
14
|
+
* - the design session had two more inline resolutions of its own, one of
|
|
15
|
+
* which additionally understood a NAMED world export that neither other
|
|
16
|
+
* lane did.
|
|
17
|
+
*
|
|
18
|
+
* All three shapes are here, and the messages stay with their callers: a
|
|
19
|
+
* project author reading "this entry has no mountable surface" needs to be
|
|
20
|
+
* told which lane and which file, and that is the caller's knowledge, not
|
|
21
|
+
* this function's.
|
|
22
|
+
*
|
|
23
|
+
* `entry-adjudication-single-owner.test.ts` is the tripwire; the canvas lane
|
|
24
|
+
* has its own single owner in the engine (`resolveCanvasEntryAdapter`, reached
|
|
25
|
+
* through `canvas-entry-runtime.ts`) and is not adjudicated here.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { RootAdapter } from '@volter/editor-project/adapter';
|
|
29
|
+
import { resolveR3FEntryAdapterForEditor } from './r3f-entry-runtime';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Runtime shape a `RootAdapter` export must satisfy — the same check
|
|
33
|
+
* `guardModuleShape` makes for `{ module }` adapters: a non-null object with a
|
|
34
|
+
* non-empty string `id` and a function `mount`.
|
|
35
|
+
*/
|
|
36
|
+
export function isRootAdapterShaped(candidate: unknown): candidate is RootAdapter {
|
|
37
|
+
if (candidate === null || typeof candidate !== 'object') return false;
|
|
38
|
+
const obj = candidate as Record<string, unknown>;
|
|
39
|
+
return (
|
|
40
|
+
typeof obj['id'] === 'string' && obj['id'].length > 0 && typeof obj['mount'] === 'function'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** What a caller may ask for beyond "read this module the usual way". */
|
|
45
|
+
export interface EntryAdjudicationOptions {
|
|
46
|
+
/**
|
|
47
|
+
* The manifest's `world.export` — ONE component in a module that exports
|
|
48
|
+
* several (a game's `App.tsx` exporting `Scene` beside `Hud` and `App`).
|
|
49
|
+
*
|
|
50
|
+
* It takes precedence over an `adapter` export, and it is handed to the same
|
|
51
|
+
* resolver a default export is rather than being adapted here, so an entry
|
|
52
|
+
* module and a named world component reach fiber down one path. A name the
|
|
53
|
+
* module does not export is a NAMED failure, never a silent fall-back to
|
|
54
|
+
* `default` — which, for such a module, would mount the whole game into the
|
|
55
|
+
* Scene view.
|
|
56
|
+
*/
|
|
57
|
+
readonly namedExport?: string | undefined;
|
|
58
|
+
/** The entry path, for the named-export failure message only. */
|
|
59
|
+
readonly entryPath?: string | undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the `RootAdapter` a three-surface entry module means, or `null` when
|
|
64
|
+
* it means none — the caller raises its own error for that case.
|
|
65
|
+
*
|
|
66
|
+
* The precedence, in one place: an explicit `namedExport` first (when the
|
|
67
|
+
* caller asked for one), then a pre-built `adapter` export, then the engine's
|
|
68
|
+
* own answer for a module that default-exports its React world component.
|
|
69
|
+
*/
|
|
70
|
+
export async function adjudicateThreeEntry(
|
|
71
|
+
mod: Record<string, unknown>,
|
|
72
|
+
rootId: string,
|
|
73
|
+
opts?: EntryAdjudicationOptions,
|
|
74
|
+
): Promise<RootAdapter | null> {
|
|
75
|
+
const named = opts?.namedExport;
|
|
76
|
+
if (named !== undefined) {
|
|
77
|
+
const picked = mod[named];
|
|
78
|
+
if (typeof picked !== 'function') {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`three root "${rootId}": ${opts?.entryPath ?? 'the entry module'} has no exported ` +
|
|
81
|
+
`component "${named}" (manifest \`world.export\`) — it exports ` +
|
|
82
|
+
`${Object.keys(mod).join(', ') || '(nothing)'}.`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return resolveR3FEntryAdapterForEditor({ default: picked }, rootId);
|
|
86
|
+
}
|
|
87
|
+
if (isRootAdapterShaped(mod['adapter'])) return mod['adapter'];
|
|
88
|
+
return resolveR3FEntryAdapterForEditor(mod, rootId);
|
|
89
|
+
}
|