@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
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* `world-session-state.ts`'s own stance — nothing here needs a
|
|
23
23
|
* project-switch reset within a single browser tab.
|
|
24
24
|
*/
|
|
25
|
-
import type { UIStory } from '
|
|
25
|
+
import type { UIStory } from './story-types';
|
|
26
26
|
|
|
27
27
|
const _overlays = new Map<string, Record<string, UIStory>>();
|
|
28
28
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
/** Portable design-time state used by React-root authoring. */
|
|
4
|
+
export interface UIStory {
|
|
5
|
+
label: string;
|
|
6
|
+
data: Record<string, unknown>;
|
|
7
|
+
props?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Optional React component catalog used by the root authoring adapter. */
|
|
11
|
+
export type UIComponentRegistry = Record<string, ComponentType<Record<string, unknown>>>;
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
AssetDropProvider,
|
|
16
16
|
AuthoringAdapter,
|
|
17
17
|
AuthoringCapabilities,
|
|
18
|
+
AuthoringProvenance,
|
|
18
19
|
ComponentsProvider,
|
|
19
20
|
EditorNode,
|
|
20
21
|
FileMapProvider,
|
|
@@ -56,7 +57,8 @@ function fieldKindToPropertyType(kind: FieldKind): PropertyDescriptor['type'] {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
// ---------------------------------------------------------------------------
|
|
59
|
-
// D8
|
|
60
|
+
// D8 compatibility activation index over project `*.vscn.json` files.
|
|
61
|
+
// Only the active document is projected into the hierarchy (D19/D20).
|
|
60
62
|
// ---------------------------------------------------------------------------
|
|
61
63
|
|
|
62
64
|
const SCENE_ROOT_PREFIX = 'sceneRoot:';
|
|
@@ -77,7 +79,9 @@ function normalizeScenePath(path: string): string {
|
|
|
77
79
|
return path.startsWith('/') ? path.slice(1) : path;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
/** Exported for `edit-mode-authoring.ts`'s `switchFocusedWorld` (A4) — the
|
|
83
|
+
* one scene-fetch path both root activation and world focus-switch share. */
|
|
84
|
+
export async function fetchSceneDoc(path: string): Promise<SceneFile> {
|
|
81
85
|
// Mirrors `initial-scene.ts`'s browser-vs-server branch: browser builds
|
|
82
86
|
// read scene files through the active `StorageBackend` (OPFS/File System
|
|
83
87
|
// Access), not `fetch()`; the server dev build serves them over HTTP.
|
|
@@ -101,6 +105,7 @@ function toNode(e: SceneEntity, parentId: string | null): EditorNode {
|
|
|
101
105
|
return {
|
|
102
106
|
id: e.id!,
|
|
103
107
|
label: e.name,
|
|
108
|
+
role: 'entity',
|
|
104
109
|
kind: kindOf(e),
|
|
105
110
|
parentId,
|
|
106
111
|
childIds: (e.children ?? []).map((c) => c.id!),
|
|
@@ -322,23 +327,35 @@ export class VgaiSceneAuthoringAdapter implements AuthoringAdapter {
|
|
|
322
327
|
persist: true,
|
|
323
328
|
};
|
|
324
329
|
|
|
330
|
+
readonly provenance: AuthoringProvenance = {
|
|
331
|
+
source: 'document',
|
|
332
|
+
label: 'scene',
|
|
333
|
+
detail: 'Authored scene document — edits save to the .vscn scene file.',
|
|
334
|
+
};
|
|
335
|
+
|
|
325
336
|
subscribe(listener: () => void): () => void {
|
|
326
337
|
return this.store.subscribe(listener);
|
|
327
338
|
}
|
|
328
339
|
|
|
329
340
|
readonly hierarchy: HierarchyProvider = {
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
341
|
+
// D19/D20 — hierarchy is the active world content, not a project-wide
|
|
342
|
+
// checklist. The active scene is a real document row; other scene files
|
|
343
|
+
// remain assets until opened. A transient/untitled store (unit fixtures,
|
|
344
|
+
// browser import before first save) honestly has no document and retains
|
|
345
|
+
// the flat entity-root shape.
|
|
346
|
+
roots: () => {
|
|
347
|
+
const path = this.activeScenePath();
|
|
348
|
+
return path ? [this.memberNode(path)!] : this.store.entities.map((e) => toNode(e, null));
|
|
349
|
+
},
|
|
337
350
|
node: (id) => {
|
|
338
351
|
const memberPath = pathForSceneRootId(id);
|
|
339
352
|
if (memberPath !== null) return this.memberNode(memberPath);
|
|
340
353
|
const hit = findInTree(this.store.entities, id);
|
|
341
|
-
|
|
354
|
+
if (!hit) return null;
|
|
355
|
+
return toNode(
|
|
356
|
+
hit.entity,
|
|
357
|
+
hit.parentId ?? (this.activeScenePath() ? sceneRootId(this.activeScenePath()!) : null),
|
|
358
|
+
);
|
|
342
359
|
},
|
|
343
360
|
object3D: (id) => this.store.objectMap.get(id) ?? null,
|
|
344
361
|
idForObject3D: (o) => (getUserData(o, 'entityId') as string | undefined) ?? null,
|
|
@@ -351,36 +368,48 @@ export class VgaiSceneAuthoringAdapter implements AuthoringAdapter {
|
|
|
351
368
|
return getCachedProjectScenes();
|
|
352
369
|
}
|
|
353
370
|
|
|
354
|
-
private
|
|
355
|
-
return this.
|
|
356
|
-
|
|
357
|
-
|
|
371
|
+
private activeScenePath(): string | null {
|
|
372
|
+
return this.store.savePath ? normalizeScenePath(this.store.savePath) : null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
private sceneLabel(path: string): string {
|
|
376
|
+
const file = path.split('/').pop() ?? path;
|
|
377
|
+
const stem = file.replace(/\.vscn\.json$/i, '').replace(/\.json$/i, '');
|
|
378
|
+
const words = stem
|
|
379
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
380
|
+
.replace(/[-_]+/g, ' ')
|
|
381
|
+
.trim();
|
|
382
|
+
return words ? words.replace(/\b\w/g, (letter) => letter.toUpperCase()) : file;
|
|
358
383
|
}
|
|
359
384
|
|
|
360
385
|
/** `null` for a path that isn't (or is no longer) a known project scene —
|
|
361
386
|
* a stale/dangling `sceneRoot:` id resolves to nothing rather than a
|
|
362
387
|
* fabricated node. */
|
|
363
388
|
private memberNode(path: string): EditorNode | null {
|
|
364
|
-
|
|
389
|
+
const activePath = this.activeScenePath();
|
|
390
|
+
if (!this.scenesList().includes(path) && activePath !== path) return null;
|
|
365
391
|
const isActive =
|
|
366
392
|
this.store.savePath !== null && normalizeScenePath(this.store.savePath) === path;
|
|
367
393
|
return {
|
|
368
394
|
id: sceneRootId(path),
|
|
369
|
-
label: path,
|
|
370
|
-
|
|
395
|
+
label: this.sceneLabel(path),
|
|
396
|
+
secondaryLabel: path,
|
|
397
|
+
role: 'document',
|
|
398
|
+
kind: 'scene',
|
|
371
399
|
parentId: null,
|
|
372
|
-
childIds: [],
|
|
373
|
-
// D8 — member of
|
|
374
|
-
// `
|
|
375
|
-
//
|
|
376
|
-
// the ● / ○ radio from `rootGroups` data, not from this flag alone).
|
|
400
|
+
childIds: isActive ? this.store.entities.map((entity) => entity.id!) : [],
|
|
401
|
+
// D8 — member of the compatibility activation index, not its active
|
|
402
|
+
// document (see `authoring.ts`'s `inactiveRoot` contract). Inactive
|
|
403
|
+
// members are resolvable for file opening but are not hierarchy roots.
|
|
377
404
|
flags: isActive ? {} : { inactiveRoot: true },
|
|
378
405
|
};
|
|
379
406
|
}
|
|
380
407
|
|
|
381
408
|
/**
|
|
382
|
-
*
|
|
383
|
-
*
|
|
409
|
+
* Activation index over project `*.vscn.json` assets. Only its active member
|
|
410
|
+
* is projected as the hierarchy's document row; inactive members stay in
|
|
411
|
+
* this file-opening index instead of becoming a scene checklist. `activate()`
|
|
412
|
+
* flushes a pending autosave first (belt: the
|
|
384
413
|
* 300ms debounce could otherwise lose an edit made just before switching),
|
|
385
414
|
* then loads the target scene into the live store exactly like opening it
|
|
386
415
|
* from the asset browser (`DefaultEditorLayout.tsx`'s `openSceneFromAsset`
|
|
@@ -676,7 +705,7 @@ export class VgaiSceneAuthoringAdapter implements AuthoringAdapter {
|
|
|
676
705
|
this.store.moveEntity([id], parentId, index);
|
|
677
706
|
},
|
|
678
707
|
// A2 parity growth — the former toolbar-only `addMenuItems` list (12
|
|
679
|
-
// kinds), now driving the shell's generic Create
|
|
708
|
+
// kinds), now driving the shell's generic Create submenu for ANY
|
|
680
709
|
// parent (root via the toolbar `+`, or a specific row via the context
|
|
681
710
|
// menu) — a capability the old toolbar-only Add menu never had.
|
|
682
711
|
creatableKinds: () =>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { AuthoringAdapter } from '@engine/adapter';
|
|
2
|
+
import type { EditorStore } from '../editor-store';
|
|
3
|
+
import { CompositeAuthoringAdapter } from './composite-authoring-adapter';
|
|
4
|
+
import { resolveFocusedViewportWorldId } from './world-hidden-viewport';
|
|
5
|
+
import { isWorldHidden } from './world-session-state';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The one native root whose viewport tools may claim the current selection.
|
|
9
|
+
*
|
|
10
|
+
* Viewport content can contain several roots at once, but editing chrome needs
|
|
11
|
+
* one unambiguous coordinate system and capability owner. Empty,
|
|
12
|
+
* organizational, and cross-root selections therefore resolve to `null`.
|
|
13
|
+
*/
|
|
14
|
+
export interface ViewportToolContext {
|
|
15
|
+
readonly worldId: string | null;
|
|
16
|
+
readonly kind: string;
|
|
17
|
+
readonly adapter: AuthoringAdapter;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function compositeContext(
|
|
21
|
+
composite: CompositeAuthoringAdapter,
|
|
22
|
+
selectedIds: readonly string[],
|
|
23
|
+
): ViewportToolContext | null {
|
|
24
|
+
const children = composite.childAdapters();
|
|
25
|
+
let owner: (typeof children)[number] | null = null;
|
|
26
|
+
|
|
27
|
+
for (const id of selectedIds) {
|
|
28
|
+
const child =
|
|
29
|
+
children.find((candidate) => composite.groupNodeId(candidate.worldId) === id) ??
|
|
30
|
+
children.find((candidate) => composite.ownerOf(id) === candidate.worldId);
|
|
31
|
+
// Kind/projection folders and stale ids do not claim native-world tools.
|
|
32
|
+
if (!child) return null;
|
|
33
|
+
// A heterogeneous selection has no single native editing context.
|
|
34
|
+
if (owner && owner.worldId !== child.worldId) return null;
|
|
35
|
+
owner = child;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return owner ? { worldId: owner.worldId, kind: owner.kind, adapter: owner.adapter } : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve viewport-tool ownership from selection, never from which roots
|
|
43
|
+
* merely exist or happen to be visible.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveViewportToolContext(
|
|
46
|
+
adapter: AuthoringAdapter,
|
|
47
|
+
selectedIds: Iterable<string>,
|
|
48
|
+
): ViewportToolContext | null {
|
|
49
|
+
const ids = [...selectedIds];
|
|
50
|
+
if (ids.length === 0) return null;
|
|
51
|
+
if (adapter instanceof CompositeAuthoringAdapter) return compositeContext(adapter, ids);
|
|
52
|
+
|
|
53
|
+
// Legacy/single-adapter sessions have no manifest kind metadata. Keep the
|
|
54
|
+
// fallback structural and conservative: a rect-capable selection is DOM;
|
|
55
|
+
// a selection whose every node resolves to Object3D is Three.js. Unknown
|
|
56
|
+
// adapters advertise no kind-specific chrome rather than guessing.
|
|
57
|
+
if (ids.some((id) => adapter.hierarchy.node(id) === null)) return null;
|
|
58
|
+
if (adapter.rects) return { worldId: null, kind: 'react', adapter };
|
|
59
|
+
if (ids.every((id) => adapter.hierarchy.object3D(id) !== null)) {
|
|
60
|
+
return { worldId: null, kind: 'threejs', adapter };
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether the selected owner is actually painted in this viewport. DOM
|
|
67
|
+
* layers follow their world eye; among several Three.js roots only the one
|
|
68
|
+
* focused into the editor's single live Three.js scene can own its cockpit.
|
|
69
|
+
*/
|
|
70
|
+
export function isViewportToolContextVisible(
|
|
71
|
+
store: EditorStore,
|
|
72
|
+
context: ViewportToolContext | null,
|
|
73
|
+
): boolean {
|
|
74
|
+
if (!context) return false;
|
|
75
|
+
if (context.worldId !== null && isWorldHidden(context.worldId)) return false;
|
|
76
|
+
if (context.kind !== 'threejs' || context.worldId === null) return true;
|
|
77
|
+
return resolveFocusedViewportWorldId(store) === context.worldId;
|
|
78
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Session-only responsive canvas size for DOM-backed authoring roots. */
|
|
2
|
+
export interface WorldCanvasViewport {
|
|
3
|
+
readonly width: number | null;
|
|
4
|
+
readonly height: number | null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const FILL_VIEWPORT: WorldCanvasViewport = { width: null, height: null };
|
|
8
|
+
let viewport: WorldCanvasViewport = FILL_VIEWPORT;
|
|
9
|
+
const listeners = new Set<() => void>();
|
|
10
|
+
|
|
11
|
+
export function getWorldCanvasViewport(): WorldCanvasViewport {
|
|
12
|
+
return viewport;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function setWorldCanvasViewport(width: number | null, height: number | null): void {
|
|
16
|
+
const nextWidth = width === null ? null : Math.max(1, Math.round(width));
|
|
17
|
+
const nextHeight = height === null ? null : Math.max(1, Math.round(height));
|
|
18
|
+
if (viewport.width === nextWidth && viewport.height === nextHeight) return;
|
|
19
|
+
viewport = { width: nextWidth, height: nextHeight };
|
|
20
|
+
for (const listener of listeners) listener();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resetWorldCanvasViewport(): void {
|
|
24
|
+
setWorldCanvasViewport(null, null);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function subscribeWorldCanvasViewport(listener: () => void): () => void {
|
|
28
|
+
listeners.add(listener);
|
|
29
|
+
return () => listeners.delete(listener);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function _resetWorldCanvasViewportForTest(): void {
|
|
33
|
+
viewport = FILL_VIEWPORT;
|
|
34
|
+
listeners.clear();
|
|
35
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* World-hidden eye (D9, `world-session-state.ts`) applied to the EDIT-mode
|
|
3
|
+
* threejs viewport.
|
|
4
|
+
*
|
|
5
|
+
* Bug this fixes: the eye on a hierarchy world-group row (`world:<id>
|
|
6
|
+
* (<kind>)`, shown for 2+ world manifests) toggles `isWorldHidden(worldId)`
|
|
7
|
+
* and culls the hierarchy subtree, but for a react/pixi world that's ALSO the
|
|
8
|
+
* only thing that mattered — `design-time-layers.ts`'s `applySessionStyle`
|
|
9
|
+
* sets `display:none` on that world's DOM layer. A threejs world has no DOM
|
|
10
|
+
* layer; nothing consumed `isWorldHidden` for its actual `THREE.Object3D`s
|
|
11
|
+
* before this module existed, so the eye was a no-op in the viewport.
|
|
12
|
+
*
|
|
13
|
+
* Session-local only (never touches entity descriptors/undo/the `.vscn`
|
|
14
|
+
* file, matching the DOM roots' own `display:none` semantics): every call
|
|
15
|
+
* RECOMPUTES `obj.visible` from two already-authoritative, already-live
|
|
16
|
+
* sources —
|
|
17
|
+
* - the entity's OWN visible flag, read back off `userData['entity']`
|
|
18
|
+
* (the descriptor `scene-sync.ts` stamps onto every object it builds —
|
|
19
|
+
* see ARCHITECTURE.md's "the world node IS the entity"), which is the
|
|
20
|
+
* SAME flag the generic inspector's eye writes via
|
|
21
|
+
* `VgaiSceneAuthoringAdapter.inspector.set(id, 'visible', …)`;
|
|
22
|
+
* - `!isWorldHidden(focusedWorldId)`.
|
|
23
|
+
* ANDing per call (never "remembering" a previous visibility) is what makes
|
|
24
|
+
* this rebuild-proof: a full or single-entity `scene-sync.ts` rebuild
|
|
25
|
+
* re-stamps `obj.visible = entity.visible !== false` from the descriptor,
|
|
26
|
+
* silently dropping any one-shot override — but the very next call here puts
|
|
27
|
+
* the world-hidden override back, and it never clobbers a per-entity
|
|
28
|
+
* `visible:false` because that flag is read fresh from the descriptor each
|
|
29
|
+
* time rather than assumed. Callers must re-invoke this after every rebuild
|
|
30
|
+
* AND after the toggle itself; `editor-viewport.ts`'s `syncFromStore()` is
|
|
31
|
+
* exactly that seam — it already re-runs on every `store.subscribe` notify,
|
|
32
|
+
* which covers both a scene-sync rebuild (which notifies) and
|
|
33
|
+
* `GameHierarchy.tsx`'s toggle handler (`toggleWorldHidden` +
|
|
34
|
+
* `store.notifyIngestEdit()`).
|
|
35
|
+
*
|
|
36
|
+
* Multi-threejs-world limitation (by design, not an oversight): a composite's
|
|
37
|
+
* `store.objectMap`/live THREE.Scene only ever holds ONE threejs world's real
|
|
38
|
+
* objects — the "focused" one resolved by
|
|
39
|
+
* `edit-mode-authoring.ts`'s `resolveFocusedThreejsWorldId` at composite
|
|
40
|
+
* install time (whichever world's scene `loadInitialScene` loaded into the
|
|
41
|
+
* store). Every OTHER threejs-kind world in the manifest gets a read-only
|
|
42
|
+
* `BoundaryAuthoringAdapter` with NO live geometry in this viewport at all.
|
|
43
|
+
* A4's `switchFocusedWorld` (landed — `edit-mode-authoring.ts`) changes WHICH
|
|
44
|
+
* world is the focused one (document + `?scene=` + rebuild together), but at
|
|
45
|
+
* any instant there is still exactly ONE: `resolveFocusedViewportWorldId`
|
|
46
|
+
* below only ever needs to name ONE worldId, not walk per-object ownership —
|
|
47
|
+
* true CONCURRENT multi-threejs-world rendering remains out of scope.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
import { getUserData } from '@engine/scene/user-data';
|
|
51
|
+
import * as THREE from 'three';
|
|
52
|
+
import type { EditorStore } from '../editor-store';
|
|
53
|
+
import { getActiveAuthoring } from './active-adapter';
|
|
54
|
+
import { CompositeAuthoringAdapter } from './composite-authoring-adapter';
|
|
55
|
+
import { isWorldHidden } from './world-session-state';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The worldId of the ONE threejs world actually rendered in this viewport
|
|
59
|
+
* (`store.objectMap`'s contents), or `null` when the active adapter isn't a
|
|
60
|
+
* composite (single-world project — no world-group row/eye exists for it at
|
|
61
|
+
* all, so there is nothing to resolve) or declares no threejs world.
|
|
62
|
+
*
|
|
63
|
+
* Distinguishes the focused, LIVE `VgaiSceneAuthoringAdapter` from every
|
|
64
|
+
* other (possibly also `kind: 'threejs'`) child's read-only
|
|
65
|
+
* `BoundaryAuthoringAdapter` by `capabilities.transform` — the Boundary
|
|
66
|
+
* adapter's capabilities are all `false` (see `boundary-authoring-adapter.ts`),
|
|
67
|
+
* while `VgaiSceneAuthoringAdapter` is the only child that ever reports
|
|
68
|
+
* `transform: true`. This reuses an existing, already-load-bearing signal
|
|
69
|
+
* instead of adding a new `instanceof` check or a parallel "which world is
|
|
70
|
+
* focused" bookkeeping slot.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveFocusedViewportWorldId(store: EditorStore): string | null {
|
|
73
|
+
const active = getActiveAuthoring(store);
|
|
74
|
+
if (!(active instanceof CompositeAuthoringAdapter)) return null;
|
|
75
|
+
const focused = active
|
|
76
|
+
.childAdapters()
|
|
77
|
+
.find((c) => c.kind === 'threejs' && c.adapter.capabilities.transform);
|
|
78
|
+
return focused?.worldId ?? null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Apply the eye toggle to every object in `objectMap` (see module doc
|
|
83
|
+
* comment for the full rebuild-proofing rationale). `focusedWorldId` is
|
|
84
|
+
* accepted as a parameter (rather than re-resolved internally) so a caller
|
|
85
|
+
* that already computed it once per call (e.g. for a cheap re-apply
|
|
86
|
+
* signature) doesn't pay for `resolveFocusedViewportWorldId`'s composite walk
|
|
87
|
+
* twice.
|
|
88
|
+
*/
|
|
89
|
+
export function applyWorldHiddenVisibility(
|
|
90
|
+
objectMap: ReadonlyMap<string, THREE.Object3D>,
|
|
91
|
+
focusedWorldId: string | null,
|
|
92
|
+
): void {
|
|
93
|
+
const hidden = focusedWorldId !== null && isWorldHidden(focusedWorldId);
|
|
94
|
+
for (const obj of objectMap.values()) {
|
|
95
|
+
// Structural read of the ONE descriptor field this module consumes —
|
|
96
|
+
// deliberately not the SceneEntity type (the authoring-inversion ratchet:
|
|
97
|
+
// only the first-party adapter allowlist may import it).
|
|
98
|
+
const entity = getUserData(obj, 'entity') as { visible?: boolean } | undefined;
|
|
99
|
+
// Defensive fallback for the (should-never-happen) case of an objectMap
|
|
100
|
+
// entry with no stamped descriptor: leave its current visibility alone
|
|
101
|
+
// rather than guessing, UNLESS the world itself is hidden (the eye still
|
|
102
|
+
// wins — nothing in a hidden world should render).
|
|
103
|
+
const ownVisible = entity ? entity.visible !== false : obj.visible;
|
|
104
|
+
obj.visible = hidden ? false : ownVisible;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Matches `scene-sync.ts`'s `applyEnvironment` DEFAULT_BG — what an editor
|
|
110
|
+
* viewport with NO authored environment shows. A single shared instance so
|
|
111
|
+
* the per-notify re-apply below never allocates.
|
|
112
|
+
*/
|
|
113
|
+
const HIDDEN_WORLD_BACKGROUND = new THREE.Color('#aaaaaa');
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The scene-level half of the eye: `applyWorldHiddenVisibility` above only
|
|
117
|
+
* covers entity `Object3D`s (the `objectMap`), but a threejs world also
|
|
118
|
+
* renders things that live directly on the `THREE.Scene` — the authored
|
|
119
|
+
* `background` color/skybox, `fog`, IBL `environment`, and the
|
|
120
|
+
* `envObject`-tagged ambient light `applyEnvironment` adds. None of those are
|
|
121
|
+
* entities, so before this function existed, hiding the world left the
|
|
122
|
+
* viewport painted in the world's background color (looking exactly like
|
|
123
|
+
* "the eye didn't work").
|
|
124
|
+
*
|
|
125
|
+
* Same recompute-don't-remember contract as above: this only ever writes the
|
|
126
|
+
* SUPPRESSED state, idempotently, and callers invoke it on every notify while
|
|
127
|
+
* the world is hidden (an inspector environment edit mid-hide re-paints the
|
|
128
|
+
* authored values via `_applyEnvironment` — the very next notify re-suppresses
|
|
129
|
+
* them here). The restore path is `EditorStore.reapplyEnvironment()`, whose
|
|
130
|
+
* truth is `_sceneMeta.environment` — never a snapshot taken here.
|
|
131
|
+
*
|
|
132
|
+
* Post-processing (bloom/vignette) is the composer's half: `ViewportPanel`'s
|
|
133
|
+
* `rebuildComposer` passes `undefined` instead of `store.environment` while
|
|
134
|
+
* the world is hidden; `editor-viewport.ts` bumps `composerVersion` (via
|
|
135
|
+
* `reapplyEnvironment`) on both hide/show edges so that rebuild actually runs.
|
|
136
|
+
*/
|
|
137
|
+
export function suppressWorldEnvironment(scene: THREE.Scene): void {
|
|
138
|
+
scene.background = HIDDEN_WORLD_BACKGROUND;
|
|
139
|
+
scene.fog = null;
|
|
140
|
+
scene.environment = null;
|
|
141
|
+
scene.traverse((obj) => {
|
|
142
|
+
if (getUserData(obj, 'envObject')) obj.visible = false;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Is a threejs surface currently SHOWING in the viewport? This is the single
|
|
148
|
+
* derived truth the 3D cockpit chrome gates on (spec 28,
|
|
149
|
+
* `docs/unified-world-editor/28-conditional-viewport-chrome.md`): the grid,
|
|
150
|
+
* orientation gizmo, transform toolstrip, camera readout, shading modes, and
|
|
151
|
+
* the transform gizmo itself are claims about what the user can do RIGHT NOW
|
|
152
|
+
* — showing them over a viewport with no visible 3D content is a false claim
|
|
153
|
+
* (same spirit as the adapter tier rule: degrade loudly, never advertise
|
|
154
|
+
* what isn't there). The DOM half of the chrome (`WorldSelectionOverlay`)
|
|
155
|
+
* already self-gates this way; this selector brings the pre-contract 3D
|
|
156
|
+
* chrome under the same rule.
|
|
157
|
+
*
|
|
158
|
+
* Truth table (deliberately conservative — every non-composite mode keeps
|
|
159
|
+
* today's behavior exactly):
|
|
160
|
+
* - active adapter is not a composite (single-world project, classic scene
|
|
161
|
+
* editing, ingest): `true` — no world-group eyes exist there, nothing to
|
|
162
|
+
* derive from, status quo.
|
|
163
|
+
* - composite with NO threejs-kind child at all (e.g. react-only
|
|
164
|
+
* multi-world manifest): `false` — no 3D content can EVER show.
|
|
165
|
+
* - composite whose only threejs children are unfocused read-only
|
|
166
|
+
* Boundaries (`resolveFocusedViewportWorldId` → null): `true` — such a
|
|
167
|
+
* world has no live geometry here (even with A4's `switchFocusedWorld`
|
|
168
|
+
* landed, exactly one threejs world is live at a time and this state is
|
|
169
|
+
* reachable transiently around a rebuild), but it is manifest-declared 3D
|
|
170
|
+
* content; keep the cockpit rather than flickering chrome on
|
|
171
|
+
* adapter-install timing.
|
|
172
|
+
* - composite with a focused live threejs world: `!isWorldHidden(worldId)`
|
|
173
|
+
* — the eye is the "what we're showing" signal.
|
|
174
|
+
*
|
|
175
|
+
* Recompute-per-call like everything else in this module: callers read it on
|
|
176
|
+
* every store notify (`syncFromStore` / `ViewportPanel` render), never cache
|
|
177
|
+
* it across notifies.
|
|
178
|
+
*/
|
|
179
|
+
export function isThreejsSurfaceVisible(store: EditorStore): boolean {
|
|
180
|
+
const active = getActiveAuthoring(store);
|
|
181
|
+
if (!(active instanceof CompositeAuthoringAdapter)) return true;
|
|
182
|
+
const children = active.childAdapters();
|
|
183
|
+
if (!children.some((c) => c.kind === 'threejs')) return false;
|
|
184
|
+
const focusedId = resolveFocusedViewportWorldId(store);
|
|
185
|
+
return focusedId === null ? true : !isWorldHidden(focusedId);
|
|
186
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Shared DOM-world canvas transform (spec27 §8 "space-pan" row, D4 rebuild —
|
|
3
3
|
* docs/unified-world-editor/27-visual-react-editing.md) — a module-level
|
|
4
4
|
* subscribable singleton (mirrors `eyedropper-session.ts`'s "bare singleton +
|
|
5
|
-
* own listener set" shape) holding the CURRENT SHARED pan
|
|
5
|
+
* own listener set" shape) holding the CURRENT SHARED pan + zoom
|
|
6
6
|
* applied, in LOCKSTEP, to both every design-time world-layer host
|
|
7
7
|
* (`design-time-layers.ts`'s `createLayerElement`-produced react/pixi layers)
|
|
8
8
|
* and `WorldSelectionOverlay`'s own outer wrapper.
|
|
@@ -18,17 +18,19 @@
|
|
|
18
18
|
* IDENTICAL `(x, y)` delta — a rigid subtree translation — so the
|
|
19
19
|
* subtraction is invariant under pan: `rect(id)` returns the SAME
|
|
20
20
|
* host-relative numbers at any pan offset, unmodified, with no pan-awareness
|
|
21
|
-
* needed in
|
|
22
|
-
*
|
|
21
|
+
* needed in `inspect.ts`'s geometry math or the overlay chrome consumers.
|
|
22
|
+
* Zoom scales both measured rectangles; `toHostRelative` divides their
|
|
23
|
+
* offsets and dimensions by the shared zoom before exposing authored-space
|
|
24
|
+
* geometry, while `WorldSelectionOverlay.toHostLocal` performs the inverse
|
|
25
|
+
* for pointer gestures. `PickProvider.pick` needs no changes — it
|
|
23
26
|
* hit-tests real rendered rects against the real client cursor position,
|
|
24
27
|
* both of which already reflect the applied transform natively.
|
|
25
28
|
*
|
|
26
29
|
* Applying the exact SAME transform to `WorldSelectionOverlay`'s own outer
|
|
27
30
|
* wrapper (a SIBLING of every world-layer host inside the SAME
|
|
28
31
|
* `ViewportPanel` containerRef parent — see that file's own mount-site doc
|
|
29
|
-
* comment) shifts the overlay
|
|
30
|
-
*
|
|
31
|
-
* host-relative `(x, y)` inside the panned overlay wrapper lands it at the
|
|
32
|
+
* comment) shifts and scales the overlay by the identical transform — so
|
|
33
|
+
* drawing a chrome element at `rect(id)`'s authored coordinates lands it at the
|
|
32
34
|
* SAME screen position as the panned live element it describes. This is the
|
|
33
35
|
* entire "lockstep" contract: every consumer on both sides of the pan reads
|
|
34
36
|
* ONE shared value (`getWorldPan()`) and applies it through the ONE shared
|
|
@@ -39,9 +41,10 @@
|
|
|
39
41
|
export interface WorldPan {
|
|
40
42
|
readonly x: number;
|
|
41
43
|
readonly y: number;
|
|
44
|
+
readonly zoom: number;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
const ZERO_PAN: WorldPan = { x: 0, y: 0 };
|
|
47
|
+
const ZERO_PAN: WorldPan = { x: 0, y: 0, zoom: 1 };
|
|
45
48
|
|
|
46
49
|
let _pan: WorldPan = ZERO_PAN;
|
|
47
50
|
const listeners = new Set<() => void>();
|
|
@@ -50,16 +53,27 @@ function notify(): void {
|
|
|
50
53
|
for (const l of listeners) l();
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
/** The current shared
|
|
56
|
+
/** The current shared canvas transform; x/y are CSS px and zoom is unitless. */
|
|
54
57
|
export function getWorldPan(): WorldPan {
|
|
55
58
|
return _pan;
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
/** Set the complete shared canvas transform in one subscriber notification. */
|
|
62
|
+
export function setWorldView(x: number, y: number, zoom: number): void {
|
|
63
|
+
const nextZoom = Math.min(4, Math.max(0.1, zoom));
|
|
64
|
+
if (_pan.x === x && _pan.y === y && _pan.zoom === nextZoom) return;
|
|
65
|
+
_pan = { x, y, zoom: nextZoom };
|
|
66
|
+
notify();
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
/** Set the shared pan translate to an absolute `(x, y)`. */
|
|
59
70
|
export function setWorldPan(x: number, y: number): void {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
setWorldView(x, y, _pan.zoom);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Set the shared canvas zoom while preserving its current pan. */
|
|
75
|
+
export function setWorldZoom(zoom: number): void {
|
|
76
|
+
setWorldView(_pan.x, _pan.y, zoom);
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
/** Set the shared pan translate by a RELATIVE `(dx, dy)` — a convenience
|
|
@@ -71,19 +85,19 @@ export function panWorldBy(dx: number, dy: number): void {
|
|
|
71
85
|
setWorldPan(_pan.x + dx, _pan.y + dy);
|
|
72
86
|
}
|
|
73
87
|
|
|
74
|
-
/** Reset pan to `(0, 0)` — called on a world-layer remount (`design-time-
|
|
88
|
+
/** Reset pan to `(0, 0)` and zoom to 100% — called on a world-layer remount (`design-time-
|
|
75
89
|
* layers.ts`'s `mountDesignTimeLayers`, invoked on initial edit-mode install
|
|
76
90
|
* AND every `'editor:edit-mode-rebuild'`), so switching/reloading the active
|
|
77
91
|
* world set never leaves a stale pan offset applied over a DIFFERENT layer
|
|
78
92
|
* stack. A no-op (no `notify()`) when already at rest, so a fresh session
|
|
79
93
|
* never fires a spurious subscriber tick. */
|
|
80
94
|
export function resetWorldPan(): void {
|
|
81
|
-
if (_pan.x === 0 && _pan.y === 0) return;
|
|
95
|
+
if (_pan.x === 0 && _pan.y === 0 && _pan.zoom === 1) return;
|
|
82
96
|
_pan = ZERO_PAN;
|
|
83
97
|
notify();
|
|
84
98
|
}
|
|
85
99
|
|
|
86
|
-
/** Subscribe to
|
|
100
|
+
/** Subscribe to canvas-transform changes — both `design-time-layers.ts` (imperative DOM
|
|
87
101
|
* style writes) and `WorldSelectionOverlay` (`useSyncExternalStore`) bind
|
|
88
102
|
* this so every lockstep mount point re-applies the SAME new value the
|
|
89
103
|
* instant it changes. */
|
|
@@ -102,8 +116,10 @@ export function subscribeWorldPan(listener: () => void): () => void {
|
|
|
102
116
|
* to its pre-this-feature output.
|
|
103
117
|
*/
|
|
104
118
|
export function panTransformValue(pan: WorldPan): string | undefined {
|
|
105
|
-
if (pan.x === 0 && pan.y === 0) return undefined;
|
|
106
|
-
return `translate(${pan.x}px, ${pan.y}px)`;
|
|
119
|
+
if (pan.x === 0 && pan.y === 0 && pan.zoom === 1) return undefined;
|
|
120
|
+
if (pan.zoom === 1) return `translate(${pan.x}px, ${pan.y}px)`;
|
|
121
|
+
if (pan.x === 0 && pan.y === 0) return `scale(${pan.zoom})`;
|
|
122
|
+
return `translate(${pan.x}px, ${pan.y}px) scale(${pan.zoom})`;
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
/** Test-only reset (mirrors `eyedropper-session.ts`'s/`world-session-
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* B1 only stores/exposes the toggle; nothing reads it yet.
|
|
22
22
|
*
|
|
23
23
|
* Keyed by the RAW manifest world id (`CompositeChild.worldId` /
|
|
24
|
-
* `
|
|
24
|
+
* `ResolvedAdapterRoot.id`) — the SAME id `design-time-layers.ts` gets from
|
|
25
25
|
* `composite.childAdapters()`, NOT the composite's synthetic `world:<id>`
|
|
26
26
|
* group-node id. `GameHierarchy.tsx` (whose rows ARE keyed by the group-node
|
|
27
27
|
* id) resolves the raw worldId via `compositeGroupBadge`'s `worldId` field
|