@vgai/editor 0.2.0 → 0.4.0-canary.20260715.1
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-CyiTVRi6.js +30 -0
- package/dist/assets/browserAll-fGZ_P1P8.js +1 -0
- package/dist/assets/create-runtime-BzSHBTYw.js +1 -0
- package/dist/assets/game-DQ7YHNS8.js +1 -0
- package/dist/assets/game-DX6Obomf.js +1 -0
- package/dist/assets/game-DoUJj8vY.js +1 -0
- package/dist/assets/{game-xsOho_wg.js → game-HqybI_X7.js} +3 -3
- package/dist/assets/game-NUAnyPkS.js +1 -0
- package/dist/assets/game-ck_dNIE6.js +1 -0
- package/dist/assets/{game-pt_1lQLv.js → game-hIf5LD4E.js} +1 -1
- package/dist/assets/game-loop-o2CcBMFY.js +1 -0
- package/dist/assets/{game-source-CvidN7AB.js → game-source-C8HM7gbP.js} +1 -1
- package/dist/assets/{game-BI111o5f.js → game-uR_lv3Mp.js} +3 -3
- package/dist/assets/index-B-HA1uu3.js +15412 -0
- package/dist/assets/{index-Bp5cecD6.js → index-CSBAZwMx.js} +2 -2
- package/dist/assets/{index-DL7RcQ0n.js → index-CoSRCKPS.js} +1 -1
- package/dist/assets/{index-Cs8xwv3e.js → index-D93bBuUl.js} +1 -1
- package/dist/assets/index-bu01fqch.css +1 -0
- package/dist/assets/ingest-siblings-CCkkE2h7.js +1 -0
- package/dist/assets/mount-manifest-B2ECssU2.js +1 -0
- package/dist/assets/react-18-Bmj05S8J.js +1 -0
- package/dist/assets/webworkerAll-BC6gMOLZ.js +1 -0
- package/dist/assets/world-documents-DXPcAEA0.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 +7718 -3436
- 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 +71 -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 +1236 -296
- 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 +1116 -344
- 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 +620 -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/doctor/report.ts
CHANGED
|
@@ -36,7 +36,7 @@ import type { DataDoctorFinding } from './data-checks';
|
|
|
36
36
|
/** One declared world from the manifest — enough identity to label a verdict row. */
|
|
37
37
|
export interface DoctorDeclaredWorld {
|
|
38
38
|
readonly worldId: string;
|
|
39
|
-
/** `
|
|
39
|
+
/** `ResolvedAdapterRoot.kind` (e.g. `'threejs'`/`'pixijs'`/`'react'`). */
|
|
40
40
|
readonly kind: string;
|
|
41
41
|
/** `ResolvedAdapter['identity']` (e.g. `'ingest-three'`/`'module'`/`'default-three'`). */
|
|
42
42
|
readonly identity: string;
|
|
@@ -108,6 +108,17 @@ export interface DoctorSiblingEvidence {
|
|
|
108
108
|
readonly authoringPersist: boolean;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Evidence from the editor's real Play composer. The world id is published
|
|
113
|
+
* only after `mountManifestWorlds` has returned and play-mode has installed
|
|
114
|
+
* the composite authoring surface, so presence proves both runtime mount and
|
|
115
|
+
* editor adoption without inventing kind-specific capture data.
|
|
116
|
+
*/
|
|
117
|
+
export interface DoctorRuntimeEvidence {
|
|
118
|
+
readonly runtimeMounted: true;
|
|
119
|
+
readonly authoringPresent: true;
|
|
120
|
+
}
|
|
121
|
+
|
|
111
122
|
/**
|
|
112
123
|
* Every raw datum the browser-phase runner scraped off the live page —
|
|
113
124
|
* data-in for {@link assembleDoctorReport}. Maps/sets are keyed by worldId so
|
|
@@ -128,6 +139,7 @@ export interface DoctorRawSignals {
|
|
|
128
139
|
* byte-stable — an absent map reads exactly like an empty one.
|
|
129
140
|
*/
|
|
130
141
|
readonly siblingEvidence?: ReadonlyMap<string, DoctorSiblingEvidence>;
|
|
142
|
+
readonly runtimeEvidence?: ReadonlyMap<string, DoctorRuntimeEvidence>;
|
|
131
143
|
/**
|
|
132
144
|
* Per-worldId react-ingest DOM evidence (D-L2, wave 10 Slice B,
|
|
133
145
|
* docs/WAVE10-REACT-INGEST-DOM-AUTHORING-DESIGN.md) — checked in
|
|
@@ -150,6 +162,7 @@ export interface DoctorMountedVerdict extends DoctorVerdictBase {
|
|
|
150
162
|
| DoctorIngestEvidence
|
|
151
163
|
| DoctorModuleEvidence
|
|
152
164
|
| DoctorSiblingEvidence
|
|
165
|
+
| DoctorRuntimeEvidence
|
|
153
166
|
| DoctorReactIngestEvidence;
|
|
154
167
|
}
|
|
155
168
|
|
|
@@ -165,7 +178,7 @@ export interface DoctorUnverifiableVerdict extends DoctorVerdictBase {
|
|
|
165
178
|
|
|
166
179
|
export type DoctorVerdict = DoctorMountedVerdict | DoctorFailedVerdict | DoctorUnverifiableVerdict;
|
|
167
180
|
|
|
168
|
-
/** True iff an ingest-capture evidence bundle proves a real, live, editable mount (D-A4's MOUNTED bar for ingest
|
|
181
|
+
/** True iff an ingest-capture evidence bundle proves a real, live, editable mount (D-A4's MOUNTED bar for ingest roots). */
|
|
169
182
|
function clearsIngestMountedBar(ev: DoctorIngestEvidence): boolean {
|
|
170
183
|
const reflectedOk = ev.reflectedCount !== null && ev.reflectedCount > 0;
|
|
171
184
|
const drawOk = ev.drawCountClimbed === true;
|
|
@@ -183,7 +196,7 @@ function ingestShortfallReason(worldId: string, ev: DoctorIngestEvidence): strin
|
|
|
183
196
|
);
|
|
184
197
|
}
|
|
185
198
|
|
|
186
|
-
/** True iff react-ingest DOM evidence proves a real, live, editable mount (D-L3's MOUNTED bar for react-ingest
|
|
199
|
+
/** True iff react-ingest DOM evidence proves a real, live, editable mount (D-L3's MOUNTED bar for react-ingest roots) — ALL four legs, never partial credit. */
|
|
187
200
|
function clearsReactIngestMountedBar(ev: DoctorReactIngestEvidence): boolean {
|
|
188
201
|
return (
|
|
189
202
|
ev.elementCount > 0 && ev.hierarchyNodeCount > 0 && ev.reactRootPresent && ev.setPausedPresent
|
|
@@ -292,6 +305,11 @@ export function assembleDoctorReport(
|
|
|
292
305
|
};
|
|
293
306
|
}
|
|
294
307
|
|
|
308
|
+
const runtimeEv = raw.runtimeEvidence?.get(worldId);
|
|
309
|
+
if (runtimeEv) {
|
|
310
|
+
return { worldId, kind, identity, verdict: 'MOUNTED', evidence: runtimeEv };
|
|
311
|
+
}
|
|
312
|
+
|
|
295
313
|
// D-H2 (docs/WAVE9-SIBLING-PERSISTENCE-DOCTOR-DESIGN.md Slice B): checked
|
|
296
314
|
// LAST among the "some evidence exists" branches, after ingest/module
|
|
297
315
|
// evidence and react presence — so a world whose ingest evidence exists
|
|
@@ -311,10 +329,16 @@ export function assembleDoctorReport(
|
|
|
311
329
|
kind,
|
|
312
330
|
identity,
|
|
313
331
|
verdict: 'UNVERIFIABLE',
|
|
332
|
+
// Dry-run finding (docs/ACCEPTANCE-DRIVER-BUILD-PLAN.md Wave 6 ledger —
|
|
333
|
+
// "Doctor's UNVERIFIABLE world verdict reads oddly next to 'clean'"):
|
|
334
|
+
// agents reading a healthy world's report couldn't tell from the old
|
|
335
|
+
// wording alone whether UNVERIFIABLE counted as a problem. State the
|
|
336
|
+
// verdict semantics inline, up front, before the per-world detail.
|
|
314
337
|
reason:
|
|
315
|
-
|
|
316
|
-
`
|
|
317
|
-
|
|
338
|
+
'no headless health signal exists for this world kind yet; NOT a failure (doctor treats ' +
|
|
339
|
+
`it as non-blocking) — nothing was observed for world "${worldId}" (kind "${kind}", ` +
|
|
340
|
+
`identity "${identity}"), and no mount-failure entry named it either — either this ` +
|
|
341
|
+
'identity has no headless signal wired in doctor this slice, or the mount never ran at all.',
|
|
318
342
|
};
|
|
319
343
|
});
|
|
320
344
|
}
|
|
@@ -342,30 +366,50 @@ export interface DoctorStaticSummary {
|
|
|
342
366
|
* 3 -> 4 (docs/DATA-TOOLS-DESIGN.md §6.6, W5 enforcement): the envelope
|
|
343
367
|
* gained the optional top-level `data` section (data-asset findings), and an
|
|
344
368
|
* ERROR-severity data finding now flips the exit code (see
|
|
345
|
-
* {@link doctorExitCode}).
|
|
369
|
+
* {@link doctorExitCode}). Bumped 4 -> 5: ordinary manifest roots now gain
|
|
370
|
+
* the {@link DoctorRuntimeEvidence} variant after a real editor Play mount.
|
|
346
371
|
*/
|
|
347
|
-
export const DOCTOR_VERSION =
|
|
372
|
+
export const DOCTOR_VERSION = 5 as const;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The acceptance-suite line item (docs/ACCEPTANCE-DRIVER-SPEC.md §2.4, Task
|
|
376
|
+
* 4.3): how many `tests/acceptance/*.spec.ts` files the project has, plus —
|
|
377
|
+
* iff that count is zero — the advisory message (`run-probe.ts`'s
|
|
378
|
+
* `ZERO_SPECS_MESSAGE`, the exact line `vgai probe` itself prints for an
|
|
379
|
+
* empty suite). ADVISORY ONLY: it never flips the exit code
|
|
380
|
+
* ({@link doctorExitCode} does not read it), so it is a purely additive
|
|
381
|
+
* optional envelope field — no {@link DOCTOR_VERSION} bump, the same
|
|
382
|
+
* shape-evolution pattern the optional `data` section established.
|
|
383
|
+
*/
|
|
384
|
+
export interface DoctorAcceptanceSummary {
|
|
385
|
+
readonly specCount: number;
|
|
386
|
+
readonly advisory?: string;
|
|
387
|
+
}
|
|
348
388
|
|
|
349
389
|
export interface DoctorJsonReport {
|
|
350
390
|
readonly doctorVersion: typeof DOCTOR_VERSION;
|
|
351
391
|
readonly static: DoctorStaticSummary;
|
|
352
|
-
readonly
|
|
392
|
+
readonly roots: readonly DoctorVerdict[];
|
|
353
393
|
/** Data-asset check findings (docs/DATA-TOOLS-DESIGN.md §6.6) — present iff the caller ran the data scan (run-doctor.ts always does; older/other callers may not). */
|
|
354
394
|
readonly data?: { readonly findings: readonly DataDoctorFinding[] };
|
|
395
|
+
/** Acceptance-suite summary ({@link DoctorAcceptanceSummary}) — present iff the caller scanned `tests/acceptance/` (run-doctor.ts always does; older/other callers may not). Advisory only — never affects the exit code. */
|
|
396
|
+
readonly acceptance?: DoctorAcceptanceSummary;
|
|
355
397
|
}
|
|
356
398
|
|
|
357
|
-
/** Build the stable `--json` envelope (D-A5: "stable shape, top-level `doctorVersion`, static-phase result included"; the version literal itself is {@link DOCTOR_VERSION}, currently `
|
|
399
|
+
/** Build the stable `--json` envelope (D-A5: "stable shape, top-level `doctorVersion`, static-phase result included"; the version literal itself is {@link DOCTOR_VERSION}, currently `5`). The `data` and `acceptance` sections are included only when their scans actually ran — never fabricated as an empty pass. */
|
|
358
400
|
export function buildDoctorJsonReport(
|
|
359
401
|
staticSummary: DoctorStaticSummary,
|
|
360
402
|
worldVerdicts: readonly DoctorVerdict[],
|
|
361
403
|
dataFindings?: readonly DataDoctorFinding[],
|
|
404
|
+
acceptance?: DoctorAcceptanceSummary,
|
|
362
405
|
): DoctorJsonReport {
|
|
363
406
|
const base: DoctorJsonReport = {
|
|
364
407
|
doctorVersion: DOCTOR_VERSION,
|
|
365
408
|
static: staticSummary,
|
|
366
|
-
|
|
409
|
+
roots: worldVerdicts,
|
|
367
410
|
};
|
|
368
|
-
|
|
411
|
+
const withData = dataFindings ? { ...base, data: { findings: dataFindings } } : base;
|
|
412
|
+
return acceptance ? { ...withData, acceptance } : withData;
|
|
369
413
|
}
|
|
370
414
|
|
|
371
415
|
/**
|
|
@@ -373,7 +417,7 @@ export function buildDoctorJsonReport(
|
|
|
373
417
|
* UNVERIFIABLE does not flip the exit code." Extended by W5 enforcement
|
|
374
418
|
* (docs/DATA-TOOLS-DESIGN.md §6.6): an ERROR-severity data-asset finding
|
|
375
419
|
* (schema failure, dangling ref) also exits 1 — warn/info findings never do,
|
|
376
|
-
* mirroring how UNVERIFIABLE
|
|
420
|
+
* mirroring how UNVERIFIABLE roots never do.
|
|
377
421
|
*/
|
|
378
422
|
export function doctorExitCode(
|
|
379
423
|
staticSummary: DoctorStaticSummary,
|
package/src/editor-api.ts
CHANGED
|
@@ -33,10 +33,22 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { resolveUrl } from '@engine/loader';
|
|
36
|
+
import {
|
|
37
|
+
assertEditorCompatibility,
|
|
38
|
+
assertProjectCompatibility,
|
|
39
|
+
type EditorServerCompatibility,
|
|
40
|
+
editorServerUnavailableError,
|
|
41
|
+
isStartupRecovery,
|
|
42
|
+
missingCompatibilityError,
|
|
43
|
+
ProjectCompatibilityError,
|
|
44
|
+
} from './editor-compatibility';
|
|
36
45
|
import { getEditorMode } from './editor-mode';
|
|
46
|
+
import type { HarnessChatAction, HarnessChatSnapshot } from './harness-chat-types';
|
|
47
|
+
import { parseHarnessChatSnapshot, unavailableHarnessChatSnapshot } from './harness-chat-types';
|
|
37
48
|
import { getStorageBackend } from './storage';
|
|
38
49
|
|
|
39
50
|
const BASE = '/__editor';
|
|
51
|
+
const EDITOR_CLIENT_ID = globalThis.crypto?.randomUUID?.() ?? `editor-${Date.now()}`;
|
|
40
52
|
|
|
41
53
|
/** Browser mode has no Node dev server — every `/__editor/*` op below either
|
|
42
54
|
* routes through `getStorageBackend()` or degrades explicitly; this helper
|
|
@@ -45,6 +57,55 @@ function isBrowserMode(): boolean {
|
|
|
45
57
|
return getEditorMode() === 'browser';
|
|
46
58
|
}
|
|
47
59
|
|
|
60
|
+
function parseEditorCompatibility(value: unknown): EditorServerCompatibility | null {
|
|
61
|
+
if (!value || typeof value !== 'object') return null;
|
|
62
|
+
const candidate = value as Partial<EditorServerCompatibility>;
|
|
63
|
+
if (
|
|
64
|
+
candidate.apiVersion !== 1 ||
|
|
65
|
+
(typeof candidate.engineVersion !== 'string' && candidate.engineVersion !== null) ||
|
|
66
|
+
typeof candidate.manifestVersion !== 'number' ||
|
|
67
|
+
typeof candidate.startedAt !== 'string' ||
|
|
68
|
+
!candidate.source ||
|
|
69
|
+
(candidate.source.state !== 'current' && candidate.source.state !== 'restart-required')
|
|
70
|
+
) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
if (
|
|
74
|
+
candidate.source.state === 'restart-required' &&
|
|
75
|
+
(typeof candidate.source.changedPath !== 'string' ||
|
|
76
|
+
typeof candidate.source.changedAt !== 'string')
|
|
77
|
+
) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return candidate as EditorServerCompatibility;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Read the immutable identity of the local Node process serving this editor. */
|
|
84
|
+
export async function getEditorCompatibility(): Promise<EditorServerCompatibility | null> {
|
|
85
|
+
if (isBrowserMode()) return null;
|
|
86
|
+
let res: Response;
|
|
87
|
+
try {
|
|
88
|
+
res = await fetch(`${BASE}/compatibility`, { cache: 'no-store' });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw editorServerUnavailableError(error instanceof Error ? error.message : String(error));
|
|
91
|
+
}
|
|
92
|
+
if (res.status >= 500) throw editorServerUnavailableError(`HTTP ${res.status}`);
|
|
93
|
+
if (!res.ok || !res.headers.get('content-type')?.includes('application/json')) return null;
|
|
94
|
+
try {
|
|
95
|
+
return parseEditorCompatibility(await res.json());
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Require a current handshake before any operation that activates a project. */
|
|
102
|
+
export async function requireEditorCompatibility(): Promise<EditorServerCompatibility> {
|
|
103
|
+
const identity = await getEditorCompatibility();
|
|
104
|
+
if (!identity) throw missingCompatibilityError();
|
|
105
|
+
assertEditorCompatibility(identity);
|
|
106
|
+
return identity;
|
|
107
|
+
}
|
|
108
|
+
|
|
48
109
|
/**
|
|
49
110
|
* Read a project text file, storage-backend first, then static HTTP fallback.
|
|
50
111
|
*
|
|
@@ -71,6 +132,45 @@ export async function readProjectTextFile(path: string): Promise<string | null>
|
|
|
71
132
|
return null;
|
|
72
133
|
}
|
|
73
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Check whether a project file exists WITHOUT reading its content.
|
|
137
|
+
*
|
|
138
|
+
* Storage-backend-first (`StorageBackend.exists`, already cheap on every
|
|
139
|
+
* backend: `HttpStorage.exists` stats via the parent directory LISTING
|
|
140
|
+
* (`/__editor/assets?dir=`), never a body fetch; OPFS/FSA resolve a handle),
|
|
141
|
+
* then an HTTP HEAD fallback against the static serving path for anything the
|
|
142
|
+
* storage layer doesn't know about. Prefer this over
|
|
143
|
+
* `readProjectTextFile(...) !== null` when only existence is needed — e.g. a
|
|
144
|
+
* restore-time verification probe — since that reads and discards the whole
|
|
145
|
+
* body.
|
|
146
|
+
*
|
|
147
|
+
* HEAD-support caveat (verified live with `curl -I` against the Vite dev
|
|
148
|
+
* server): real project files answer HEAD with a genuine Content-Type/
|
|
149
|
+
* Content-Length, so the fallback IS worth using. However the dev server's
|
|
150
|
+
* SPA fallback also answers a HEAD/GET for a MISSING path with 200 (it serves
|
|
151
|
+
* `index.html` rather than 404), so `res.ok` alone cannot fully distinguish
|
|
152
|
+
* "exists" from "missing" through this fallback path. This is not a new gap —
|
|
153
|
+
* the prior full-GET-based check had the identical blind spot — and it's only
|
|
154
|
+
* reached when the storage backend itself doesn't have the file (e.g. a
|
|
155
|
+
* static-shipped asset not yet materialized in OPFS/FSA); a false positive
|
|
156
|
+
* here just means a subsequent real open attempt fails through its own error
|
|
157
|
+
* path instead of at this cheap pre-check.
|
|
158
|
+
*/
|
|
159
|
+
export async function projectFileExists(path: string): Promise<boolean> {
|
|
160
|
+
const key = path.replace(/^\//, '');
|
|
161
|
+
try {
|
|
162
|
+
if (await getStorageBackend().exists(key)) return true;
|
|
163
|
+
} catch {
|
|
164
|
+
/* fall through to HTTP */
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const res = await fetch(resolveUrl(path), { method: 'HEAD' });
|
|
168
|
+
return res.ok;
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
74
174
|
export interface AssetEntry {
|
|
75
175
|
name: string;
|
|
76
176
|
type: 'file' | 'directory';
|
|
@@ -172,7 +272,9 @@ let sharedRefCount = 0;
|
|
|
172
272
|
*/
|
|
173
273
|
export function connectEvents(): EditorEventSource {
|
|
174
274
|
if (!sharedSource) {
|
|
175
|
-
sharedSource = new EventSource(
|
|
275
|
+
sharedSource = new EventSource(
|
|
276
|
+
`${BASE}/events?clientId=${encodeURIComponent(EDITOR_CLIENT_ID)}`,
|
|
277
|
+
);
|
|
176
278
|
}
|
|
177
279
|
const real = sharedSource;
|
|
178
280
|
sharedRefCount++;
|
|
@@ -207,13 +309,91 @@ export function connectEvents(): EditorEventSource {
|
|
|
207
309
|
};
|
|
208
310
|
}
|
|
209
311
|
|
|
312
|
+
/** Fetch the complete project-scoped coding-harness Chat state. The static
|
|
313
|
+
* hosted editor cannot inspect local processes, so browser mode degrades
|
|
314
|
+
* honestly while keeping the permanent Chat surface understandable. */
|
|
315
|
+
export async function getHarnessChatSnapshot(): Promise<HarnessChatSnapshot> {
|
|
316
|
+
if (isBrowserMode()) {
|
|
317
|
+
return unavailableHarnessChatSnapshot(
|
|
318
|
+
'Local coding sessions are available when this project is opened with `vgai edit`.',
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
const res = await fetch(`${BASE}/harness-chat`);
|
|
323
|
+
if (!res.ok) {
|
|
324
|
+
const body = (await res.json().catch(() => null)) as { error?: string } | null;
|
|
325
|
+
return unavailableHarnessChatSnapshot(body?.error ?? 'Harness Chat is unavailable.');
|
|
326
|
+
}
|
|
327
|
+
return parseHarnessChatSnapshot(await res.json());
|
|
328
|
+
} catch (error) {
|
|
329
|
+
return unavailableHarnessChatSnapshot(error instanceof Error ? error.message : String(error));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Perform one explicit Chat mutation. Collapsing the panel deliberately
|
|
334
|
+
* does not call this API: presentation cannot attach, detach, or hand off
|
|
335
|
+
* session control. */
|
|
336
|
+
export async function performHarnessChatAction(
|
|
337
|
+
action: HarnessChatAction,
|
|
338
|
+
): Promise<HarnessChatSnapshot> {
|
|
339
|
+
if (isBrowserMode()) {
|
|
340
|
+
throw new Error('Local coding sessions require the Node editor opened by `vgai edit`.');
|
|
341
|
+
}
|
|
342
|
+
const res = await fetch(`${BASE}/harness-chat/action`, {
|
|
343
|
+
method: 'POST',
|
|
344
|
+
headers: { 'Content-Type': 'application/json' },
|
|
345
|
+
body: JSON.stringify(action),
|
|
346
|
+
});
|
|
347
|
+
const body = (await res.json()) as {
|
|
348
|
+
error?: string;
|
|
349
|
+
snapshot?: HarnessChatSnapshot;
|
|
350
|
+
} & Partial<HarnessChatSnapshot>;
|
|
351
|
+
if (!res.ok) {
|
|
352
|
+
throw new HarnessChatRequestError(
|
|
353
|
+
body.error ?? 'Harness Chat action failed.',
|
|
354
|
+
body.snapshot ?? null,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return parseHarnessChatSnapshot(body);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export class HarnessChatRequestError extends Error {
|
|
361
|
+
constructor(
|
|
362
|
+
message: string,
|
|
363
|
+
readonly snapshot: HarnessChatSnapshot | null,
|
|
364
|
+
) {
|
|
365
|
+
super(message);
|
|
366
|
+
this.name = 'HarnessChatRequestError';
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Subscribe through the editor's one shared EventSource. */
|
|
371
|
+
export function connectHarnessChatEvents(
|
|
372
|
+
onSnapshot: (snapshot: HarnessChatSnapshot) => void,
|
|
373
|
+
): () => void {
|
|
374
|
+
if (isBrowserMode()) return () => {};
|
|
375
|
+
const events = connectEvents();
|
|
376
|
+
const listener = (event: MessageEvent) => {
|
|
377
|
+
try {
|
|
378
|
+
onSnapshot(parseHarnessChatSnapshot(JSON.parse(event.data as string)));
|
|
379
|
+
} catch {
|
|
380
|
+
/* A malformed optional update cannot break the rest of the editor. */
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
events.addEventListener('harness-chat', listener);
|
|
384
|
+
return () => {
|
|
385
|
+
events.removeEventListener('harness-chat', listener);
|
|
386
|
+
events.close();
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
210
390
|
/** Report the current editor state to the server (called by command-listener). */
|
|
211
391
|
export async function reportEditorState(state: Record<string, unknown>): Promise<void> {
|
|
212
392
|
try {
|
|
213
393
|
await fetch(`${BASE}/state`, {
|
|
214
394
|
method: 'POST',
|
|
215
395
|
headers: { 'Content-Type': 'application/json' },
|
|
216
|
-
body: JSON.stringify(state),
|
|
396
|
+
body: JSON.stringify({ ...state, _clientId: EDITOR_CLIENT_ID }),
|
|
217
397
|
});
|
|
218
398
|
} catch {
|
|
219
399
|
/* best-effort */
|
|
@@ -222,8 +402,10 @@ export async function reportEditorState(state: Record<string, unknown>): Promise
|
|
|
222
402
|
|
|
223
403
|
/** Report a command result back to the server so the SDK/CLI gets the response.
|
|
224
404
|
* `data` carries an optional payload for commands that answer with data
|
|
225
|
-
* (e.g. `capture-viewport`'s base64 PNG
|
|
226
|
-
*
|
|
405
|
+
* (e.g. `capture-viewport`'s base64 PNG, the Wave 5 debug-seam reads) —
|
|
406
|
+
* forwarded verbatim through `/__editor/command-result` →
|
|
407
|
+
* `commandResponseFor` → the SDK transports, on the failure path too (where
|
|
408
|
+
* it carries structured `{ code: ... }` markers). */
|
|
227
409
|
export async function reportCommandResult(
|
|
228
410
|
requestId: string,
|
|
229
411
|
ok: boolean,
|
|
@@ -289,7 +471,7 @@ export async function listExamples(): Promise<ExampleInfo[]> {
|
|
|
289
471
|
export async function createProject(
|
|
290
472
|
name: string,
|
|
291
473
|
location: string,
|
|
292
|
-
template: '
|
|
474
|
+
template: 'default' | '2d' | 'react' | 'example',
|
|
293
475
|
exampleId?: string,
|
|
294
476
|
): Promise<{ ok: boolean; path?: string; config?: { name: string }; error?: string }> {
|
|
295
477
|
if (isBrowserMode()) {
|
|
@@ -370,7 +552,13 @@ export async function saveThumbnail(dataUrl: string): Promise<boolean> {
|
|
|
370
552
|
/** Query the server for the currently active project (if any). */
|
|
371
553
|
export async function getCurrentServerProject(): Promise<{
|
|
372
554
|
path: string;
|
|
373
|
-
config: {
|
|
555
|
+
config: {
|
|
556
|
+
manifestVersion: 2;
|
|
557
|
+
name: string;
|
|
558
|
+
version: string;
|
|
559
|
+
engine: { version: string };
|
|
560
|
+
[key: string]: unknown;
|
|
561
|
+
};
|
|
374
562
|
/** Whether the project ships src/ui/registry.ts (scene-UI project mode, D9). */
|
|
375
563
|
hasUiRegistry?: boolean;
|
|
376
564
|
/** Whether this server is the no-monorepo-checkout packaged runtime
|
|
@@ -387,7 +575,13 @@ export async function getCurrentServerProject(): Promise<{
|
|
|
387
575
|
const data = (await res.json()) as {
|
|
388
576
|
project: {
|
|
389
577
|
path: string;
|
|
390
|
-
config: {
|
|
578
|
+
config: {
|
|
579
|
+
manifestVersion: 2;
|
|
580
|
+
name: string;
|
|
581
|
+
version: string;
|
|
582
|
+
engine: { version: string };
|
|
583
|
+
[key: string]: unknown;
|
|
584
|
+
};
|
|
391
585
|
hasUiRegistry?: boolean;
|
|
392
586
|
packaged?: boolean;
|
|
393
587
|
} | null;
|
|
@@ -399,21 +593,56 @@ export async function getCurrentServerProject(): Promise<{
|
|
|
399
593
|
}
|
|
400
594
|
|
|
401
595
|
/** Open/switch to a project by its filesystem path. */
|
|
402
|
-
export async function openProjectOnServer(
|
|
403
|
-
|
|
404
|
-
|
|
596
|
+
export async function openProjectOnServer(projectPath: string): Promise<{
|
|
597
|
+
ok: boolean;
|
|
598
|
+
config?: {
|
|
599
|
+
manifestVersion: 2;
|
|
600
|
+
name: string;
|
|
601
|
+
version: string;
|
|
602
|
+
engine: { version: string };
|
|
603
|
+
[key: string]: unknown;
|
|
604
|
+
};
|
|
605
|
+
}> {
|
|
405
606
|
if (isBrowserMode()) {
|
|
406
607
|
throw new Error(
|
|
407
608
|
'Opening a project by filesystem path needs the Node dev server. In browser mode, use ' +
|
|
408
609
|
'"Open Folder…" instead — it picks a real on-disk folder via File System Access.',
|
|
409
610
|
);
|
|
410
611
|
}
|
|
612
|
+
const compatibility = await requireEditorCompatibility();
|
|
411
613
|
const res = await fetch(`${BASE}/open-project`, {
|
|
412
614
|
method: 'POST',
|
|
413
615
|
headers: { 'Content-Type': 'application/json' },
|
|
414
616
|
body: JSON.stringify({ path: projectPath }),
|
|
415
617
|
});
|
|
416
|
-
|
|
618
|
+
let payload: unknown;
|
|
619
|
+
try {
|
|
620
|
+
payload = await res.json();
|
|
621
|
+
} catch {
|
|
622
|
+
throw editorServerUnavailableError(`HTTP ${res.status} returned an invalid response`);
|
|
623
|
+
}
|
|
624
|
+
if (!res.ok) {
|
|
625
|
+
if (payload && typeof payload === 'object') {
|
|
626
|
+
const failure = payload as Record<string, unknown>;
|
|
627
|
+
if (typeof failure['error'] === 'string' && isStartupRecovery(failure['recovery'])) {
|
|
628
|
+
throw new ProjectCompatibilityError(failure['error'], failure['recovery']);
|
|
629
|
+
}
|
|
630
|
+
if (typeof failure['error'] === 'string') throw new Error(failure['error']);
|
|
631
|
+
}
|
|
632
|
+
throw new Error(`Could not open project at ${projectPath}: HTTP ${res.status}`);
|
|
633
|
+
}
|
|
634
|
+
const result = payload as {
|
|
635
|
+
ok: boolean;
|
|
636
|
+
config?: {
|
|
637
|
+
manifestVersion: 2;
|
|
638
|
+
name: string;
|
|
639
|
+
version: string;
|
|
640
|
+
engine: { version: string };
|
|
641
|
+
[key: string]: unknown;
|
|
642
|
+
};
|
|
643
|
+
};
|
|
644
|
+
if (result.config) assertProjectCompatibility(result.config, compatibility);
|
|
645
|
+
return result;
|
|
417
646
|
}
|
|
418
647
|
|
|
419
648
|
// ---------------------------------------------------------------------------
|
|
@@ -475,6 +704,12 @@ export interface LogEntry {
|
|
|
475
704
|
sub?: string;
|
|
476
705
|
msg: string;
|
|
477
706
|
meta?: Record<string, unknown>;
|
|
707
|
+
/** Engine fixed-step counter at write time (SYNTHETIC-PLAYER-SPEC §3.3) —
|
|
708
|
+
* present only while a live play session's debug seam can be read, so
|
|
709
|
+
* entries correlate frame-exactly with `ctx.debug.emit` events. */
|
|
710
|
+
tick?: number;
|
|
711
|
+
/** Accumulated sim-seconds at write time — same source as `tick`. */
|
|
712
|
+
simT?: number;
|
|
478
713
|
}
|
|
479
714
|
|
|
480
715
|
/** Start a new log session. Returns the filename, or null on failure. Always
|
|
@@ -539,8 +774,8 @@ export async function flushLogEntries(entries: LogEntry[]): Promise<boolean> {
|
|
|
539
774
|
// The whole feature is a Node-only proxy to Poly Haven/ambientcg (CORS +
|
|
540
775
|
// SSRF-allowlist reasons to stay server-side) that writes into
|
|
541
776
|
// `<project>/public/asset-library/...` via raw `node:fs`, not
|
|
542
|
-
// `StorageBackend` — there is no browser-mode equivalent.
|
|
543
|
-
//
|
|
777
|
+
// `StorageBackend` — there is no browser-mode equivalent. The server-only
|
|
778
|
+
// Asset Library navigator is unavailable in browser mode; the functions below are a
|
|
544
779
|
// defensive backstop for any other caller: read-only lookups degrade to an
|
|
545
780
|
// honest empty result (no fetch attempted), the one mutating call
|
|
546
781
|
// (`downloadOnlineAsset`) throws instead.
|
|
@@ -559,6 +794,20 @@ export interface OnlineAsset {
|
|
|
559
794
|
author?: string;
|
|
560
795
|
sourceUrl?: string;
|
|
561
796
|
cloudHosted?: boolean;
|
|
797
|
+
variantId?: string;
|
|
798
|
+
variantCount?: number;
|
|
799
|
+
sizeBytes?: number;
|
|
800
|
+
animationCount?: number;
|
|
801
|
+
deliveryKinds?: ('local-ssd' | 'cloud' | 'remote')[];
|
|
802
|
+
addedAt?: string;
|
|
803
|
+
capabilities?: {
|
|
804
|
+
previewable: boolean;
|
|
805
|
+
importable: boolean;
|
|
806
|
+
runtimeReady: boolean;
|
|
807
|
+
animated: boolean;
|
|
808
|
+
deliveryAvailable: boolean;
|
|
809
|
+
health: 'healthy' | 'source-only' | 'unsupported';
|
|
810
|
+
};
|
|
562
811
|
}
|
|
563
812
|
|
|
564
813
|
export interface AssetFileOption {
|
|
@@ -569,31 +818,31 @@ export interface AssetFileOption {
|
|
|
569
818
|
includes?: { relativePath: string; url: string; size: number }[];
|
|
570
819
|
}
|
|
571
820
|
|
|
821
|
+
export interface OnlineAssetPreview {
|
|
822
|
+
path: string;
|
|
823
|
+
format: string;
|
|
824
|
+
label: string;
|
|
825
|
+
sourceAssetId: string;
|
|
826
|
+
materialPath?: string;
|
|
827
|
+
}
|
|
828
|
+
|
|
572
829
|
// ---------------------------------------------------------------------------
|
|
573
|
-
// Downloaded-asset cache (client-side, keyed by
|
|
830
|
+
// Downloaded-asset cache (client-side, keyed by exact provider identity)
|
|
574
831
|
// ---------------------------------------------------------------------------
|
|
575
832
|
|
|
576
|
-
/** Maps "source:
|
|
833
|
+
/** Maps "source:asset-id" → local serving path. Display names are never identity. */
|
|
577
834
|
const downloadedAssetPaths = new Map<string, string>();
|
|
578
835
|
|
|
579
836
|
/** Listeners notified when the download cache changes. */
|
|
580
837
|
const downloadListeners = new Set<() => void>();
|
|
581
838
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
return nameOrId
|
|
585
|
-
.toLowerCase()
|
|
586
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
587
|
-
.replace(/^-|-$/g, '');
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function assetCacheKey(source: string, nameOrId: string): string {
|
|
591
|
-
return `${source}:${toSlug(nameOrId)}`;
|
|
839
|
+
function assetCacheKey(source: string, assetId: string): string {
|
|
840
|
+
return `${source}:${assetId}`;
|
|
592
841
|
}
|
|
593
842
|
|
|
594
843
|
/** Check if an online asset has already been downloaded. Returns local path or null. */
|
|
595
|
-
export function getDownloadedAssetPath(source: string,
|
|
596
|
-
return downloadedAssetPaths.get(assetCacheKey(source,
|
|
844
|
+
export function getDownloadedAssetPath(source: string, assetId: string): string | null {
|
|
845
|
+
return downloadedAssetPaths.get(assetCacheKey(source, assetId)) ?? null;
|
|
597
846
|
}
|
|
598
847
|
|
|
599
848
|
/** Subscribe to download cache changes. Returns unsubscribe function. */
|
|
@@ -610,10 +859,10 @@ export async function loadDownloadedAssets(): Promise<void> {
|
|
|
610
859
|
const res = await fetch(`${BASE}/asset-library/downloaded`);
|
|
611
860
|
if (!res.ok) return;
|
|
612
861
|
const data = (await res.json()) as {
|
|
613
|
-
downloaded: { source: string;
|
|
862
|
+
downloaded: { source: string; id: string; path: string }[];
|
|
614
863
|
};
|
|
615
864
|
for (const entry of data.downloaded) {
|
|
616
|
-
downloadedAssetPaths.set(
|
|
865
|
+
downloadedAssetPaths.set(assetCacheKey(entry.source, entry.id), `/${entry.path}`);
|
|
617
866
|
}
|
|
618
867
|
for (const cb of downloadListeners) cb();
|
|
619
868
|
} catch {
|
|
@@ -649,11 +898,26 @@ export async function getOnlineAssetFiles(source: string, id: string): Promise<A
|
|
|
649
898
|
if (isBrowserMode()) return [];
|
|
650
899
|
const qs = new URLSearchParams({ source, id });
|
|
651
900
|
const res = await fetch(`${BASE}/asset-library/files?${qs}`);
|
|
652
|
-
if (!res.ok)
|
|
901
|
+
if (!res.ok) {
|
|
902
|
+
const body = (await res.json().catch(() => null)) as { error?: string } | null;
|
|
903
|
+
throw new Error(body?.error ?? `Asset delivery lookup failed (${res.status}).`);
|
|
904
|
+
}
|
|
653
905
|
const data = (await res.json()) as { files: AssetFileOption[] };
|
|
654
906
|
return data.files;
|
|
655
907
|
}
|
|
656
908
|
|
|
909
|
+
/** Resolve a read-only Asset Lab model from the editor's catalog cache. */
|
|
910
|
+
export async function getOnlineAssetPreview(
|
|
911
|
+
source: string,
|
|
912
|
+
id: string,
|
|
913
|
+
): Promise<OnlineAssetPreview | null> {
|
|
914
|
+
if (isBrowserMode()) return null;
|
|
915
|
+
const qs = new URLSearchParams({ source, id });
|
|
916
|
+
const res = await fetch(`${BASE}/asset-library/preview?${qs}`);
|
|
917
|
+
if (!res.ok) return null;
|
|
918
|
+
return (await res.json()) as OnlineAssetPreview;
|
|
919
|
+
}
|
|
920
|
+
|
|
657
921
|
/** Download an online asset into the project's public/asset-library/ folder. */
|
|
658
922
|
export async function downloadOnlineAsset(params: {
|
|
659
923
|
source: string;
|
|
@@ -682,7 +946,7 @@ export async function downloadOnlineAsset(params: {
|
|
|
682
946
|
};
|
|
683
947
|
// Cache the local path on success
|
|
684
948
|
if (result.ok && result.path) {
|
|
685
|
-
downloadedAssetPaths.set(assetCacheKey(params.source, params.
|
|
949
|
+
downloadedAssetPaths.set(assetCacheKey(params.source, params.id), `/${result.path}`);
|
|
686
950
|
for (const cb of downloadListeners) cb();
|
|
687
951
|
}
|
|
688
952
|
return result;
|