@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
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build split (W3, docs/EDITOR-WORKSPACE-SHELL-TODO.md §5/§8/§9 W3 —
|
|
3
|
+
* inventory rows B4 + B15's W3 share): the permanent bottom BUILD tab
|
|
4
|
+
* (`BuildPanel.tsx`, deleted) mixed configuration + output log + progress in
|
|
5
|
+
* one drawer pane. Split per §5/§8:
|
|
6
|
+
*
|
|
7
|
+
* - **Build Profiles** (configuration: target list, project name /
|
|
8
|
+
* resolution / app id, the Build trigger) → a center DOCUMENT (kind
|
|
9
|
+
* `'build-profiles'`). Chosen over the §5 row's focused-dialog
|
|
10
|
+
* alternative because (a) §7.1 names Build Profiles among the real
|
|
11
|
+
* first-party document implementers, (b) the configuration is a
|
|
12
|
+
* persistent authored surface (it writes `vgai.game.json` through the
|
|
13
|
+
* history service) someone tunes and re-runs, not a one-shot confirm —
|
|
14
|
+
* document semantics, and (c) a modal would block the viewport while a
|
|
15
|
+
* build streams. A future focused "quick build" dialog stays additive.
|
|
16
|
+
* - **Build Output** (the log) → a workspace-utility registration
|
|
17
|
+
* (`build-output`) — rendered by Dockview as a movable utility panel that
|
|
18
|
+
* defaults to the bottom edge group, exactly like story utilities. Always available in server mode so
|
|
19
|
+
* it is reachable before the first build (it owns its empty state);
|
|
20
|
+
* starting a build raises `editor:show-build-output` so its current group
|
|
21
|
+
* reveals and focuses it (the old `editor:show-build-tab` reflex, split in two).
|
|
22
|
+
* - **build progress** → a workspace-STATUS registration
|
|
23
|
+
* (`build-progress`, W0 registry). Registering now is correct even
|
|
24
|
+
* though the status bar doesn't exist yet: the registry renders nowhere
|
|
25
|
+
* until W4 consumes it — the contribution is inert, typed, tested.
|
|
26
|
+
*
|
|
27
|
+
* The build SESSION (phase/log/result) is a module-scope store so all three
|
|
28
|
+
* surfaces observe one truth — the old panel lost its log if the tab
|
|
29
|
+
* unmounted mid-build; the session now outlives any one surface.
|
|
30
|
+
*
|
|
31
|
+
* Build ACTIONS remain reachable everywhere they were: the PlayBar hammer,
|
|
32
|
+
* the palette `build.*` actions, and the `show-build` control command all
|
|
33
|
+
* open the Build Profiles document (whose Build button runs the same
|
|
34
|
+
* `startExport` server flow as before).
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { faGlobe } from '@fortawesome/free-solid-svg-icons';
|
|
38
|
+
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
39
|
+
import {
|
|
40
|
+
accent,
|
|
41
|
+
Button,
|
|
42
|
+
bg,
|
|
43
|
+
border,
|
|
44
|
+
danger,
|
|
45
|
+
EditorIcon,
|
|
46
|
+
EditorListButton,
|
|
47
|
+
fontMono,
|
|
48
|
+
success,
|
|
49
|
+
TextInput,
|
|
50
|
+
text,
|
|
51
|
+
} from '../design-system';
|
|
52
|
+
import { getDownloadUrl, readProjectTextFile, saveFile, startExport } from '../editor-api';
|
|
53
|
+
import { getProjectFileHistory } from '../history/project-file-history';
|
|
54
|
+
import { createHmrRegistrationGroup } from '../hmr-registration-group';
|
|
55
|
+
import {
|
|
56
|
+
openWorkspaceDocument,
|
|
57
|
+
type WorkspaceDocumentContentProps,
|
|
58
|
+
} from '../workspace-document-registry';
|
|
59
|
+
import { registerWorkspaceStatus } from '../workspace-status-registry';
|
|
60
|
+
import { registerWorkspaceUtility } from '../workspace-utility-registry';
|
|
61
|
+
import { workspaceHistoryService } from './workspace-history';
|
|
62
|
+
|
|
63
|
+
export type BuildTarget = 'web';
|
|
64
|
+
export type BuildPhase = 'idle' | 'building' | 'done';
|
|
65
|
+
|
|
66
|
+
export interface BuildResult {
|
|
67
|
+
ok: boolean;
|
|
68
|
+
code: number | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface ProjectConfig {
|
|
72
|
+
name: string;
|
|
73
|
+
resolution: { width: number; height: number };
|
|
74
|
+
appId: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const MANIFEST_PATH = 'vgai.game.json';
|
|
78
|
+
|
|
79
|
+
const DEFAULT_CONFIG: ProjectConfig = {
|
|
80
|
+
name: 'My Game',
|
|
81
|
+
resolution: { width: 1280, height: 720 },
|
|
82
|
+
appId: 'com.example.mygame',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const TARGETS: {
|
|
86
|
+
id: BuildTarget;
|
|
87
|
+
label: string;
|
|
88
|
+
subtitle: string;
|
|
89
|
+
output: string;
|
|
90
|
+
icon: typeof faGlobe;
|
|
91
|
+
}[] = [
|
|
92
|
+
{ id: 'web', label: 'Web', subtitle: 'HTML5 for itch.io', output: 'vgai-web.zip', icon: faGlobe },
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
// --- The build SESSION store (one truth for document/utility/status) -------
|
|
96
|
+
|
|
97
|
+
export interface BuildSession {
|
|
98
|
+
readonly phase: BuildPhase;
|
|
99
|
+
readonly target: BuildTarget;
|
|
100
|
+
readonly log: readonly string[];
|
|
101
|
+
readonly result: BuildResult | null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let _session: BuildSession = { phase: 'idle', target: 'web', log: [], result: null };
|
|
105
|
+
let _version = 0;
|
|
106
|
+
const _listeners = new Set<() => void>();
|
|
107
|
+
let _abort: AbortController | null = null;
|
|
108
|
+
|
|
109
|
+
function setSession(patch: Partial<BuildSession>): void {
|
|
110
|
+
_session = { ..._session, ...patch };
|
|
111
|
+
_version++;
|
|
112
|
+
for (const fn of _listeners) fn();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Subscribe to build-session changes. Returns unsubscribe. */
|
|
116
|
+
export function subscribeBuildSession(fn: () => void): () => void {
|
|
117
|
+
_listeners.add(fn);
|
|
118
|
+
return () => {
|
|
119
|
+
_listeners.delete(fn);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Monotonic change counter — the `getSnapshot` for `useSyncExternalStore`. */
|
|
124
|
+
export function buildSessionVersion(): number {
|
|
125
|
+
return _version;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The current build session (stable reference between changes). */
|
|
129
|
+
export function getBuildSession(): BuildSession {
|
|
130
|
+
return _session;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Back to idle (the `Build Again` reset). No-op mid-build. */
|
|
134
|
+
export function resetBuildSession(): void {
|
|
135
|
+
if (_session.phase === 'building') return;
|
|
136
|
+
setSession({ phase: 'idle', log: [], result: null });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Abort a running build (the old panel's Cancel, unchanged semantics). */
|
|
140
|
+
export function cancelBuild(): void {
|
|
141
|
+
_abort?.abort();
|
|
142
|
+
setSession({ phase: 'idle' });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Run a build — the exact `startExport` SSE-stream consumption the old
|
|
147
|
+
* panel's `startBuild` had, moved to module scope so the session outlives
|
|
148
|
+
* any one surface. Raises `editor:show-build-output` so the bottom area
|
|
149
|
+
* focuses the Build Output utility.
|
|
150
|
+
*/
|
|
151
|
+
export async function startBuild(target: BuildTarget): Promise<void> {
|
|
152
|
+
if (_session.phase === 'building') return;
|
|
153
|
+
setSession({ phase: 'building', target, log: [], result: null });
|
|
154
|
+
if (typeof window !== 'undefined') {
|
|
155
|
+
window.dispatchEvent(new CustomEvent('editor:show-build-output'));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const controller = new AbortController();
|
|
159
|
+
_abort = controller;
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const res = await startExport(target, controller.signal);
|
|
163
|
+
|
|
164
|
+
if (!res.ok) {
|
|
165
|
+
const err = await res.json().catch(() => ({ error: 'Build request failed' }));
|
|
166
|
+
setSession({
|
|
167
|
+
phase: 'done',
|
|
168
|
+
log: [..._session.log, `Error: ${err.error ?? res.statusText}`],
|
|
169
|
+
result: { ok: false, code: null },
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const reader = res.body!.getReader();
|
|
175
|
+
const decoder = new TextDecoder();
|
|
176
|
+
let buffer = '';
|
|
177
|
+
|
|
178
|
+
while (true) {
|
|
179
|
+
const { done, value } = await reader.read();
|
|
180
|
+
if (done) break;
|
|
181
|
+
buffer += decoder.decode(value, { stream: true });
|
|
182
|
+
|
|
183
|
+
const parts = buffer.split('\n\n');
|
|
184
|
+
buffer = parts.pop()!;
|
|
185
|
+
|
|
186
|
+
for (const part of parts) {
|
|
187
|
+
if (part.startsWith(':')) continue;
|
|
188
|
+
const lines = part.split('\n');
|
|
189
|
+
let event = '';
|
|
190
|
+
let data = '';
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
if (line.startsWith('event: ')) event = line.slice(7);
|
|
193
|
+
else if (line.startsWith('data: ')) data = line.slice(6);
|
|
194
|
+
}
|
|
195
|
+
if (event === 'log') {
|
|
196
|
+
let lineText: string;
|
|
197
|
+
try {
|
|
198
|
+
lineText = JSON.parse(data) as string;
|
|
199
|
+
} catch {
|
|
200
|
+
lineText = data;
|
|
201
|
+
}
|
|
202
|
+
setSession({ log: [..._session.log, lineText] });
|
|
203
|
+
} else if (event === 'done') {
|
|
204
|
+
let result: BuildResult;
|
|
205
|
+
try {
|
|
206
|
+
result = JSON.parse(data) as BuildResult;
|
|
207
|
+
} catch {
|
|
208
|
+
result = { ok: false, code: null };
|
|
209
|
+
}
|
|
210
|
+
setSession({ phase: 'done', result });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch (err: unknown) {
|
|
215
|
+
if ((err as Error).name !== 'AbortError') {
|
|
216
|
+
setSession({
|
|
217
|
+
phase: 'done',
|
|
218
|
+
log: [..._session.log, `Error: ${(err as Error).message}`],
|
|
219
|
+
result: { ok: false, code: null },
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Test-only reset. */
|
|
226
|
+
export function __resetBuildSessionForTest(): void {
|
|
227
|
+
_abort?.abort();
|
|
228
|
+
_abort = null;
|
|
229
|
+
_session = { phase: 'idle', target: 'web', log: [], result: null };
|
|
230
|
+
_version++;
|
|
231
|
+
for (const fn of _listeners) fn();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// --- Shell contributions (utility + status) ---------------------------------
|
|
235
|
+
|
|
236
|
+
const registrationGroup = createHmrRegistrationGroup(import.meta.hot, 'build-contributions');
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Idempotent one-time registration of the build shell contributions: the
|
|
240
|
+
* on-demand `Build Output` utility and the `build-progress` status
|
|
241
|
+
* contribution. Call in server mode only (builds spawn
|
|
242
|
+
* `npm run <script>` server-side — no browser-mode equivalent, same gate the
|
|
243
|
+
* old tab had). Output appears when a build starts, when a failure requests
|
|
244
|
+
* attention, or when the user explicitly opens it; an empty output tab does
|
|
245
|
+
* not occupy the clean default strip.
|
|
246
|
+
*/
|
|
247
|
+
export function ensureBuildContributionsRegistered(): void {
|
|
248
|
+
registrationGroup.ensure((track) => {
|
|
249
|
+
track(
|
|
250
|
+
registerWorkspaceUtility({
|
|
251
|
+
id: 'build-output',
|
|
252
|
+
title: 'Build Output',
|
|
253
|
+
Content: BuildOutputUtility,
|
|
254
|
+
visibleByDefault: false,
|
|
255
|
+
closeable: true,
|
|
256
|
+
}),
|
|
257
|
+
);
|
|
258
|
+
track(
|
|
259
|
+
registerWorkspaceStatus({
|
|
260
|
+
id: 'build-progress',
|
|
261
|
+
align: 'left',
|
|
262
|
+
Content: BuildProgressStatus,
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Test-only: allow re-registration after a registry reset. */
|
|
269
|
+
export function __resetBuildContributionsForTest(): void {
|
|
270
|
+
registrationGroup.reset();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Build Output utility content: the session log (+ result line), or its
|
|
274
|
+
* own empty state before any build has run. */
|
|
275
|
+
export function BuildOutputUtility() {
|
|
276
|
+
useSyncExternalStore(subscribeBuildSession, buildSessionVersion);
|
|
277
|
+
const session = _session;
|
|
278
|
+
const logRef = useRef<HTMLDivElement>(null);
|
|
279
|
+
|
|
280
|
+
// Auto-scroll (the old panel's log behavior, unchanged).
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (session.phase === 'idle' && session.log.length === 0) {
|
|
286
|
+
return (
|
|
287
|
+
<div
|
|
288
|
+
data-testid="build-output-empty"
|
|
289
|
+
style={{ padding: 12, fontSize: 12, color: text[2], maxWidth: 480 }}
|
|
290
|
+
>
|
|
291
|
+
<p style={{ margin: 0 }}>
|
|
292
|
+
No build output yet — run a build from the <b>Build Profiles</b> document (toolbar hammer,
|
|
293
|
+
or the palette's Build actions).
|
|
294
|
+
</p>
|
|
295
|
+
</div>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
return (
|
|
299
|
+
<div
|
|
300
|
+
data-testid="build-output-utility"
|
|
301
|
+
style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}
|
|
302
|
+
>
|
|
303
|
+
<div ref={logRef} style={logBoxStyle}>
|
|
304
|
+
{session.log.map((line, i) => (
|
|
305
|
+
<div key={i}>{line || ' '}</div>
|
|
306
|
+
))}
|
|
307
|
+
{session.phase === 'building' && (
|
|
308
|
+
<div
|
|
309
|
+
style={{
|
|
310
|
+
display: 'inline-block',
|
|
311
|
+
width: 6,
|
|
312
|
+
height: 14,
|
|
313
|
+
background: accent,
|
|
314
|
+
animation: 'blink 1s step-end infinite',
|
|
315
|
+
}}
|
|
316
|
+
/>
|
|
317
|
+
)}
|
|
318
|
+
{session.phase === 'done' && session.result && (
|
|
319
|
+
<div style={{ color: session.result.ok ? success : danger, marginTop: 4 }}>
|
|
320
|
+
{session.result.ok
|
|
321
|
+
? 'Build complete.'
|
|
322
|
+
: `Build failed (exit code: ${session.result.code ?? 'unknown'}).`}
|
|
323
|
+
</div>
|
|
324
|
+
)}
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Compact build-progress status contribution (§5 "build progress | status
|
|
331
|
+
* bar"). Nothing while idle; consumed by nothing until W4's status bar. */
|
|
332
|
+
export function BuildProgressStatus() {
|
|
333
|
+
useSyncExternalStore(subscribeBuildSession, buildSessionVersion);
|
|
334
|
+
const session = _session;
|
|
335
|
+
if (session.phase === 'idle') return null;
|
|
336
|
+
if (session.phase === 'building') {
|
|
337
|
+
return (
|
|
338
|
+
<span data-testid="status-build-progress" style={{ fontSize: 10, color: accent }}>
|
|
339
|
+
Building {session.target}…
|
|
340
|
+
</span>
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
return (
|
|
344
|
+
<span
|
|
345
|
+
data-testid="status-build-progress"
|
|
346
|
+
style={{ fontSize: 10, color: session.result?.ok ? success : danger }}
|
|
347
|
+
>
|
|
348
|
+
{session.result?.ok ? 'Build ✓' : 'Build ✗'}
|
|
349
|
+
</span>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// --- The Build Profiles document --------------------------------------------
|
|
354
|
+
|
|
355
|
+
/** Stable §7.1 identity of the single Build Profiles document. */
|
|
356
|
+
export const BUILD_PROFILES_DOCUMENT_ID = 'workspace:build-profiles';
|
|
357
|
+
|
|
358
|
+
/** The narrow store surface (T6.3 tab hand-off). */
|
|
359
|
+
export interface BuildDocumentStore {
|
|
360
|
+
setActiveViewportTab(tab: 'scene' | 'game'): void;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Open (or activate) the center `Build Profiles` document — the target of
|
|
364
|
+
* every build ACTION entry point (PlayBar hammer, palette, `show-build`). */
|
|
365
|
+
export function openBuildProfilesDocument(store: BuildDocumentStore): string {
|
|
366
|
+
return openWorkspaceDocument({
|
|
367
|
+
id: BUILD_PROFILES_DOCUMENT_ID,
|
|
368
|
+
title: 'Build Profiles',
|
|
369
|
+
kind: 'build-profiles',
|
|
370
|
+
Content: BuildProfilesDocumentContent,
|
|
371
|
+
closeable: true,
|
|
372
|
+
onActivate: () => store.setActiveViewportTab('scene'),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** The configuration surface — the old panel's target list + settings form,
|
|
377
|
+
* minus the log (Build Output's job now). */
|
|
378
|
+
export function BuildProfilesDocumentContent(_props: WorkspaceDocumentContentProps) {
|
|
379
|
+
useSyncExternalStore(subscribeBuildSession, buildSessionVersion);
|
|
380
|
+
const session = _session;
|
|
381
|
+
// Session history via the workspace-history bridge (see its doc comment);
|
|
382
|
+
// the per-service manager is cached inside getProjectFileHistory.
|
|
383
|
+
const history = workspaceHistoryService();
|
|
384
|
+
const fileHistory = history ? getProjectFileHistory(history) : null;
|
|
385
|
+
const [config, setConfig] = useState<ProjectConfig>(DEFAULT_CONFIG);
|
|
386
|
+
const [selected, setSelected] = useState<BuildTarget>('web');
|
|
387
|
+
const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
388
|
+
|
|
389
|
+
const loadConfig = useCallback(async () => {
|
|
390
|
+
const manifestText = await readProjectTextFile(MANIFEST_PATH);
|
|
391
|
+
const manifest = manifestText
|
|
392
|
+
? (JSON.parse(manifestText) as Partial<ProjectConfig>)
|
|
393
|
+
: DEFAULT_CONFIG;
|
|
394
|
+
setConfig({
|
|
395
|
+
...DEFAULT_CONFIG,
|
|
396
|
+
...manifest,
|
|
397
|
+
resolution: { ...DEFAULT_CONFIG.resolution, ...manifest.resolution },
|
|
398
|
+
});
|
|
399
|
+
}, []);
|
|
400
|
+
|
|
401
|
+
useEffect(() => {
|
|
402
|
+
void loadConfig().catch(() => {});
|
|
403
|
+
return (
|
|
404
|
+
fileHistory?.subscribeAll((path) => {
|
|
405
|
+
if (path === MANIFEST_PATH) void loadConfig().catch(() => {});
|
|
406
|
+
}) ?? (() => {})
|
|
407
|
+
);
|
|
408
|
+
}, [fileHistory, loadConfig]);
|
|
409
|
+
|
|
410
|
+
useEffect(
|
|
411
|
+
() => () => {
|
|
412
|
+
if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
|
|
413
|
+
},
|
|
414
|
+
[],
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
// Merge only the build-owned fields into the manifest. Roots and every
|
|
418
|
+
// unrelated authored field must survive a Build Profiles edit.
|
|
419
|
+
const saveConfig = useCallback(
|
|
420
|
+
(cfg: ProjectConfig) => {
|
|
421
|
+
if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
|
|
422
|
+
saveTimerRef.current = setTimeout(async () => {
|
|
423
|
+
const currentText = await readProjectTextFile(MANIFEST_PATH);
|
|
424
|
+
if (!currentText)
|
|
425
|
+
throw new Error(`Cannot edit build settings: ${MANIFEST_PATH} is missing`);
|
|
426
|
+
const manifest = JSON.parse(currentText) as Record<string, unknown>;
|
|
427
|
+
const content = `${JSON.stringify(
|
|
428
|
+
{ ...manifest, name: cfg.name, resolution: cfg.resolution, appId: cfg.appId },
|
|
429
|
+
null,
|
|
430
|
+
2,
|
|
431
|
+
)}\n`;
|
|
432
|
+
if (!fileHistory) {
|
|
433
|
+
await saveFile(MANIFEST_PATH, content);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
await fileHistory
|
|
437
|
+
.write(MANIFEST_PATH, content, {
|
|
438
|
+
label: 'Edit Build Settings',
|
|
439
|
+
kind: 'settings',
|
|
440
|
+
contentType: 'application/json',
|
|
441
|
+
})
|
|
442
|
+
.catch(() => void loadConfig().catch(() => {}));
|
|
443
|
+
}, 500);
|
|
444
|
+
},
|
|
445
|
+
[fileHistory, loadConfig],
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const updateConfig = useCallback(
|
|
449
|
+
(patch: Partial<ProjectConfig>) => {
|
|
450
|
+
setConfig((prev) => {
|
|
451
|
+
const next = { ...prev, ...patch };
|
|
452
|
+
saveConfig(next);
|
|
453
|
+
return next;
|
|
454
|
+
});
|
|
455
|
+
},
|
|
456
|
+
[saveConfig],
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const updateResolution = useCallback(
|
|
460
|
+
(key: 'width' | 'height', value: number) => {
|
|
461
|
+
setConfig((prev) => {
|
|
462
|
+
const next = { ...prev, resolution: { ...prev.resolution, [key]: Math.round(value) } };
|
|
463
|
+
saveConfig(next);
|
|
464
|
+
return next;
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
[saveConfig],
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const phase = session.phase;
|
|
471
|
+
const targetInfo = TARGETS.find((t) => t.id === selected)!;
|
|
472
|
+
|
|
473
|
+
return (
|
|
474
|
+
<div
|
|
475
|
+
data-testid="build-profiles-document"
|
|
476
|
+
style={{ display: 'flex', flex: 1, minHeight: 0, background: bg[1], pointerEvents: 'auto' }}
|
|
477
|
+
>
|
|
478
|
+
{/* Left: target list (the "profiles") */}
|
|
479
|
+
<div style={targetListStyle}>
|
|
480
|
+
{TARGETS.map((t) => (
|
|
481
|
+
<EditorListButton
|
|
482
|
+
key={t.id}
|
|
483
|
+
selected={selected === t.id}
|
|
484
|
+
disabled={phase === 'building'}
|
|
485
|
+
onClick={() => {
|
|
486
|
+
if (phase !== 'building') setSelected(t.id);
|
|
487
|
+
}}
|
|
488
|
+
style={{
|
|
489
|
+
padding: '8px 12px',
|
|
490
|
+
opacity: phase === 'building' && selected !== t.id ? 0.4 : 1,
|
|
491
|
+
}}
|
|
492
|
+
>
|
|
493
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
494
|
+
<EditorIcon
|
|
495
|
+
icon={t.icon}
|
|
496
|
+
style={{ fontSize: 13, color: selected === t.id ? accent : text[2] }}
|
|
497
|
+
/>
|
|
498
|
+
<div>
|
|
499
|
+
<div
|
|
500
|
+
style={{
|
|
501
|
+
fontSize: 12,
|
|
502
|
+
fontWeight: 600,
|
|
503
|
+
color: selected === t.id ? text[1] : text[2],
|
|
504
|
+
}}
|
|
505
|
+
>
|
|
506
|
+
{t.label}
|
|
507
|
+
</div>
|
|
508
|
+
<div style={{ fontSize: 10, color: text[3], marginTop: 1 }}>{t.subtitle}</div>
|
|
509
|
+
</div>
|
|
510
|
+
</div>
|
|
511
|
+
</EditorListButton>
|
|
512
|
+
))}
|
|
513
|
+
</div>
|
|
514
|
+
|
|
515
|
+
{/* Right: settings + phase-dependent action row */}
|
|
516
|
+
<div
|
|
517
|
+
style={{
|
|
518
|
+
flex: 1,
|
|
519
|
+
display: 'flex',
|
|
520
|
+
flexDirection: 'column',
|
|
521
|
+
minWidth: 0,
|
|
522
|
+
padding: '10px 16px',
|
|
523
|
+
maxWidth: 560,
|
|
524
|
+
}}
|
|
525
|
+
>
|
|
526
|
+
<div style={sectionHeaderStyle}>Project</div>
|
|
527
|
+
|
|
528
|
+
<div style={fieldRowStyle}>
|
|
529
|
+
<label style={labelStyle}>Name</label>
|
|
530
|
+
<TextInput
|
|
531
|
+
className="vgai-input"
|
|
532
|
+
style={{ flex: 1 }}
|
|
533
|
+
value={config.name}
|
|
534
|
+
onChange={(e) => updateConfig({ name: e.target.value })}
|
|
535
|
+
/>
|
|
536
|
+
</div>
|
|
537
|
+
|
|
538
|
+
<div style={fieldRowStyle}>
|
|
539
|
+
<label style={labelStyle}>Resolution</label>
|
|
540
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 4, flex: 1 }}>
|
|
541
|
+
<TextInput
|
|
542
|
+
type="number"
|
|
543
|
+
className="vgai-input"
|
|
544
|
+
style={{ width: 0, flex: 1 }}
|
|
545
|
+
value={config.resolution.width}
|
|
546
|
+
onChange={(e) => {
|
|
547
|
+
const v = Number.parseInt(e.target.value, 10);
|
|
548
|
+
if (Number.isFinite(v) && v > 0) updateResolution('width', v);
|
|
549
|
+
}}
|
|
550
|
+
/>
|
|
551
|
+
<span style={{ fontSize: 10, color: text[3] }}>×</span>
|
|
552
|
+
<TextInput
|
|
553
|
+
type="number"
|
|
554
|
+
className="vgai-input"
|
|
555
|
+
style={{ width: 0, flex: 1 }}
|
|
556
|
+
value={config.resolution.height}
|
|
557
|
+
onChange={(e) => {
|
|
558
|
+
const v = Number.parseInt(e.target.value, 10);
|
|
559
|
+
if (Number.isFinite(v) && v > 0) updateResolution('height', v);
|
|
560
|
+
}}
|
|
561
|
+
/>
|
|
562
|
+
</div>
|
|
563
|
+
</div>
|
|
564
|
+
|
|
565
|
+
<div style={fieldRowStyle}>
|
|
566
|
+
<label style={labelStyle}>App ID</label>
|
|
567
|
+
<TextInput
|
|
568
|
+
className="vgai-input"
|
|
569
|
+
style={{ flex: 1 }}
|
|
570
|
+
value={config.appId}
|
|
571
|
+
onChange={(e) => updateConfig({ appId: e.target.value })}
|
|
572
|
+
/>
|
|
573
|
+
</div>
|
|
574
|
+
|
|
575
|
+
<div style={{ ...fieldRowStyle, marginTop: 4 }}>
|
|
576
|
+
<label style={labelStyle}>Output</label>
|
|
577
|
+
<span style={{ fontSize: 12, color: text[2] }}>{targetInfo.output}</span>
|
|
578
|
+
</div>
|
|
579
|
+
|
|
580
|
+
{/* Action row: Build / Cancel / result — the log itself streams into
|
|
581
|
+
the Build Output utility (the split's whole point). */}
|
|
582
|
+
<div
|
|
583
|
+
style={{
|
|
584
|
+
marginTop: 12,
|
|
585
|
+
display: 'flex',
|
|
586
|
+
alignItems: 'center',
|
|
587
|
+
gap: 8,
|
|
588
|
+
paddingTop: 8,
|
|
589
|
+
borderTop: `1px solid ${border[1]}`,
|
|
590
|
+
}}
|
|
591
|
+
>
|
|
592
|
+
{phase === 'building' ? (
|
|
593
|
+
<>
|
|
594
|
+
<span style={{ fontSize: 12, fontWeight: 600, color: accent }}>
|
|
595
|
+
Building for {TARGETS.find((t) => t.id === session.target)?.label ?? session.target}
|
|
596
|
+
… (log in Build Output below)
|
|
597
|
+
</span>
|
|
598
|
+
<Button size="compact" onClick={cancelBuild}>
|
|
599
|
+
Cancel
|
|
600
|
+
</Button>
|
|
601
|
+
</>
|
|
602
|
+
) : (
|
|
603
|
+
<>
|
|
604
|
+
{phase === 'done' && session.result && (
|
|
605
|
+
<span
|
|
606
|
+
data-testid="build-profiles-result"
|
|
607
|
+
style={{
|
|
608
|
+
fontSize: 12,
|
|
609
|
+
fontWeight: 600,
|
|
610
|
+
color: session.result.ok ? success : danger,
|
|
611
|
+
}}
|
|
612
|
+
>
|
|
613
|
+
{session.result.ok
|
|
614
|
+
? '✓ Build complete'
|
|
615
|
+
: `✗ Build failed (exit ${session.result.code ?? 'unknown'})`}
|
|
616
|
+
</span>
|
|
617
|
+
)}
|
|
618
|
+
{phase === 'done' && session.result?.ok && session.target === 'web' && (
|
|
619
|
+
<a
|
|
620
|
+
href={getDownloadUrl(targetInfo.output)}
|
|
621
|
+
download={targetInfo.output}
|
|
622
|
+
className="vgai-btn"
|
|
623
|
+
data-variant="secondary"
|
|
624
|
+
data-size="default"
|
|
625
|
+
style={{
|
|
626
|
+
textDecoration: 'none',
|
|
627
|
+
display: 'inline-flex',
|
|
628
|
+
}}
|
|
629
|
+
>
|
|
630
|
+
Download
|
|
631
|
+
</a>
|
|
632
|
+
)}
|
|
633
|
+
<Button
|
|
634
|
+
variant="primary"
|
|
635
|
+
data-testid="build-profiles-build"
|
|
636
|
+
onClick={() => {
|
|
637
|
+
if (phase === 'done') resetBuildSession();
|
|
638
|
+
void startBuild(selected);
|
|
639
|
+
}}
|
|
640
|
+
style={{ marginLeft: 'auto' }}
|
|
641
|
+
>
|
|
642
|
+
{phase === 'done' ? 'Build Again' : 'Build'}
|
|
643
|
+
</Button>
|
|
644
|
+
</>
|
|
645
|
+
)}
|
|
646
|
+
</div>
|
|
647
|
+
</div>
|
|
648
|
+
</div>
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/* ---- Styles (carried from the old panel) ---- */
|
|
653
|
+
|
|
654
|
+
const targetListStyle: React.CSSProperties = {
|
|
655
|
+
width: 160,
|
|
656
|
+
flexShrink: 0,
|
|
657
|
+
borderRight: `1px solid ${border[1]}`,
|
|
658
|
+
overflowY: 'auto',
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
const sectionHeaderStyle: React.CSSProperties = {
|
|
662
|
+
fontSize: 10,
|
|
663
|
+
fontWeight: 700,
|
|
664
|
+
textTransform: 'uppercase',
|
|
665
|
+
color: text[3],
|
|
666
|
+
letterSpacing: '0.05em',
|
|
667
|
+
marginBottom: 6,
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
const fieldRowStyle: React.CSSProperties = {
|
|
671
|
+
display: 'flex',
|
|
672
|
+
alignItems: 'center',
|
|
673
|
+
gap: 6,
|
|
674
|
+
marginBottom: 4,
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
const labelStyle: React.CSSProperties = {
|
|
678
|
+
width: 65,
|
|
679
|
+
fontSize: 11,
|
|
680
|
+
color: text[2],
|
|
681
|
+
flexShrink: 0,
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
const logBoxStyle: React.CSSProperties = {
|
|
685
|
+
flex: 1,
|
|
686
|
+
overflowY: 'auto',
|
|
687
|
+
background: bg[0],
|
|
688
|
+
padding: '4px 8px',
|
|
689
|
+
fontFamily: fontMono,
|
|
690
|
+
fontSize: 11,
|
|
691
|
+
lineHeight: '16px',
|
|
692
|
+
color: text[2],
|
|
693
|
+
whiteSpace: 'pre-wrap',
|
|
694
|
+
wordBreak: 'break-all',
|
|
695
|
+
minHeight: 0,
|
|
696
|
+
};
|