@vgai/editor 0.2.0 → 0.4.0-canary.20260715.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/README.md +24 -0
- package/dist/assets/axe-C7YBtOuM.js +30 -0
- package/dist/assets/browserAll-DDtGGE48.js +1 -0
- package/dist/assets/create-runtime-DbTlBlKG.js +1 -0
- package/dist/assets/{game-BI111o5f.js → game-BE0r7DMI.js} +3 -3
- package/dist/assets/game-BdTHR2_X.js +1 -0
- package/dist/assets/{game-pt_1lQLv.js → game-C3iQGSqo.js} +1 -1
- package/dist/assets/game-CcHfYxjP.js +1 -0
- package/dist/assets/game-Cqzj_lpD.js +1 -0
- package/dist/assets/game-DSZdeCHu.js +1 -0
- package/dist/assets/game-Dch9sAPc.js +1 -0
- package/dist/assets/{game-xsOho_wg.js → game-SAFn90pA.js} +3 -3
- package/dist/assets/game-loop-DMIgSMV-.js +1 -0
- package/dist/assets/{game-source-CvidN7AB.js → game-source-Dk83w26t.js} +1 -1
- package/dist/assets/{index-Bp5cecD6.js → index-4idIsHHJ.js} +2 -2
- package/dist/assets/{index-DL7RcQ0n.js → index-C-LhOTBN.js} +1 -1
- package/dist/assets/{index-Cs8xwv3e.js → index-CDkxceqE.js} +1 -1
- package/dist/assets/index-CQEfi217.js +15412 -0
- package/dist/assets/index-Dvi-V0NE.css +1 -0
- package/dist/assets/ingest-siblings-s0P5I1Ub.js +1 -0
- package/dist/assets/mount-manifest-DXMOlF79.js +1 -0
- package/dist/assets/react-18-C9eHn8x1.js +1 -0
- package/dist/assets/webworkerAll-Fn0e-ouF.js +1 -0
- package/dist/assets/world-documents-GM0a9Kv7.js +1 -0
- package/dist/index.html +15 -13
- package/dist/ingest/racing-game/vgai.game.json +5 -4
- package/dist/ingest/simcity/vgai.game.json +5 -4
- package/dist/ingest/tanks/vgai.game.json +5 -4
- package/dist/project-scripts.bundle.js +3917 -0
- package/dist/scenes/untitled.vscn.json +2 -0
- package/dist-server/packaged.mjs +7697 -3435
- package/package.json +31 -4
- package/scripts/probe-reporter.ts +349 -0
- package/scripts/run-probe.ts +2260 -0
- package/server/asset-catalog-audit.ts +268 -0
- package/server/asset-catalog-v2.ts +146 -0
- package/server/asset-library-routes.ts +276 -58
- package/server/canonical-path.ts +1 -1
- package/server/dev.ts +148 -44
- package/server/editor-server.ts +888 -61
- package/server/editor-sse.ts +115 -4
- package/server/harness-chat-service.ts +796 -0
- package/server/local-asset-catalog.ts +157 -22
- package/server/packaged.ts +51 -21
- package/server/process-shutdown.ts +136 -0
- package/server/prod.ts +9 -6
- package/server/project-hmr-files.ts +41 -0
- package/server/project-operations.ts +343 -0
- package/server/project-optimize-deps-entries.ts +4 -4
- package/server/project-output-writer.ts +156 -0
- package/server/project-validation.ts +140 -0
- package/server/project-view.ts +8 -50
- package/server/server-utils.ts +75 -10
- package/server/session-registry.ts +12 -0
- package/src/EditorContext.tsx +107 -3
- package/src/action-registry.ts +115 -17
- package/src/adapter-resolver.ts +213 -478
- package/src/asset-events.ts +64 -1
- package/src/asset-preview.ts +906 -0
- package/src/asset-selection.ts +121 -0
- package/src/asset-workflow/asset-capabilities.ts +259 -0
- package/src/asset-workflow/asset-editor-errors.ts +46 -0
- package/src/asset-workflow/asset-import-pipeline.ts +176 -0
- package/src/asset-workflow/asset-library-query.ts +107 -0
- package/src/asset-workflow/asset-types.ts +192 -0
- package/src/asset-workflow/asset-workflow-quality.ts +65 -0
- package/src/asset-workflow/import-contract.ts +113 -0
- package/src/asset-workflow/model-conversion.ts +126 -0
- package/src/asset-workflow/model-inspection.ts +185 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +153 -0
- package/src/asset-workflow/project-asset-operations.ts +119 -0
- package/src/asset-workflow/thumbnail-system.ts +172 -0
- package/src/authoring/active-systems.ts +15 -0
- package/src/authoring/boundary-authoring-adapter.ts +16 -5
- package/src/authoring/composite-authoring-adapter.ts +305 -124
- package/src/authoring/design-time-layers.ts +300 -117
- package/src/authoring/edit-mode-authoring.ts +278 -293
- package/src/authoring/embed-only-adapter.ts +4 -2
- package/src/authoring/first-party-hierarchy-menu.ts +7 -2
- package/src/authoring/first-party-inspector/AddSectionMenu.tsx +5 -12
- package/src/authoring/first-party-inspector/EnvironmentSection.tsx +53 -126
- package/src/authoring/first-party-inspector/HeaderSection.tsx +54 -65
- package/src/authoring/first-party-inspector/is-first-party-selection.ts +30 -11
- package/src/authoring/first-party-inspector/register.tsx +13 -2
- package/src/authoring/ingest-authoring-adapter.ts +142 -45
- package/src/authoring/mount-failure-report.ts +5 -5
- package/src/authoring/provenance.ts +41 -0
- package/src/authoring/react-dom-authoring-adapter.ts +92 -92
- package/src/authoring/react-inspector-section.tsx +489 -51
- package/src/authoring/react-world-authoring-adapter.ts +541 -306
- package/src/authoring/story-overlay.ts +1 -1
- package/src/authoring/story-types.ts +11 -0
- package/src/authoring/vgai-scene-authoring-adapter.ts +54 -25
- package/src/authoring/viewport-tool-context.ts +78 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +186 -0
- package/src/authoring/world-pan-state.ts +33 -17
- package/src/authoring/world-session-state.ts +1 -1
- package/src/authoring/world2d-authoring-adapter.ts +109 -52
- package/src/availability-tick.ts +56 -0
- package/src/browser-play-entry.ts +5 -5
- package/src/browser-project-scripts.ts +1 -0
- package/src/build-identity.ts +15 -0
- package/src/command-listener.ts +893 -19
- package/src/components/AlignToolbar.tsx +74 -63
- package/src/components/AppRoot.tsx +193 -26
- package/src/components/ApplicationMenus.tsx +272 -0
- package/src/components/AssetBrowser.tsx +1450 -293
- package/src/components/AssetEditorShell.tsx +141 -0
- package/src/components/AssetImportDetails.tsx +218 -0
- package/src/components/AssetLibraryPanel.tsx +14 -0
- package/src/components/CameraInfo.tsx +11 -20
- package/src/components/CenterDocuments.tsx +585 -0
- package/src/components/CommandPalette.tsx +75 -30
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +57 -112
- package/src/components/DebugConsolePanel.tsx +461 -0
- package/src/components/DebuggerPanel.tsx +165 -0
- package/src/components/DefaultEditorLayout.tsx +167 -231
- package/src/components/GameHierarchy.tsx +873 -290
- package/src/components/GamePerformanceOverlay.tsx +46 -0
- package/src/components/HarnessChatPanel.tsx +2352 -0
- package/src/components/HistoryPanel.tsx +226 -0
- package/src/components/Inspector.tsx +153 -99
- package/src/components/InspectorToolSection.tsx +6 -4
- package/src/components/MountFailureBanner.tsx +31 -48
- package/src/components/NetworkSection.tsx +25 -14
- package/src/components/OnlineAssetBrowser.tsx +794 -343
- package/src/components/OverlayReportBanner.tsx +38 -64
- package/src/components/PerformancePanel.tsx +206 -0
- package/src/components/PlayBar.tsx +93 -263
- package/src/components/PrefabSaveDialog.tsx +55 -127
- package/src/components/ProjectHeader.css +60 -0
- package/src/components/ProjectHeader.tsx +170 -105
- package/src/components/ProjectScreen.tsx +246 -184
- package/src/components/ReactCanvasControls.tsx +185 -0
- package/src/components/ResolutionPicker.tsx +85 -0
- package/src/components/RootActivateDialog.tsx +39 -68
- package/src/components/SaveDialog.tsx +55 -127
- package/src/components/SaveStatus.tsx +11 -29
- package/src/components/StartupLoadingScreen.tsx +96 -0
- package/src/components/StateWatchPanel.tsx +302 -0
- package/src/components/StatsOverlay.tsx +12 -5
- package/src/components/StatusBar.tsx +64 -0
- package/src/components/ThemeManagerDialog.css +89 -0
- package/src/components/ThemeManagerDialog.tsx +371 -0
- package/src/components/ToolHost.tsx +26 -15
- package/src/components/Toolbar.tsx +117 -214
- package/src/components/VgaiVoxelLogo.tsx +89 -0
- package/src/components/ViewportOverlay.tsx +97 -172
- package/src/components/ViewportPanel.tsx +130 -31
- package/src/components/WorkspaceDock.tsx +891 -0
- package/src/components/WorldSelectionOverlay.tsx +51 -63
- package/src/components/WorldTextEditor.tsx +9 -5
- package/src/components/asset-documents.tsx +390 -0
- package/src/components/asset-editor-persistence.ts +44 -0
- package/src/components/asset-selection-section.tsx +338 -0
- package/src/components/asset-viewers/AudioViewer.tsx +73 -21
- package/src/components/asset-viewers/GenericJsonViewer.tsx +26 -11
- package/src/components/asset-viewers/ImageViewer.tsx +43 -11
- package/src/components/asset-viewers/InputMapViewer.tsx +512 -134
- package/src/components/asset-viewers/MaterialAssetEditor.tsx +540 -0
- package/src/components/asset-viewers/ModelViewer.tsx +805 -121
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +177 -60
- package/src/components/asset-viewers/SourceAssetViewer.tsx +121 -0
- package/src/components/asset-workflow.css +506 -0
- package/src/components/build-documents.tsx +696 -0
- package/src/components/core-utilities.tsx +75 -0
- package/src/components/data-documents.tsx +364 -0
- package/src/components/{DataPanel.tsx → data-editor.tsx} +138 -288
- package/src/components/debugger-panel-state.ts +57 -0
- package/src/components/inspector-widgets/AlignmentGrid.tsx +8 -14
- package/src/components/inspector-widgets/BorderEditor.tsx +41 -33
- package/src/components/inspector-widgets/ColorPicker.tsx +39 -39
- package/src/components/inspector-widgets/FontPicker.tsx +13 -21
- package/src/components/inspector-widgets/GradientEditor.tsx +11 -19
- package/src/components/inspector-widgets/ScrubbableInput.tsx +6 -3
- package/src/components/inspector-widgets/ShadowEditor.tsx +20 -33
- package/src/components/inspector-widgets/shared.tsx +90 -49
- package/src/components/inspectors/AnimationSection.tsx +39 -40
- package/src/components/inspectors/AudioSection.tsx +28 -39
- package/src/components/inspectors/BoneAttachmentSection.tsx +19 -24
- package/src/components/inspectors/CameraSection.tsx +14 -23
- package/src/components/inspectors/ComponentsSection.tsx +39 -43
- package/src/components/inspectors/JointsSection.tsx +34 -43
- package/src/components/inspectors/LightSection.tsx +90 -35
- package/src/components/inspectors/MaterialSection.tsx +137 -174
- package/src/components/inspectors/MeshSection.tsx +9 -17
- package/src/components/inspectors/NavigationSection.tsx +8 -15
- package/src/components/inspectors/ParticleSection.tsx +91 -76
- package/src/components/inspectors/PhysicsSection.tsx +42 -46
- package/src/components/inspectors/PostProcessingSection.tsx +28 -36
- package/src/components/inspectors/RenderingSection.tsx +12 -20
- package/src/components/inspectors/ShadowSection.tsx +7 -8
- package/src/components/inspectors/SplineSection.tsx +31 -42
- package/src/components/inspectors/TransformSection.tsx +14 -22
- package/src/components/patterns/Dialog.tsx +95 -0
- package/src/components/patterns/Fields.tsx +44 -0
- package/src/components/patterns/List.tsx +25 -0
- package/src/components/patterns/StateSurface.tsx +40 -0
- package/src/components/patterns/Surfaces.tsx +115 -0
- package/src/components/patterns/Tabs.tsx +80 -0
- package/src/components/patterns/Toolbar.tsx +51 -0
- package/src/components/patterns/Tree.tsx +72 -0
- package/src/components/primitives/Button.tsx +46 -0
- package/src/components/primitives/ColorInput.tsx +11 -47
- package/src/components/primitives/EditorIcon.tsx +99 -0
- package/src/components/primitives/FormControls.tsx +71 -0
- package/src/components/primitives/JsonInput.tsx +15 -2
- package/src/components/primitives/Layout.tsx +100 -0
- package/src/components/primitives/Menu.tsx +105 -0
- package/src/components/primitives/NumberInput.tsx +8 -35
- package/src/components/primitives/Panel.tsx +49 -44
- package/src/components/primitives/SectionHeader.tsx +10 -28
- package/src/components/primitives/Text.tsx +46 -0
- package/src/components/primitives/Tooltip.tsx +20 -12
- package/src/components/primitives/Vec3Input.tsx +3 -0
- package/src/components/primitives/banner-tones.ts +33 -0
- package/src/components/primitives/editor-icons.ts +102 -0
- package/src/components/primitives/panel-header-styles.ts +30 -0
- package/src/components/project-operation-documents.tsx +226 -0
- package/src/components/status-contributions.tsx +175 -0
- package/src/components/story-documents-register.ts +15 -0
- package/src/components/story-documents.tsx +1185 -0
- package/src/components/tool-documents.tsx +110 -0
- package/src/components/workspace-dock.css +293 -0
- package/src/components/workspace-history.ts +33 -0
- package/src/components/world-documents.tsx +145 -0
- package/src/components/xstate-documents.tsx +209 -0
- package/src/composite-screenshot.ts +248 -0
- package/src/design-system/index.ts +70 -0
- package/src/design-system-stories/Foundation.stories.tsx +183 -0
- package/src/design-system-stories/Patterns.stories.tsx +251 -0
- package/src/design-system-stories/Workspace.stories.tsx +538 -0
- package/src/doctor/report.ts +57 -13
- package/src/editor-api.ts +295 -31
- package/src/editor-compatibility.ts +200 -0
- package/src/editor-console.ts +35 -17
- package/src/editor-hotkeys.ts +85 -33
- package/src/editor-store.ts +538 -233
- package/src/editor-viewport.ts +160 -34
- package/src/entity-factory.ts +14 -3
- package/src/gizmo-registry.ts +21 -5
- package/src/harness-chat-editor-context.ts +37 -0
- package/src/harness-chat-indicator.ts +68 -0
- package/src/harness-chat-layout.ts +53 -0
- package/src/harness-chat-lifecycle.ts +170 -0
- package/src/harness-chat-reconnect.ts +85 -0
- package/src/harness-chat-types.ts +409 -0
- package/src/hierarchy-drop.ts +50 -0
- package/src/hierarchy-expansion-state.ts +100 -0
- package/src/hierarchy-menu-registry.ts +1 -1
- package/src/hierarchy-projection.ts +76 -0
- package/src/history/editor-session.ts +21 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-service.ts +904 -0
- package/src/history/json-history-resource.ts +165 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +266 -0
- package/src/history/project-root-history-backends.ts +53 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +108 -0
- package/src/history/source-history-backend.ts +374 -0
- package/src/history/types.ts +100 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hosted-example.ts +12 -2
- package/src/hotkeys.ts +89 -18
- package/src/ingest/discovery-react.ts +9 -10
- package/src/ingest/discovery.ts +4 -4
- package/src/ingest/discovery2d.ts +5 -5
- package/src/ingest/games/bloom-composer/vgai.game.json +3 -3
- package/src/ingest/games/embed-bundle/vgai.game.json +3 -3
- package/src/ingest/games/games-fps/vgai.game.json +3 -3
- package/src/ingest/games/iframe-reachable/vgai.game.json +3 -3
- package/src/ingest/games/interactive-cubes/vgai.game.json +3 -3
- package/src/ingest/games/mismatched-three/vgai.game.json +3 -3
- package/src/ingest/games/own-three-bundled/vgai.game.json +3 -3
- package/src/ingest/games/pointerlock-maze/vgai.game.json +7 -4
- package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +3 -3
- package/src/ingest/games-2d/bunnymark/vgai.game.json +3 -3
- package/src/ingest/games-2d/flappy/vgai.game.json +3 -3
- package/src/ingest/games-2d/flappy-pixi/vgai.game.json +3 -3
- package/src/ingest/games-2d/puzzling-potions/vgai.game.json +3 -3
- package/src/ingest/games-2d/tilemap-camera/vgai.game.json +3 -3
- package/src/ingest/games-react/react-rpg/vgai.game.json +3 -3
- package/src/ingest/types.ts +1 -1
- package/src/ingest-mode.ts +47 -52
- package/src/ingest-siblings.ts +23 -33
- package/src/initial-scene.ts +1 -2
- package/src/main.tsx +16 -8
- package/src/manifest-project.ts +10 -12
- package/src/model-thumbnail.ts +261 -26
- package/src/module-mode.ts +44 -50
- package/src/play-log-events.ts +26 -0
- package/src/play-mode.ts +495 -401
- package/src/play-scene-adoption.ts +23 -0
- package/src/playground/main.ts +68 -24
- package/src/playwright-shim.ts +473 -0
- package/src/project-manager.ts +90 -10
- package/src/project-operations.ts +112 -0
- package/src/project-story-discovery.ts +25 -0
- package/src/project-tool-discovery.ts +43 -0
- package/src/scene-serializer.ts +0 -6
- package/src/scene-sync.ts +7 -2
- package/src/script-loader.ts +3 -3
- package/src/skeleton-helper.ts +57 -0
- package/src/storage/seed.ts +68 -8
- package/src/stories/StoryPreviewMount.tsx +265 -0
- package/src/stories/action-stories.fixture.tsx +44 -0
- package/src/stories/button-stories.fixture.tsx +50 -0
- package/src/stories/compose-project-stories.ts +182 -0
- package/src/stories/loader-stories.fixture.tsx +53 -0
- package/src/stories/story-discovery.ts +130 -0
- package/src/stories/story-registry.ts +146 -0
- package/src/theme-css.ts +1049 -0
- package/src/theme-library.ts +238 -0
- package/src/theme-preference.ts +130 -0
- package/src/theme.ts +527 -0
- package/src/tool-loader.ts +80 -24
- package/src/ui-source/inspect.ts +27 -4
- package/src/ui-source/source-write-backend.ts +83 -22
- package/src/widgets/index.ts +9 -18
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-dock-controller.ts +64 -0
- package/src/workspace-document-ids.ts +5 -0
- package/src/workspace-document-registry.ts +367 -0
- package/src/workspace-state-persistence.ts +455 -0
- package/src/workspace-status-registry.ts +110 -0
- package/src/workspace-utility-commands.ts +49 -0
- package/src/workspace-utility-registry.ts +161 -0
- package/src/world-document-routing.ts +16 -0
- package/src/xstate-inspect/XStateMachineInspector.tsx +544 -0
- package/src/xstate-inspect/character-animation-machine.fixture.ts +74 -0
- package/src/xstate-inspect/use-live-actor-state.ts +39 -0
- package/src/xstate-inspect/xstate-graph.ts +265 -0
- package/src/xstate-inspect/xstate-layout.ts +76 -0
- package/src/z-index.ts +26 -0
- package/template/.claude/skills/editor/SKILL.md +129 -90
- package/template/AGENTS.md +1110 -0
- package/template/CLAUDE.md +1 -512
- package/template/IDIOMS.md +50 -0
- package/template/check-idioms.ts +706 -0
- package/template/manifest-entry-modules-plugin.ts +53 -0
- package/template/package.json +13 -4
- package/template/public/scenes/default.scn2d.json +6 -0
- package/template/public/scenes/default.vscn.json +7 -1
- package/template/public/scenes/untitled.vscn.json +10 -0
- package/template/scripts/playtest.ts +40 -0
- package/template/server/loopback.ts +75 -0
- package/template/server/rooms/arena-room.ts +69 -1
- package/template/server/rooms/game-room.ts +68 -1
- package/template/src/data/README.md +9 -4
- package/template/src/data/data-ref.ts +35 -0
- package/template/src/data/tuning.schema.ts +1 -1
- package/template/src/main.ts +41 -62
- package/template/src/manifest-entry-modules.d.ts +3 -0
- package/template/src/operations/README.md +88 -0
- package/template/src/runtime/pixi-adapter.ts +24 -0
- package/template/src/scripts/components/data-spinner.ts +43 -2
- package/template/src/scripts/components/spin-lap-math.ts +27 -0
- package/template/src/tools/README.md +5 -3
- package/template/src/tools/example.tool.tsx +13 -7
- package/template/src/ui/Button.stories.tsx +66 -0
- package/template/src/ui/RebindPanel.tsx +2 -6
- package/template/src/ui/game.tsx +17 -0
- package/template/tests/acceptance/example.spec.ts +64 -0
- package/template/tests/logic/example.test.ts +31 -0
- package/template/tests/logic/room.test.ts +100 -0
- package/template/tests/tsconfig.json +30 -0
- package/template/validate-manifest.ts +5 -5
- package/template/validate-scenes.ts +50 -18
- package/template/vgai.game.json +20 -13
- package/template/vite.config.ts +77 -16
- package/vite-plugin-ui-oid.ts +261 -17
- package/dist/assets/CCDIKSolver-Dz-ysxZF.js +0 -1
- package/dist/assets/SelectionBox-BXsv_6aH.js +0 -1
- package/dist/assets/browser-project-scripts-DOru4L1x.js +0 -2
- package/dist/assets/browserAll-CEkWYlFk.js +0 -1
- package/dist/assets/create-runtime-DYIWr3va.js +0 -1
- package/dist/assets/data-asset-Bb9sIM7i.js +0 -5
- package/dist/assets/game-8SDZPPgH.js +0 -1
- package/dist/assets/game-CevorxyV.js +0 -1
- package/dist/assets/game-Di7XlywD.js +0 -1
- package/dist/assets/game-DjnCaw8R.js +0 -1
- package/dist/assets/game-Q9A0aXSV.js +0 -1
- package/dist/assets/index-CPVV6pjw.js +0 -1
- package/dist/assets/index-CpOnYyeD.js +0 -213
- package/dist/assets/index-Dr8H_6Rl.js +0 -1
- package/dist/assets/index-_bCaipUk.js +0 -13650
- package/dist/assets/ingest-siblings-Dz8eEqrV.js +0 -1
- package/dist/assets/mount-manifest-Bhk0ohsl.js +0 -1
- package/dist/assets/scene-query-BivAHhJu.js +0 -1
- package/dist/assets/webworkerAll-BLrH0F6F.js +0 -1
- package/dist/assets/xstate-animation-binding-1NGLmw6y.js +0 -1
- package/dist/assets/xstate.esm-BgUD33nl.js +0 -3
- package/src/authoring/scene-ui-layer.ts +0 -485
- package/src/authoring/ui-authoring-adapter.ts +0 -325
- package/src/authoring/ui-hierarchy-menu.ts +0 -40
- package/src/authoring/ui-inspector-sections.tsx +0 -156
- package/src/authoring/undo-timeline.ts +0 -145
- package/src/components/BottomPanel.tsx +0 -326
- package/src/components/BuildPanel.tsx +0 -455
- package/src/components/FolderTree.tsx +0 -179
- package/src/components/RightPanel.tsx +0 -166
- package/src/components/WorldContextMenu.tsx +0 -257
- package/src/components/asset-viewers/AssetViewer.tsx +0 -38
- package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +0 -129
- package/src/scene-ui-mode/active-scene-ui.ts +0 -58
- package/src/scene-ui-mode/scene-ui-palette-panel.tsx +0 -152
- package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +0 -29
- package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +0 -74
- package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +0 -49
- package/src/scene-ui-mode/ui-prefab-io.ts +0 -76
- package/src/ui-editor/inspector.tsx +0 -563
- package/src/ui-editor/node-ops.ts +0 -266
- package/src/ui-editor/overlay.tsx +0 -380
- package/src/ui-editor/palette.tsx +0 -114
- package/src/ui-editor/react-store.ts +0 -78
- package/src/ui-editor/timeline-panel.tsx +0 -443
- package/src/ui-editor/ui-edit-store.ts +0 -552
- package/src/ui-editor/ui-prefab.ts +0 -95
- package/template/project.json +0 -9
- package/template/src/scripts/main.ts +0 -94
- package/template/src/ui/DefaultHud.tsx +0 -40
- package/template/src/ui/game-state.tsx +0 -37
- package/template/src/ui/mount-default-hud.tsx +0 -19
- package/template/src/ui/mount-ui.ts +0 -15
- package/vite-plugin-react-world-provider.ts +0 -81
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* editor-multi-root-selection seam, generalized to N-ary (T6.1 slice 3,
|
|
6
6
|
* docs/MULTI-WORLD-DESIGN.md §1.F — GAME-ROOT-DESIGN §6.4's v1 scope, verbatim):
|
|
7
7
|
* one top-level GROUP NODE per world (`world:<id> (<kind>)`), the child adapter's
|
|
8
|
-
* own roots nested beneath it.
|
|
8
|
+
* own roots nested beneath it. The project/game identity already lives in the
|
|
9
|
+
* editor chrome, so the authoring projection starts at those real world roots —
|
|
10
|
+
* it does not add a redundant synthetic Game row. Routing is by GROUP-NODE OWNERSHIP (which child
|
|
9
11
|
* adapter's tree actually contains a given node id) — not the old 2-child `w2d:`
|
|
10
12
|
* id-prefix sniff, which cannot generalize past exactly one 2D surface.
|
|
11
13
|
*
|
|
@@ -37,6 +39,7 @@ import type {
|
|
|
37
39
|
} from '@engine/adapter';
|
|
38
40
|
import type * as THREE from 'three';
|
|
39
41
|
import { editorConsole } from '../editor-console';
|
|
42
|
+
import type { HierarchyProjection, HierarchyProjectionGroup } from '../hierarchy-projection';
|
|
40
43
|
|
|
41
44
|
/** One child world's authoring adapter, ordered as given to the constructor. */
|
|
42
45
|
export interface CompositeChild {
|
|
@@ -44,6 +47,11 @@ export interface CompositeChild {
|
|
|
44
47
|
readonly worldId: string;
|
|
45
48
|
/** World render substrate kind — shown in the group node's label. */
|
|
46
49
|
readonly kind: string;
|
|
50
|
+
/** Runtime manifest roots are the default. A nested authoring surface is
|
|
51
|
+
* adapter-owned content of its parent world, not another runtime world. */
|
|
52
|
+
readonly role?: 'world' | 'surface';
|
|
53
|
+
/** Optional user-facing label for a nested authoring surface. */
|
|
54
|
+
readonly label?: string;
|
|
47
55
|
readonly adapter: AuthoringAdapter;
|
|
48
56
|
/**
|
|
49
57
|
* D12 (B4) — the world's INSTALL-TIME manifest zOrder (`world.zOrder ?? 0`),
|
|
@@ -54,11 +62,14 @@ export interface CompositeChild {
|
|
|
54
62
|
* what "install-time" means here.
|
|
55
63
|
*/
|
|
56
64
|
readonly zOrder?: number;
|
|
65
|
+
/** Manifest pausing policy, retained for read-only play-mode inspection. */
|
|
66
|
+
readonly pausable?: boolean;
|
|
67
|
+
/** Adapter-owned content declaration, retained for play-mode inspection. */
|
|
68
|
+
readonly content?: string;
|
|
57
69
|
/**
|
|
58
70
|
* C3 (docs/unified-world-editor/26-unified-editor-spec.md §4 C3) — nest
|
|
59
|
-
* this child's group node UNDER another child's group node instead of
|
|
60
|
-
*
|
|
61
|
-
* top-level root when there is no `gameRoot`). Used by the scene-UI child
|
|
71
|
+
* this child's group node UNDER another child's group node instead of
|
|
72
|
+
* directly in the hierarchy root list. Used by the scene-UI child
|
|
62
73
|
* (`edit-mode-authoring.ts`'s UI child, `kind: 'scene-ui'`) to appear
|
|
63
74
|
* "under its owning world" in the ONE hierarchy, per the C3 spec text.
|
|
64
75
|
* Absent ⇒ today's flat top-level shape, unchanged.
|
|
@@ -67,7 +78,14 @@ export interface CompositeChild {
|
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
const GROUP_PREFIX = 'world:';
|
|
70
|
-
const
|
|
81
|
+
const KIND_PREFIX = 'kind:';
|
|
82
|
+
const PROJECTION_PREFIX = 'projection:';
|
|
83
|
+
|
|
84
|
+
const KIND_LABELS: Readonly<Record<string, string>> = {
|
|
85
|
+
threejs: 'Three.js',
|
|
86
|
+
react: 'React',
|
|
87
|
+
pixijs: 'PixiJS',
|
|
88
|
+
};
|
|
71
89
|
|
|
72
90
|
function groupNodeId(worldId: string): string {
|
|
73
91
|
return `${GROUP_PREFIX}${worldId}`;
|
|
@@ -79,19 +97,34 @@ function isGroupNodeId(id: string): boolean {
|
|
|
79
97
|
return id.startsWith(GROUP_PREFIX);
|
|
80
98
|
}
|
|
81
99
|
|
|
100
|
+
function kindNodeId(kind: string): string {
|
|
101
|
+
return `${KIND_PREFIX}${kind}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function projectionNodeId(id: string): string {
|
|
105
|
+
return `${PROJECTION_PREFIX}${id}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isOrganizationNodeId(id: string): boolean {
|
|
109
|
+
return id.startsWith(KIND_PREFIX) || id.startsWith(PROJECTION_PREFIX);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function humanizeId(id: string): string {
|
|
113
|
+
const words = id.replace(/[-_]+/g, ' ').trim();
|
|
114
|
+
return words ? words.replace(/\b\w/g, (letter) => letter.toUpperCase()) : id;
|
|
115
|
+
}
|
|
116
|
+
|
|
82
117
|
/**
|
|
83
|
-
* A4 (D8) —
|
|
118
|
+
* A4 (D8) — manifest-backed properties on each authored root, implemented by
|
|
84
119
|
* `edit-mode-authoring.ts`'s `ManifestAuthoring` over the RAW `vgai.game.json`
|
|
85
120
|
* (read-modify-write, never round-tripped through Zod). Defined here (not
|
|
86
121
|
* imported from `edit-mode-authoring.ts`) to avoid a circular import — that
|
|
87
122
|
* module already imports `CompositeAuthoringAdapter`.
|
|
88
123
|
*/
|
|
89
|
-
export interface
|
|
90
|
-
/**
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
getGameName(): string;
|
|
94
|
-
setGameName(name: string): void;
|
|
124
|
+
export interface RootManifestProvider {
|
|
125
|
+
/** Adapter-owned content declaration shown on the world boundary (for
|
|
126
|
+
* example `Scene · scenes/default.vscn.json` or `Entry · src/ui/App.tsx`). */
|
|
127
|
+
getWorldContentSource?(worldId: string): string | undefined;
|
|
95
128
|
getWorldZOrder(worldId: string): number;
|
|
96
129
|
setWorldZOrder(worldId: string, value: number): void;
|
|
97
130
|
getWorldPausable(worldId: string): boolean;
|
|
@@ -145,12 +178,13 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
145
178
|
* mutating it here never aliases a caller's own array.
|
|
146
179
|
*/
|
|
147
180
|
private readonly children: CompositeChild[];
|
|
148
|
-
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
private readonly
|
|
181
|
+
/** Optional writable manifest surface for world composition properties.
|
|
182
|
+
* Runtime still has one semantic Game root; the authoring hierarchy does not
|
|
183
|
+
* mirror that project-level wrapper because the editor chrome already owns it. */
|
|
184
|
+
private readonly manifest: RootManifestProvider | null;
|
|
185
|
+
/** Optional editor-only organization; never consulted by runtime mounting. */
|
|
186
|
+
private readonly projection: HierarchyProjection | null;
|
|
187
|
+
private readonly resolvedProjectionGroups: Array<HierarchyProjectionGroup & { roots: string[] }>;
|
|
154
188
|
|
|
155
189
|
/**
|
|
156
190
|
* N-ary (T6.1 slice 3): an ordered `{ worldId, kind, adapter }[]` — one group
|
|
@@ -161,19 +195,23 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
161
195
|
* this slice (T6.1 slice 4, docs/MULTI-WORLD-DESIGN.md §1.H): its only
|
|
162
196
|
* production call site was `world2d-mode.ts`'s dev-console-only hook, deleted
|
|
163
197
|
* with the rest of the world2d silo. Every production caller now goes through
|
|
164
|
-
* the N-ary array constructor (`play-mode.ts`'s `
|
|
198
|
+
* the N-ary array constructor (`play-mode.ts`'s `installPlayWorldAuthoring`).
|
|
165
199
|
*
|
|
166
|
-
* `
|
|
167
|
-
*
|
|
168
|
-
* synthetic `game` root; every other caller omits it and gets today's
|
|
169
|
-
* shape (world groups as roots) byte-for-byte unchanged.
|
|
200
|
+
* `manifest` is an optional writable provider. Omitting it leaves root
|
|
201
|
+
* composition properties read-only.
|
|
170
202
|
*/
|
|
171
|
-
constructor(
|
|
203
|
+
constructor(
|
|
204
|
+
children: ReadonlyArray<CompositeChild>,
|
|
205
|
+
manifest?: RootManifestProvider,
|
|
206
|
+
projection?: HierarchyProjection,
|
|
207
|
+
) {
|
|
172
208
|
this.children = [...children];
|
|
173
|
-
this.
|
|
209
|
+
this.manifest = manifest ?? null;
|
|
210
|
+
this.projection = projection ?? null;
|
|
174
211
|
if (children.length === 0) {
|
|
175
212
|
throw new Error('CompositeAuthoringAdapter: at least one child world is required.');
|
|
176
213
|
}
|
|
214
|
+
this.resolvedProjectionGroups = this.resolveProjectionGroups();
|
|
177
215
|
this.capabilities = computeCapabilities(this.children);
|
|
178
216
|
}
|
|
179
217
|
|
|
@@ -193,8 +231,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
193
231
|
* No-op (loud warn, #18 discipline) when `worldId` doesn't name an existing
|
|
194
232
|
* child — never silently routes to the wrong world or grows the array.
|
|
195
233
|
* Callers are responsible for their own side effects around the swap
|
|
196
|
-
* (
|
|
197
|
-
* `store.notifyIngestEdit()` so panels re-render against the new child) —
|
|
234
|
+
* (`store.notifyIngestEdit()` so panels re-render against the new child) —
|
|
198
235
|
* this method only owns the swap + capability recompute.
|
|
199
236
|
*/
|
|
200
237
|
replaceChild(worldId: string, adapter: AuthoringAdapter): void {
|
|
@@ -212,9 +249,8 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
212
249
|
// `{ ..., zOrder }`) because `zOrder` is an OPTIONAL property under this
|
|
213
250
|
// repo's `exactOptionalPropertyTypes` — an explicit `zOrder: undefined`
|
|
214
251
|
// is not the same as an absent property to the type checker.
|
|
215
|
-
const
|
|
216
|
-
this.children[index] =
|
|
217
|
-
zOrder === undefined ? { worldId, kind, adapter } : { worldId, kind, adapter, zOrder };
|
|
252
|
+
const previous = this.children[index]!;
|
|
253
|
+
this.children[index] = { ...previous, worldId, adapter };
|
|
218
254
|
this.capabilities = computeCapabilities(this.children);
|
|
219
255
|
}
|
|
220
256
|
|
|
@@ -224,12 +260,17 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
224
260
|
return groupNodeId(worldId);
|
|
225
261
|
}
|
|
226
262
|
|
|
263
|
+
/** True for a read-only shell node manufactured only for hierarchy layout. */
|
|
264
|
+
isOrganizationNode(nodeId: string): boolean {
|
|
265
|
+
return isOrganizationNodeId(nodeId);
|
|
266
|
+
}
|
|
267
|
+
|
|
227
268
|
/** The child that owns `id` — by ACTUAL ownership (its hierarchy resolves the
|
|
228
269
|
* id), not by any prefix convention. `null` for a group-node id or an id no
|
|
229
270
|
* child recognizes. O(children) per call; fine at editor-UI scale, and avoids
|
|
230
271
|
* a cache that could go stale as a live tree adds/removes nodes. */
|
|
231
272
|
private findOwnerChild(id: string): CompositeChild | null {
|
|
232
|
-
if (isGroupNodeId(id)) return null;
|
|
273
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) return null;
|
|
233
274
|
for (const child of this.children) {
|
|
234
275
|
if (child.adapter.hierarchy.node(id)) return child;
|
|
235
276
|
}
|
|
@@ -276,24 +317,17 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
276
317
|
adapter: AuthoringAdapter;
|
|
277
318
|
kind: string;
|
|
278
319
|
zOrder: number;
|
|
320
|
+
role: 'world' | 'surface';
|
|
279
321
|
}> {
|
|
280
322
|
return this.children.map((c, index) => ({
|
|
281
323
|
worldId: c.worldId,
|
|
282
324
|
adapter: c.adapter,
|
|
283
325
|
kind: c.kind,
|
|
284
326
|
zOrder: c.zOrder ?? index,
|
|
327
|
+
role: c.role ?? 'world',
|
|
285
328
|
}));
|
|
286
329
|
}
|
|
287
330
|
|
|
288
|
-
/** PUBLIC (A4, D8) — the synthetic game-root node's id when this composite
|
|
289
|
-
* was built with a `gameRoot` manifest surface, else `null`. Mirrors
|
|
290
|
-
* `groupNodeId()`'s "ask the adapter, don't hardcode the id" precedent —
|
|
291
|
-
* the shell (`GameHierarchy.tsx`) reads this instead of the literal
|
|
292
|
-
* string, so it never needs to know the reserved id itself. */
|
|
293
|
-
get gameRootId(): string | null {
|
|
294
|
-
return this.gameRoot ? GAME_ROOT_ID : null;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
331
|
/**
|
|
298
332
|
* C3 — a child declared with `parentWorldId` nests its group node UNDER
|
|
299
333
|
* that parent's group node instead of at the top level: the parent's
|
|
@@ -307,16 +341,18 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
307
341
|
const self = this.children.find((c) => c.worldId === worldId);
|
|
308
342
|
const parentId = self?.parentWorldId
|
|
309
343
|
? groupNodeId(self.parentWorldId)
|
|
310
|
-
: this.
|
|
311
|
-
? GAME_ROOT_ID
|
|
312
|
-
: null;
|
|
344
|
+
: this.parentForTopLevelWorld(worldId, kind);
|
|
313
345
|
const nestedGroupIds = this.children
|
|
314
346
|
.filter((c) => c.parentWorldId === worldId)
|
|
315
347
|
.map((c) => groupNodeId(c.worldId));
|
|
316
348
|
return {
|
|
317
349
|
id: groupNodeId(worldId),
|
|
318
|
-
label:
|
|
319
|
-
|
|
350
|
+
label:
|
|
351
|
+
self?.role === 'surface'
|
|
352
|
+
? (self.label ?? `${worldId} (${kind})`)
|
|
353
|
+
: this.labelForWorld(worldId, kind),
|
|
354
|
+
role: self?.role === 'surface' ? 'document' : 'root',
|
|
355
|
+
kind,
|
|
320
356
|
parentId,
|
|
321
357
|
childIds: [...childRoots.map((r) => r.id), ...nestedGroupIds],
|
|
322
358
|
flags: { runtimeOnly: true },
|
|
@@ -324,45 +360,126 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
324
360
|
}
|
|
325
361
|
|
|
326
362
|
/** C3 — every child NOT nested under another (`parentWorldId` unset): the
|
|
327
|
-
* top-level group set
|
|
328
|
-
* returned directly (see callers). A nested child is reached only via its
|
|
363
|
+
* top-level group set. A nested child is reached only via its
|
|
329
364
|
* parent's `childIds` (`groupNode` above), never listed here too — a node
|
|
330
365
|
* in two parents' `childIds` would render twice / cycle the row walk. */
|
|
331
366
|
private topLevelChildren(): CompositeChild[] {
|
|
332
367
|
return this.children.filter((c) => !c.parentWorldId);
|
|
333
368
|
}
|
|
334
369
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
private
|
|
370
|
+
private runtimeRootChildren(): CompositeChild[] {
|
|
371
|
+
return this.topLevelChildren().filter((c) => (c.role ?? 'world') === 'world');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private resolveProjectionGroups(): Array<HierarchyProjectionGroup & { roots: string[] }> {
|
|
375
|
+
const known = new Set(this.runtimeRootChildren().map((child) => child.worldId));
|
|
376
|
+
const placed = new Set<string>();
|
|
377
|
+
return (this.projection?.groups ?? []).flatMap((group) => {
|
|
378
|
+
const roots = group.roots.filter((rootId) => {
|
|
379
|
+
if (!known.has(rootId)) {
|
|
380
|
+
editorConsole.warn(
|
|
381
|
+
`[CompositeAuthoringAdapter] hierarchy group "${group.label}" references unknown root "${rootId}" — ignoring.`,
|
|
382
|
+
'authoring',
|
|
383
|
+
);
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
if (placed.has(rootId)) {
|
|
387
|
+
editorConsole.warn(
|
|
388
|
+
`[CompositeAuthoringAdapter] hierarchy root "${rootId}" is referenced by more than one group; the tree projection uses its first placement.`,
|
|
389
|
+
'authoring',
|
|
390
|
+
);
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
placed.add(rootId);
|
|
394
|
+
return true;
|
|
395
|
+
});
|
|
396
|
+
return roots.length > 0 ? [{ ...group, roots }] : [];
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private projectionGroups(): Array<HierarchyProjectionGroup & { roots: string[] }> {
|
|
401
|
+
return this.resolvedProjectionGroups;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private projectionGroupFor(worldId: string): HierarchyProjectionGroup | undefined {
|
|
405
|
+
return this.projectionGroups().find((group) => group.roots.includes(worldId));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private unprojectedRoots(): CompositeChild[] {
|
|
409
|
+
const projected = new Set(this.projectionGroups().flatMap((group) => group.roots));
|
|
410
|
+
return this.runtimeRootChildren().filter((child) => !projected.has(child.worldId));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private rootsOfKind(kind: string): CompositeChild[] {
|
|
414
|
+
return this.unprojectedRoots().filter((child) => child.kind === kind);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private parentForTopLevelWorld(worldId: string, kind: string): string | null {
|
|
418
|
+
const group = this.projectionGroupFor(worldId);
|
|
419
|
+
if (group) return projectionNodeId(group.id);
|
|
420
|
+
return this.rootsOfKind(kind).length > 1 ? kindNodeId(kind) : null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private labelForWorld(worldId: string, kind: string): string {
|
|
424
|
+
const configured = this.projection?.rootLabels?.[worldId]?.trim();
|
|
425
|
+
if (configured) return configured;
|
|
426
|
+
return this.rootsOfKind(kind).length === 1 && !this.projectionGroupFor(worldId)
|
|
427
|
+
? (KIND_LABELS[kind] ?? humanizeId(kind))
|
|
428
|
+
: humanizeId(worldId);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private kindNode(kind: string): EditorNode {
|
|
432
|
+
return {
|
|
433
|
+
id: kindNodeId(kind),
|
|
434
|
+
label: KIND_LABELS[kind] ?? humanizeId(kind),
|
|
435
|
+
role: 'folder',
|
|
436
|
+
kind: 'group',
|
|
437
|
+
parentId: null,
|
|
438
|
+
childIds: this.rootsOfKind(kind).map((child) => groupNodeId(child.worldId)),
|
|
439
|
+
flags: { runtimeOnly: true },
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
private projectionGroupNode(group: HierarchyProjectionGroup & { roots: string[] }): EditorNode {
|
|
340
444
|
return {
|
|
341
|
-
id:
|
|
342
|
-
label:
|
|
445
|
+
id: projectionNodeId(group.id),
|
|
446
|
+
label: group.label,
|
|
447
|
+
role: 'folder',
|
|
343
448
|
kind: 'group',
|
|
344
449
|
parentId: null,
|
|
345
|
-
childIds:
|
|
450
|
+
childIds: group.roots.map(groupNodeId),
|
|
346
451
|
flags: { runtimeOnly: true },
|
|
347
452
|
};
|
|
348
453
|
}
|
|
349
454
|
|
|
455
|
+
private defaultProjectionNodeIds(): string[] {
|
|
456
|
+
const ids: string[] = [];
|
|
457
|
+
const seenKinds = new Set<string>();
|
|
458
|
+
for (const child of this.unprojectedRoots()) {
|
|
459
|
+
if (seenKinds.has(child.kind)) continue;
|
|
460
|
+
seenKinds.add(child.kind);
|
|
461
|
+
const peers = this.rootsOfKind(child.kind);
|
|
462
|
+
ids.push(peers.length > 1 ? kindNodeId(child.kind) : groupNodeId(child.worldId));
|
|
463
|
+
}
|
|
464
|
+
return ids;
|
|
465
|
+
}
|
|
466
|
+
|
|
350
467
|
readonly hierarchy: HierarchyProvider = {
|
|
351
|
-
roots: (): EditorNode[] =>
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// own `childIds`, never listed as a root of its own.
|
|
358
|
-
if (this.gameRoot) return [this.gameRootNode()];
|
|
359
|
-
return this.topLevelChildren().map((c) =>
|
|
360
|
-
this.groupNode(c.worldId, c.kind, c.adapter.hierarchy.roots()),
|
|
361
|
-
);
|
|
362
|
-
},
|
|
468
|
+
roots: (): EditorNode[] => [
|
|
469
|
+
...this.projectionGroups().map((group) => this.projectionGroupNode(group)),
|
|
470
|
+
...this.defaultProjectionNodeIds()
|
|
471
|
+
.map((id) => this.hierarchy.node(id))
|
|
472
|
+
.filter((node): node is EditorNode => node !== null),
|
|
473
|
+
],
|
|
363
474
|
node: (id) => {
|
|
364
|
-
if (id
|
|
365
|
-
|
|
475
|
+
if (id.startsWith(KIND_PREFIX)) {
|
|
476
|
+
const kind = id.slice(KIND_PREFIX.length);
|
|
477
|
+
return this.rootsOfKind(kind).length > 1 ? this.kindNode(kind) : null;
|
|
478
|
+
}
|
|
479
|
+
if (id.startsWith(PROJECTION_PREFIX)) {
|
|
480
|
+
const groupId = id.slice(PROJECTION_PREFIX.length);
|
|
481
|
+
const group = this.projectionGroups().find((candidate) => candidate.id === groupId);
|
|
482
|
+
return group ? this.projectionGroupNode(group) : null;
|
|
366
483
|
}
|
|
367
484
|
if (isGroupNodeId(id)) {
|
|
368
485
|
const worldId = id.slice(GROUP_PREFIX.length);
|
|
@@ -380,7 +497,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
380
497
|
return node.parentId === null ? { ...node, parentId: groupNodeId(owner.worldId) } : node;
|
|
381
498
|
},
|
|
382
499
|
object3D: (id) =>
|
|
383
|
-
id
|
|
500
|
+
isGroupNodeId(id) || isOrganizationNodeId(id) ? null : this.route(id).hierarchy.object3D(id),
|
|
384
501
|
idForObject3D: (o: THREE.Object3D) => {
|
|
385
502
|
for (const child of this.children) {
|
|
386
503
|
const id = child.adapter.hierarchy.idForObject3D(o);
|
|
@@ -400,7 +517,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
400
517
|
return [...seen];
|
|
401
518
|
},
|
|
402
519
|
set: (ids) => {
|
|
403
|
-
// A group-node id
|
|
520
|
+
// A group-node id is not a real entity in any
|
|
404
521
|
// child's tree — route it specially rather than forwarding a bogus id
|
|
405
522
|
// nobody owns. Route each REAL id to its owning child.
|
|
406
523
|
//
|
|
@@ -419,9 +536,9 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
419
536
|
// sharing the backing store reaches every other child too) — but
|
|
420
537
|
// forward the SYNTHETIC ID ITSELF (not an empty clear) when one was
|
|
421
538
|
// given. This is why `RightPanel.tsx`'s `[...store.selectedEntityIds][0]`
|
|
422
|
-
// (and `Inspector.tsx`'s identical read) can show
|
|
423
|
-
//
|
|
424
|
-
//
|
|
539
|
+
// (and `Inspector.tsx`'s identical read) can show a world group's own
|
|
540
|
+
// properties: every stock adapter shares the SAME `EditorStore`, so
|
|
541
|
+
// writing `'world:<id>'` into any one
|
|
425
542
|
// child's selection lands in that one shared `selectedEntityIds` set,
|
|
426
543
|
// which both those raw-store reads AND this composite's OWN `get()`
|
|
427
544
|
// (via the owning-adapter's `selection.get()`) then see identically —
|
|
@@ -429,7 +546,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
429
546
|
const byChild = new Map<AuthoringAdapter, string[]>();
|
|
430
547
|
let synthetic: string | null = null;
|
|
431
548
|
for (const id of ids) {
|
|
432
|
-
if (isGroupNodeId(id) || id
|
|
549
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) {
|
|
433
550
|
synthetic = id; // last one wins — only used when NOTHING else claims byChild
|
|
434
551
|
continue;
|
|
435
552
|
}
|
|
@@ -461,24 +578,40 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
461
578
|
};
|
|
462
579
|
|
|
463
580
|
/**
|
|
464
|
-
* A4 (D8) —
|
|
465
|
-
*
|
|
581
|
+
* A4 (D8) — each adapter root's stable `id`, `zOrder`/`pausable`/`kind` are
|
|
582
|
+
* a MANIFEST concept, not something any child
|
|
466
583
|
* adapter's own inspector knows about — intercepted here, BEFORE `route()`,
|
|
467
584
|
* so a real per-world adapter is never asked about an id it doesn't own.
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
* the ordinary routed behavior unchanged.
|
|
585
|
+
* Without a writable provider, these synthetic nodes still expose read-only
|
|
586
|
+
* composition facts instead of being routed into a child that cannot own them.
|
|
471
587
|
*/
|
|
472
|
-
private
|
|
473
|
-
if (
|
|
474
|
-
|
|
475
|
-
return [{ path: 'name', label: 'Name', type: 'string' }];
|
|
588
|
+
private rootProperties(id: string): PropertyDescriptor[] | null {
|
|
589
|
+
if (isOrganizationNodeId(id)) {
|
|
590
|
+
return [{ path: 'organization', label: 'Authoring Group', type: 'string', readonly: true }];
|
|
476
591
|
}
|
|
477
|
-
if (
|
|
592
|
+
if (
|
|
593
|
+
isGroupNodeId(id) &&
|
|
594
|
+
this.children.some((c) => (c.role ?? 'world') === 'world' && groupNodeId(c.worldId) === id)
|
|
595
|
+
) {
|
|
596
|
+
const child = this.children.find((candidate) => groupNodeId(candidate.worldId) === id);
|
|
478
597
|
return [
|
|
598
|
+
{ path: 'id', label: 'Root ID', type: 'string', readonly: true },
|
|
479
599
|
{ path: 'kind', label: 'Kind', type: 'string', readonly: true },
|
|
480
|
-
|
|
481
|
-
|
|
600
|
+
...(this.manifest?.getWorldContentSource || child?.content
|
|
601
|
+
? ([{ path: 'content', label: 'Content', type: 'string', readonly: true }] as const)
|
|
602
|
+
: []),
|
|
603
|
+
{
|
|
604
|
+
path: 'zOrder',
|
|
605
|
+
label: 'Z Order',
|
|
606
|
+
type: 'number',
|
|
607
|
+
...(this.manifest ? {} : { readonly: true }),
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
path: 'pausable',
|
|
611
|
+
label: 'Pausable',
|
|
612
|
+
type: 'boolean',
|
|
613
|
+
...(this.manifest ? {} : { readonly: true }),
|
|
614
|
+
},
|
|
482
615
|
];
|
|
483
616
|
}
|
|
484
617
|
return null;
|
|
@@ -486,44 +619,47 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
486
619
|
|
|
487
620
|
readonly inspector: InspectorProvider = {
|
|
488
621
|
properties: (id): PropertyDescriptor[] =>
|
|
489
|
-
this.
|
|
622
|
+
this.rootProperties(id) ?? this.route(id).inspector?.properties(id) ?? [],
|
|
490
623
|
get: (id, path) => {
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
|
|
624
|
+
if (isOrganizationNodeId(id)) {
|
|
625
|
+
return path === 'organization' ? 'Editor-only hierarchy projection' : undefined;
|
|
626
|
+
}
|
|
627
|
+
const worldId = isGroupNodeId(id) ? id.slice(GROUP_PREFIX.length) : null;
|
|
628
|
+
const child = worldId ? this.children.find((c) => c.worldId === worldId) : undefined;
|
|
629
|
+
if (child && (child.role ?? 'world') === 'world') {
|
|
630
|
+
if (path === 'id') return child.worldId;
|
|
631
|
+
if (path === 'kind') return child.kind;
|
|
632
|
+
if (path === 'content') {
|
|
633
|
+
return this.manifest?.getWorldContentSource?.(worldId!) ?? child.content;
|
|
494
634
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
if (child) {
|
|
498
|
-
if (path === 'kind') return child.kind;
|
|
499
|
-
if (path === 'zOrder') return this.gameRoot.getWorldZOrder(worldId!);
|
|
500
|
-
if (path === 'pausable') return this.gameRoot.getWorldPausable(worldId!);
|
|
501
|
-
return undefined;
|
|
635
|
+
if (path === 'zOrder') {
|
|
636
|
+
return this.manifest?.getWorldZOrder(worldId!) ?? child.zOrder ?? 0;
|
|
502
637
|
}
|
|
638
|
+
if (path === 'pausable') {
|
|
639
|
+
return this.manifest?.getWorldPausable(worldId!) ?? child.pausable ?? true;
|
|
640
|
+
}
|
|
641
|
+
return undefined;
|
|
503
642
|
}
|
|
504
643
|
return this.route(id).inspector?.get(id, path);
|
|
505
644
|
},
|
|
506
645
|
set: (id, path, value) => {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if (child) {
|
|
515
|
-
if (path === 'zOrder') this.gameRoot.setWorldZOrder(worldId!, Number(value));
|
|
516
|
-
else if (path === 'pausable') this.gameRoot.setWorldPausable(worldId!, Boolean(value));
|
|
517
|
-
return;
|
|
646
|
+
const worldId = isGroupNodeId(id) ? id.slice(GROUP_PREFIX.length) : null;
|
|
647
|
+
const child = worldId ? this.children.find((c) => c.worldId === worldId) : undefined;
|
|
648
|
+
if (child && (child.role ?? 'world') === 'world') {
|
|
649
|
+
if (this.manifest && path === 'zOrder') {
|
|
650
|
+
this.manifest.setWorldZOrder(worldId!, Number(value));
|
|
651
|
+
} else if (this.manifest && path === 'pausable') {
|
|
652
|
+
this.manifest.setWorldPausable(worldId!, Boolean(value));
|
|
518
653
|
}
|
|
654
|
+
return;
|
|
519
655
|
}
|
|
520
656
|
this.route(id).inspector?.set(id, path, value);
|
|
521
657
|
},
|
|
522
658
|
remove: (id, path) => {
|
|
523
|
-
// U2 —
|
|
659
|
+
// U2 — root-group synthetic props have no removable override;
|
|
524
660
|
// everything else routes to the owning child (which optional-omits
|
|
525
661
|
// `remove` unless it's the react-world adapter).
|
|
526
|
-
if (
|
|
662
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) return;
|
|
527
663
|
this.route(id).inspector?.remove?.(id, path);
|
|
528
664
|
},
|
|
529
665
|
};
|
|
@@ -542,6 +678,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
542
678
|
private resolveStructureTarget(
|
|
543
679
|
parentId: string | null | undefined,
|
|
544
680
|
): { child: CompositeChild; forwardParentId: string | undefined } | null {
|
|
681
|
+
if (parentId != null && isOrganizationNodeId(parentId)) return null;
|
|
545
682
|
if (parentId != null && isGroupNodeId(parentId)) {
|
|
546
683
|
const worldId = parentId.slice(GROUP_PREFIX.length);
|
|
547
684
|
const child = this.children.find((c) => c.worldId === worldId);
|
|
@@ -549,7 +686,16 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
549
686
|
}
|
|
550
687
|
if (parentId != null) {
|
|
551
688
|
const child = this.findOwnerChild(parentId);
|
|
552
|
-
|
|
689
|
+
if (!child) return null;
|
|
690
|
+
const parent = child.adapter.hierarchy.node(parentId);
|
|
691
|
+
return {
|
|
692
|
+
child,
|
|
693
|
+
// Documents and design states organize an adapter's native roots;
|
|
694
|
+
// they are not native parent ids. The structure contract's root
|
|
695
|
+
// spelling is `undefined`/`null`, so forward that explicitly.
|
|
696
|
+
forwardParentId:
|
|
697
|
+
parent?.role === 'document' || parent?.role === 'story' ? undefined : parentId,
|
|
698
|
+
};
|
|
553
699
|
}
|
|
554
700
|
const child = this.children.find((c) => c.adapter.structure);
|
|
555
701
|
return child ? { child, forwardParentId: undefined } : null;
|
|
@@ -578,7 +724,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
578
724
|
return target.child.adapter.structure.create(kind, target.forwardParentId);
|
|
579
725
|
},
|
|
580
726
|
remove: (id) => {
|
|
581
|
-
if (isGroupNodeId(id)) return;
|
|
727
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) return;
|
|
582
728
|
const owner = this.findOwnerChild(id);
|
|
583
729
|
if (!owner?.adapter.structure) {
|
|
584
730
|
editorConsole.warn(
|
|
@@ -594,8 +740,35 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
594
740
|
// doc comment).
|
|
595
741
|
return owner.adapter.structure.remove(id);
|
|
596
742
|
},
|
|
743
|
+
removeMany: async (ids) => {
|
|
744
|
+
const groups = new Map<CompositeChild, string[]>();
|
|
745
|
+
for (const id of ids) {
|
|
746
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) continue;
|
|
747
|
+
const owner = this.findOwnerChild(id);
|
|
748
|
+
if (!owner?.adapter.structure) {
|
|
749
|
+
editorConsole.warn(
|
|
750
|
+
`[CompositeAuthoringAdapter] removeMany: no owner/structure for id "${id}"`,
|
|
751
|
+
'authoring',
|
|
752
|
+
);
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
const ownedIds = groups.get(owner);
|
|
756
|
+
if (ownedIds) ownedIds.push(id);
|
|
757
|
+
else groups.set(owner, [id]);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Preserve each child's strongest atomicity guarantee. In the common case
|
|
761
|
+
// (one world's multi-selection), this is one child batch and therefore one
|
|
762
|
+
// history transaction. Cross-world selections remain one ordered batch per
|
|
763
|
+
// owner because no child can soundly mutate another world's document.
|
|
764
|
+
for (const [owner, ownedIds] of groups) {
|
|
765
|
+
const structure = owner.adapter.structure!;
|
|
766
|
+
if (structure.removeMany) await structure.removeMany(ownedIds);
|
|
767
|
+
else for (const id of ownedIds) await structure.remove(id);
|
|
768
|
+
}
|
|
769
|
+
},
|
|
597
770
|
duplicate: (id) => {
|
|
598
|
-
if (isGroupNodeId(id)) return id;
|
|
771
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) return id;
|
|
599
772
|
const owner = this.findOwnerChild(id);
|
|
600
773
|
if (!owner?.adapter.structure) {
|
|
601
774
|
editorConsole.warn(
|
|
@@ -622,16 +795,21 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
622
795
|
const newOwner = this.findOwnerChild(newParentId);
|
|
623
796
|
if (!newOwner || newOwner.worldId !== owner.worldId) {
|
|
624
797
|
editorConsole.warn(
|
|
625
|
-
`[CompositeAuthoringAdapter] reparent: refusing to move "${id}" across
|
|
798
|
+
`[CompositeAuthoringAdapter] reparent: refusing to move "${id}" across roots ` +
|
|
626
799
|
`(target parent "${newParentId}" is not in world "${owner.worldId}")`,
|
|
627
800
|
'authoring',
|
|
628
801
|
);
|
|
629
802
|
return;
|
|
630
803
|
}
|
|
804
|
+
const newParent = newOwner.adapter.hierarchy.node(newParentId);
|
|
805
|
+
if (newParent?.role === 'document' || newParent?.role === 'story') {
|
|
806
|
+
owner.adapter.structure.reparent(id, null);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
631
809
|
owner.adapter.structure.reparent(id, newParentId);
|
|
632
810
|
},
|
|
633
811
|
reorder: (id, beforeSiblingId) => {
|
|
634
|
-
if (isGroupNodeId(id)) return;
|
|
812
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) return;
|
|
635
813
|
const owner = this.findOwnerChild(id);
|
|
636
814
|
if (!owner) return;
|
|
637
815
|
// Cross-world guard: a non-null sibling anchor MUST belong to the same
|
|
@@ -656,7 +834,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
656
834
|
// instead since they're triggered from an explicit, always-visible menu
|
|
657
835
|
// item, same as duplicate/delete).
|
|
658
836
|
wrap: (id, wrapperTag) => {
|
|
659
|
-
if (isGroupNodeId(id)) return;
|
|
837
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) return;
|
|
660
838
|
const owner = this.findOwnerChild(id);
|
|
661
839
|
if (!owner?.adapter.structure?.wrap) {
|
|
662
840
|
editorConsole.warn(
|
|
@@ -668,7 +846,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
668
846
|
owner.adapter.structure.wrap(id, wrapperTag);
|
|
669
847
|
},
|
|
670
848
|
unwrap: (id) => {
|
|
671
|
-
if (isGroupNodeId(id)) return;
|
|
849
|
+
if (isGroupNodeId(id) || isOrganizationNodeId(id)) return;
|
|
672
850
|
const owner = this.findOwnerChild(id);
|
|
673
851
|
if (!owner?.adapter.structure?.unwrap) {
|
|
674
852
|
editorConsole.warn(
|
|
@@ -703,8 +881,12 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
703
881
|
const owner = this.findOwnerChild(nodeId);
|
|
704
882
|
// Unknown real id: same "first child" floor `route()` uses elsewhere, forwarding
|
|
705
883
|
// the id unchanged (parity with the pre-existing bogus-id behavior).
|
|
884
|
+
const node = owner?.adapter.hierarchy.node(nodeId);
|
|
706
885
|
return owner
|
|
707
|
-
? {
|
|
886
|
+
? {
|
|
887
|
+
adapter: owner.adapter,
|
|
888
|
+
forwardId: node?.role === 'document' || node?.role === 'story' ? '' : nodeId,
|
|
889
|
+
}
|
|
708
890
|
: { adapter: this.children[0]!.adapter, forwardId: nodeId };
|
|
709
891
|
}
|
|
710
892
|
|
|
@@ -738,7 +920,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
738
920
|
|
|
739
921
|
/**
|
|
740
922
|
* D8 — union of every child's own {@link RootGroup}s, each group id prefixed
|
|
741
|
-
* `<worldId>:` so groups from different
|
|
923
|
+
* `<worldId>:` so groups from different roots never collide. `activate`
|
|
742
924
|
* strips the prefix back off to find the owning child and the child's own
|
|
743
925
|
* (unprefixed) group id.
|
|
744
926
|
*/
|
|
@@ -844,9 +1026,8 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
844
1026
|
* failure without aborting the rest; `destination` joins child destinations
|
|
845
1027
|
* with `' + '`.
|
|
846
1028
|
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
* one in a Composite changes nothing about that registration.
|
|
1029
|
+
* History is resource-driven, so the composite needs no ordering or inverse
|
|
1030
|
+
* logic of its own; its children journal into the open project's service.
|
|
850
1031
|
*/
|
|
851
1032
|
get persistence(): PersistenceProvider {
|
|
852
1033
|
// Always a REAL (never `undefined`) provider — see composite-authoring-adapter's
|
|
@@ -869,7 +1050,7 @@ export class CompositeAuthoringAdapter implements AuthoringAdapter {
|
|
|
869
1050
|
} catch (err) {
|
|
870
1051
|
console.error(
|
|
871
1052
|
`[CompositeAuthoringAdapter] save failed for world "${c.worldId}" ` +
|
|
872
|
-
`(destination: ${p.destination}) — continuing with remaining
|
|
1053
|
+
`(destination: ${p.destination}) — continuing with remaining roots:`,
|
|
873
1054
|
err,
|
|
874
1055
|
);
|
|
875
1056
|
}
|