@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, Checkbox, Select, TextInput } from '../design-system';
|
|
1
2
|
/**
|
|
2
3
|
* React-world inspector section (C2, spec 27 §5 C2,
|
|
3
4
|
* docs/unified-world-editor/27-visual-react-editing.md) — the "wire the
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
* `PropertyDescriptor.group` (falling back to one flat "Style" group for an
|
|
20
21
|
* adapter, like `ReactDomAuthoringAdapter`, whose descriptors declare none),
|
|
21
22
|
* mapping each descriptor to the right C1 widget instead of a generic
|
|
22
|
-
* `<
|
|
23
|
+
* `<TextInput>` — see `widgetKindFor`'s doc comment for the exact mapping table.
|
|
23
24
|
* `suppresses: ['properties']` replaces the shell's generic property grid
|
|
24
25
|
* for a matched node (Rule zero: this section still talks to the node ONLY
|
|
25
26
|
* through `adapter.inspector`/`adapter.hierarchy`, never the DOM/fiber the
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
*/
|
|
40
41
|
|
|
41
42
|
import type { AuthoringAdapter, EditorNode, PropertyDescriptor } from '@engine/adapter';
|
|
42
|
-
import { useMemo } from 'react';
|
|
43
|
+
import { useMemo, useState } from 'react';
|
|
43
44
|
import { groupProperties } from '../components/inspector-property-grouping';
|
|
44
45
|
import {
|
|
45
46
|
AlignmentGrid,
|
|
@@ -71,12 +72,15 @@ import {
|
|
|
71
72
|
TextShadowEditor,
|
|
72
73
|
THEME,
|
|
73
74
|
} from '../components/inspector-widgets';
|
|
75
|
+
import { LABEL_COL_WIDTH } from '../components/inspector-widgets/shared';
|
|
76
|
+
import { DisclosureIcon, themeVars } from '../design-system';
|
|
74
77
|
import { useEditorStore } from '../EditorContext';
|
|
75
78
|
import {
|
|
76
79
|
type InspectorSectionProps,
|
|
77
80
|
registerInspectorSections,
|
|
78
81
|
} from '../inspector-section-registry';
|
|
79
82
|
import { effectiveColorFromChain, getAvailableFonts } from '../ui-source/inspect';
|
|
83
|
+
import type { OidEntry } from '../ui-source/oid-transform';
|
|
80
84
|
import { CompositeAuthoringAdapter } from './composite-authoring-adapter';
|
|
81
85
|
import { beginEyedropperSession } from './eyedropper-session';
|
|
82
86
|
import { ReactDomAuthoringAdapter } from './react-dom-authoring-adapter';
|
|
@@ -294,7 +298,7 @@ function Field({
|
|
|
294
298
|
data-testid={testId ? `${testId}-row` : undefined}
|
|
295
299
|
style={{ ...rowStyle, padding: '4px 8px' }}
|
|
296
300
|
>
|
|
297
|
-
<span style={{ width:
|
|
301
|
+
<span style={{ width: LABEL_COL_WIDTH, flexShrink: 0, fontSize: 11, color: THEME.textMuted }}>
|
|
298
302
|
{label}
|
|
299
303
|
</span>
|
|
300
304
|
<div style={{ flex: 1, minWidth: 0 }}>{children}</div>
|
|
@@ -374,21 +378,16 @@ function GradientRow({ prop, raw, disabled, testId, commit }: KindRowProps): Rea
|
|
|
374
378
|
if (!parsed) {
|
|
375
379
|
return (
|
|
376
380
|
<Field label={prop.label} testId={testId}>
|
|
377
|
-
<
|
|
381
|
+
<Button
|
|
378
382
|
type="button"
|
|
383
|
+
variant="secondary"
|
|
384
|
+
className="vgai-field-trigger"
|
|
379
385
|
data-testid={testId}
|
|
380
386
|
disabled={disabled}
|
|
381
387
|
onClick={() => commit(prop.path, composeGradient(DEFAULT_GRADIENT))}
|
|
382
|
-
style={{
|
|
383
|
-
...inputStyle(disabled),
|
|
384
|
-
width: '100%',
|
|
385
|
-
textAlign: 'left',
|
|
386
|
-
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
387
|
-
color: THEME.textMuted,
|
|
388
|
-
}}
|
|
389
388
|
>
|
|
390
389
|
No gradient — click to add
|
|
391
|
-
</
|
|
390
|
+
</Button>
|
|
392
391
|
</Field>
|
|
393
392
|
);
|
|
394
393
|
}
|
|
@@ -538,15 +537,15 @@ function ScrubRow({ prop, raw, disabled, testId, commit }: KindRowProps): React.
|
|
|
538
537
|
function SelectRow({ prop, raw, disabled, testId, commit }: KindRowProps): React.ReactElement {
|
|
539
538
|
return (
|
|
540
539
|
<Field label={prop.label} testId={testId}>
|
|
541
|
-
<
|
|
540
|
+
<Select
|
|
541
|
+
className="vgai-select"
|
|
542
542
|
data-testid={testId}
|
|
543
543
|
value={String(raw ?? '')}
|
|
544
544
|
disabled={disabled}
|
|
545
545
|
onChange={(e) => commit(prop.path, e.target.value)}
|
|
546
546
|
style={{
|
|
547
|
-
...inputStyle(disabled),
|
|
548
547
|
width: '100%',
|
|
549
|
-
|
|
548
|
+
...(disabled ? { background: THEME.dynamicBg, color: THEME.dynamic } : null),
|
|
550
549
|
}}
|
|
551
550
|
>
|
|
552
551
|
{(prop.options ?? []).map((o) => (
|
|
@@ -554,7 +553,7 @@ function SelectRow({ prop, raw, disabled, testId, commit }: KindRowProps): React
|
|
|
554
553
|
{String(o)}
|
|
555
554
|
</option>
|
|
556
555
|
))}
|
|
557
|
-
</
|
|
556
|
+
</Select>
|
|
558
557
|
</Field>
|
|
559
558
|
);
|
|
560
559
|
}
|
|
@@ -564,8 +563,8 @@ function CheckboxRow({ prop, raw, disabled, testId, commit }: KindRowProps): Rea
|
|
|
564
563
|
<label
|
|
565
564
|
style={{ display: 'flex', gap: 6, alignItems: 'center', padding: '6px 8px', fontSize: 12 }}
|
|
566
565
|
>
|
|
567
|
-
<
|
|
568
|
-
|
|
566
|
+
<Checkbox
|
|
567
|
+
className="vgai-checkbox"
|
|
569
568
|
data-testid={testId}
|
|
570
569
|
checked={raw !== false}
|
|
571
570
|
disabled={disabled}
|
|
@@ -599,7 +598,7 @@ function JsonRow({ prop, raw, disabled, testId, commit }: KindRowProps): React.R
|
|
|
599
598
|
function TextRow({ prop, raw, disabled, testId, commit }: KindRowProps): React.ReactElement {
|
|
600
599
|
return (
|
|
601
600
|
<Field label={prop.label} testId={testId}>
|
|
602
|
-
<
|
|
601
|
+
<TextInput
|
|
603
602
|
type="text"
|
|
604
603
|
data-testid={testId}
|
|
605
604
|
readOnly={disabled}
|
|
@@ -897,6 +896,256 @@ function rowKey(row: PropertyRow): string {
|
|
|
897
896
|
return `align:${row.justify.path}`;
|
|
898
897
|
}
|
|
899
898
|
|
|
899
|
+
function compactScrub(
|
|
900
|
+
adapter: AuthoringAdapter,
|
|
901
|
+
id: string,
|
|
902
|
+
prop: PropertyDescriptor | undefined,
|
|
903
|
+
label: string,
|
|
904
|
+
commit: (path: string, value: unknown) => void,
|
|
905
|
+
): React.ReactElement | null {
|
|
906
|
+
if (!prop) return null;
|
|
907
|
+
const unit = UNITLESS_NUMBER_PROPS.has(propNameFromPath(prop.path)) ? '' : 'px';
|
|
908
|
+
const testId = testIdFor(prop.path);
|
|
909
|
+
return (
|
|
910
|
+
<ScrubbableInput
|
|
911
|
+
key={prop.path}
|
|
912
|
+
testId={testId}
|
|
913
|
+
label={label}
|
|
914
|
+
value={numericValue(adapter.inspector?.get(id, prop.path))}
|
|
915
|
+
unit={unit}
|
|
916
|
+
disabled={prop.readonly}
|
|
917
|
+
onChange={(value) => commit(prop.path, value)}
|
|
918
|
+
onChangeEnd={(value) => commit(prop.path, value)}
|
|
919
|
+
style={{ flex: 1, minWidth: 0 }}
|
|
920
|
+
/>
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function SemanticLayout({
|
|
925
|
+
adapter,
|
|
926
|
+
id,
|
|
927
|
+
props,
|
|
928
|
+
fonts,
|
|
929
|
+
showConstraints,
|
|
930
|
+
onToggleConstraints,
|
|
931
|
+
}: {
|
|
932
|
+
adapter: AuthoringAdapter;
|
|
933
|
+
id: string;
|
|
934
|
+
props: readonly PropertyDescriptor[];
|
|
935
|
+
fonts: readonly string[];
|
|
936
|
+
showConstraints: boolean;
|
|
937
|
+
onToggleConstraints: () => void;
|
|
938
|
+
}): React.ReactElement {
|
|
939
|
+
const store = useEditorStore();
|
|
940
|
+
const byName = new Map(props.map((prop) => [propNameFromPath(prop.path), prop] as const));
|
|
941
|
+
const commit = (path: string, value: unknown): void => {
|
|
942
|
+
adapter.inspector?.set(id, path, value);
|
|
943
|
+
store.notifyIngestEdit();
|
|
944
|
+
};
|
|
945
|
+
const display = String(
|
|
946
|
+
adapter.inspector?.get(id, byName.get('display')?.path ?? 'style.display') ?? '',
|
|
947
|
+
);
|
|
948
|
+
const isFlex = display === 'flex' || display === 'inline-flex';
|
|
949
|
+
const displayProp = byName.get('display');
|
|
950
|
+
const gapProp = byName.get('gap');
|
|
951
|
+
const alignRows = buildPropertyRows(
|
|
952
|
+
props.filter((prop) => ['justifyContent', 'alignItems'].includes(propNameFromPath(prop.path))),
|
|
953
|
+
);
|
|
954
|
+
const ordinary = ['flexDirection', 'flexWrap', 'overflow']
|
|
955
|
+
.map((name) => byName.get(name))
|
|
956
|
+
.filter((prop): prop is PropertyDescriptor => !!prop);
|
|
957
|
+
return (
|
|
958
|
+
<div style={{ padding: '2px 8px 7px' }}>
|
|
959
|
+
<div style={{ display: 'flex', gap: 6 }}>
|
|
960
|
+
{compactScrub(adapter, id, byName.get('width'), 'W', commit)}
|
|
961
|
+
{compactScrub(adapter, id, byName.get('height'), 'H', commit)}
|
|
962
|
+
</div>
|
|
963
|
+
{showConstraints && (
|
|
964
|
+
<div data-testid="react-layout-constraints" style={{ marginTop: 5 }}>
|
|
965
|
+
<div style={{ display: 'flex', gap: 6 }}>
|
|
966
|
+
{compactScrub(adapter, id, byName.get('minWidth'), 'Min W', commit)}
|
|
967
|
+
{compactScrub(adapter, id, byName.get('minHeight'), 'Min H', commit)}
|
|
968
|
+
</div>
|
|
969
|
+
<div style={{ display: 'flex', gap: 6, marginTop: 4 }}>
|
|
970
|
+
{compactScrub(adapter, id, byName.get('maxWidth'), 'Max W', commit)}
|
|
971
|
+
{compactScrub(adapter, id, byName.get('maxHeight'), 'Max H', commit)}
|
|
972
|
+
</div>
|
|
973
|
+
</div>
|
|
974
|
+
)}
|
|
975
|
+
<Button
|
|
976
|
+
type="button"
|
|
977
|
+
variant="ghost"
|
|
978
|
+
size="compact"
|
|
979
|
+
data-testid="react-layout-toggle-constraints"
|
|
980
|
+
onClick={onToggleConstraints}
|
|
981
|
+
style={{ display: 'flex', margin: '2px auto 5px' }}
|
|
982
|
+
>
|
|
983
|
+
{showConstraints ? '− constraints' : '··· constraints'}
|
|
984
|
+
</Button>
|
|
985
|
+
<div
|
|
986
|
+
style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.35fr) minmax(0, 1fr)', gap: 6 }}
|
|
987
|
+
>
|
|
988
|
+
{displayProp && (
|
|
989
|
+
<Select
|
|
990
|
+
data-testid={testIdFor(displayProp.path)}
|
|
991
|
+
value={display}
|
|
992
|
+
disabled={displayProp.readonly}
|
|
993
|
+
onChange={(event) => commit(displayProp.path, event.target.value)}
|
|
994
|
+
style={{ ...inputStyle(displayProp.readonly), width: '100%', cursor: 'pointer' }}
|
|
995
|
+
>
|
|
996
|
+
{(displayProp.options ?? []).map((option) => (
|
|
997
|
+
<option key={String(option)} value={String(option)}>
|
|
998
|
+
{String(option)}
|
|
999
|
+
</option>
|
|
1000
|
+
))}
|
|
1001
|
+
</Select>
|
|
1002
|
+
)}
|
|
1003
|
+
{compactScrub(adapter, id, gapProp, 'Gap', commit)}
|
|
1004
|
+
</div>
|
|
1005
|
+
{isFlex && (
|
|
1006
|
+
<div data-testid="react-layout-flex-controls" style={{ marginTop: 5 }}>
|
|
1007
|
+
{ordinary
|
|
1008
|
+
.filter((prop) => propNameFromPath(prop.path) !== 'overflow')
|
|
1009
|
+
.map((prop) => (
|
|
1010
|
+
<SingleRow key={prop.path} adapter={adapter} id={id} prop={prop} fonts={fonts} />
|
|
1011
|
+
))}
|
|
1012
|
+
{alignRows.map((row) =>
|
|
1013
|
+
row.kind === 'single' ? (
|
|
1014
|
+
<SingleRow
|
|
1015
|
+
key={rowKey(row)}
|
|
1016
|
+
adapter={adapter}
|
|
1017
|
+
id={id}
|
|
1018
|
+
prop={row.prop}
|
|
1019
|
+
fonts={fonts}
|
|
1020
|
+
/>
|
|
1021
|
+
) : (
|
|
1022
|
+
<ComboRow key={rowKey(row)} adapter={adapter} id={id} row={row} />
|
|
1023
|
+
),
|
|
1024
|
+
)}
|
|
1025
|
+
</div>
|
|
1026
|
+
)}
|
|
1027
|
+
{ordinary
|
|
1028
|
+
.filter((prop) => propNameFromPath(prop.path) === 'overflow')
|
|
1029
|
+
.map((prop) => (
|
|
1030
|
+
<SingleRow key={prop.path} adapter={adapter} id={id} prop={prop} fonts={fonts} />
|
|
1031
|
+
))}
|
|
1032
|
+
</div>
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function SemanticPosition({
|
|
1037
|
+
adapter,
|
|
1038
|
+
id,
|
|
1039
|
+
props,
|
|
1040
|
+
fonts,
|
|
1041
|
+
}: {
|
|
1042
|
+
adapter: AuthoringAdapter;
|
|
1043
|
+
id: string;
|
|
1044
|
+
props: readonly PropertyDescriptor[];
|
|
1045
|
+
fonts: readonly string[];
|
|
1046
|
+
}): React.ReactElement {
|
|
1047
|
+
const store = useEditorStore();
|
|
1048
|
+
const byName = new Map(props.map((prop) => [propNameFromPath(prop.path), prop] as const));
|
|
1049
|
+
const position = byName.get('position');
|
|
1050
|
+
const positionValue = String(
|
|
1051
|
+
position ? (adapter.inspector?.get(id, position.path) ?? 'static') : 'static',
|
|
1052
|
+
);
|
|
1053
|
+
const commit = (path: string, value: unknown): void => {
|
|
1054
|
+
adapter.inspector?.set(id, path, value);
|
|
1055
|
+
store.notifyIngestEdit();
|
|
1056
|
+
};
|
|
1057
|
+
const offsets = ['top', 'right', 'bottom', 'left'];
|
|
1058
|
+
const itemProps = ['zIndex', 'flexGrow', 'flexShrink', 'flexBasis', 'alignSelf']
|
|
1059
|
+
.map((name) => byName.get(name))
|
|
1060
|
+
.filter((prop): prop is PropertyDescriptor => !!prop);
|
|
1061
|
+
return (
|
|
1062
|
+
<div style={{ padding: '2px 8px 7px' }}>
|
|
1063
|
+
{position && (
|
|
1064
|
+
<div
|
|
1065
|
+
data-testid={testIdFor(position.path)}
|
|
1066
|
+
style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 2 }}
|
|
1067
|
+
>
|
|
1068
|
+
{(position.options ?? []).map((option) => {
|
|
1069
|
+
const value = String(option);
|
|
1070
|
+
const active = value === positionValue;
|
|
1071
|
+
return (
|
|
1072
|
+
<Button
|
|
1073
|
+
key={value}
|
|
1074
|
+
type="button"
|
|
1075
|
+
variant={active ? 'primary' : 'secondary'}
|
|
1076
|
+
size="compact"
|
|
1077
|
+
aria-pressed={active}
|
|
1078
|
+
title={value}
|
|
1079
|
+
disabled={position.readonly}
|
|
1080
|
+
onClick={() => commit(position.path, value)}
|
|
1081
|
+
>
|
|
1082
|
+
{value.slice(0, 3)}
|
|
1083
|
+
</Button>
|
|
1084
|
+
);
|
|
1085
|
+
})}
|
|
1086
|
+
</div>
|
|
1087
|
+
)}
|
|
1088
|
+
{positionValue !== 'static' && (
|
|
1089
|
+
<div
|
|
1090
|
+
data-testid="react-position-offsets"
|
|
1091
|
+
style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4, marginTop: 6 }}
|
|
1092
|
+
>
|
|
1093
|
+
{offsets.map((name) =>
|
|
1094
|
+
compactScrub(adapter, id, byName.get(name), name[0]!.toUpperCase(), commit),
|
|
1095
|
+
)}
|
|
1096
|
+
</div>
|
|
1097
|
+
)}
|
|
1098
|
+
{itemProps.length > 0 && (
|
|
1099
|
+
<div style={{ borderTop: `1px solid ${THEME.border}`, marginTop: 7, paddingTop: 3 }}>
|
|
1100
|
+
<div style={{ color: THEME.textMuted, fontSize: 9, padding: '2px 0 1px' }}>
|
|
1101
|
+
STACKING & FLEX ITEM
|
|
1102
|
+
</div>
|
|
1103
|
+
{itemProps.map((prop) => (
|
|
1104
|
+
<SingleRow key={prop.path} adapter={adapter} id={id} prop={prop} fonts={fonts} />
|
|
1105
|
+
))}
|
|
1106
|
+
</div>
|
|
1107
|
+
)}
|
|
1108
|
+
</div>
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
function SemanticSpacing({
|
|
1113
|
+
adapter,
|
|
1114
|
+
id,
|
|
1115
|
+
props,
|
|
1116
|
+
}: {
|
|
1117
|
+
adapter: AuthoringAdapter;
|
|
1118
|
+
id: string;
|
|
1119
|
+
props: readonly PropertyDescriptor[];
|
|
1120
|
+
}): React.ReactElement {
|
|
1121
|
+
const store = useEditorStore();
|
|
1122
|
+
const byName = new Map(props.map((prop) => [propNameFromPath(prop.path), prop] as const));
|
|
1123
|
+
const commit = (path: string, value: unknown): void => {
|
|
1124
|
+
adapter.inspector?.set(id, path, value);
|
|
1125
|
+
store.notifyIngestEdit();
|
|
1126
|
+
};
|
|
1127
|
+
const cells = [
|
|
1128
|
+
['marginTop', 'M T'],
|
|
1129
|
+
['marginRight', 'M R'],
|
|
1130
|
+
['marginBottom', 'M B'],
|
|
1131
|
+
['marginLeft', 'M L'],
|
|
1132
|
+
['paddingTop', 'P T'],
|
|
1133
|
+
['paddingRight', 'P R'],
|
|
1134
|
+
['paddingBottom', 'P B'],
|
|
1135
|
+
['paddingLeft', 'P L'],
|
|
1136
|
+
] as const;
|
|
1137
|
+
return (
|
|
1138
|
+
<div style={{ padding: '2px 8px 8px' }}>
|
|
1139
|
+
<div style={{ color: THEME.textMuted, fontSize: 9, marginBottom: 4 }}>
|
|
1140
|
+
MARGIN · OUTSIDE / PADDING · INSIDE
|
|
1141
|
+
</div>
|
|
1142
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }}>
|
|
1143
|
+
{cells.map(([name, label]) => compactScrub(adapter, id, byName.get(name), label, commit))}
|
|
1144
|
+
</div>
|
|
1145
|
+
</div>
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
900
1149
|
function GroupBlock({
|
|
901
1150
|
name,
|
|
902
1151
|
groupId,
|
|
@@ -912,31 +1161,142 @@ function GroupBlock({
|
|
|
912
1161
|
id: string;
|
|
913
1162
|
fonts: readonly string[];
|
|
914
1163
|
}): React.ReactElement {
|
|
1164
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
1165
|
+
const [showConstraints, setShowConstraints] = useState(false);
|
|
915
1166
|
const rows = buildPropertyRows(props);
|
|
916
1167
|
return (
|
|
917
1168
|
<div
|
|
918
1169
|
data-testid={`react-inspector-group-${groupId}`}
|
|
919
1170
|
style={{ borderTop: `1px solid ${THEME.border}`, marginTop: 4 }}
|
|
920
1171
|
>
|
|
921
|
-
<
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
textTransform: 'uppercase',
|
|
928
|
-
letterSpacing: 0.4,
|
|
929
|
-
}}
|
|
1172
|
+
<Button
|
|
1173
|
+
type="button"
|
|
1174
|
+
variant="ghost"
|
|
1175
|
+
className="vgai-inspector-group-trigger"
|
|
1176
|
+
aria-expanded={!collapsed}
|
|
1177
|
+
onClick={() => setCollapsed((value) => !value)}
|
|
930
1178
|
>
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
<
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1179
|
+
<span
|
|
1180
|
+
aria-hidden="true"
|
|
1181
|
+
style={{ display: 'inline-flex', justifyContent: 'center', width: 8 }}
|
|
1182
|
+
>
|
|
1183
|
+
<DisclosureIcon direction={collapsed ? 'right' : 'down'} />
|
|
1184
|
+
</span>
|
|
1185
|
+
<span>{name}</span>
|
|
1186
|
+
</Button>
|
|
1187
|
+
{!collapsed && name === 'Layout' && (
|
|
1188
|
+
<SemanticLayout
|
|
1189
|
+
adapter={adapter}
|
|
1190
|
+
id={id}
|
|
1191
|
+
props={props}
|
|
1192
|
+
fonts={fonts}
|
|
1193
|
+
showConstraints={showConstraints}
|
|
1194
|
+
onToggleConstraints={() => setShowConstraints((value) => !value)}
|
|
1195
|
+
/>
|
|
1196
|
+
)}
|
|
1197
|
+
{!collapsed && name === 'Position' && (
|
|
1198
|
+
<SemanticPosition adapter={adapter} id={id} props={props} fonts={fonts} />
|
|
1199
|
+
)}
|
|
1200
|
+
{!collapsed && name === 'Spacing' && (
|
|
1201
|
+
<SemanticSpacing adapter={adapter} id={id} props={props} />
|
|
939
1202
|
)}
|
|
1203
|
+
{!collapsed &&
|
|
1204
|
+
name !== 'Layout' &&
|
|
1205
|
+
name !== 'Position' &&
|
|
1206
|
+
name !== 'Spacing' &&
|
|
1207
|
+
rows.map((row) =>
|
|
1208
|
+
row.kind === 'single' ? (
|
|
1209
|
+
<SingleRow key={rowKey(row)} adapter={adapter} id={id} prop={row.prop} fonts={fonts} />
|
|
1210
|
+
) : (
|
|
1211
|
+
<ComboRow key={rowKey(row)} adapter={adapter} id={id} row={row} />
|
|
1212
|
+
),
|
|
1213
|
+
)}
|
|
1214
|
+
</div>
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
function BreadcrumbBar({
|
|
1219
|
+
adapter,
|
|
1220
|
+
node,
|
|
1221
|
+
}: {
|
|
1222
|
+
adapter: AuthoringAdapter;
|
|
1223
|
+
node: EditorNode;
|
|
1224
|
+
}): React.ReactElement {
|
|
1225
|
+
const chain: EditorNode[] = [];
|
|
1226
|
+
let current: EditorNode | null = node;
|
|
1227
|
+
let guard = 0;
|
|
1228
|
+
while (current && guard++ < 100) {
|
|
1229
|
+
chain.unshift(current);
|
|
1230
|
+
current = current.parentId ? adapter.hierarchy.node(current.parentId) : null;
|
|
1231
|
+
}
|
|
1232
|
+
return (
|
|
1233
|
+
<nav
|
|
1234
|
+
aria-label="Selection ancestry"
|
|
1235
|
+
data-testid="react-inspector-breadcrumbs"
|
|
1236
|
+
style={{
|
|
1237
|
+
display: 'flex',
|
|
1238
|
+
alignItems: 'center',
|
|
1239
|
+
gap: 3,
|
|
1240
|
+
padding: '6px 8px',
|
|
1241
|
+
overflowX: 'auto',
|
|
1242
|
+
borderBottom: `1px solid ${THEME.border}`,
|
|
1243
|
+
whiteSpace: 'nowrap',
|
|
1244
|
+
}}
|
|
1245
|
+
>
|
|
1246
|
+
{chain.map((entry, index) => (
|
|
1247
|
+
<span key={entry.id} style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}>
|
|
1248
|
+
{index > 0 && <span style={{ color: THEME.textMuted }}>/</span>}
|
|
1249
|
+
<Button
|
|
1250
|
+
type="button"
|
|
1251
|
+
variant="ghost"
|
|
1252
|
+
size="compact"
|
|
1253
|
+
aria-current={entry.id === node.id ? 'page' : undefined}
|
|
1254
|
+
title={entry.secondaryLabel ?? entry.label}
|
|
1255
|
+
onClick={() => adapter.selection?.set([entry.id])}
|
|
1256
|
+
>
|
|
1257
|
+
{entry.label}
|
|
1258
|
+
</Button>
|
|
1259
|
+
</span>
|
|
1260
|
+
))}
|
|
1261
|
+
</nav>
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function StoryActions({
|
|
1266
|
+
owner,
|
|
1267
|
+
node,
|
|
1268
|
+
}: {
|
|
1269
|
+
owner: ReactWorldAuthoringAdapter;
|
|
1270
|
+
node: EditorNode;
|
|
1271
|
+
}): React.ReactElement {
|
|
1272
|
+
const args = owner.portableArgs(node.id);
|
|
1273
|
+
return (
|
|
1274
|
+
<div
|
|
1275
|
+
data-testid="react-story-controls-header"
|
|
1276
|
+
style={{
|
|
1277
|
+
display: 'flex',
|
|
1278
|
+
alignItems: 'center',
|
|
1279
|
+
justifyContent: 'space-between',
|
|
1280
|
+
gap: 8,
|
|
1281
|
+
padding: '7px 8px',
|
|
1282
|
+
borderBottom: `1px solid ${THEME.border}`,
|
|
1283
|
+
}}
|
|
1284
|
+
>
|
|
1285
|
+
<div style={{ minWidth: 0 }}>
|
|
1286
|
+
<div style={{ color: THEME.text, fontSize: 12, fontWeight: 600 }}>{node.label}</div>
|
|
1287
|
+
<div style={{ color: THEME.textMuted, fontSize: 10 }}>
|
|
1288
|
+
{args ? `${Object.keys(args).length} args · session controls` : 'Portable CSF story'}
|
|
1289
|
+
</div>
|
|
1290
|
+
</div>
|
|
1291
|
+
<Button
|
|
1292
|
+
type="button"
|
|
1293
|
+
variant="secondary"
|
|
1294
|
+
size="compact"
|
|
1295
|
+
data-testid="react-story-reset-args"
|
|
1296
|
+
onClick={() => owner.resetPortableArgs(node.id)}
|
|
1297
|
+
>
|
|
1298
|
+
Reset
|
|
1299
|
+
</Button>
|
|
940
1300
|
</div>
|
|
941
1301
|
);
|
|
942
1302
|
}
|
|
@@ -949,38 +1309,111 @@ function GroupBlock({
|
|
|
949
1309
|
* `data-oid` component-source writes); a `ReactDomAuthoringAdapter`
|
|
950
1310
|
* (ingested, overlay-only — never touches source) gets its own honest note
|
|
951
1311
|
* instead, so neither lane's write model is ever misrepresented. */
|
|
1312
|
+
/**
|
|
1313
|
+
* C4 (spec §9): "the editor offers open/copy-path/go-to-line operations" for a DOM
|
|
1314
|
+
* element's mapped source location. Read-only — this ONLY surfaces where the source is
|
|
1315
|
+
* and copies a `file:line` locator; it has no write path (that stays SourceWriteBackend's
|
|
1316
|
+
* bounded style/className/text/struct ops, wired elsewhere). `open` degrades honestly to
|
|
1317
|
+
* copy-path when no editor-launch protocol is configured (documented fallback, not a
|
|
1318
|
+
* silent no-op): most terminals/OS shells don't register a handler for arbitrary custom
|
|
1319
|
+
* URI schemes, so we copy a `vscode://file/<abs>:<line>` link users can paste, rather than
|
|
1320
|
+
* assume a scheme is registered and fail silently if it isn't.
|
|
1321
|
+
*/
|
|
1322
|
+
function SourceLocationRow({ entry }: { entry: OidEntry }): React.ReactElement {
|
|
1323
|
+
const [copied, setCopied] = useState<'path' | 'link' | null>(null);
|
|
1324
|
+
const filePath = `${entry.file}:${entry.line}`;
|
|
1325
|
+
const copy = async (text: string, kind: 'path' | 'link'): Promise<void> => {
|
|
1326
|
+
try {
|
|
1327
|
+
await navigator.clipboard.writeText(text);
|
|
1328
|
+
setCopied(kind);
|
|
1329
|
+
setTimeout(() => setCopied(null), 1500);
|
|
1330
|
+
} catch {
|
|
1331
|
+
// Clipboard API unavailable (permissions/non-secure context) — the row still shows
|
|
1332
|
+
// the path as selectable text, so the user can copy it manually.
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
return (
|
|
1336
|
+
<div
|
|
1337
|
+
data-testid="react-source-location"
|
|
1338
|
+
style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 4, flexWrap: 'wrap' }}
|
|
1339
|
+
>
|
|
1340
|
+
<code style={{ fontFamily: FONT_MONO, color: THEME.textMuted }}>{filePath}</code>
|
|
1341
|
+
<Button
|
|
1342
|
+
type="button"
|
|
1343
|
+
data-testid="source-copy-path"
|
|
1344
|
+
onClick={() => void copy(filePath, 'path')}
|
|
1345
|
+
style={{ width: 'auto' }}
|
|
1346
|
+
>
|
|
1347
|
+
{copied === 'path' ? 'Copied' : 'Copy path'}
|
|
1348
|
+
</Button>
|
|
1349
|
+
<Button
|
|
1350
|
+
type="button"
|
|
1351
|
+
data-testid="source-open"
|
|
1352
|
+
title="Copies a vscode://file link (go-to-line) — no in-editor open protocol is wired yet"
|
|
1353
|
+
onClick={() => void copy(`vscode://file/${entry.file}:${entry.line}`, 'link')}
|
|
1354
|
+
style={{ width: 'auto' }}
|
|
1355
|
+
>
|
|
1356
|
+
{copied === 'link' ? 'Copied' : 'Open (copy link)'}
|
|
1357
|
+
</Button>
|
|
1358
|
+
</div>
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
952
1362
|
function LaneDisclosure({
|
|
953
1363
|
owner,
|
|
954
1364
|
node,
|
|
1365
|
+
nodeId,
|
|
955
1366
|
}: {
|
|
956
1367
|
owner: ReactWorldAuthoringAdapter | ReactDomAuthoringAdapter;
|
|
957
1368
|
node: EditorNode | null;
|
|
1369
|
+
nodeId: string;
|
|
958
1370
|
}): React.ReactElement {
|
|
959
1371
|
const isLaneB = owner instanceof ReactWorldAuthoringAdapter;
|
|
1372
|
+
// C4: only Lane B (the OID/JSX source-writer path) has a source-location mapping —
|
|
1373
|
+
// Lane A (`ReactDomAuthoringAdapter`, an ingested/overlay session) never stamps
|
|
1374
|
+
// `data-oid` or has an OID index (see its file doc comment), so it has nothing to
|
|
1375
|
+
// resolve here; that's the intended DOM-only degradation, not a bug.
|
|
1376
|
+
const sourceEntry = isLaneB ? owner.sourceLocation(nodeId) : undefined;
|
|
1377
|
+
const semanticCopy =
|
|
1378
|
+
node?.role === 'story' ? (
|
|
1379
|
+
<>
|
|
1380
|
+
<strong style={{ color: THEME.accent }}>Story preview.</strong>{' '}
|
|
1381
|
+
<code style={{ fontFamily: FONT_MONO }}>{node.label}</code> is the active portable CSF
|
|
1382
|
+
design state.
|
|
1383
|
+
</>
|
|
1384
|
+
) : node?.role === 'document' ? (
|
|
1385
|
+
<>
|
|
1386
|
+
<strong style={{ color: THEME.accent }}>CSF document.</strong>{' '}
|
|
1387
|
+
<code style={{ fontFamily: FONT_MONO }}>{node.secondaryLabel ?? node.label}</code> owns this
|
|
1388
|
+
root's design states.
|
|
1389
|
+
</>
|
|
1390
|
+
) : null;
|
|
960
1391
|
return (
|
|
961
1392
|
<div
|
|
962
1393
|
data-testid={isLaneB ? 'react-lane-b-disclosure' : 'react-overlay-disclosure'}
|
|
963
1394
|
style={{
|
|
964
1395
|
padding: 8,
|
|
965
1396
|
borderBottom: `1px solid ${THEME.border}`,
|
|
966
|
-
background: isLaneB ?
|
|
1397
|
+
background: isLaneB ? themeVars.accent.muted : THEME.neutralHover,
|
|
967
1398
|
color: THEME.text,
|
|
968
1399
|
fontSize: 11,
|
|
969
1400
|
lineHeight: 1.5,
|
|
970
1401
|
}}
|
|
971
1402
|
>
|
|
972
|
-
{
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1403
|
+
{semanticCopy ??
|
|
1404
|
+
(isLaneB ? (
|
|
1405
|
+
<>
|
|
1406
|
+
<strong style={{ color: THEME.accent }}>Component source.</strong> Edits change{' '}
|
|
1407
|
+
<code style={{ fontFamily: FONT_MONO }}>{node?.label ?? 'this component'}</code>'s
|
|
1408
|
+
source — every rendered instance updates.
|
|
1409
|
+
</>
|
|
1410
|
+
) : (
|
|
1411
|
+
<>
|
|
1412
|
+
<strong style={{ color: THEME.textMuted }}>Session overlay (ingested game).</strong>{' '}
|
|
1413
|
+
Edits are NOT written to source — they persist as a per-project overlay only.
|
|
1414
|
+
</>
|
|
1415
|
+
))}
|
|
1416
|
+
{sourceEntry && <SourceLocationRow entry={sourceEntry} />}
|
|
984
1417
|
</div>
|
|
985
1418
|
);
|
|
986
1419
|
}
|
|
@@ -1008,7 +1441,11 @@ function ReactPropertiesSection({
|
|
|
1008
1441
|
|
|
1009
1442
|
return (
|
|
1010
1443
|
<div data-testid="react-inspector-section">
|
|
1011
|
-
<
|
|
1444
|
+
<BreadcrumbBar adapter={adapter} node={node!} />
|
|
1445
|
+
{node?.role === 'story' && owner instanceof ReactWorldAuthoringAdapter && (
|
|
1446
|
+
<StoryActions owner={owner} node={node} />
|
|
1447
|
+
)}
|
|
1448
|
+
<LaneDisclosure owner={owner} node={node} nodeId={nodeId} />
|
|
1012
1449
|
{ungrouped.length > 0 && (
|
|
1013
1450
|
<GroupBlock
|
|
1014
1451
|
name="Style"
|
|
@@ -1037,5 +1474,6 @@ function ReactPropertiesSection({
|
|
|
1037
1474
|
registerInspectorSections({
|
|
1038
1475
|
match: isReactDomSelection,
|
|
1039
1476
|
Section: ReactPropertiesSection,
|
|
1040
|
-
|
|
1477
|
+
panelName: 'React Inspector',
|
|
1478
|
+
suppresses: ['header', 'transform', 'properties', 'components'],
|
|
1041
1479
|
});
|