@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,6 +1,12 @@
|
|
|
1
|
+
import { Checkbox, FileInput, Select, TextInput, themeVars } from '../design-system';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
3
|
+
* Data-asset EDITING machinery (docs/DATA-TOOLS-DESIGN.md §2.4, re-homed by
|
|
4
|
+
* the workspace-shell program W3 — docs/EDITOR-WORKSPACE-SHELL-TODO.md §9/W3,
|
|
5
|
+
* inventory row B5). Formerly `DataPanel.tsx`, the permanent bottom DATA tab;
|
|
6
|
+
* that tab is gone — each data asset now opens as a NAMED center form/table
|
|
7
|
+
* document (`data-documents.tsx`, kind `'data'`, title = the file's own name
|
|
8
|
+
* per §8, e.g. `tuning.data.json`). This module keeps the editing internals
|
|
9
|
+
* both eras share, unchanged:
|
|
4
10
|
*
|
|
5
11
|
* Architecture (spec §2.1): widgets are generated from the EMITTED
|
|
6
12
|
* `*.schema.json` (JSON Schema, the interchange artifact) via
|
|
@@ -11,38 +17,31 @@
|
|
|
11
17
|
* - **table** (`z.record`) → a grid: one row per key in the data file, one
|
|
12
18
|
* column per row-schema field, every cell a widget.
|
|
13
19
|
*
|
|
14
|
-
* Writes (D1, §2.5): every widget commit
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* Writes (D1, §2.5): every widget commit hands the whole updated file to the
|
|
21
|
+
* caller's `writeFile` immediately — no local buffering, no save button. The
|
|
22
|
+
* write PATH itself (key-order fold → serialize → POST `/__editor/data-file`
|
|
23
|
+
* or history write) lives with the document host (`data-documents.tsx`) and
|
|
24
|
+
* is byte-identical to the old panel's; DATA-TOOLS-DESIGN's transaction rules
|
|
25
|
+
* stay authoritative. The `"$schema"` key is held out of editing everywhere
|
|
26
|
+
* and re-serialized first so git diffs stay minimal.
|
|
18
27
|
*
|
|
19
28
|
* Table rows can be added/removed (W6b) — see `TableEditor` below, which also
|
|
20
29
|
* carries CSV export/import (W6c, `data-csv.ts`) — a spreadsheet-friendly
|
|
21
|
-
* bulk-edit path for the same table. The empty state (`
|
|
30
|
+
* bulk-edit path for the same table. The empty state (`DataEmptyState`, W6d)
|
|
22
31
|
* teaches the create-a-data-asset recipe with a "Copy starter files" button;
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* editor concern).
|
|
32
|
+
* it now renders inside the `Data Assets` center document when the project
|
|
33
|
+
* has no data assets yet. Still pending: adding a whole new FIELD to a
|
|
34
|
+
* schema (a `.schema.ts` code change, not an editor concern).
|
|
27
35
|
*
|
|
28
36
|
* Cross-asset refs (§2.2): a field declared with `dataRef(target)`
|
|
29
37
|
* (`@engine/data/data-ref`) renders as a `stem#key` PICKER instead of free
|
|
30
|
-
* text — `RefOptionsContext`
|
|
31
|
-
*
|
|
32
|
-
* `
|
|
38
|
+
* text — the host provides `RefOptionsContext` (loaded files reduced to
|
|
39
|
+
* `stem -> key[]`) and it threads to `Widget`'s `'ref'` case (deep in
|
|
40
|
+
* `FieldList`/`TableEditor`) without prop-drilling.
|
|
33
41
|
*/
|
|
34
42
|
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
Fragment,
|
|
38
|
-
useCallback,
|
|
39
|
-
useContext,
|
|
40
|
-
useEffect,
|
|
41
|
-
useMemo,
|
|
42
|
-
useRef,
|
|
43
|
-
useState,
|
|
44
|
-
} from 'react';
|
|
45
|
-
import { connectDataFileEvents } from '../asset-events';
|
|
43
|
+
import { createContext, Fragment, useContext, useEffect, useRef, useState } from 'react';
|
|
44
|
+
import { Button, ColorInput, IconButton, NumberInput, Tooltip } from '../design-system';
|
|
46
45
|
import { csvToTable, tableToCsv } from './data-csv';
|
|
47
46
|
import {
|
|
48
47
|
type DataAssetShape,
|
|
@@ -50,19 +49,16 @@ import {
|
|
|
50
49
|
defaultValueForDescriptor,
|
|
51
50
|
describeDataSchema,
|
|
52
51
|
} from './inspectors/json-schema-fields';
|
|
53
|
-
import { ColorInput } from './primitives/ColorInput';
|
|
54
|
-
import { NumberInput } from './primitives/NumberInput';
|
|
55
|
-
import { Tooltip } from './primitives/Tooltip';
|
|
56
52
|
|
|
57
53
|
/** One file as served by GET /__editor/data-files (raw text, client parses). */
|
|
58
|
-
interface ServerDataFile {
|
|
54
|
+
export interface ServerDataFile {
|
|
59
55
|
path: string;
|
|
60
56
|
content: string;
|
|
61
57
|
schema: string | null;
|
|
62
58
|
}
|
|
63
59
|
|
|
64
60
|
/** One file after client-side parsing + schema classification. */
|
|
65
|
-
interface LoadedDataFile {
|
|
61
|
+
export interface LoadedDataFile {
|
|
66
62
|
/** Project-relative path, e.g. `src/data/tuning.data.json`. */
|
|
67
63
|
path: string;
|
|
68
64
|
/** Parsed data object (`"$schema"` entry included), or null on parse error. */
|
|
@@ -87,33 +83,38 @@ interface LoadedDataFile {
|
|
|
87
83
|
|
|
88
84
|
/**
|
|
89
85
|
* Every loaded file's own top-level keys, by asset stem — the `'ref'` widget
|
|
90
|
-
* kind's picker options. `
|
|
91
|
-
* file's raw text (`GET /__editor/data-files`);
|
|
92
|
-
* state into `stem -> key[]` once per
|
|
93
|
-
* `Widget` (deep inside `FieldList`/`TableEditor`)
|
|
94
|
-
* prop through every intermediate component — the
|
|
95
|
-
* field's `refTarget` addresses
|
|
86
|
+
* kind's picker options. The document host (`data-documents.tsx`) already
|
|
87
|
+
* fetches and parses every data file's raw text (`GET /__editor/data-files`);
|
|
88
|
+
* this just reduces that same state into `stem -> key[]` once per files
|
|
89
|
+
* change and threads it to `Widget` (deep inside `FieldList`/`TableEditor`)
|
|
90
|
+
* via context instead of a prop through every intermediate component — the
|
|
91
|
+
* SAME shape a `dataRef` field's `refTarget` addresses
|
|
92
|
+
* (`json-schema-fields.ts`).
|
|
96
93
|
*/
|
|
97
|
-
const RefOptionsContext = createContext<ReadonlyMap<string, string[]>>(new Map());
|
|
98
|
-
|
|
99
|
-
const inputStyle: React.CSSProperties = {
|
|
100
|
-
width: '100%',
|
|
101
|
-
background: '#1e1e1e',
|
|
102
|
-
color: '#c5c8ce',
|
|
103
|
-
border: '1px solid #333',
|
|
104
|
-
borderRadius: 2,
|
|
105
|
-
padding: '2px 4px',
|
|
106
|
-
fontSize: 12,
|
|
107
|
-
boxSizing: 'border-box',
|
|
108
|
-
};
|
|
94
|
+
export const RefOptionsContext = createContext<ReadonlyMap<string, string[]>>(new Map());
|
|
109
95
|
|
|
110
96
|
const labelStyle: React.CSSProperties = {
|
|
111
97
|
fontSize: 11,
|
|
112
|
-
color:
|
|
98
|
+
color: themeVars.content.muted,
|
|
113
99
|
width: 140,
|
|
114
100
|
flexShrink: 0,
|
|
115
101
|
};
|
|
116
102
|
|
|
103
|
+
/**
|
|
104
|
+
* B-10: the table header row previously reused the plain label span with no
|
|
105
|
+
* visual separation from data rows — now a real header band (surface
|
|
106
|
+
* background + hairline bottom border), the Unity/Godot table convention.
|
|
107
|
+
*/
|
|
108
|
+
const tableHeadCellStyle: React.CSSProperties = {
|
|
109
|
+
...labelStyle,
|
|
110
|
+
width: 'auto',
|
|
111
|
+
fontWeight: 600,
|
|
112
|
+
alignSelf: 'stretch',
|
|
113
|
+
background: themeVars.surface.chrome,
|
|
114
|
+
borderBottom: `1px solid ${themeVars.boundary.default}`,
|
|
115
|
+
padding: '3px 4px',
|
|
116
|
+
};
|
|
117
|
+
|
|
117
118
|
/** Scan a (valid-JSON) string literal starting at the opening quote; returns the index just past the closing quote. */
|
|
118
119
|
function skipJsonString(text: string, i: number): number {
|
|
119
120
|
let j = i + 1;
|
|
@@ -196,7 +197,7 @@ function extractTopLevelKeyOrder(text: string): string[] {
|
|
|
196
197
|
return keys;
|
|
197
198
|
}
|
|
198
199
|
|
|
199
|
-
function parseServerFile(f: ServerDataFile): LoadedDataFile {
|
|
200
|
+
export function parseServerFile(f: ServerDataFile): LoadedDataFile {
|
|
200
201
|
let data: Record<string, unknown> | null = null;
|
|
201
202
|
let error: string | undefined;
|
|
202
203
|
try {
|
|
@@ -240,7 +241,10 @@ function parseServerFile(f: ServerDataFile): LoadedDataFile {
|
|
|
240
241
|
* SESSION of edits, not just the first one: each commit's output order
|
|
241
242
|
* becomes the next commit's `prevOrder`.
|
|
242
243
|
*/
|
|
243
|
-
function nextKeyOrder(
|
|
244
|
+
export function nextKeyOrder(
|
|
245
|
+
prevOrder: string[] | undefined,
|
|
246
|
+
obj: Record<string, unknown>,
|
|
247
|
+
): string[] {
|
|
244
248
|
const existing = prevOrder ?? Object.keys(obj);
|
|
245
249
|
const kept = [...new Set(existing)].filter((k) => k in obj);
|
|
246
250
|
const added = Object.keys(obj).filter((k) => !existing.includes(k));
|
|
@@ -260,7 +264,7 @@ function nextKeyOrder(prevOrder: string[] | undefined, obj: Record<string, unkno
|
|
|
260
264
|
* the very first value edit. Emitting the object manually, key by key, in
|
|
261
265
|
* the exact order the caller hands us, sidesteps that reindexing entirely.
|
|
262
266
|
*/
|
|
263
|
-
function serializeDataFile(obj: Record<string, unknown>, keyOrder?: string[]): string {
|
|
267
|
+
export function serializeDataFile(obj: Record<string, unknown>, keyOrder?: string[]): string {
|
|
264
268
|
const hasSchema = '$schema' in obj;
|
|
265
269
|
const rest = (keyOrder ?? Object.keys(obj)).filter((k) => k !== '$schema' && k in obj);
|
|
266
270
|
const order = hasSchema ? ['$schema', ...rest] : rest;
|
|
@@ -282,7 +286,7 @@ function serializeDataFile(obj: Record<string, unknown>, keyOrder?: string[]): s
|
|
|
282
286
|
* than throwing — the write always succeeds, matching the panel's other
|
|
283
287
|
* commit paths (`setCell` has the same defensive fallback).
|
|
284
288
|
*/
|
|
285
|
-
function setDeep(
|
|
289
|
+
export function setDeep(
|
|
286
290
|
obj: Record<string, unknown>,
|
|
287
291
|
path: string[],
|
|
288
292
|
value: unknown,
|
|
@@ -298,139 +302,6 @@ function setDeep(
|
|
|
298
302
|
return { ...obj, [head]: setDeep(childObj, rest, value) };
|
|
299
303
|
}
|
|
300
304
|
|
|
301
|
-
export function DataPanel({ active }: { active: boolean }) {
|
|
302
|
-
const [files, setFiles] = useState<LoadedDataFile[]>([]);
|
|
303
|
-
const [selectedPath, setSelectedPath] = useState<string | null>(null);
|
|
304
|
-
|
|
305
|
-
const refresh = useCallback(async () => {
|
|
306
|
-
try {
|
|
307
|
-
const res = await fetch('/__editor/data-files');
|
|
308
|
-
if (!res.ok) return;
|
|
309
|
-
const { files: raw } = (await res.json()) as { files: ServerDataFile[] };
|
|
310
|
-
setFiles(raw.map(parseServerFile));
|
|
311
|
-
} catch {
|
|
312
|
-
/* server unreachable — keep whatever we last had */
|
|
313
|
-
}
|
|
314
|
-
}, []);
|
|
315
|
-
|
|
316
|
-
// Re-scan whenever the tab is shown (D3: discovery IS the folder scan —
|
|
317
|
-
// never a cached manifest, so activating the tab always reflects disk).
|
|
318
|
-
useEffect(() => {
|
|
319
|
-
if (active) void refresh();
|
|
320
|
-
}, [active, refresh]);
|
|
321
|
-
|
|
322
|
-
// W6a: a NEW `src/data/*.data.json`/`*.schema.json` (or a deleted one)
|
|
323
|
-
// appears/disappears in the master list without switching tabs — the
|
|
324
|
-
// server's `src/`-scoped watcher broadcasts add/unlink only (a `change`
|
|
325
|
-
// event is deliberately excluded: it would echo this panel's own
|
|
326
|
-
// `/__editor/data-file` POST writes back into a refetch, clobbering
|
|
327
|
-
// in-flight drafts). `refresh` is the same rescan the tab-activation
|
|
328
|
-
// effect above already runs.
|
|
329
|
-
useEffect(() => connectDataFileEvents(() => void refresh()), [refresh]);
|
|
330
|
-
|
|
331
|
-
const selected = files.find((f) => f.path === selectedPath) ?? files[0] ?? null;
|
|
332
|
-
|
|
333
|
-
// `'ref'` widget picker options (§2.2's designer payoff): every loaded
|
|
334
|
-
// file's own top-level keys, keyed by asset stem — recomputed only when
|
|
335
|
-
// `files` actually changes (a fresh fetch, or a local commit updating one
|
|
336
|
-
// file's `data` in place).
|
|
337
|
-
const refOptionsByStem = useMemo(() => {
|
|
338
|
-
const map = new Map<string, string[]>();
|
|
339
|
-
for (const f of files) {
|
|
340
|
-
if (!f.data) continue;
|
|
341
|
-
const stem = f.path.replace(/^src\/data\//, '').replace(/\.data\.json$/, '');
|
|
342
|
-
map.set(
|
|
343
|
-
stem,
|
|
344
|
-
Object.keys(f.data).filter((k) => k !== '$schema'),
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
return map;
|
|
348
|
-
}, [files]);
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* D1 (spec §2.5): commit → write the file IMMEDIATELY, no buffering. The
|
|
352
|
-
* local copy is updated in the same tick so widgets stay live; disk is the
|
|
353
|
-
* document. D5 resolution: this deliberately BYPASSES the play-mode overlay
|
|
354
|
-
* (scene-entity-only) — a running game receives the change through the
|
|
355
|
-
* shipped `.data.json` HMR path instead (see the `/__editor/data-file`
|
|
356
|
-
* route's doc comment in editor-server.ts).
|
|
357
|
-
*
|
|
358
|
-
* Takes the whole `LoadedDataFile` (not just its path, #6) so it can read
|
|
359
|
-
* that file's CURRENT `keyOrder` straight from the caller's own prop/state
|
|
360
|
-
* closure — no stale-closure risk from reaching into the panel's `files`
|
|
361
|
-
* state inside this `useCallback([])`. `nextKeyOrder` folds the commit's
|
|
362
|
-
* keys into that order once, and the result becomes this file's `keyOrder`
|
|
363
|
-
* going forward, so a whole session of edits keeps the file's original
|
|
364
|
-
* on-disk key order (integer-like keys included) stable throughout.
|
|
365
|
-
*/
|
|
366
|
-
const writeFile = useCallback((file: LoadedDataFile, nextData: Record<string, unknown>) => {
|
|
367
|
-
const order = nextKeyOrder(file.keyOrder, nextData);
|
|
368
|
-
const content = serializeDataFile(nextData, order);
|
|
369
|
-
setFiles((prev) =>
|
|
370
|
-
prev.map((f) => (f.path === file.path ? { ...f, data: nextData, keyOrder: order } : f)),
|
|
371
|
-
);
|
|
372
|
-
void fetch('/__editor/data-file', {
|
|
373
|
-
method: 'POST',
|
|
374
|
-
headers: { 'Content-Type': 'application/json' },
|
|
375
|
-
body: JSON.stringify({ path: file.path, content }),
|
|
376
|
-
});
|
|
377
|
-
}, []);
|
|
378
|
-
|
|
379
|
-
if (files.length === 0) {
|
|
380
|
-
return <EmptyState />;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return (
|
|
384
|
-
<RefOptionsContext.Provider value={refOptionsByStem}>
|
|
385
|
-
<div style={{ display: 'flex', flex: 1, minHeight: 0 }}>
|
|
386
|
-
{/* File list (master) */}
|
|
387
|
-
<div
|
|
388
|
-
style={{
|
|
389
|
-
width: 200,
|
|
390
|
-
flexShrink: 0,
|
|
391
|
-
borderRight: '1px solid #333',
|
|
392
|
-
overflowY: 'auto',
|
|
393
|
-
padding: '4px 0',
|
|
394
|
-
}}
|
|
395
|
-
>
|
|
396
|
-
{files.map((f) => {
|
|
397
|
-
const isSelected = f.path === selected?.path;
|
|
398
|
-
const name = f.path.replace(/^src\/data\//, '').replace(/\.data\.json$/, '');
|
|
399
|
-
return (
|
|
400
|
-
<button
|
|
401
|
-
key={f.path}
|
|
402
|
-
type="button"
|
|
403
|
-
data-testid={`data-file-${name}`}
|
|
404
|
-
onClick={() => setSelectedPath(f.path)}
|
|
405
|
-
title={f.path}
|
|
406
|
-
style={{
|
|
407
|
-
display: 'block',
|
|
408
|
-
width: '100%',
|
|
409
|
-
textAlign: 'left',
|
|
410
|
-
background: isSelected ? '#2c2c2c' : 'transparent',
|
|
411
|
-
color: isSelected ? '#c5c8ce' : '#8c8c8c',
|
|
412
|
-
border: 'none',
|
|
413
|
-
borderLeft: isSelected ? '2px solid #579EFF' : '2px solid transparent',
|
|
414
|
-
padding: '4px 10px',
|
|
415
|
-
cursor: 'pointer',
|
|
416
|
-
fontSize: 12,
|
|
417
|
-
}}
|
|
418
|
-
>
|
|
419
|
-
{name}
|
|
420
|
-
</button>
|
|
421
|
-
);
|
|
422
|
-
})}
|
|
423
|
-
</div>
|
|
424
|
-
|
|
425
|
-
{/* Editor (detail) */}
|
|
426
|
-
<div style={{ flex: 1, minWidth: 0, overflow: 'auto', padding: 10 }}>
|
|
427
|
-
{selected && <DataFileEditor file={selected} writeFile={writeFile} />}
|
|
428
|
-
</div>
|
|
429
|
-
</div>
|
|
430
|
-
</RefOptionsContext.Provider>
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
305
|
/**
|
|
435
306
|
* A minimal, paste-ready `.schema.ts` + `.data.json` pair (W6d) — mirrors the
|
|
436
307
|
* shape of the shipped `tuning.*` trio (template/src/data/README.md), just
|
|
@@ -459,14 +330,16 @@ export type Example = z.infer<typeof ExampleSchema>;
|
|
|
459
330
|
`;
|
|
460
331
|
|
|
461
332
|
/**
|
|
462
|
-
*
|
|
463
|
-
* no `src/data/*.data.json` yet. Teaches the concrete recipe rather than
|
|
464
|
-
* naming the shape, plus a one-click "Copy starter files" so the first
|
|
465
|
-
* doesn't require memorizing the trio from scratch.
|
|
466
|
-
* `
|
|
467
|
-
*
|
|
333
|
+
* Data-assets empty state (W6d, docs/DATA-TOOLS-DESIGN.md §3.5): the project
|
|
334
|
+
* has no `src/data/*.data.json` yet. Teaches the concrete recipe rather than
|
|
335
|
+
* just naming the shape, plus a one-click "Copy starter files" so the first
|
|
336
|
+
* asset doesn't require memorizing the trio from scratch. Rendered by the
|
|
337
|
+
* `Data Assets` center document (`data-documents.tsx`) since W3 re-homed the
|
|
338
|
+
* bottom DATA tab. Once the files exist + `npm run emit-schemas` has run,
|
|
339
|
+
* the new asset appears automatically — no reload, W6a's src-watcher SSE
|
|
340
|
+
* channel picks it up.
|
|
468
341
|
*/
|
|
469
|
-
function
|
|
342
|
+
export function DataEmptyState() {
|
|
470
343
|
const [copied, setCopied] = useState(false);
|
|
471
344
|
|
|
472
345
|
const copyStarter = () => {
|
|
@@ -477,7 +350,7 @@ function EmptyState() {
|
|
|
477
350
|
};
|
|
478
351
|
|
|
479
352
|
return (
|
|
480
|
-
<div style={{ padding: 12, fontSize: 12, color:
|
|
353
|
+
<div style={{ padding: 12, fontSize: 12, color: themeVars.content.muted, maxWidth: 480 }}>
|
|
481
354
|
<p style={{ margin: '0 0 8px' }}>
|
|
482
355
|
No data assets yet — a data asset is a <code>src/data/*.data.json</code> file governed by a
|
|
483
356
|
Zod schema (docs/DATA-TOOLS-DESIGN.md §2).
|
|
@@ -500,27 +373,14 @@ function EmptyState() {
|
|
|
500
373
|
See the project's <code>src/data/README.md</code> for the full trio doctrine and the shipped{' '}
|
|
501
374
|
<code>tuning.*</code> worked example.
|
|
502
375
|
</p>
|
|
503
|
-
<
|
|
504
|
-
type="button"
|
|
505
|
-
data-testid="data-empty-copy-starter"
|
|
506
|
-
onClick={copyStarter}
|
|
507
|
-
style={{
|
|
508
|
-
background: '#2c2c2c',
|
|
509
|
-
color: '#c5c8ce',
|
|
510
|
-
border: '1px solid #444',
|
|
511
|
-
borderRadius: 2,
|
|
512
|
-
padding: '4px 10px',
|
|
513
|
-
fontSize: 12,
|
|
514
|
-
cursor: 'pointer',
|
|
515
|
-
}}
|
|
516
|
-
>
|
|
376
|
+
<Button size="compact" data-testid="data-empty-copy-starter" onClick={copyStarter}>
|
|
517
377
|
{copied ? 'Copied' : 'Copy starter files'}
|
|
518
|
-
</
|
|
378
|
+
</Button>
|
|
519
379
|
</div>
|
|
520
380
|
);
|
|
521
381
|
}
|
|
522
382
|
|
|
523
|
-
function DataFileEditor({
|
|
383
|
+
export function DataFileEditor({
|
|
524
384
|
file,
|
|
525
385
|
writeFile,
|
|
526
386
|
}: {
|
|
@@ -577,10 +437,16 @@ function DataFileEditor({
|
|
|
577
437
|
);
|
|
578
438
|
}
|
|
579
439
|
|
|
580
|
-
function Message({ text, tone }: { text: string; tone: 'error' | 'warn' }) {
|
|
440
|
+
function Message({ text: message, tone }: { text: string; tone: 'error' | 'warn' }) {
|
|
581
441
|
return (
|
|
582
|
-
<div
|
|
583
|
-
{
|
|
442
|
+
<div
|
|
443
|
+
style={{
|
|
444
|
+
fontSize: 12,
|
|
445
|
+
color: tone === 'error' ? themeVars.semantic.danger : themeVars.semantic.warning,
|
|
446
|
+
maxWidth: 560,
|
|
447
|
+
}}
|
|
448
|
+
>
|
|
449
|
+
{message}
|
|
584
450
|
</div>
|
|
585
451
|
);
|
|
586
452
|
}
|
|
@@ -685,43 +551,24 @@ function TableEditor({
|
|
|
685
551
|
return (
|
|
686
552
|
<div>
|
|
687
553
|
<div style={{ display: 'flex', gap: 6, marginBottom: 8 }}>
|
|
688
|
-
<
|
|
689
|
-
|
|
554
|
+
<Button
|
|
555
|
+
size="compact"
|
|
690
556
|
data-testid="data-csv-export"
|
|
691
557
|
onClick={exportCsv}
|
|
692
558
|
title="Download this table as a CSV file"
|
|
693
|
-
style={{
|
|
694
|
-
background: '#2c2c2c',
|
|
695
|
-
color: '#c5c8ce',
|
|
696
|
-
border: '1px solid #444',
|
|
697
|
-
borderRadius: 2,
|
|
698
|
-
padding: '3px 10px',
|
|
699
|
-
fontSize: 12,
|
|
700
|
-
cursor: 'pointer',
|
|
701
|
-
}}
|
|
702
559
|
>
|
|
703
560
|
Export CSV
|
|
704
|
-
</
|
|
705
|
-
<
|
|
706
|
-
|
|
561
|
+
</Button>
|
|
562
|
+
<Button
|
|
563
|
+
size="compact"
|
|
707
564
|
data-testid="data-csv-import"
|
|
708
565
|
onClick={() => fileInputRef.current?.click()}
|
|
709
566
|
title="Import replaces every row in this file (undo is git)."
|
|
710
|
-
style={{
|
|
711
|
-
background: '#2c2c2c',
|
|
712
|
-
color: '#c5c8ce',
|
|
713
|
-
border: '1px solid #444',
|
|
714
|
-
borderRadius: 2,
|
|
715
|
-
padding: '3px 10px',
|
|
716
|
-
fontSize: 12,
|
|
717
|
-
cursor: 'pointer',
|
|
718
|
-
}}
|
|
719
567
|
>
|
|
720
568
|
Import CSV
|
|
721
|
-
</
|
|
722
|
-
<
|
|
569
|
+
</Button>
|
|
570
|
+
<FileInput
|
|
723
571
|
ref={fileInputRef}
|
|
724
|
-
type="file"
|
|
725
572
|
accept=".csv,text/csv"
|
|
726
573
|
style={{ display: 'none' }}
|
|
727
574
|
onChange={(e) => void importCsv(e.target.files)}
|
|
@@ -743,11 +590,11 @@ function TableEditor({
|
|
|
743
590
|
width: 'max-content',
|
|
744
591
|
}}
|
|
745
592
|
>
|
|
746
|
-
{/* Header row */}
|
|
747
|
-
<span style={
|
|
593
|
+
{/* Header row (B-10: real header band, not a bare label span) */}
|
|
594
|
+
<span style={tableHeadCellStyle} />
|
|
748
595
|
{rowFields.map((f) => {
|
|
749
596
|
const head = (
|
|
750
|
-
<span key={f.name} style={
|
|
597
|
+
<span key={f.name} style={tableHeadCellStyle}>
|
|
751
598
|
{f.name}
|
|
752
599
|
</span>
|
|
753
600
|
);
|
|
@@ -759,14 +606,21 @@ function TableEditor({
|
|
|
759
606
|
head
|
|
760
607
|
);
|
|
761
608
|
})}
|
|
762
|
-
<span />
|
|
609
|
+
<span style={tableHeadCellStyle} />
|
|
763
610
|
|
|
764
611
|
{rowKeys.map((rowKey) => {
|
|
765
612
|
const row = data[rowKey];
|
|
766
613
|
const rowObj = row && typeof row === 'object' ? (row as Record<string, unknown>) : {};
|
|
767
614
|
return (
|
|
768
615
|
<Fragment key={rowKey}>
|
|
769
|
-
<span
|
|
616
|
+
<span
|
|
617
|
+
style={{
|
|
618
|
+
...labelStyle,
|
|
619
|
+
width: 'auto',
|
|
620
|
+
color: themeVars.content.primary,
|
|
621
|
+
fontWeight: 600,
|
|
622
|
+
}}
|
|
623
|
+
>
|
|
770
624
|
{rowKey}
|
|
771
625
|
</span>
|
|
772
626
|
{rowFields.map((f) => (
|
|
@@ -778,30 +632,24 @@ function TableEditor({
|
|
|
778
632
|
testId={`data-cell-${rowKey}-${f.name}`}
|
|
779
633
|
/>
|
|
780
634
|
))}
|
|
781
|
-
<
|
|
635
|
+
<IconButton
|
|
782
636
|
type="button"
|
|
637
|
+
variant="danger"
|
|
638
|
+
size="compact"
|
|
639
|
+
aria-label={`Remove row ${rowKey}`}
|
|
783
640
|
data-testid={`data-row-remove-${rowKey}`}
|
|
784
641
|
title="Remove row (undo is git)"
|
|
785
642
|
onClick={() => removeRow(rowKey)}
|
|
786
|
-
style={{
|
|
787
|
-
background: 'transparent',
|
|
788
|
-
border: 'none',
|
|
789
|
-
color: '#8c8c8c',
|
|
790
|
-
cursor: 'pointer',
|
|
791
|
-
fontSize: 13,
|
|
792
|
-
lineHeight: 1,
|
|
793
|
-
padding: '2px 4px',
|
|
794
|
-
}}
|
|
795
643
|
>
|
|
796
644
|
×
|
|
797
|
-
</
|
|
645
|
+
</IconButton>
|
|
798
646
|
</Fragment>
|
|
799
647
|
);
|
|
800
648
|
})}
|
|
801
649
|
</div>
|
|
802
650
|
|
|
803
651
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 8 }}>
|
|
804
|
-
<
|
|
652
|
+
<TextInput
|
|
805
653
|
data-testid="data-row-add-key"
|
|
806
654
|
placeholder="new-row-key"
|
|
807
655
|
value={newKey}
|
|
@@ -812,24 +660,12 @@ function TableEditor({
|
|
|
812
660
|
onKeyDown={(e) => {
|
|
813
661
|
if (e.key === 'Enter') addRow();
|
|
814
662
|
}}
|
|
815
|
-
|
|
663
|
+
className="vgai-input"
|
|
664
|
+
style={{ width: 160 }}
|
|
816
665
|
/>
|
|
817
|
-
<
|
|
818
|
-
type="button"
|
|
819
|
-
data-testid="data-row-add"
|
|
820
|
-
onClick={addRow}
|
|
821
|
-
style={{
|
|
822
|
-
background: '#2c2c2c',
|
|
823
|
-
color: '#c5c8ce',
|
|
824
|
-
border: '1px solid #444',
|
|
825
|
-
borderRadius: 2,
|
|
826
|
-
padding: '3px 10px',
|
|
827
|
-
fontSize: 12,
|
|
828
|
-
cursor: 'pointer',
|
|
829
|
-
}}
|
|
830
|
-
>
|
|
666
|
+
<Button size="compact" data-testid="data-row-add" onClick={addRow}>
|
|
831
667
|
Add row
|
|
832
|
-
</
|
|
668
|
+
</Button>
|
|
833
669
|
</div>
|
|
834
670
|
{addError && (
|
|
835
671
|
<div style={{ marginTop: 4 }}>
|
|
@@ -901,8 +737,8 @@ function FieldList({
|
|
|
901
737
|
key={f.name}
|
|
902
738
|
data-testid={testId}
|
|
903
739
|
style={{
|
|
904
|
-
border:
|
|
905
|
-
borderRadius:
|
|
740
|
+
border: `1px solid ${themeVars.boundary.default}`,
|
|
741
|
+
borderRadius: themeVars.shape.small,
|
|
906
742
|
padding: '6px 8px',
|
|
907
743
|
marginBottom: 6,
|
|
908
744
|
marginLeft: Math.min(path.length, 3) * 10,
|
|
@@ -936,7 +772,9 @@ function FieldList({
|
|
|
936
772
|
|
|
937
773
|
function GroupHeader({ name, description }: { name: string; description?: string | undefined }) {
|
|
938
774
|
const label = (
|
|
939
|
-
<div style={{ fontSize: 11, color:
|
|
775
|
+
<div style={{ fontSize: 11, color: themeVars.content.muted, fontWeight: 600, marginBottom: 4 }}>
|
|
776
|
+
{name}
|
|
777
|
+
</div>
|
|
940
778
|
);
|
|
941
779
|
return description ? (
|
|
942
780
|
<Tooltip text={description} position="right">
|
|
@@ -975,8 +813,8 @@ function Widget({
|
|
|
975
813
|
return <NumberCell field={field} value={value} onCommit={onCommit} testId={testId} />;
|
|
976
814
|
case 'boolean':
|
|
977
815
|
return (
|
|
978
|
-
<
|
|
979
|
-
|
|
816
|
+
<Checkbox
|
|
817
|
+
className="vgai-checkbox"
|
|
980
818
|
data-testid={testId}
|
|
981
819
|
checked={Boolean(value)}
|
|
982
820
|
onChange={(e) => onCommit(e.target.checked)}
|
|
@@ -990,18 +828,19 @@ function Widget({
|
|
|
990
828
|
);
|
|
991
829
|
case 'enum':
|
|
992
830
|
return (
|
|
993
|
-
<
|
|
831
|
+
<Select
|
|
994
832
|
data-testid={testId}
|
|
995
833
|
value={value == null ? '' : String(value)}
|
|
996
834
|
onChange={(e) => onCommit(e.target.value)}
|
|
997
|
-
|
|
835
|
+
className="vgai-select"
|
|
836
|
+
style={{ width: '100%' }}
|
|
998
837
|
>
|
|
999
838
|
{(field.options ?? []).map((opt) => (
|
|
1000
839
|
<option key={opt} value={opt}>
|
|
1001
840
|
{opt}
|
|
1002
841
|
</option>
|
|
1003
842
|
))}
|
|
1004
|
-
</
|
|
843
|
+
</Select>
|
|
1005
844
|
);
|
|
1006
845
|
case 'string':
|
|
1007
846
|
return <StringCell value={value} onCommit={onCommit} testId={testId} />;
|
|
@@ -1026,11 +865,12 @@ function Widget({
|
|
|
1026
865
|
const options = targetKeys.map((k) => `${targetStem}#${k}`);
|
|
1027
866
|
const isDangling = current !== '' && !options.includes(current);
|
|
1028
867
|
return (
|
|
1029
|
-
<
|
|
868
|
+
<Select
|
|
1030
869
|
data-testid={testId}
|
|
1031
870
|
value={current}
|
|
1032
871
|
onChange={(e) => onCommit(e.target.value)}
|
|
1033
|
-
|
|
872
|
+
className="vgai-select"
|
|
873
|
+
style={{ width: '100%' }}
|
|
1034
874
|
>
|
|
1035
875
|
<option value="">—</option>
|
|
1036
876
|
{options.map((opt) => (
|
|
@@ -1039,7 +879,7 @@ function Widget({
|
|
|
1039
879
|
</option>
|
|
1040
880
|
))}
|
|
1041
881
|
{isDangling && <option value={current}>{current} (dangling)</option>}
|
|
1042
|
-
</
|
|
882
|
+
</Select>
|
|
1043
883
|
);
|
|
1044
884
|
}
|
|
1045
885
|
default:
|
|
@@ -1147,7 +987,7 @@ function StringCell({
|
|
|
1147
987
|
};
|
|
1148
988
|
|
|
1149
989
|
return (
|
|
1150
|
-
<
|
|
990
|
+
<TextInput
|
|
1151
991
|
data-testid={testId}
|
|
1152
992
|
value={draft}
|
|
1153
993
|
onChange={(e) => setDraft(e.target.value)}
|
|
@@ -1162,7 +1002,8 @@ function StringCell({
|
|
|
1162
1002
|
(e.target as HTMLInputElement).blur();
|
|
1163
1003
|
}
|
|
1164
1004
|
}}
|
|
1165
|
-
|
|
1005
|
+
className="vgai-input"
|
|
1006
|
+
style={{ width: '100%' }}
|
|
1166
1007
|
/>
|
|
1167
1008
|
);
|
|
1168
1009
|
}
|
|
@@ -1230,7 +1071,7 @@ function JsonCell({
|
|
|
1230
1071
|
|
|
1231
1072
|
return (
|
|
1232
1073
|
<div>
|
|
1233
|
-
<
|
|
1074
|
+
<TextInput
|
|
1234
1075
|
data-testid={testId}
|
|
1235
1076
|
value={draft}
|
|
1236
1077
|
onChange={(e) => {
|
|
@@ -1248,7 +1089,16 @@ function JsonCell({
|
|
|
1248
1089
|
(e.target as HTMLInputElement).blur();
|
|
1249
1090
|
}
|
|
1250
1091
|
}}
|
|
1251
|
-
|
|
1092
|
+
className="vgai-input"
|
|
1093
|
+
style={
|
|
1094
|
+
invalid
|
|
1095
|
+
? {
|
|
1096
|
+
width: '100%',
|
|
1097
|
+
borderColor: themeVars.semantic.danger,
|
|
1098
|
+
color: themeVars.semantic.danger,
|
|
1099
|
+
}
|
|
1100
|
+
: { width: '100%' }
|
|
1101
|
+
}
|
|
1252
1102
|
title={
|
|
1253
1103
|
invalid
|
|
1254
1104
|
? 'Not valid JSON — nothing written. Fix the JSON or press Escape to revert.'
|
|
@@ -1258,7 +1108,7 @@ function JsonCell({
|
|
|
1258
1108
|
{invalid && (
|
|
1259
1109
|
<div
|
|
1260
1110
|
data-testid={`${testId}-error`}
|
|
1261
|
-
style={{ fontSize: 10, color:
|
|
1111
|
+
style={{ fontSize: 10, color: themeVars.semantic.danger, marginTop: 2 }}
|
|
1262
1112
|
>
|
|
1263
1113
|
Not valid JSON — nothing written. Fix the JSON or press Escape to revert.
|
|
1264
1114
|
</div>
|