@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
|
@@ -18,13 +18,14 @@ import { CompositeAuthoringAdapter } from '../composite-authoring-adapter';
|
|
|
18
18
|
import { VgaiSceneAuthoringAdapter } from '../vgai-scene-authoring-adapter';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* True when `node`
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
21
|
+
* True when `node` is a real Object3D-backed entity owned by a first-party
|
|
22
|
+
* `VgaiSceneAuthoringAdapter`, or when `node` is null and the active adapter
|
|
23
|
+
* has a first-party scene child (the no-selection Environment state).
|
|
24
|
+
*
|
|
25
|
+
* A scene document row is intentionally false: it is owned by the scene
|
|
26
|
+
* adapter for hierarchy routing, but it is not a SceneEntity and therefore
|
|
27
|
+
* cannot be passed to entity-only sections such as HeaderSection. Document
|
|
28
|
+
* rows have their own Environment registration in `register.tsx`.
|
|
28
29
|
*/
|
|
29
30
|
export function isFirstPartySelection(node: EditorNode | null, adapter: AuthoringAdapter): boolean {
|
|
30
31
|
const id = node?.id ?? null;
|
|
@@ -35,10 +36,28 @@ export function isFirstPartySelection(node: EditorNode | null, adapter: Authorin
|
|
|
35
36
|
const owner = adapter.ownerOf(id);
|
|
36
37
|
if (owner === null) return false; // world:<id> group node or unrecognized id -> generic
|
|
37
38
|
const child = adapter.childAdapters().find((c) => c.worldId === owner);
|
|
38
|
-
return
|
|
39
|
+
return (
|
|
40
|
+
child?.adapter instanceof VgaiSceneAuthoringAdapter &&
|
|
41
|
+
child.adapter.hierarchy.object3D(id) !== null
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (!(adapter instanceof VgaiSceneAuthoringAdapter)) return false;
|
|
45
|
+
return id === null || adapter.hierarchy.object3D(id) !== null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** True only for a first-party scene's synthetic document/root row. */
|
|
49
|
+
export function isFirstPartySceneDocument(
|
|
50
|
+
node: EditorNode | null,
|
|
51
|
+
adapter: AuthoringAdapter,
|
|
52
|
+
): boolean {
|
|
53
|
+
if (!node || node.role !== 'document') return false;
|
|
54
|
+
if (adapter instanceof CompositeAuthoringAdapter) {
|
|
55
|
+
const owner = adapter.ownerOf(node.id);
|
|
56
|
+
if (owner === null) return false;
|
|
57
|
+
return (
|
|
58
|
+
adapter.childAdapters().find((child) => child.worldId === owner)?.adapter instanceof
|
|
59
|
+
VgaiSceneAuthoringAdapter
|
|
60
|
+
);
|
|
39
61
|
}
|
|
40
|
-
// Bare (non-composite) adapter: first-party-ness doesn't depend on
|
|
41
|
-
// selection at all — a bare `VgaiSceneAuthoringAdapter` is first-party
|
|
42
|
-
// whether or not anything is selected; a bare ingest/module override never is.
|
|
43
62
|
return adapter instanceof VgaiSceneAuthoringAdapter;
|
|
44
63
|
}
|
|
@@ -44,9 +44,9 @@ import {
|
|
|
44
44
|
import { AddSectionMenu } from './AddSectionMenu';
|
|
45
45
|
import { EnvironmentSection } from './EnvironmentSection';
|
|
46
46
|
import { HeaderSection } from './HeaderSection';
|
|
47
|
-
import { isFirstPartySelection } from './is-first-party-selection';
|
|
47
|
+
import { isFirstPartySceneDocument, isFirstPartySelection } from './is-first-party-selection';
|
|
48
48
|
|
|
49
|
-
export { isFirstPartySelection };
|
|
49
|
+
export { isFirstPartySceneDocument, isFirstPartySelection };
|
|
50
50
|
|
|
51
51
|
/** A first-party selection (an actual node, not the deselect state). */
|
|
52
52
|
const isSelected = (node: EditorNode | null, adapter: AuthoringAdapter): boolean =>
|
|
@@ -56,6 +56,10 @@ const isSelected = (node: EditorNode | null, adapter: AuthoringAdapter): boolean
|
|
|
56
56
|
const isDeselected = (node: EditorNode | null, adapter: AuthoringAdapter): boolean =>
|
|
57
57
|
node === null && isFirstPartySelection(node, adapter);
|
|
58
58
|
|
|
59
|
+
/** A first-party scene document row shows scene settings, not entity fields. */
|
|
60
|
+
const isSceneDocument = (node: EditorNode | null, adapter: AuthoringAdapter): boolean =>
|
|
61
|
+
isFirstPartySceneDocument(node, adapter);
|
|
62
|
+
|
|
59
63
|
/**
|
|
60
64
|
* Adapt a `SectionProps`-based typed section (the existing
|
|
61
65
|
* `components/inspectors/*.tsx` components, which already read the live
|
|
@@ -95,6 +99,13 @@ registerInspectorSections({ match: isSelected, Section: wrap(NavigationSection)
|
|
|
95
99
|
registerInspectorSections({ match: isSelected, Section: wrap(ComponentsSection) });
|
|
96
100
|
registerInspectorSections({ match: isSelected, Section: wrap(AddSectionMenu) });
|
|
97
101
|
|
|
102
|
+
registerInspectorSections({
|
|
103
|
+
match: isSceneDocument,
|
|
104
|
+
Section: EnvironmentSection,
|
|
105
|
+
suppresses: ['header', 'transform', 'properties', 'components', 'stories'],
|
|
106
|
+
panelName: 'Inspector',
|
|
107
|
+
});
|
|
108
|
+
|
|
98
109
|
registerInspectorSections({
|
|
99
110
|
match: isDeselected,
|
|
100
111
|
Section: EnvironmentSection,
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
import type {
|
|
31
31
|
AuthoringAdapter,
|
|
32
32
|
AuthoringCapabilities,
|
|
33
|
+
AuthoringProvenance,
|
|
33
34
|
EditorNode,
|
|
34
35
|
HierarchyProvider,
|
|
35
36
|
InspectorProvider,
|
|
@@ -47,12 +48,31 @@ import {
|
|
|
47
48
|
colorMaterialOf,
|
|
48
49
|
} from '@engine/adapter/ingest/overlay-apply';
|
|
49
50
|
import type * as THREE from 'three';
|
|
51
|
+
import { editorConsole } from '../editor-console';
|
|
50
52
|
import type { EditorStore } from '../editor-store';
|
|
53
|
+
import { JsonHistoryResource } from '../history/json-history-resource';
|
|
51
54
|
import { captureSubtreeToVscn } from './ingest-capture';
|
|
52
55
|
import type { IngestOverlay } from './ingest-overlay';
|
|
53
|
-
import { notifyUndoableOp, registerUndoAdapter } from './undo-timeline';
|
|
54
56
|
import { raycastPick } from './viewport-raycast';
|
|
55
57
|
|
|
58
|
+
interface IngestHistoryState {
|
|
59
|
+
overlay: IngestOverlay;
|
|
60
|
+
objects: Record<
|
|
61
|
+
string,
|
|
62
|
+
{
|
|
63
|
+
name: string;
|
|
64
|
+
visible: boolean;
|
|
65
|
+
transform: Transform;
|
|
66
|
+
color?: string;
|
|
67
|
+
lightIntensity?: number;
|
|
68
|
+
lightColor?: string;
|
|
69
|
+
camera?: { fov: number; near: number; far: number };
|
|
70
|
+
castShadow: boolean;
|
|
71
|
+
receiveShadow: boolean;
|
|
72
|
+
}
|
|
73
|
+
>;
|
|
74
|
+
}
|
|
75
|
+
|
|
56
76
|
/** Loop-gate hooks so a stable edit can pause the game that owns the rAF. */
|
|
57
77
|
export interface IngestLoopControl {
|
|
58
78
|
pause(): void;
|
|
@@ -115,9 +135,17 @@ export class IngestAuthoringAdapter implements AuthoringAdapter {
|
|
|
115
135
|
/** Accumulated edits (this session + reapplied overlay), keyed by stable id. */
|
|
116
136
|
private overlay: IngestOverlay = {};
|
|
117
137
|
private dirty = false;
|
|
138
|
+
private readonly historyResource: JsonHistoryResource<IngestHistoryState> | null;
|
|
118
139
|
|
|
119
140
|
readonly capabilities: AuthoringCapabilities;
|
|
120
141
|
|
|
142
|
+
readonly provenance: AuthoringProvenance = {
|
|
143
|
+
source: 'foreign',
|
|
144
|
+
label: 'overlay',
|
|
145
|
+
detail:
|
|
146
|
+
'Unmodified ingested game — edits persist to an editor overlay, never to the game source.',
|
|
147
|
+
};
|
|
148
|
+
|
|
121
149
|
constructor(
|
|
122
150
|
private readonly store: EditorStore,
|
|
123
151
|
private readonly scene: THREE.Scene,
|
|
@@ -128,12 +156,6 @@ export class IngestAuthoringAdapter implements AuthoringAdapter {
|
|
|
128
156
|
this.destination =
|
|
129
157
|
opts.destination ?? (opts.persist ? '(ingest overlay)' : '(runtime-only — not persisted)');
|
|
130
158
|
this.camera = opts.camera;
|
|
131
|
-
// T3.2 slice 2: this adapter IS the undo/redo target the editor-global
|
|
132
|
-
// timeline dispatches to for entries it records below (`pushUndo`).
|
|
133
|
-
// `ingest-mode.ts`'s `exitIngestMode()` unregisters it on teardown, so a
|
|
134
|
-
// Ctrl+Z reaching an entry from a since-uninstalled ingest session drops it
|
|
135
|
-
// (with a warn) instead of undoing against a stale live scene.
|
|
136
|
-
registerUndoAdapter(this);
|
|
137
159
|
this.capabilities = {
|
|
138
160
|
transform: true,
|
|
139
161
|
material: true,
|
|
@@ -151,6 +173,33 @@ export class IngestAuthoringAdapter implements AuthoringAdapter {
|
|
|
151
173
|
};
|
|
152
174
|
this.refresh();
|
|
153
175
|
if (opts.overlay) this.applyOverlay(opts.overlay);
|
|
176
|
+
const history = store.projectHistory;
|
|
177
|
+
this.historyResource = history
|
|
178
|
+
? new JsonHistoryResource({
|
|
179
|
+
history,
|
|
180
|
+
kind: opts.persist ? 'overlay' : 'session-state',
|
|
181
|
+
scope: opts.persist ? 'project' : 'session',
|
|
182
|
+
...(opts.persist
|
|
183
|
+
? { location: (opts.destination ?? '.vgai/overlays/ingest.json').replace(/^\//, '') }
|
|
184
|
+
: { sessionId: `ingest-${crypto.randomUUID()}` }),
|
|
185
|
+
displayName: opts.destination ?? 'Ingest edits',
|
|
186
|
+
capture: () => this.captureHistoryState(),
|
|
187
|
+
// The snapshot carries a mirror of every live Object3D (the restore
|
|
188
|
+
// payload), but an ingested game is a RUNNING game that animates its
|
|
189
|
+
// own objects every frame — so the full snapshot stops matching a
|
|
190
|
+
// frame after it is taken. Only the OVERLAY is this resource's real
|
|
191
|
+
// content (it is what `serialize()`/`save()` writes to disk, and only
|
|
192
|
+
// editor edits touch it), so it alone decides "did something else
|
|
193
|
+
// change this resource?". Without this, every ingest transaction
|
|
194
|
+
// failed preflight with `content-conflict` and undo silently did
|
|
195
|
+
// nothing (issue #81).
|
|
196
|
+
conflictIdentity: (state) => state.overlay,
|
|
197
|
+
restore: async (state) => {
|
|
198
|
+
this.restoreHistoryState(state);
|
|
199
|
+
if (this.persistFn) await this.persistFn(this.overlay);
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
: null;
|
|
154
203
|
}
|
|
155
204
|
|
|
156
205
|
/**
|
|
@@ -231,6 +280,7 @@ export class IngestAuthoringAdapter implements AuthoringAdapter {
|
|
|
231
280
|
return {
|
|
232
281
|
id,
|
|
233
282
|
label: o.name || o.type || 'Object3D',
|
|
283
|
+
role: 'entity',
|
|
234
284
|
kind: kindOf(o),
|
|
235
285
|
parentId,
|
|
236
286
|
childIds: o.children.map((c) => this.idOf(c)!).filter(Boolean),
|
|
@@ -292,26 +342,22 @@ export class IngestAuthoringAdapter implements AuthoringAdapter {
|
|
|
292
342
|
// The game owns this object's transform; the editor freezes the game loop so
|
|
293
343
|
// its gizmo edit isn't overwritten next frame, then resumes on release.
|
|
294
344
|
owner: (): TransformOwner => 'external',
|
|
295
|
-
beginEdit: (
|
|
345
|
+
beginEdit: () => {
|
|
346
|
+
this.historyResource?.assertCanMutate();
|
|
296
347
|
this.loop?.pause();
|
|
297
|
-
this.
|
|
348
|
+
this.editStartState = this.captureHistoryState();
|
|
298
349
|
},
|
|
299
350
|
apply: (id, t) => this.writeTransform(id, t),
|
|
300
351
|
endEdit: (id) => {
|
|
301
352
|
this.loop?.resume();
|
|
302
|
-
|
|
303
|
-
this.
|
|
304
|
-
if (!prev) return;
|
|
305
|
-
const next = this.transforms.get(id);
|
|
306
|
-
this.pushUndo({
|
|
307
|
-
undo: () => this.writeTransform(id, prev),
|
|
308
|
-
redo: () => this.writeTransform(id, next),
|
|
309
|
-
});
|
|
353
|
+
this.recordHistory(`Transform ${this.byId.get(id)?.name || 'Object'}`, this.editStartState);
|
|
354
|
+
this.editStartState = undefined;
|
|
310
355
|
},
|
|
311
356
|
};
|
|
312
357
|
|
|
313
358
|
/** Write a transform to the live object + record it in the overlay (no undo push). */
|
|
314
359
|
private writeTransform(id: string, t: Transform): void {
|
|
360
|
+
this.historyResource?.assertCanMutate();
|
|
315
361
|
const o = this.byId.get(id);
|
|
316
362
|
if (!o) return;
|
|
317
363
|
o.position.set(t.position[0], t.position[1], t.position[2]);
|
|
@@ -376,12 +422,10 @@ export class IngestAuthoringAdapter implements AuthoringAdapter {
|
|
|
376
422
|
return undefined;
|
|
377
423
|
},
|
|
378
424
|
set: (id, path, value) => {
|
|
379
|
-
|
|
425
|
+
this.historyResource?.assertCanMutate();
|
|
426
|
+
const before = this.captureHistoryState();
|
|
380
427
|
this.writeProp(id, path, value);
|
|
381
|
-
this.
|
|
382
|
-
undo: () => this.writeProp(id, path, prev),
|
|
383
|
-
redo: () => this.writeProp(id, path, value),
|
|
384
|
-
});
|
|
428
|
+
this.recordHistory(`Set ${this.byId.get(id)?.name || 'Object'} ${path}`, before);
|
|
385
429
|
},
|
|
386
430
|
};
|
|
387
431
|
|
|
@@ -417,34 +461,87 @@ export class IngestAuthoringAdapter implements AuthoringAdapter {
|
|
|
417
461
|
}
|
|
418
462
|
|
|
419
463
|
// --- Overlay-backed undo/redo (feat-edit-undo for ingested games) ---
|
|
420
|
-
private
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
464
|
+
private editStartState: IngestHistoryState | undefined;
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Journal a before→after snapshot pair into project history, LOUDLY.
|
|
468
|
+
*
|
|
469
|
+
* A rejected `record(...)` means the edit IS applied to the live scene but has
|
|
470
|
+
* no history entry: it cannot be undone and will not survive a reload.
|
|
471
|
+
* Swallowing that (the previous `.catch(() => {})`) turned a persistence
|
|
472
|
+
* failure into silent data loss — the user sees their edit, then loses it with
|
|
473
|
+
* no indication anything went wrong.
|
|
474
|
+
*/
|
|
475
|
+
private recordHistory(label: string, before: IngestHistoryState | undefined): void {
|
|
476
|
+
if (!this.historyResource || !before) return;
|
|
477
|
+
void this.historyResource
|
|
478
|
+
.record(label, before, this.captureHistoryState())
|
|
479
|
+
.catch((err: unknown) => {
|
|
480
|
+
editorConsole.error(
|
|
481
|
+
`[ingest] failed to journal "${label}" into project history — the edit is applied ` +
|
|
482
|
+
`but has no history entry (it cannot be undone and will not survive reload): ` +
|
|
483
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
484
|
+
);
|
|
485
|
+
});
|
|
428
486
|
}
|
|
429
487
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
this.
|
|
437
|
-
|
|
438
|
-
|
|
488
|
+
dispose(): void {
|
|
489
|
+
this.historyResource?.dispose();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private captureHistoryState(): IngestHistoryState {
|
|
493
|
+
const objects: IngestHistoryState['objects'] = {};
|
|
494
|
+
for (const [id, object] of this.byId) {
|
|
495
|
+
const material = colorMaterialOf(object);
|
|
496
|
+
const light = lightOf(object);
|
|
497
|
+
const camera = perspectiveCameraOf(object);
|
|
498
|
+
objects[id] = {
|
|
499
|
+
name: object.name,
|
|
500
|
+
visible: object.visible,
|
|
501
|
+
transform: this.transforms.get(id),
|
|
502
|
+
...(material ? { color: `#${material.color.getHexString()}` } : {}),
|
|
503
|
+
...(light
|
|
504
|
+
? {
|
|
505
|
+
lightIntensity: light.intensity,
|
|
506
|
+
lightColor: `#${light.color.getHexString()}`,
|
|
507
|
+
}
|
|
508
|
+
: {}),
|
|
509
|
+
...(camera ? { camera: { fov: camera.fov, near: camera.near, far: camera.far } } : {}),
|
|
510
|
+
castShadow: object.castShadow,
|
|
511
|
+
receiveShadow: object.receiveShadow,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
return { overlay: structuredClone(this.overlay), objects };
|
|
439
515
|
}
|
|
440
516
|
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
517
|
+
private restoreHistoryState(state: IngestHistoryState): void {
|
|
518
|
+
for (const [id, value] of Object.entries(state.objects)) {
|
|
519
|
+
const object = this.byId.get(id);
|
|
520
|
+
if (!object) continue;
|
|
521
|
+
object.name = value.name;
|
|
522
|
+
object.visible = value.visible;
|
|
523
|
+
object.position.fromArray(value.transform.position);
|
|
524
|
+
object.quaternion.fromArray(value.transform.rotation);
|
|
525
|
+
object.scale.fromArray(value.transform.scale);
|
|
526
|
+
if (value.color) colorMaterialOf(object)?.color.set(value.color);
|
|
527
|
+
const light = lightOf(object);
|
|
528
|
+
if (light) {
|
|
529
|
+
if (value.lightIntensity !== undefined) light.intensity = value.lightIntensity;
|
|
530
|
+
if (value.lightColor) light.color.set(value.lightColor);
|
|
531
|
+
}
|
|
532
|
+
const camera = perspectiveCameraOf(object);
|
|
533
|
+
if (camera && value.camera) {
|
|
534
|
+
camera.fov = value.camera.fov;
|
|
535
|
+
camera.near = value.camera.near;
|
|
536
|
+
camera.far = value.camera.far;
|
|
537
|
+
camera.updateProjectionMatrix();
|
|
538
|
+
}
|
|
539
|
+
object.castShadow = value.castShadow;
|
|
540
|
+
object.receiveShadow = value.receiveShadow;
|
|
541
|
+
}
|
|
542
|
+
this.overlay = structuredClone(state.overlay);
|
|
543
|
+
this.dirty = true;
|
|
446
544
|
this.store.notifyIngestEdit();
|
|
447
|
-
return true;
|
|
448
545
|
}
|
|
449
546
|
|
|
450
547
|
// A class GETTER, not a field initializer: a field initializer runs before the
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* D-V5 (docs/WAVE5-MULTIWORLD-INGEST-DESIGN.md, F24 composite): a composite
|
|
13
13
|
* manifest (one ingest world + siblings, `ingest-siblings.ts`) can fail
|
|
14
|
-
* SEVERAL
|
|
14
|
+
* SEVERAL roots independently in one mount attempt (a `default-three`
|
|
15
15
|
* sibling, a throwing `default-react` sibling, …) — a single-slot "last
|
|
16
16
|
* failure wins" model would silently drop every failure but the latest, the
|
|
17
17
|
* exact F9 anti-pattern this module exists to close. This generalizes the
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
|
|
39
39
|
/** One manifest world's mount failure — the exact fields the banner shows. */
|
|
40
40
|
export interface MountFailureReport {
|
|
41
|
-
/** The failing world's manifest id (`
|
|
41
|
+
/** The failing world's manifest id (`ResolvedAdapterRoot.id`), or the
|
|
42
42
|
* `?ingest=<id>` URL-fallback route's discovered game/folder id. */
|
|
43
43
|
readonly worldId: string;
|
|
44
|
-
/** The world's render substrate kind (`
|
|
44
|
+
/** The world's render substrate kind (`ResolvedAdapterRoot.kind`, e.g.
|
|
45
45
|
* `'threejs'`/`'pixijs'`/`'react'`) — or, for the URL-fallback route
|
|
46
|
-
* (which has no `
|
|
46
|
+
* (which has no `ResolvedAdapterRoot` to read a kind from), the same
|
|
47
47
|
* vocabulary inferred from which fixture glob resolved the id. */
|
|
48
48
|
readonly kind: string;
|
|
49
49
|
/** The resolved adapter identity (`ResolvedAdapter['identity']` — e.g.
|
|
@@ -55,7 +55,7 @@ export interface MountFailureReport {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/** D-V5: an ORDERED list now backs the slot (insertion order — the order
|
|
58
|
-
*
|
|
58
|
+
* roots actually failed in, which for a composite's sibling loop is
|
|
59
59
|
* manifest order). Every existing (wave-4) accessor below is a thin view
|
|
60
60
|
* over this same array — never a second, parallel source of truth. */
|
|
61
61
|
let _reports: MountFailureReport[] = [];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seam-level provenance resolution (spec 29 §5, docs/unified-world-editor/
|
|
3
|
+
* 29-hierarchy-philosophy.md): which {@link AuthoringProvenance} governs a
|
|
4
|
+
* given hierarchy node. Provenance is declared once per adapter and rendered
|
|
5
|
+
* at the seam (world group row / panel header); affordance code also reads it
|
|
6
|
+
* for the REASON string on a disabled operation ("the subject is present but
|
|
7
|
+
* the operation isn't" — the complement of spec 28's hide-when-absent rule).
|
|
8
|
+
*
|
|
9
|
+
* Rule zero: roots are resolved by ASKING the composite (`groupNodeId` /
|
|
10
|
+
* `ownerOf`), never by decoding adapter-minted id strings here.
|
|
11
|
+
*/
|
|
12
|
+
import type { AuthoringAdapter, AuthoringProvenance } from '@engine/adapter';
|
|
13
|
+
import { CompositeAuthoringAdapter } from './composite-authoring-adapter';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The provenance governing `nodeId` under the ACTIVE adapter: the owning
|
|
17
|
+
* child's declaration inside a composite (a `world:<id>` group row resolves
|
|
18
|
+
* to that world's own child), the adapter's own declaration otherwise.
|
|
19
|
+
* `null` when nothing was declared (the shell shows no badge and falls back
|
|
20
|
+
* to a generic reason — never fabricates a claim).
|
|
21
|
+
*/
|
|
22
|
+
export function provenanceForNode(
|
|
23
|
+
adapter: AuthoringAdapter,
|
|
24
|
+
nodeId: string,
|
|
25
|
+
): AuthoringProvenance | null {
|
|
26
|
+
if (!(adapter instanceof CompositeAuthoringAdapter)) return adapter.provenance ?? null;
|
|
27
|
+
for (const child of adapter.childAdapters()) {
|
|
28
|
+
if (adapter.groupNodeId(child.worldId) === nodeId) return child.adapter.provenance ?? null;
|
|
29
|
+
}
|
|
30
|
+
const worldId = adapter.ownerOf(nodeId);
|
|
31
|
+
if (worldId === null) return null;
|
|
32
|
+
const owner = adapter.childAdapters().find((c) => c.worldId === worldId);
|
|
33
|
+
return owner?.adapter.provenance ?? null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The reason string for an affordance that is present but unavailable —
|
|
37
|
+
* the adapter's own explanation when it declared one, a plain honest
|
|
38
|
+
* fallback when it didn't. */
|
|
39
|
+
export function unavailableReason(provenance: AuthoringProvenance | null): string {
|
|
40
|
+
return provenance?.detail ?? "Not supported by this world's adapter.";
|
|
41
|
+
}
|