@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
package/src/project-manager.ts
CHANGED
|
@@ -6,22 +6,38 @@
|
|
|
6
6
|
* Projects can also be switched at runtime via `switchProject()`.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { loadGameManifest } from '@engine/manifest/load';
|
|
10
|
+
import { BUNDLED_EDITOR_COMPATIBILITY } from './build-identity';
|
|
11
|
+
import {
|
|
12
|
+
getCurrentServerProject,
|
|
13
|
+
openProjectOnServer,
|
|
14
|
+
requireEditorCompatibility,
|
|
15
|
+
} from './editor-api';
|
|
16
|
+
import { assertProjectCompatibility } from './editor-compatibility';
|
|
10
17
|
import { getEditorMode } from './editor-mode';
|
|
18
|
+
import { type HierarchyProjection, parseHierarchyProjection } from './hierarchy-projection';
|
|
11
19
|
import { getStorageBackend, restorePersistedProjectRoot } from './storage';
|
|
12
20
|
import { readProjectManifest, seedIfEmpty } from './storage/seed';
|
|
13
21
|
|
|
14
22
|
export interface ProjectConfig {
|
|
23
|
+
/** Project-file schema version. Kept distinct from game and engine versions. */
|
|
24
|
+
manifestVersion: 2;
|
|
15
25
|
name: string;
|
|
26
|
+
/** The authored game's own release version. */
|
|
27
|
+
version: string;
|
|
28
|
+
/** Exact engine identity the project is pinned to. */
|
|
29
|
+
engine: { version: string };
|
|
30
|
+
/** Editor-only organization. It never changes which runtime roots mount. */
|
|
31
|
+
authoring?: { hierarchy?: HierarchyProjection | undefined } | undefined;
|
|
16
32
|
resolution?: { width: number; height: number } | undefined;
|
|
17
33
|
appId?: string | undefined;
|
|
34
|
+
/** Stable preferred port generated with the project; launchers may fall forward when occupied. */
|
|
35
|
+
editorPort?: number | undefined;
|
|
18
36
|
/**
|
|
19
37
|
* The project's default scene path, or `null` when the manifest's first
|
|
20
38
|
* world declares no scene (e.g. an `entry`-only world — §7.1-12). Always
|
|
21
|
-
* present when
|
|
22
|
-
*
|
|
23
|
-
* (verbatim pass-through — §1C), which `loadInitialScene` treats the same
|
|
24
|
-
* as `null`. Consumers must NOT default a missing/null value to a guessed
|
|
39
|
+
* present when derived from the manifest (`readProjectView`, server-side).
|
|
40
|
+
* Consumers must NOT default a missing/null value to a guessed
|
|
25
41
|
* path — that was the bug (initial-scene.ts guessing
|
|
26
42
|
* 'scenes/default.vscn.json' and silently writing a placeholder scene).
|
|
27
43
|
*/
|
|
@@ -31,7 +47,7 @@ export interface ProjectConfig {
|
|
|
31
47
|
export interface ActiveProject {
|
|
32
48
|
/** Absolute path to the project root (as passed via URL param / env). */
|
|
33
49
|
rootPath: string;
|
|
34
|
-
/** Parsed
|
|
50
|
+
/** Parsed vgai.game.json project configuration plus derived editor fields. */
|
|
35
51
|
config: ProjectConfig;
|
|
36
52
|
/**
|
|
37
53
|
* Set when this project is a BUNDLED EXAMPLE opened in the hosted (browser)
|
|
@@ -50,13 +66,74 @@ function _notify(): void {
|
|
|
50
66
|
for (const fn of _listeners) fn();
|
|
51
67
|
}
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
function describeManifestError(error: unknown): string {
|
|
70
|
+
const issues =
|
|
71
|
+
error && typeof error === 'object' && Array.isArray((error as { issues?: unknown }).issues)
|
|
72
|
+
? (error as { issues: unknown[] }).issues
|
|
73
|
+
: null;
|
|
74
|
+
if (!issues) return error instanceof Error ? error.message : String(error);
|
|
75
|
+
|
|
76
|
+
return issues
|
|
77
|
+
.map((issue) => {
|
|
78
|
+
if (!issue || typeof issue !== 'object') return String(issue);
|
|
79
|
+
const candidate = issue as { path?: unknown; message?: unknown };
|
|
80
|
+
const path = Array.isArray(candidate.path)
|
|
81
|
+
? candidate.path.map(String).join('.') || '(root)'
|
|
82
|
+
: '(root)';
|
|
83
|
+
const message =
|
|
84
|
+
typeof candidate.message === 'string' ? candidate.message : 'Invalid manifest value';
|
|
85
|
+
return `${path}: ${message}`;
|
|
86
|
+
})
|
|
87
|
+
.join('\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Open a project by its root path. Fetches the sole v2 project manifest. */
|
|
54
91
|
export async function openProject(rootPath: string): Promise<ActiveProject> {
|
|
55
|
-
const
|
|
92
|
+
const compatibility = await requireEditorCompatibility();
|
|
93
|
+
const res = await fetch('/vgai.game.json');
|
|
56
94
|
if (!res.ok) {
|
|
57
|
-
throw new Error(
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Could not open project at ${rootPath}.\n` +
|
|
97
|
+
`Failed to load vgai.game.json: ${res.status} ${res.statusText}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
let rawManifest: unknown;
|
|
101
|
+
try {
|
|
102
|
+
rawManifest = await res.json();
|
|
103
|
+
} catch (error) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Could not open project at ${rootPath}.\n` +
|
|
106
|
+
`vgai.game.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
assertProjectCompatibility(
|
|
110
|
+
rawManifest && typeof rawManifest === 'object'
|
|
111
|
+
? (rawManifest as { manifestVersion?: unknown; engine?: { version?: unknown } })
|
|
112
|
+
: {},
|
|
113
|
+
compatibility,
|
|
114
|
+
);
|
|
115
|
+
let manifest: ReturnType<typeof loadGameManifest>;
|
|
116
|
+
try {
|
|
117
|
+
manifest = loadGameManifest(rawManifest);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Could not open project at ${rootPath}.\n` +
|
|
121
|
+
`vgai.game.json does not match the current project format:\n${describeManifestError(error)}`,
|
|
122
|
+
);
|
|
58
123
|
}
|
|
59
|
-
const config =
|
|
124
|
+
const config: ProjectConfig = {
|
|
125
|
+
manifestVersion: manifest.manifestVersion,
|
|
126
|
+
name: manifest.name,
|
|
127
|
+
version: manifest.version,
|
|
128
|
+
engine: manifest.engine,
|
|
129
|
+
appId: manifest.appId,
|
|
130
|
+
editorPort: manifest.editorPort,
|
|
131
|
+
resolution: manifest.resolution,
|
|
132
|
+
authoring: manifest.authoring
|
|
133
|
+
? { hierarchy: parseHierarchyProjection(manifest.authoring.hierarchy) }
|
|
134
|
+
: undefined,
|
|
135
|
+
defaultScene: manifest.roots.find((root) => root.scene !== undefined)?.scene ?? null,
|
|
136
|
+
};
|
|
60
137
|
_activeProject = { rootPath, config };
|
|
61
138
|
_notify();
|
|
62
139
|
return _activeProject;
|
|
@@ -114,8 +191,10 @@ export function getProjectPathFromURL(): string | null {
|
|
|
114
191
|
* Used in production where __VGAI_PROJECT_PATH__ isn't available at build time.
|
|
115
192
|
*/
|
|
116
193
|
export async function detectServerProject(): Promise<ActiveProject | null> {
|
|
194
|
+
const compatibility = await requireEditorCompatibility();
|
|
117
195
|
const info = await getCurrentServerProject();
|
|
118
196
|
if (!info) return null;
|
|
197
|
+
assertProjectCompatibility(info.config, compatibility);
|
|
119
198
|
_activeProject = { rootPath: info.path, config: info.config as ProjectConfig };
|
|
120
199
|
_notify();
|
|
121
200
|
return _activeProject;
|
|
@@ -157,6 +236,7 @@ export async function detectProject(): Promise<ActiveProject | null> {
|
|
|
157
236
|
const backend = getStorageBackend();
|
|
158
237
|
await seedIfEmpty(backend);
|
|
159
238
|
const config = await readProjectManifest(backend);
|
|
239
|
+
assertProjectCompatibility(config, BUNDLED_EDITOR_COMPATIBILITY);
|
|
160
240
|
_activeProject = { rootPath: restored?.status === 'granted' ? 'fsa' : 'browser', config };
|
|
161
241
|
_notify();
|
|
162
242
|
return _activeProject;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { connectOperationFileEvents } from './asset-events';
|
|
2
|
+
import { getEditorMode } from './editor-mode';
|
|
3
|
+
import { onProjectChange } from './project-manager';
|
|
4
|
+
|
|
5
|
+
export interface ProjectOperationCatalogEntry {
|
|
6
|
+
name: string;
|
|
7
|
+
summary: string;
|
|
8
|
+
description: string;
|
|
9
|
+
sourcePath: string;
|
|
10
|
+
inputSchema: unknown;
|
|
11
|
+
resultSchema: unknown;
|
|
12
|
+
errors: Array<{ code: string; summary: string; dataSchema?: unknown }>;
|
|
13
|
+
requires: Record<string, boolean | undefined>;
|
|
14
|
+
host: 'node' | 'editor-browser' | 'runtime-page';
|
|
15
|
+
mutates: boolean;
|
|
16
|
+
supportsDryRun: boolean;
|
|
17
|
+
longRunning: boolean;
|
|
18
|
+
permission: { risk: 'read' | 'write' | 'destructive'; summary: string };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ProjectOperationCatalog {
|
|
22
|
+
operations: ProjectOperationCatalogEntry[];
|
|
23
|
+
loadErrors: Array<{ sourcePath: string; message: string }>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type ProjectOperationOutcome =
|
|
27
|
+
| { ok: true; data: unknown }
|
|
28
|
+
| { ok: false; error: { code: string; message: string; data?: unknown; issues?: unknown[] } };
|
|
29
|
+
|
|
30
|
+
const EMPTY_CATALOG: ProjectOperationCatalog = { operations: [], loadErrors: [] };
|
|
31
|
+
let catalog = EMPTY_CATALOG;
|
|
32
|
+
let version = 0;
|
|
33
|
+
const listeners = new Set<() => void>();
|
|
34
|
+
|
|
35
|
+
function publish(next: ProjectOperationCatalog): void {
|
|
36
|
+
catalog = next;
|
|
37
|
+
version++;
|
|
38
|
+
for (const listener of listeners) listener();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getProjectOperations(): ProjectOperationCatalog {
|
|
42
|
+
return catalog;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function projectOperationsVersion(): number {
|
|
46
|
+
return version;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function subscribeProjectOperations(listener: () => void): () => void {
|
|
50
|
+
listeners.add(listener);
|
|
51
|
+
return () => listeners.delete(listener);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function refreshProjectOperations(): Promise<ProjectOperationCatalog> {
|
|
55
|
+
if (getEditorMode() === 'browser') {
|
|
56
|
+
publish(EMPTY_CATALOG);
|
|
57
|
+
return EMPTY_CATALOG;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch('/__editor/project-operations');
|
|
61
|
+
if (!response.ok) throw new Error(`catalog request failed (${response.status})`);
|
|
62
|
+
const next = (await response.json()) as ProjectOperationCatalog;
|
|
63
|
+
publish(next);
|
|
64
|
+
return next;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const next = {
|
|
67
|
+
operations: [],
|
|
68
|
+
loadErrors: [
|
|
69
|
+
{
|
|
70
|
+
sourcePath: 'src/operations',
|
|
71
|
+
message: error instanceof Error ? error.message : String(error),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
publish(next);
|
|
76
|
+
return next;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function runProjectOperation(
|
|
81
|
+
name: string,
|
|
82
|
+
input: unknown,
|
|
83
|
+
options: { confirm?: boolean } = {},
|
|
84
|
+
): Promise<ProjectOperationOutcome> {
|
|
85
|
+
const response = await fetch('/__editor/project-operations/run', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify({ name, input, confirm: options.confirm === true }),
|
|
89
|
+
});
|
|
90
|
+
const outcome = (await response.json()) as ProjectOperationOutcome;
|
|
91
|
+
if (!outcome || typeof outcome !== 'object' || typeof outcome.ok !== 'boolean') {
|
|
92
|
+
throw new Error(`Operation request returned an invalid response (${response.status}).`);
|
|
93
|
+
}
|
|
94
|
+
return outcome;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Editor-init lifecycle for Node-side project operation discovery. */
|
|
98
|
+
export function startProjectOperationDiscovery(): () => void {
|
|
99
|
+
void refreshProjectOperations();
|
|
100
|
+
const stopProjectWatch = onProjectChange(() => void refreshProjectOperations());
|
|
101
|
+
const stopFileWatch = connectOperationFileEvents(() => void refreshProjectOperations());
|
|
102
|
+
return () => {
|
|
103
|
+
stopProjectWatch();
|
|
104
|
+
stopFileWatch();
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function __resetProjectOperationsForTest(): void {
|
|
109
|
+
catalog = EMPTY_CATALOG;
|
|
110
|
+
version = 0;
|
|
111
|
+
listeners.clear();
|
|
112
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Storybook discovery lifecycle. Story navigation is a project
|
|
3
|
+
* capability, not a side effect of mounting a particular React world or
|
|
4
|
+
* opening a Story document, so the editor starts discovery once at init.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { connectStoryFileEvents } from './asset-events';
|
|
8
|
+
import { getCurrentProject, onProjectChange } from './project-manager';
|
|
9
|
+
import { refreshProjectStories } from './stories/story-registry';
|
|
10
|
+
|
|
11
|
+
function refresh(): void {
|
|
12
|
+
void refreshProjectStories(getCurrentProject());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Discover immediately, then re-discover on project switches and CSF file
|
|
16
|
+
* add/remove events. In-flight refreshes are allowed to settle. */
|
|
17
|
+
export function startProjectStoryDiscovery(): () => void {
|
|
18
|
+
refresh();
|
|
19
|
+
const stopProjectWatch = onProjectChange(refresh);
|
|
20
|
+
const stopStoryFileWatch = connectStoryFileEvents(refresh);
|
|
21
|
+
return () => {
|
|
22
|
+
stopProjectWatch();
|
|
23
|
+
stopStoryFileWatch();
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-tool discovery LIFECYCLE (W0, docs/EDITOR-WORKSPACE-SHELL-TODO.md
|
|
3
|
+
* §7.2/§9 W0 — "project tool discovery must move out of `BottomPanel` and
|
|
4
|
+
* into project/editor initialization").
|
|
5
|
+
*
|
|
6
|
+
* Until W0, `BottomPanel.tsx` anchored discovery in its own mount effects:
|
|
7
|
+
* the dock was both the DISCOVERER and one consumer of project tools, so
|
|
8
|
+
* inspector-placement tools only loaded because the bottom dock happened to
|
|
9
|
+
* be mounted. This module owns that lifecycle instead; the editor shell
|
|
10
|
+
* starts it once at editor initialization (`EditorContext.tsx`'s
|
|
11
|
+
* `EditorProvider`), and the placement hosts (center tool documents,
|
|
12
|
+
* workspace-utility registrations — `components/tool-documents.tsx`,
|
|
13
|
+
* tool-loader.ts's `getGlobalTools`/`subscribeGlobalTools`) are plain
|
|
14
|
+
* consumers of the already-discovered store — proven by
|
|
15
|
+
* `test/project-tool-discovery.test.ts`, which runs discovery with no
|
|
16
|
+
* component module imported at all.
|
|
17
|
+
*
|
|
18
|
+
* The pass itself is unchanged (`refreshProjectTools`, tool-loader.ts):
|
|
19
|
+
* discover on start, re-discover when the open project changes, and
|
|
20
|
+
* re-discover on server-watched `src/tools/` add/unlink events (W6a,
|
|
21
|
+
* docs/DATA-TOOLS-DESIGN.md §7). Edit-HMR stays where it was — tool-loader's
|
|
22
|
+
* own module-level `vgai:script-update` listener.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { connectToolFileEvents } from './asset-events';
|
|
26
|
+
import { onProjectChange } from './project-manager';
|
|
27
|
+
import { refreshProjectTools } from './tool-loader';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Start the project-tool discovery lifecycle: one immediate discovery pass,
|
|
31
|
+
* plus re-discovery on project change and on tool-file add/unlink events.
|
|
32
|
+
* Returns a stop function that disconnects both subscriptions (the pass in
|
|
33
|
+
* flight is left to finish — `refreshProjectTools` never throws).
|
|
34
|
+
*/
|
|
35
|
+
export function startProjectToolDiscovery(): () => void {
|
|
36
|
+
void refreshProjectTools();
|
|
37
|
+
const stopProjectWatch = onProjectChange(() => void refreshProjectTools());
|
|
38
|
+
const stopToolFileWatch = connectToolFileEvents(() => void refreshProjectTools());
|
|
39
|
+
return () => {
|
|
40
|
+
stopProjectWatch();
|
|
41
|
+
stopToolFileWatch();
|
|
42
|
+
};
|
|
43
|
+
}
|
package/src/scene-serializer.ts
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { SceneEntity, SceneEnvironment, SceneFile } from '@engine/scene/scene-types';
|
|
13
|
-
import type { UIRoot } from '@engine/scene/schema';
|
|
14
13
|
import { getUserData } from '@engine/scene/user-data';
|
|
15
14
|
import type * as THREE from 'three';
|
|
16
15
|
|
|
@@ -18,8 +17,6 @@ export interface SceneMetadata {
|
|
|
18
17
|
version: number;
|
|
19
18
|
name: string;
|
|
20
19
|
environment?: SceneEnvironment;
|
|
21
|
-
/** Scene-graph UI (the `ui` field) — preserved through load/serialize (A1/C9). */
|
|
22
|
-
ui?: UIRoot[];
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
type Transform = NonNullable<SceneEntity['transform']>;
|
|
@@ -68,9 +65,6 @@ export function serializeScene(scene: THREE.Scene, meta: SceneMetadata): SceneFi
|
|
|
68
65
|
if (meta.environment) {
|
|
69
66
|
doc.environment = meta.environment;
|
|
70
67
|
}
|
|
71
|
-
if (meta.ui && meta.ui.length > 0) {
|
|
72
|
-
doc.ui = meta.ui;
|
|
73
|
-
}
|
|
74
68
|
|
|
75
69
|
return doc;
|
|
76
70
|
}
|
package/src/scene-sync.ts
CHANGED
|
@@ -81,6 +81,7 @@ const prefabPreviewCache = new Map<string, PrefabFile>();
|
|
|
81
81
|
|
|
82
82
|
/** Cache for loaded .mat.json materials. */
|
|
83
83
|
const materialRefCache = new Map<string, SceneMaterial>();
|
|
84
|
+
let materialRefCacheGeneration = 0;
|
|
84
85
|
|
|
85
86
|
/** Cache for processed environment map textures. */
|
|
86
87
|
const envTextureCache = new Map<string, THREE.Texture>();
|
|
@@ -134,6 +135,7 @@ export function clearPrefabPreviewCache(): void {
|
|
|
134
135
|
|
|
135
136
|
/** Invalidate the material ref cache (e.g. when a .mat.json is saved). */
|
|
136
137
|
export function clearMaterialRefCache(): void {
|
|
138
|
+
materialRefCacheGeneration++;
|
|
137
139
|
materialRefCache.clear();
|
|
138
140
|
}
|
|
139
141
|
|
|
@@ -160,11 +162,12 @@ function resolveEntityMaterial(
|
|
|
160
162
|
// unreachable/404 file).
|
|
161
163
|
const ref = entity.materialRef;
|
|
162
164
|
const id = entity.id;
|
|
163
|
-
|
|
165
|
+
const generation = materialRefCacheGeneration;
|
|
166
|
+
fetch(resolveUrl(ref), { cache: 'no-store' })
|
|
164
167
|
.then((res) => (res.ok ? res.json() : null))
|
|
165
168
|
.then((json) => (json === null ? null : parseMaterialFile(json, ref)))
|
|
166
169
|
.then((file) => {
|
|
167
|
-
if (!file) return;
|
|
170
|
+
if (!file || generation !== materialRefCacheGeneration) return;
|
|
168
171
|
materialRefCache.set(ref, file.material);
|
|
169
172
|
// Rebuild the entity's preview object with the loaded material
|
|
170
173
|
if (id) {
|
|
@@ -384,6 +387,7 @@ export function updateEntityPreview(
|
|
|
384
387
|
|
|
385
388
|
const existing = objectMap.get(entity.id);
|
|
386
389
|
if (!existing) return;
|
|
390
|
+
const skeletonVisible = Boolean(getUserData(existing, 'skeletonVisible'));
|
|
387
391
|
|
|
388
392
|
// Unregister old particle systems (only on the entity itself, not child entities)
|
|
389
393
|
unregisterParticles(existing, batchedRenderer ?? null);
|
|
@@ -416,6 +420,7 @@ export function updateEntityPreview(
|
|
|
416
420
|
visualEntity = resolveEntityMaterial(entity, objectMap, scene);
|
|
417
421
|
}
|
|
418
422
|
const newObj = createEntityObject(visualEntity);
|
|
423
|
+
setUserData(newObj, 'skeletonVisible', skeletonVisible);
|
|
419
424
|
// Restore the original (unresolved) entity as the source of truth.
|
|
420
425
|
// This preserves prefab ref, materialRef, and any instance-level overrides.
|
|
421
426
|
setUserData(newObj, 'entity', JSON.parse(JSON.stringify(entity)) as SceneEntity);
|
package/src/script-loader.ts
CHANGED
|
@@ -72,8 +72,7 @@ function extractProjectScripts(
|
|
|
72
72
|
if (!adapter && !setup) {
|
|
73
73
|
throw new Error(
|
|
74
74
|
`Project script at ${scriptEntry} exports neither \`adapter\` nor \`setup\` — every ` +
|
|
75
|
-
'first-party entry module exports at least one
|
|
76
|
-
'packages/editor/template/src/scripts/main.ts).',
|
|
75
|
+
'explicit first-party entry module exports at least one.',
|
|
77
76
|
);
|
|
78
77
|
}
|
|
79
78
|
|
|
@@ -109,7 +108,8 @@ export function setLoadedComponents(components: Record<string, GameComponentClas
|
|
|
109
108
|
/**
|
|
110
109
|
* Load project scripts from the given project path.
|
|
111
110
|
*
|
|
112
|
-
*
|
|
111
|
+
* A legacy or explicitly declared project entry (defaulting to
|
|
112
|
+
* `src/scripts/main.ts` for compatibility) must export
|
|
113
113
|
* EITHER (or both — the EDITOR prefers `setup`, which alone threads
|
|
114
114
|
* `editorPreview` + the firstParty HMR brand, see
|
|
115
115
|
* `resolveDefaultThreeAdapter`; standalone prefers `adapter`,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getUserData, setUserData } from '@engine/scene/user-data';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { EDITOR_LAYER } from './editor-layers';
|
|
4
|
+
|
|
5
|
+
const SKELETON_HELPER_TYPE = 'skeletons';
|
|
6
|
+
const SKELETON_PARENT_COLOR = new THREE.Color(0x00e5ff);
|
|
7
|
+
const SKELETON_CHILD_COLOR = new THREE.Color(0xffb000);
|
|
8
|
+
|
|
9
|
+
function applyEditorSkeletonColors(helper: THREE.SkeletonHelper): void {
|
|
10
|
+
const colors = helper.geometry.getAttribute('color');
|
|
11
|
+
// SkeletonHelper emits two vertices per bone link. Three's stock pure
|
|
12
|
+
// blue/green endpoints disappear against vgai's dark viewport, especially
|
|
13
|
+
// in captures. Keep the native helper geometry, but use editor-contrast
|
|
14
|
+
// colors so the inspection affordance is actually visible.
|
|
15
|
+
for (let i = 0; i < colors.count; i += 2) {
|
|
16
|
+
colors.setXYZ(i, SKELETON_PARENT_COLOR.r, SKELETON_PARENT_COLOR.g, SKELETON_PARENT_COLOR.b);
|
|
17
|
+
if (i + 1 < colors.count) {
|
|
18
|
+
colors.setXYZ(i + 1, SKELETON_CHILD_COLOR.r, SKELETON_CHILD_COLOR.g, SKELETON_CHILD_COLOR.b);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
colors.needsUpdate = true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ensureSkeletonHelper(root: THREE.Object3D): THREE.SkeletonHelper | null {
|
|
25
|
+
let existing: THREE.SkeletonHelper | null = null;
|
|
26
|
+
let hasSkinnedMesh = false;
|
|
27
|
+
root.traverse((object) => {
|
|
28
|
+
if (getUserData(object, 'editorHelperType') === SKELETON_HELPER_TYPE) {
|
|
29
|
+
existing = object as THREE.SkeletonHelper;
|
|
30
|
+
}
|
|
31
|
+
if ((object as THREE.SkinnedMesh).isSkinnedMesh) hasSkinnedMesh = true;
|
|
32
|
+
});
|
|
33
|
+
if (existing || !hasSkinnedMesh) return existing;
|
|
34
|
+
|
|
35
|
+
const helper = new THREE.SkeletonHelper(root);
|
|
36
|
+
if (helper.bones.length === 0) {
|
|
37
|
+
helper.dispose();
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
applyEditorSkeletonColors(helper);
|
|
41
|
+
|
|
42
|
+
// SkeletonHelper normally expects to be a scene-root sibling. Keeping it
|
|
43
|
+
// under the entity makes lifecycle cleanup automatic, so use entity-local
|
|
44
|
+
// coordinates instead of applying the entity world transform twice.
|
|
45
|
+
helper.matrix = new THREE.Matrix4();
|
|
46
|
+
helper.matrixAutoUpdate = false;
|
|
47
|
+
helper.name = 'EditorSkeletonHelper';
|
|
48
|
+
helper.renderOrder = 999;
|
|
49
|
+
helper.visible = Boolean(getUserData(root, 'skeletonVisible'));
|
|
50
|
+
setUserData(helper, 'editorHelper', true);
|
|
51
|
+
setUserData(helper, 'editorHelperType', SKELETON_HELPER_TYPE);
|
|
52
|
+
setUserData(helper, 'skeletonEnabled', helper.visible);
|
|
53
|
+
helper.traverse((child) => child.layers.set(EDITOR_LAYER));
|
|
54
|
+
root.add(helper);
|
|
55
|
+
root.updateMatrixWorld(true);
|
|
56
|
+
return helper;
|
|
57
|
+
}
|
package/src/storage/seed.ts
CHANGED
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
* require routing the engine's asset pipeline through the backend.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { loadGameManifest } from '@engine/manifest/load';
|
|
12
13
|
import { parseSceneFile } from '@engine/scene/parse';
|
|
14
|
+
import { BUNDLED_ENGINE_VERSION } from '../build-identity';
|
|
13
15
|
import type { StorageBackend } from './types';
|
|
14
16
|
|
|
15
17
|
export const STARTER_SCENE_PATH = 'scenes/default.vscn.json';
|
|
16
18
|
const STARTER_INPUTMAP_PATH = 'inputmaps/default.inputmap.json';
|
|
17
|
-
const PROJECT_MANIFEST_PATH = '
|
|
19
|
+
const PROJECT_MANIFEST_PATH = 'vgai.game.json';
|
|
20
|
+
const STARTER_UI_PATH = 'src/ui/game.tsx';
|
|
18
21
|
|
|
19
22
|
/** Minimal, asset-free scene: a camera, a light, a ground plane, and a box. */
|
|
20
23
|
const STARTER_SCENE = {
|
|
@@ -64,10 +67,47 @@ const STARTER_INPUTMAP = {
|
|
|
64
67
|
};
|
|
65
68
|
|
|
66
69
|
const PROJECT_MANIFEST = {
|
|
70
|
+
manifestVersion: 2 as const,
|
|
67
71
|
name: 'Browser Project',
|
|
68
|
-
|
|
72
|
+
version: '0.1.0',
|
|
73
|
+
engine: { version: BUNDLED_ENGINE_VERSION },
|
|
74
|
+
roots: [
|
|
75
|
+
{ id: 'world', adapter: 'threejs', scene: STARTER_SCENE_PATH },
|
|
76
|
+
{ id: 'ui', adapter: 'react', entry: STARTER_UI_PATH, zOrder: 100 },
|
|
77
|
+
],
|
|
69
78
|
};
|
|
70
79
|
|
|
80
|
+
const STARTER_UI = `export default function GameUI() {
|
|
81
|
+
return <div style={{ position: 'absolute', left: 24, top: 20, color: 'white' }}>Browser Project</div>;
|
|
82
|
+
}\n`;
|
|
83
|
+
|
|
84
|
+
async function refreshSeededManifestIdentity(backend: StorageBackend): Promise<void> {
|
|
85
|
+
if (!(await backend.exists(PROJECT_MANIFEST_PATH))) {
|
|
86
|
+
await backend.write(PROJECT_MANIFEST_PATH, JSON.stringify(PROJECT_MANIFEST, null, 2));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const current = JSON.parse(await backend.read(PROJECT_MANIFEST_PATH)) as Record<
|
|
91
|
+
string,
|
|
92
|
+
unknown
|
|
93
|
+
>;
|
|
94
|
+
const engine = current['engine'];
|
|
95
|
+
if (
|
|
96
|
+
current['name'] === PROJECT_MANIFEST.name &&
|
|
97
|
+
engine &&
|
|
98
|
+
typeof engine === 'object' &&
|
|
99
|
+
(engine as Record<string, unknown>)['version'] !== BUNDLED_ENGINE_VERSION
|
|
100
|
+
) {
|
|
101
|
+
await backend.write(
|
|
102
|
+
PROJECT_MANIFEST_PATH,
|
|
103
|
+
JSON.stringify({ ...current, engine: { version: BUNDLED_ENGINE_VERSION } }, null, 2),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// Manifest parsing reports its own actionable error when the project opens.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
71
111
|
/**
|
|
72
112
|
* Seed the Starter project into `backend` if it has not been seeded yet.
|
|
73
113
|
* Idempotent — returns immediately once a VALID default scene exists.
|
|
@@ -79,6 +119,7 @@ const PROJECT_MANIFEST = {
|
|
|
79
119
|
* bare scene on every visit.
|
|
80
120
|
*/
|
|
81
121
|
export async function seedIfEmpty(backend: StorageBackend): Promise<void> {
|
|
122
|
+
await refreshSeededManifestIdentity(backend);
|
|
82
123
|
if (await backend.exists(STARTER_SCENE_PATH)) {
|
|
83
124
|
// Validate the existing scene; only re-seed the scene file if it's broken.
|
|
84
125
|
try {
|
|
@@ -91,16 +132,35 @@ export async function seedIfEmpty(backend: StorageBackend): Promise<void> {
|
|
|
91
132
|
}
|
|
92
133
|
await backend.write(STARTER_SCENE_PATH, JSON.stringify(STARTER_SCENE, null, 2));
|
|
93
134
|
await backend.write(STARTER_INPUTMAP_PATH, JSON.stringify(STARTER_INPUTMAP, null, 2));
|
|
94
|
-
await backend.write(
|
|
135
|
+
await backend.write(STARTER_UI_PATH, STARTER_UI);
|
|
95
136
|
}
|
|
96
137
|
|
|
97
138
|
/** Read the project manifest from a backend, or a default if absent. */
|
|
98
|
-
export async function readProjectManifest(
|
|
99
|
-
|
|
100
|
-
|
|
139
|
+
export async function readProjectManifest(backend: StorageBackend): Promise<{
|
|
140
|
+
manifestVersion: 2;
|
|
141
|
+
name: string;
|
|
142
|
+
version: string;
|
|
143
|
+
engine: { version: string };
|
|
144
|
+
defaultScene: string;
|
|
145
|
+
}> {
|
|
101
146
|
try {
|
|
102
|
-
|
|
147
|
+
const manifest = loadGameManifest(JSON.parse(await backend.read(PROJECT_MANIFEST_PATH)));
|
|
148
|
+
return {
|
|
149
|
+
manifestVersion: manifest.manifestVersion,
|
|
150
|
+
name: manifest.name,
|
|
151
|
+
version: manifest.version,
|
|
152
|
+
engine: manifest.engine,
|
|
153
|
+
defaultScene:
|
|
154
|
+
manifest.roots.find((candidate) => candidate.scene !== undefined)?.scene ??
|
|
155
|
+
STARTER_SCENE_PATH,
|
|
156
|
+
};
|
|
103
157
|
} catch {
|
|
104
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
manifestVersion: PROJECT_MANIFEST.manifestVersion,
|
|
160
|
+
name: PROJECT_MANIFEST.name,
|
|
161
|
+
version: PROJECT_MANIFEST.version,
|
|
162
|
+
engine: PROJECT_MANIFEST.engine,
|
|
163
|
+
defaultScene: STARTER_SCENE_PATH,
|
|
164
|
+
};
|
|
105
165
|
}
|
|
106
166
|
}
|