@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Button, IconButton, Select, TextInput, themeVars } from '../design-system';
|
|
1
2
|
/**
|
|
2
3
|
* Project screen — shown before the editor when no project is specified.
|
|
3
4
|
*
|
|
@@ -7,6 +8,12 @@
|
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import { useCallback, useEffect, useReducer, useRef, useState } from 'react';
|
|
11
|
+
import {
|
|
12
|
+
BUNDLED_EDITOR_COMPATIBILITY,
|
|
13
|
+
BUNDLED_EDITOR_VERSION,
|
|
14
|
+
BUNDLED_ENGINE_VERSION,
|
|
15
|
+
} from '../build-identity';
|
|
16
|
+
import { TONE_ERROR } from '../design-system';
|
|
10
17
|
import {
|
|
11
18
|
browseFolder,
|
|
12
19
|
createProject,
|
|
@@ -17,6 +24,11 @@ import {
|
|
|
17
24
|
type RecentProject,
|
|
18
25
|
revealInFinder,
|
|
19
26
|
} from '../editor-api';
|
|
27
|
+
import {
|
|
28
|
+
assertProjectCompatibility,
|
|
29
|
+
ProjectCompatibilityError,
|
|
30
|
+
type StartupRecovery,
|
|
31
|
+
} from '../editor-compatibility';
|
|
20
32
|
import { getEditorMode } from '../editor-mode';
|
|
21
33
|
import { type ActiveProject, type ProjectConfig, setActiveProject } from '../project-manager';
|
|
22
34
|
import {
|
|
@@ -26,6 +38,7 @@ import {
|
|
|
26
38
|
type StorageBackend,
|
|
27
39
|
} from '../storage';
|
|
28
40
|
import { readProjectManifest, seedIfEmpty } from '../storage/seed';
|
|
41
|
+
import { zIndex } from '../z-index';
|
|
29
42
|
|
|
30
43
|
// ---------------------------------------------------------------------------
|
|
31
44
|
// Injected stylesheet for interactive states
|
|
@@ -33,12 +46,18 @@ import { readProjectManifest, seedIfEmpty } from '../storage/seed';
|
|
|
33
46
|
|
|
34
47
|
const STYLE_ID = 'vgai-project-screen';
|
|
35
48
|
|
|
49
|
+
// D-15: this CSS block used to hand-retype its own palette (three different
|
|
50
|
+
// themeVars.accent.default blues inside one file, its own menu recipe, its own button shapes)
|
|
51
|
+
// because a template string can't share inline-style constants. It now
|
|
52
|
+
// interpolates `theme.ts` tokens directly — same delivery mechanism
|
|
53
|
+
// (className + injected sheet, the only way to get :hover/:focus-visible/
|
|
54
|
+
// :disabled here), one source of truth for the values.
|
|
36
55
|
const CSS = `
|
|
37
56
|
.ps-card {
|
|
38
57
|
position: relative;
|
|
39
|
-
background:
|
|
40
|
-
border: 1px solid
|
|
41
|
-
border-radius:
|
|
58
|
+
background: ${themeVars.surface.panel};
|
|
59
|
+
border: 1px solid ${themeVars.boundary.default};
|
|
60
|
+
border-radius: ${themeVars.shape.large};
|
|
42
61
|
padding: 14px 14px 12px;
|
|
43
62
|
cursor: pointer;
|
|
44
63
|
text-align: left;
|
|
@@ -47,21 +66,21 @@ const CSS = `
|
|
|
47
66
|
transition: border-color 0.15s, background 0.15s;
|
|
48
67
|
width: 100%;
|
|
49
68
|
}
|
|
50
|
-
.ps-card:hover { border-color:
|
|
51
|
-
.ps-card:focus-visible { outline: 1px solid
|
|
52
|
-
.ps-card:disabled { opacity: 0.5; cursor: default; }
|
|
69
|
+
.ps-card:hover { border-color: ${themeVars.boundary.strong}; background: ${themeVars.surface.chrome}; }
|
|
70
|
+
.ps-card:focus-visible { outline: 1px solid ${themeVars.accent.default}; outline-offset: 1px; }
|
|
71
|
+
.ps-card:disabled, .ps-card[aria-disabled="true"] { opacity: 0.5; cursor: default; }
|
|
53
72
|
|
|
54
73
|
.ps-card-remove {
|
|
55
74
|
position: absolute;
|
|
56
75
|
top: 6px;
|
|
57
76
|
right: 6px;
|
|
58
|
-
background:
|
|
77
|
+
background: ${themeVars.surface.raised};
|
|
59
78
|
border: none;
|
|
60
|
-
color:
|
|
61
|
-
font-size:
|
|
79
|
+
color: ${themeVars.content.dim};
|
|
80
|
+
font-size: var(--vgai-font-xl);
|
|
62
81
|
width: 20px;
|
|
63
82
|
height: 20px;
|
|
64
|
-
border-radius:
|
|
83
|
+
border-radius: ${themeVars.shape.small};
|
|
65
84
|
cursor: pointer;
|
|
66
85
|
opacity: 0;
|
|
67
86
|
transition: opacity 0.15s;
|
|
@@ -70,58 +89,19 @@ const CSS = `
|
|
|
70
89
|
justify-content: center;
|
|
71
90
|
}
|
|
72
91
|
.ps-card:hover .ps-card-remove { opacity: 1; }
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
padding: 10px 20px;
|
|
81
|
-
font-size: 14px;
|
|
82
|
-
font-weight: 600;
|
|
83
|
-
font-family: inherit;
|
|
84
|
-
cursor: pointer;
|
|
85
|
-
transition: background 0.15s;
|
|
86
|
-
}
|
|
87
|
-
.ps-btn-primary:hover { background: #4a9eff; }
|
|
88
|
-
.ps-btn-primary:focus-visible { outline: 1px solid #579eff; outline-offset: 1px; }
|
|
89
|
-
.ps-btn-primary:disabled { opacity: 0.5; cursor: default; }
|
|
90
|
-
|
|
91
|
-
.ps-btn-secondary {
|
|
92
|
-
background: #333;
|
|
93
|
-
color: #c5c8ce;
|
|
94
|
-
border: none;
|
|
95
|
-
border-radius: 6px;
|
|
96
|
-
padding: 10px 20px;
|
|
97
|
-
font-size: 14px;
|
|
98
|
-
font-family: inherit;
|
|
99
|
-
cursor: pointer;
|
|
100
|
-
transition: background 0.15s;
|
|
101
|
-
white-space: nowrap;
|
|
102
|
-
}
|
|
103
|
-
.ps-btn-secondary:hover { background: #444; }
|
|
104
|
-
.ps-btn-secondary:focus-visible { outline: 1px solid #579eff; outline-offset: 1px; }
|
|
105
|
-
.ps-btn-secondary:disabled { opacity: 0.5; cursor: default; }
|
|
106
|
-
|
|
107
|
-
.ps-input {
|
|
108
|
-
flex: 1;
|
|
109
|
-
background: #2a2a2a;
|
|
110
|
-
border: 1px solid #333;
|
|
111
|
-
border-radius: 6px;
|
|
112
|
-
padding: 8px 12px;
|
|
113
|
-
font-size: 14px;
|
|
114
|
-
color: #e0e0e0;
|
|
115
|
-
outline: none;
|
|
116
|
-
font-family: inherit;
|
|
117
|
-
}
|
|
118
|
-
.ps-input:focus { border-color: #579eff; }
|
|
92
|
+
/* Hover fill is one neutral step above the resting bg-3 (#333) — that step
|
|
93
|
+
renders as #444444, a value the bg ramp deliberately has no slot for
|
|
94
|
+
(adding a bg-4 for one hover state would invite ramp sprawl). border-2 is
|
|
95
|
+
the token that carries #444444; the theme already shares values across
|
|
96
|
+
ramps by design (border-1 ≡ bg-3 — see theme.ts), so this is the same
|
|
97
|
+
documented cross-ramp reuse, not drift. (U6 punch-list #4.) */
|
|
98
|
+
.ps-card-remove:hover { background: ${themeVars.boundary.strong}; color: ${themeVars.content.primary}; }
|
|
119
99
|
|
|
120
100
|
.ps-template {
|
|
121
101
|
flex: 1;
|
|
122
|
-
background:
|
|
123
|
-
border: 1px solid
|
|
124
|
-
border-radius:
|
|
102
|
+
background: ${themeVars.surface.panel};
|
|
103
|
+
border: 1px solid ${themeVars.boundary.default};
|
|
104
|
+
border-radius: ${themeVars.shape.large};
|
|
125
105
|
padding: 12px 14px;
|
|
126
106
|
cursor: pointer;
|
|
127
107
|
text-align: left;
|
|
@@ -129,49 +109,50 @@ const CSS = `
|
|
|
129
109
|
color: inherit;
|
|
130
110
|
transition: border-color 0.15s;
|
|
131
111
|
}
|
|
132
|
-
.ps-template:hover { border-color:
|
|
133
|
-
.ps-template
|
|
112
|
+
.ps-template:hover { border-color: ${themeVars.boundary.strong}; }
|
|
113
|
+
.ps-template:focus-visible { outline: 1px solid ${themeVars.accent.default}; outline-offset: 1px; }
|
|
114
|
+
.ps-template[data-selected="true"] { border-color: ${themeVars.accent.default}; }
|
|
134
115
|
|
|
135
116
|
.ps-ctx-menu {
|
|
136
117
|
position: fixed;
|
|
137
|
-
background:
|
|
138
|
-
border: 1px solid
|
|
139
|
-
border-radius:
|
|
118
|
+
background: ${themeVars.surface.chrome};
|
|
119
|
+
border: 1px solid ${themeVars.boundary.strong};
|
|
120
|
+
border-radius: ${themeVars.shape.medium};
|
|
140
121
|
padding: 4px 0;
|
|
141
122
|
min-width: 160px;
|
|
142
|
-
z-index:
|
|
143
|
-
box-shadow:
|
|
123
|
+
z-index: ${zIndex.dropdown};
|
|
124
|
+
box-shadow: ${themeVars.elevation.medium};
|
|
144
125
|
}
|
|
145
126
|
.ps-ctx-item {
|
|
146
127
|
display: block;
|
|
147
128
|
width: 100%;
|
|
148
129
|
background: none;
|
|
149
130
|
border: none;
|
|
150
|
-
color:
|
|
131
|
+
color: ${themeVars.content.primary};
|
|
151
132
|
font: inherit;
|
|
152
|
-
font-size:
|
|
133
|
+
font-size: var(--vgai-font-lg);
|
|
153
134
|
padding: 6px 14px;
|
|
154
135
|
text-align: left;
|
|
155
136
|
cursor: pointer;
|
|
156
137
|
}
|
|
157
|
-
.ps-ctx-item:hover { background:
|
|
138
|
+
.ps-ctx-item:hover { background: ${themeVars.accent.muted}; }
|
|
158
139
|
.ps-ctx-sep {
|
|
159
140
|
height: 1px;
|
|
160
|
-
background:
|
|
141
|
+
background: ${themeVars.boundary.strong};
|
|
161
142
|
margin: 4px 0;
|
|
162
143
|
}
|
|
163
144
|
|
|
164
145
|
.ps-thumbnail {
|
|
165
146
|
width: 100%;
|
|
166
147
|
aspect-ratio: 16/9;
|
|
167
|
-
background:
|
|
168
|
-
border-radius:
|
|
148
|
+
background: ${themeVars.surface.shell};
|
|
149
|
+
border-radius: ${themeVars.shape.small};
|
|
169
150
|
margin-bottom: 8px;
|
|
170
151
|
overflow: hidden;
|
|
171
152
|
display: flex;
|
|
172
153
|
align-items: center;
|
|
173
154
|
justify-content: center;
|
|
174
|
-
color:
|
|
155
|
+
color: ${themeVars.surface.raised};
|
|
175
156
|
font-size: 24px;
|
|
176
157
|
}
|
|
177
158
|
.ps-thumbnail img {
|
|
@@ -200,12 +181,12 @@ interface ScreenState {
|
|
|
200
181
|
view: 'home' | 'new-project';
|
|
201
182
|
recentProjects: RecentProject[];
|
|
202
183
|
loaded: boolean;
|
|
203
|
-
error:
|
|
184
|
+
error: ScreenError | null;
|
|
204
185
|
busy: boolean; // opening or creating
|
|
205
186
|
// New project form
|
|
206
187
|
projectName: string;
|
|
207
188
|
projectLocation: string;
|
|
208
|
-
template: '
|
|
189
|
+
template: 'default' | '2d' | 'react' | 'example';
|
|
209
190
|
exampleId: string | null;
|
|
210
191
|
availableExamples: ExampleInfo[];
|
|
211
192
|
}
|
|
@@ -213,12 +194,12 @@ interface ScreenState {
|
|
|
213
194
|
type ScreenAction =
|
|
214
195
|
| { type: 'LOADED'; projects: RecentProject[] }
|
|
215
196
|
| { type: 'SET_VIEW'; view: 'home' | 'new-project' }
|
|
216
|
-
| { type: 'SET_ERROR'; error:
|
|
197
|
+
| { type: 'SET_ERROR'; error: unknown | null }
|
|
217
198
|
| { type: 'SET_BUSY'; busy: boolean }
|
|
218
199
|
| { type: 'REMOVE_RECENT'; path: string }
|
|
219
200
|
| { type: 'SET_NAME'; name: string }
|
|
220
201
|
| { type: 'SET_LOCATION'; location: string }
|
|
221
|
-
| { type: 'SET_TEMPLATE'; template: '
|
|
202
|
+
| { type: 'SET_TEMPLATE'; template: 'default' | '2d' | 'react' | 'example' }
|
|
222
203
|
| { type: 'SET_EXAMPLE_ID'; exampleId: string }
|
|
223
204
|
| { type: 'SET_AVAILABLE_EXAMPLES'; examples: ExampleInfo[] };
|
|
224
205
|
|
|
@@ -230,11 +211,24 @@ const initialState: ScreenState = {
|
|
|
230
211
|
busy: false,
|
|
231
212
|
projectName: 'My Game',
|
|
232
213
|
projectLocation: '~/VGAI Projects',
|
|
233
|
-
template: '
|
|
214
|
+
template: 'default',
|
|
234
215
|
exampleId: null,
|
|
235
216
|
availableExamples: [],
|
|
236
217
|
};
|
|
237
218
|
|
|
219
|
+
interface ScreenError {
|
|
220
|
+
message: string;
|
|
221
|
+
recovery?: StartupRecovery;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function screenError(error: unknown): ScreenError | null {
|
|
225
|
+
if (error === null) return null;
|
|
226
|
+
if (error instanceof ProjectCompatibilityError) {
|
|
227
|
+
return { message: error.message, recovery: error.recovery };
|
|
228
|
+
}
|
|
229
|
+
return { message: error instanceof Error ? error.message : String(error) };
|
|
230
|
+
}
|
|
231
|
+
|
|
238
232
|
function reducer(state: ScreenState, action: ScreenAction): ScreenState {
|
|
239
233
|
switch (action.type) {
|
|
240
234
|
case 'LOADED':
|
|
@@ -247,7 +241,7 @@ function reducer(state: ScreenState, action: ScreenAction): ScreenState {
|
|
|
247
241
|
case 'SET_VIEW':
|
|
248
242
|
return { ...state, view: action.view, error: null };
|
|
249
243
|
case 'SET_ERROR':
|
|
250
|
-
return { ...state, error: action.error, busy: false };
|
|
244
|
+
return { ...state, error: screenError(action.error), busy: false };
|
|
251
245
|
case 'SET_BUSY':
|
|
252
246
|
return { ...state, busy: action.busy, error: null };
|
|
253
247
|
case 'REMOVE_RECENT':
|
|
@@ -324,7 +318,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
324
318
|
dispatch({ type: 'SET_ERROR', error: `Failed to open project at ${path}` });
|
|
325
319
|
}
|
|
326
320
|
} catch (err) {
|
|
327
|
-
dispatch({ type: 'SET_ERROR', error: err
|
|
321
|
+
dispatch({ type: 'SET_ERROR', error: err });
|
|
328
322
|
}
|
|
329
323
|
},
|
|
330
324
|
[onOpen],
|
|
@@ -358,7 +352,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
358
352
|
dispatch({ type: 'SET_ERROR', error: result.error ?? 'Failed to create project.' });
|
|
359
353
|
}
|
|
360
354
|
} catch (err) {
|
|
361
|
-
dispatch({ type: 'SET_ERROR', error: err
|
|
355
|
+
dispatch({ type: 'SET_ERROR', error: err });
|
|
362
356
|
}
|
|
363
357
|
}, [state.projectName, state.projectLocation, state.template, state.exampleId, onOpen]);
|
|
364
358
|
|
|
@@ -367,7 +361,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
367
361
|
const path = await browseFolder('Select a project folder');
|
|
368
362
|
if (path) openProject(path);
|
|
369
363
|
} catch (err) {
|
|
370
|
-
dispatch({ type: 'SET_ERROR', error: err
|
|
364
|
+
dispatch({ type: 'SET_ERROR', error: err });
|
|
371
365
|
}
|
|
372
366
|
}, [openProject]);
|
|
373
367
|
|
|
@@ -376,7 +370,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
376
370
|
const path = await browseFolder('Choose project location');
|
|
377
371
|
if (path) dispatch({ type: 'SET_LOCATION', location: path });
|
|
378
372
|
} catch (err) {
|
|
379
|
-
dispatch({ type: 'SET_ERROR', error: err
|
|
373
|
+
dispatch({ type: 'SET_ERROR', error: err });
|
|
380
374
|
}
|
|
381
375
|
}, []);
|
|
382
376
|
|
|
@@ -386,6 +380,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
386
380
|
async (backend: StorageBackend) => {
|
|
387
381
|
await seedIfEmpty(backend);
|
|
388
382
|
const config = await readProjectManifest(backend);
|
|
383
|
+
assertProjectCompatibility(config, BUNDLED_EDITOR_COMPATIBILITY);
|
|
389
384
|
const project: ActiveProject = { rootPath: 'fsa', config };
|
|
390
385
|
setActiveProject(project);
|
|
391
386
|
onOpen(project);
|
|
@@ -396,17 +391,25 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
396
391
|
// Browser-first mode: open a REAL on-disk folder via the File System Access
|
|
397
392
|
// API (Chromium). The picked folder is remembered so a reload reopens it.
|
|
398
393
|
const handleOpenFolderFsa = useCallback(async () => {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
394
|
+
try {
|
|
395
|
+
const pick = getHostCapabilities().pickProjectRoot;
|
|
396
|
+
if (!pick) return;
|
|
397
|
+
const backend = await pick();
|
|
398
|
+
if (backend) await openFsaBackend(backend);
|
|
399
|
+
} catch (error) {
|
|
400
|
+
dispatch({ type: 'SET_ERROR', error });
|
|
401
|
+
}
|
|
403
402
|
}, [openFsaBackend]);
|
|
404
403
|
|
|
405
404
|
// Reopen the remembered on-disk folder — re-grants read/write permission
|
|
406
405
|
// (this click IS the required user gesture) and opens it.
|
|
407
406
|
const handleReopenFolder = useCallback(async () => {
|
|
408
|
-
|
|
409
|
-
|
|
407
|
+
try {
|
|
408
|
+
const backend = await reopenPersistedProjectRoot();
|
|
409
|
+
if (backend) await openFsaBackend(backend);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
dispatch({ type: 'SET_ERROR', error });
|
|
412
|
+
}
|
|
410
413
|
}, [openFsaBackend]);
|
|
411
414
|
|
|
412
415
|
const slug = state.projectName
|
|
@@ -419,12 +422,14 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
419
422
|
<div style={S.inner}>
|
|
420
423
|
<header style={S.header}>
|
|
421
424
|
<div style={S.title}>VGAI ENGINE</div>
|
|
422
|
-
<div style={S.version}>
|
|
425
|
+
<div style={S.version}>
|
|
426
|
+
Editor v{BUNDLED_EDITOR_VERSION} · Engine v{BUNDLED_ENGINE_VERSION}
|
|
427
|
+
</div>
|
|
423
428
|
</header>
|
|
424
429
|
|
|
425
430
|
{state.error && (
|
|
426
431
|
<ErrorBanner
|
|
427
|
-
|
|
432
|
+
error={state.error}
|
|
428
433
|
onDismiss={() => dispatch({ type: 'SET_ERROR', error: null })}
|
|
429
434
|
/>
|
|
430
435
|
)}
|
|
@@ -436,26 +441,32 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
|
|
|
436
441
|
gap: 10,
|
|
437
442
|
alignItems: 'center',
|
|
438
443
|
margin: '2px 0 14px',
|
|
439
|
-
color:
|
|
440
|
-
fontSize:
|
|
444
|
+
color: themeVars.content.muted,
|
|
445
|
+
fontSize: 'var(--vgai-font-lg)',
|
|
441
446
|
}}
|
|
442
447
|
>
|
|
443
448
|
<span>Projects are stored in your browser (OPFS) — nothing leaves this device.</span>
|
|
444
449
|
{typeof window !== 'undefined' && 'showDirectoryPicker' in window && (
|
|
445
450
|
<>
|
|
446
451
|
{persistedRootName && (
|
|
447
|
-
<
|
|
452
|
+
<Button
|
|
448
453
|
type="button"
|
|
449
|
-
|
|
454
|
+
variant="primary"
|
|
455
|
+
size="comfortable"
|
|
450
456
|
onClick={handleReopenFolder}
|
|
451
457
|
data-testid="reopen-folder"
|
|
452
458
|
>
|
|
453
459
|
Reopen “{persistedRootName}”
|
|
454
|
-
</
|
|
460
|
+
</Button>
|
|
455
461
|
)}
|
|
456
|
-
<
|
|
462
|
+
<Button
|
|
463
|
+
type="button"
|
|
464
|
+
variant="secondary"
|
|
465
|
+
size="comfortable"
|
|
466
|
+
onClick={handleOpenFolderFsa}
|
|
467
|
+
>
|
|
457
468
|
Open Folder…
|
|
458
|
-
</
|
|
469
|
+
</Button>
|
|
459
470
|
</>
|
|
460
471
|
)}
|
|
461
472
|
</div>
|
|
@@ -543,12 +554,12 @@ function HomeView({
|
|
|
543
554
|
</section>
|
|
544
555
|
|
|
545
556
|
<div style={S.actions}>
|
|
546
|
-
<
|
|
557
|
+
<Button type="button" variant="primary" size="comfortable" onClick={onNewProject}>
|
|
547
558
|
+ New Project
|
|
548
|
-
</
|
|
549
|
-
<
|
|
559
|
+
</Button>
|
|
560
|
+
<Button type="button" variant="secondary" size="comfortable" onClick={onBrowse}>
|
|
550
561
|
Open Project...
|
|
551
|
-
</
|
|
562
|
+
</Button>
|
|
552
563
|
</div>
|
|
553
564
|
</>
|
|
554
565
|
);
|
|
@@ -575,12 +586,21 @@ function ProjectCard({ project, disabled, onOpen, onRemove }: ProjectCardProps)
|
|
|
575
586
|
|
|
576
587
|
return (
|
|
577
588
|
<>
|
|
578
|
-
<
|
|
579
|
-
|
|
589
|
+
<div
|
|
590
|
+
role="button"
|
|
591
|
+
tabIndex={disabled ? -1 : 0}
|
|
592
|
+
aria-disabled={disabled}
|
|
580
593
|
className="ps-card"
|
|
581
|
-
onClick={
|
|
594
|
+
onClick={() => {
|
|
595
|
+
if (!disabled) onOpen();
|
|
596
|
+
}}
|
|
597
|
+
onKeyDown={(event) => {
|
|
598
|
+
if (!disabled && (event.key === 'Enter' || event.key === ' ')) {
|
|
599
|
+
event.preventDefault();
|
|
600
|
+
onOpen();
|
|
601
|
+
}
|
|
602
|
+
}}
|
|
582
603
|
onContextMenu={handleContextMenu}
|
|
583
|
-
disabled={disabled}
|
|
584
604
|
>
|
|
585
605
|
<div className="ps-thumbnail">
|
|
586
606
|
{project.thumbnail ? <img src={project.thumbnail} alt={project.name} /> : '\u25a6'}
|
|
@@ -590,7 +610,7 @@ function ProjectCard({ project, disabled, onOpen, onRemove }: ProjectCardProps)
|
|
|
590
610
|
{shortenPath(project.path)}
|
|
591
611
|
</div>
|
|
592
612
|
<div style={S.cardMeta}>{formatRelativeTime(project.lastOpened)}</div>
|
|
593
|
-
<
|
|
613
|
+
<Button
|
|
594
614
|
type="button"
|
|
595
615
|
className="ps-card-remove"
|
|
596
616
|
title="Remove from list"
|
|
@@ -600,8 +620,8 @@ function ProjectCard({ project, disabled, onOpen, onRemove }: ProjectCardProps)
|
|
|
600
620
|
}}
|
|
601
621
|
>
|
|
602
622
|
×
|
|
603
|
-
</
|
|
604
|
-
</
|
|
623
|
+
</Button>
|
|
624
|
+
</div>
|
|
605
625
|
|
|
606
626
|
{ctxMenu && (
|
|
607
627
|
<ContextMenu
|
|
@@ -664,7 +684,7 @@ function ContextMenu({ x, y, onClose, items }: ContextMenuProps) {
|
|
|
664
684
|
item.separator ? (
|
|
665
685
|
<div key={`sep-${i}`} className="ps-ctx-sep" />
|
|
666
686
|
) : (
|
|
667
|
-
<
|
|
687
|
+
<Button
|
|
668
688
|
key={item.label}
|
|
669
689
|
type="button"
|
|
670
690
|
className="ps-ctx-item"
|
|
@@ -674,7 +694,7 @@ function ContextMenu({ x, y, onClose, items }: ContextMenuProps) {
|
|
|
674
694
|
}}
|
|
675
695
|
>
|
|
676
696
|
{item.label}
|
|
677
|
-
</
|
|
697
|
+
</Button>
|
|
678
698
|
),
|
|
679
699
|
)}
|
|
680
700
|
</div>
|
|
@@ -688,14 +708,14 @@ function ContextMenu({ x, y, onClose, items }: ContextMenuProps) {
|
|
|
688
708
|
interface NewProjectFormProps {
|
|
689
709
|
name: string;
|
|
690
710
|
location: string;
|
|
691
|
-
template: '
|
|
711
|
+
template: 'default' | '2d' | 'react' | 'example';
|
|
692
712
|
exampleId: string | null;
|
|
693
713
|
availableExamples: ExampleInfo[];
|
|
694
714
|
slug: string;
|
|
695
715
|
busy: boolean;
|
|
696
716
|
onNameChange: (name: string) => void;
|
|
697
717
|
onLocationChange: (location: string) => void;
|
|
698
|
-
onTemplateChange: (template: '
|
|
718
|
+
onTemplateChange: (template: 'default' | '2d' | 'react' | 'example') => void;
|
|
699
719
|
onExampleIdChange: (exampleId: string) => void;
|
|
700
720
|
onBrowseLocation: () => void;
|
|
701
721
|
onCancel: () => void;
|
|
@@ -724,47 +744,58 @@ function NewProjectForm({
|
|
|
724
744
|
|
|
725
745
|
<label style={S.label}>
|
|
726
746
|
Name
|
|
727
|
-
<
|
|
747
|
+
<TextInput
|
|
748
|
+
style={{ flex: 1 }}
|
|
749
|
+
value={name}
|
|
750
|
+
onChange={(e) => onNameChange(e.target.value)}
|
|
751
|
+
/>
|
|
728
752
|
</label>
|
|
729
753
|
|
|
730
754
|
<label style={S.label}>
|
|
731
755
|
Location
|
|
732
756
|
<div style={S.locationRow}>
|
|
733
|
-
<
|
|
734
|
-
|
|
757
|
+
<TextInput
|
|
758
|
+
style={{ flex: 1 }}
|
|
735
759
|
value={location}
|
|
736
760
|
onChange={(e) => onLocationChange(e.target.value)}
|
|
737
761
|
/>
|
|
738
|
-
<
|
|
762
|
+
<Button type="button" variant="secondary" size="comfortable" onClick={onBrowseLocation}>
|
|
739
763
|
Browse
|
|
740
|
-
</
|
|
764
|
+
</Button>
|
|
741
765
|
</div>
|
|
742
766
|
</label>
|
|
743
767
|
|
|
744
768
|
<div style={S.label}>
|
|
745
769
|
Template
|
|
746
770
|
<div style={S.templateRow}>
|
|
747
|
-
<
|
|
771
|
+
<Button
|
|
748
772
|
type="button"
|
|
749
773
|
className="ps-template"
|
|
750
|
-
data-selected={template === '
|
|
751
|
-
onClick={() => onTemplateChange('
|
|
774
|
+
data-selected={template === 'default'}
|
|
775
|
+
onClick={() => onTemplateChange('default')}
|
|
752
776
|
>
|
|
753
|
-
<div style={S.templateName}>
|
|
754
|
-
<div style={S.templateDesc}>
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
777
|
+
<div style={S.templateName}>3D + UI</div>
|
|
778
|
+
<div style={S.templateDesc}>Three.js game root plus React UI root</div>
|
|
779
|
+
</Button>
|
|
780
|
+
<Button
|
|
781
|
+
type="button"
|
|
782
|
+
className="ps-template"
|
|
783
|
+
data-selected={template === '2d'}
|
|
784
|
+
onClick={() => onTemplateChange('2d')}
|
|
785
|
+
>
|
|
786
|
+
<div style={S.templateName}>2D + UI</div>
|
|
787
|
+
<div style={S.templateDesc}>PixiJS game root plus React UI root</div>
|
|
788
|
+
</Button>
|
|
789
|
+
<Button
|
|
759
790
|
type="button"
|
|
760
791
|
className="ps-template"
|
|
761
|
-
data-selected={template === '
|
|
762
|
-
onClick={() => onTemplateChange('
|
|
792
|
+
data-selected={template === 'react'}
|
|
793
|
+
onClick={() => onTemplateChange('react')}
|
|
763
794
|
>
|
|
764
|
-
<div style={S.templateName}>
|
|
765
|
-
<div style={S.templateDesc}>
|
|
766
|
-
</
|
|
767
|
-
<
|
|
795
|
+
<div style={S.templateName}>React</div>
|
|
796
|
+
<div style={S.templateDesc}>React-only game root</div>
|
|
797
|
+
</Button>
|
|
798
|
+
<Button
|
|
768
799
|
type="button"
|
|
769
800
|
className="ps-template"
|
|
770
801
|
data-selected={template === 'example'}
|
|
@@ -774,12 +805,11 @@ function NewProjectForm({
|
|
|
774
805
|
<div style={S.templateDesc}>
|
|
775
806
|
Start from a real example project (e.g. the multiplayer arena)
|
|
776
807
|
</div>
|
|
777
|
-
</
|
|
808
|
+
</Button>
|
|
778
809
|
</div>
|
|
779
810
|
{template === 'example' && (
|
|
780
|
-
<
|
|
781
|
-
|
|
782
|
-
style={{ marginTop: 10 }}
|
|
811
|
+
<Select
|
|
812
|
+
style={{ flex: 1, marginTop: 10 }}
|
|
783
813
|
value={exampleId ?? ''}
|
|
784
814
|
onChange={(e) => onExampleIdChange(e.target.value)}
|
|
785
815
|
>
|
|
@@ -791,7 +821,7 @@ function NewProjectForm({
|
|
|
791
821
|
{ex.name}
|
|
792
822
|
</option>
|
|
793
823
|
))}
|
|
794
|
-
</
|
|
824
|
+
</Select>
|
|
795
825
|
)}
|
|
796
826
|
</div>
|
|
797
827
|
|
|
@@ -802,19 +832,26 @@ function NewProjectForm({
|
|
|
802
832
|
)}
|
|
803
833
|
|
|
804
834
|
<div style={S.formActions}>
|
|
805
|
-
<
|
|
835
|
+
<Button
|
|
836
|
+
type="button"
|
|
837
|
+
variant="secondary"
|
|
838
|
+
size="comfortable"
|
|
839
|
+
onClick={onCancel}
|
|
840
|
+
disabled={busy}
|
|
841
|
+
>
|
|
806
842
|
Cancel
|
|
807
|
-
</
|
|
808
|
-
<
|
|
843
|
+
</Button>
|
|
844
|
+
<Button
|
|
809
845
|
type="button"
|
|
810
|
-
|
|
846
|
+
variant="primary"
|
|
847
|
+
size="comfortable"
|
|
811
848
|
onClick={onCreate}
|
|
812
849
|
disabled={
|
|
813
850
|
busy || !name.trim() || !location.trim() || (template === 'example' && !exampleId)
|
|
814
851
|
}
|
|
815
852
|
>
|
|
816
853
|
{busy ? 'Creating...' : 'Create Project'}
|
|
817
|
-
</
|
|
854
|
+
</Button>
|
|
818
855
|
</div>
|
|
819
856
|
</div>
|
|
820
857
|
);
|
|
@@ -824,13 +861,46 @@ function NewProjectForm({
|
|
|
824
861
|
// ErrorBanner
|
|
825
862
|
// ---------------------------------------------------------------------------
|
|
826
863
|
|
|
827
|
-
function ErrorBanner({
|
|
864
|
+
function ErrorBanner({ error, onDismiss }: { error: ScreenError; onDismiss: () => void }) {
|
|
865
|
+
const command = error.recovery && 'command' in error.recovery ? error.recovery.command : null;
|
|
828
866
|
return (
|
|
829
|
-
<div style={S.error}>
|
|
830
|
-
<
|
|
831
|
-
|
|
867
|
+
<div role="alert" style={{ ...S.error, alignItems: 'flex-start', gap: 12 }}>
|
|
868
|
+
<div style={{ flex: 1 }}>
|
|
869
|
+
{error.recovery && (
|
|
870
|
+
<div style={{ marginBottom: 4, color: themeVars.content.primary, fontWeight: 600 }}>
|
|
871
|
+
{error.recovery.title}
|
|
872
|
+
</div>
|
|
873
|
+
)}
|
|
874
|
+
{error.recovery && <div style={{ marginBottom: 6 }}>{error.recovery.guidance}</div>}
|
|
875
|
+
<div>{error.message}</div>
|
|
876
|
+
{command && (
|
|
877
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>
|
|
878
|
+
<code
|
|
879
|
+
data-testid="project-screen-recovery-command"
|
|
880
|
+
style={{ fontFamily: themeVars.typography.mono }}
|
|
881
|
+
>
|
|
882
|
+
{command}
|
|
883
|
+
</code>
|
|
884
|
+
<Button
|
|
885
|
+
type="button"
|
|
886
|
+
variant="secondary"
|
|
887
|
+
size="compact"
|
|
888
|
+
onClick={() => void navigator.clipboard?.writeText(command)}
|
|
889
|
+
>
|
|
890
|
+
Copy
|
|
891
|
+
</Button>
|
|
892
|
+
</div>
|
|
893
|
+
)}
|
|
894
|
+
</div>
|
|
895
|
+
<IconButton
|
|
896
|
+
type="button"
|
|
897
|
+
variant="danger"
|
|
898
|
+
size="compact"
|
|
899
|
+
aria-label="Dismiss error"
|
|
900
|
+
onClick={onDismiss}
|
|
901
|
+
>
|
|
832
902
|
×
|
|
833
|
-
</
|
|
903
|
+
</IconButton>
|
|
834
904
|
</div>
|
|
835
905
|
);
|
|
836
906
|
}
|
|
@@ -869,12 +939,12 @@ const S = {
|
|
|
869
939
|
left: 0,
|
|
870
940
|
width: '100%',
|
|
871
941
|
height: '100%',
|
|
872
|
-
background:
|
|
942
|
+
background: themeVars.surface.shell,
|
|
873
943
|
display: 'flex',
|
|
874
944
|
alignItems: 'center',
|
|
875
945
|
justifyContent: 'center',
|
|
876
|
-
fontFamily:
|
|
877
|
-
color:
|
|
946
|
+
fontFamily: themeVars.typography.sans,
|
|
947
|
+
color: themeVars.content.primary,
|
|
878
948
|
pointerEvents: 'auto' as const,
|
|
879
949
|
},
|
|
880
950
|
inner: {
|
|
@@ -889,47 +959,39 @@ const S = {
|
|
|
889
959
|
title: {
|
|
890
960
|
fontSize: 28,
|
|
891
961
|
fontWeight: 700,
|
|
892
|
-
color:
|
|
962
|
+
color: themeVars.content.primary,
|
|
893
963
|
letterSpacing: 2,
|
|
894
964
|
},
|
|
895
965
|
version: {
|
|
896
|
-
fontSize:
|
|
897
|
-
color:
|
|
966
|
+
fontSize: 'var(--vgai-font-lg)',
|
|
967
|
+
color: themeVars.content.dim,
|
|
898
968
|
marginTop: 4,
|
|
899
969
|
},
|
|
900
970
|
error: {
|
|
901
|
-
background:
|
|
902
|
-
border:
|
|
903
|
-
borderRadius:
|
|
971
|
+
background: TONE_ERROR.bg,
|
|
972
|
+
border: `1px solid ${TONE_ERROR.border}`,
|
|
973
|
+
borderRadius: themeVars.shape.medium,
|
|
904
974
|
padding: '10px 16px',
|
|
905
975
|
marginBottom: 16,
|
|
906
|
-
fontSize:
|
|
907
|
-
color:
|
|
976
|
+
fontSize: 'var(--vgai-font-lg)',
|
|
977
|
+
color: TONE_ERROR.fg,
|
|
908
978
|
display: 'flex',
|
|
909
979
|
justifyContent: 'space-between',
|
|
910
980
|
alignItems: 'center',
|
|
911
981
|
},
|
|
912
|
-
errorClose: {
|
|
913
|
-
background: 'none',
|
|
914
|
-
border: 'none',
|
|
915
|
-
color: '#e8a0a0',
|
|
916
|
-
fontSize: 18,
|
|
917
|
-
cursor: 'pointer',
|
|
918
|
-
padding: '0 4px',
|
|
919
|
-
},
|
|
920
982
|
section: {
|
|
921
983
|
marginBottom: 24,
|
|
922
984
|
},
|
|
923
985
|
sectionTitle: {
|
|
924
|
-
fontSize:
|
|
925
|
-
color:
|
|
986
|
+
fontSize: 'var(--vgai-font-lg)',
|
|
987
|
+
color: themeVars.content.muted,
|
|
926
988
|
textTransform: 'uppercase' as const,
|
|
927
989
|
letterSpacing: 1,
|
|
928
990
|
marginBottom: 12,
|
|
929
991
|
},
|
|
930
992
|
empty: {
|
|
931
|
-
color:
|
|
932
|
-
fontSize:
|
|
993
|
+
color: themeVars.content.dim,
|
|
994
|
+
fontSize: 'var(--vgai-font-lg)',
|
|
933
995
|
padding: '24px 0',
|
|
934
996
|
textAlign: 'center' as const,
|
|
935
997
|
},
|
|
@@ -939,17 +1001,17 @@ const S = {
|
|
|
939
1001
|
gap: 10,
|
|
940
1002
|
},
|
|
941
1003
|
cardName: {
|
|
942
|
-
fontSize:
|
|
1004
|
+
fontSize: 'var(--vgai-font-xl)',
|
|
943
1005
|
fontWeight: 600,
|
|
944
|
-
color:
|
|
1006
|
+
color: themeVars.content.primary,
|
|
945
1007
|
marginBottom: 2,
|
|
946
1008
|
overflow: 'hidden',
|
|
947
1009
|
textOverflow: 'ellipsis',
|
|
948
1010
|
whiteSpace: 'nowrap' as const,
|
|
949
1011
|
},
|
|
950
1012
|
cardMeta: {
|
|
951
|
-
fontSize:
|
|
952
|
-
color:
|
|
1013
|
+
fontSize: 'var(--vgai-font-base)',
|
|
1014
|
+
color: themeVars.content.dim,
|
|
953
1015
|
overflow: 'hidden',
|
|
954
1016
|
textOverflow: 'ellipsis',
|
|
955
1017
|
whiteSpace: 'nowrap' as const,
|
|
@@ -967,8 +1029,8 @@ const S = {
|
|
|
967
1029
|
display: 'flex',
|
|
968
1030
|
flexDirection: 'column' as const,
|
|
969
1031
|
gap: 6,
|
|
970
|
-
fontSize:
|
|
971
|
-
color:
|
|
1032
|
+
fontSize: 'var(--vgai-font-lg)',
|
|
1033
|
+
color: themeVars.content.muted,
|
|
972
1034
|
},
|
|
973
1035
|
locationRow: {
|
|
974
1036
|
display: 'flex',
|
|
@@ -980,20 +1042,20 @@ const S = {
|
|
|
980
1042
|
marginTop: 4,
|
|
981
1043
|
},
|
|
982
1044
|
templateName: {
|
|
983
|
-
fontSize:
|
|
1045
|
+
fontSize: 'var(--vgai-font-xl)',
|
|
984
1046
|
fontWeight: 600,
|
|
985
|
-
color:
|
|
1047
|
+
color: themeVars.content.primary,
|
|
986
1048
|
marginBottom: 4,
|
|
987
1049
|
},
|
|
988
1050
|
templateDesc: {
|
|
989
|
-
fontSize:
|
|
990
|
-
color:
|
|
1051
|
+
fontSize: 'var(--vgai-font-base)',
|
|
1052
|
+
color: themeVars.content.dim,
|
|
991
1053
|
lineHeight: 1.4,
|
|
992
1054
|
},
|
|
993
1055
|
previewPath: {
|
|
994
|
-
fontSize:
|
|
995
|
-
color:
|
|
996
|
-
fontFamily:
|
|
1056
|
+
fontSize: 'var(--vgai-font-md)',
|
|
1057
|
+
color: themeVars.content.dim,
|
|
1058
|
+
fontFamily: themeVars.typography.mono,
|
|
997
1059
|
},
|
|
998
1060
|
formActions: {
|
|
999
1061
|
display: 'flex',
|