@volter/editor-core 0.5.57
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/BUNDLED_NOTICES +1527 -0
- package/LICENSE +686 -0
- package/LICENSE-APACHE +202 -0
- package/NOTICE +18 -0
- package/README.md +24 -0
- package/dist/build/chunk-JS4D4XZ3.js +175 -0
- package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
- package/dist/build/vite-plugin-product-contributions.js +100 -0
- package/dist/build/vite-plugin-product-contributions.js.map +7 -0
- package/dist/build/vite-plugin-shared-react.js +25 -0
- package/dist/build/vite-plugin-shared-react.js.map +7 -0
- package/dist/build/vite-plugin-shared-three.js +86 -0
- package/dist/build/vite-plugin-shared-three.js.map +7 -0
- package/dist/server/chunk-KMJC7AGO.js +98 -0
- package/dist/server/chunk-KMJC7AGO.js.map +7 -0
- package/dist/server/chunk-PSILUMGS.js +46 -0
- package/dist/server/chunk-PSILUMGS.js.map +7 -0
- package/dist/server/frame-proxy.js +255 -0
- package/dist/server/frame-proxy.js.map +7 -0
- package/dist/server/open-browser.js +53 -0
- package/dist/server/open-browser.js.map +7 -0
- package/dist/server/process-shutdown.js +127 -0
- package/dist/server/process-shutdown.js.map +7 -0
- package/dist/server/session-registry.js +141 -0
- package/dist/server/session-registry.js.map +7 -0
- package/dist/server/spawn-opener.js +11 -0
- package/dist/server/spawn-opener.js.map +7 -0
- package/dist/server/worktree-identity.js +11 -0
- package/dist/server/worktree-identity.js.map +7 -0
- package/dist-server/packaged.mjs +88816 -0
- package/package.json +118 -0
- package/scripts/build-plugins.mjs +14 -0
- package/scripts/build-server.mjs +26 -0
- package/scripts/build-session.mjs +9 -0
- package/server/account-credentials.ts +60 -0
- package/server/account-service.ts +1434 -0
- package/server/adapter-region-includes.ts +47 -0
- package/server/asset-catalog-v2.ts +175 -0
- package/server/asset-history-snapshots.ts +196 -0
- package/server/asset-ledger-store.ts +195 -0
- package/server/asset-library-routes.ts +1595 -0
- package/server/blender-wasm-artifact.ts +430 -0
- package/server/boot-timings.ts +106 -0
- package/server/canonical-path.ts +50 -0
- package/server/checkout-workspace-preflight.ts +349 -0
- package/server/cloud-asset-catalog.ts +158 -0
- package/server/coding-inference-launch.ts +237 -0
- package/server/collaboration-account-client.ts +202 -0
- package/server/collaboration-attribution.ts +122 -0
- package/server/collaboration-session.ts +934 -0
- package/server/comfyui-bridge.ts +248 -0
- package/server/console-ledger.ts +499 -0
- package/server/creation-site-transform.ts +304 -0
- package/server/creation-site-write.ts +648 -0
- package/server/data-file-serialize.ts +158 -0
- package/server/editor-brand-html.ts +72 -0
- package/server/editor-control-socket.ts +366 -0
- package/server/editor-server-options.ts +190 -0
- package/server/editor-server.ts +1483 -0
- package/server/editor-sse.ts +352 -0
- package/server/engine-provenance.ts +169 -0
- package/server/engine-source-restart.ts +117 -0
- package/server/frame-bridge.ts +115 -0
- package/server/frame-proxy.ts +490 -0
- package/server/frame-workbench.ts +259 -0
- package/server/frontend-handoff.ts +229 -0
- package/server/game-globals-shadow.ts +113 -0
- package/server/gameplay-session-retention.ts +149 -0
- package/server/gameplay-sessions.ts +123 -0
- package/server/generation-jobs.ts +201 -0
- package/server/generation-reconciler.ts +137 -0
- package/server/generative-execution-context.ts +59 -0
- package/server/git-workflow.ts +612 -0
- package/server/harness-chat-caller.ts +36 -0
- package/server/harness-chat-service.ts +1681 -0
- package/server/idle-shutdown.ts +170 -0
- package/server/js-profiling-policy.ts +52 -0
- package/server/launcher-settings.ts +84 -0
- package/server/local-asset-catalog.ts +410 -0
- package/server/managed-account-defaults.ts +22 -0
- package/server/mock-control-plane.ts +540 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/native-credential-store.ts +162 -0
- package/server/open-browser.ts +118 -0
- package/server/packaged.ts +1192 -0
- package/server/play-stall.ts +126 -0
- package/server/process-shutdown.ts +218 -0
- package/server/product-presets.ts +55 -0
- package/server/project-build-artifact.ts +95 -0
- package/server/project-components.ts +100 -0
- package/server/project-dependency-invalidation.ts +114 -0
- package/server/project-file-scan.ts +235 -0
- package/server/project-hmr-files.ts +215 -0
- package/server/project-install-roots.ts +210 -0
- package/server/project-kinds.ts +71 -0
- package/server/project-mcp-servers.ts +82 -0
- package/server/project-module-freshness.ts +109 -0
- package/server/project-module-instance.ts +375 -0
- package/server/project-optimize-deps-entries.ts +827 -0
- package/server/project-output-writer.ts +679 -0
- package/server/project-package-origin.ts +63 -0
- package/server/project-root-surface.ts +831 -0
- package/server/project-scratch-path.ts +47 -0
- package/server/project-script-hmr.ts +619 -0
- package/server/project-serving-plugins.ts +236 -0
- package/server/project-tools.ts +653 -0
- package/server/project-validation.ts +250 -0
- package/server/project-verbs.ts +127 -0
- package/server/project-view.ts +52 -0
- package/server/project-watch.ts +1134 -0
- package/server/project-work-coordinator.ts +288 -0
- package/server/provider-credentials.ts +331 -0
- package/server/recent-projects-store.ts +98 -0
- package/server/redact-secrets.ts +71 -0
- package/server/repository-presence.ts +267 -0
- package/server/routes/account.ts +357 -0
- package/server/routes/agents.ts +60 -0
- package/server/routes/assets.ts +154 -0
- package/server/routes/build.ts +13 -0
- package/server/routes/collaboration.ts +595 -0
- package/server/routes/configurations.ts +419 -0
- package/server/routes/context.ts +264 -0
- package/server/routes/control-plane.ts +1811 -0
- package/server/routes/logs.ts +272 -0
- package/server/routes/project-identity.ts +302 -0
- package/server/routes/project-open.ts +429 -0
- package/server/routes/project-source.ts +686 -0
- package/server/routes/project-state.ts +383 -0
- package/server/routes/relay.ts +1029 -0
- package/server/routes/route-helpers.ts +29 -0
- package/server/routes/served-modules.ts +138 -0
- package/server/routes/session-tabs.ts +196 -0
- package/server/routes/settings.ts +121 -0
- package/server/routes/share-control.ts +486 -0
- package/server/routes/themes.ts +116 -0
- package/server/routes/tools.ts +230 -0
- package/server/routes/worktrees.ts +595 -0
- package/server/scoped-game-css.ts +461 -0
- package/server/server-utils.ts +1194 -0
- package/server/session-product.ts +112 -0
- package/server/session-registry.ts +230 -0
- package/server/share-claims.ts +138 -0
- package/server/share-host.ts +526 -0
- package/server/share-session-gateway.ts +1362 -0
- package/server/share-tunnel.ts +123 -0
- package/server/spawn-opener.ts +74 -0
- package/server/support/play/log-format.ts +76 -0
- package/server/support/play/session-record.ts +199 -0
- package/server/support/project/build-discipline.ts +718 -0
- package/server/support/project/inspection-node.ts +119 -0
- package/server/support/project/log-naming.ts +54 -0
- package/server/support/project/provenance.ts +290 -0
- package/server/support/project/run-name.ts +18 -0
- package/server/support/project/session-journal.ts +830 -0
- package/server/support/project/shared.ts +327 -0
- package/server/tab-bootstrap.ts +62 -0
- package/server/tab-heartbeat.ts +477 -0
- package/server/tab-lifecycle.ts +550 -0
- package/server/tab-presence.ts +1508 -0
- package/server/team-agent-mirror.ts +95 -0
- package/server/twin-auth.ts +138 -0
- package/server/vendored-lock-recorder.ts +968 -0
- package/server/worktree-identity.ts +121 -0
- package/server/worktree-management.ts +398 -0
- package/server/worktree-retention.d.mts +13 -0
- package/server/worktree-retention.mjs +205 -0
- package/src/EditorContext.tsx +169 -0
- package/src/account.ts +333 -0
- package/src/action-registry.ts +532 -0
- package/src/active-product.ts +74 -0
- package/src/active-project.ts +155 -0
- package/src/adapter-editor-config.ts +25 -0
- package/src/adapter-observation.ts +49 -0
- package/src/animation/stage-transport.ts +379 -0
- package/src/animation/three-clips-subject.ts +172 -0
- package/src/api/asset-library-wire.ts +45 -0
- package/src/api/assets.ts +365 -0
- package/src/api/base.ts +10 -0
- package/src/api/build.ts +99 -0
- package/src/api/git-wire.ts +56 -0
- package/src/api/logs.ts +92 -0
- package/src/api/project-identity.ts +74 -0
- package/src/api/project-open.ts +355 -0
- package/src/api/project-source.ts +162 -0
- package/src/api/project-state.ts +107 -0
- package/src/api/relay.ts +268 -0
- package/src/api/settings.ts +36 -0
- package/src/api/themes.ts +45 -0
- package/src/api/worktrees.ts +257 -0
- package/src/asset-compare-core.ts +171 -0
- package/src/asset-compare.ts +294 -0
- package/src/asset-editor-context.tsx +100 -0
- package/src/asset-events.ts +97 -0
- package/src/asset-inspector-actions.ts +87 -0
- package/src/asset-preview-framing.ts +357 -0
- package/src/asset-preview.ts +2802 -0
- package/src/asset-selection-viewer-registry.ts +113 -0
- package/src/asset-selection.ts +133 -0
- package/src/asset-workflow/asset-capabilities.ts +344 -0
- package/src/asset-workflow/asset-import-jobs.ts +106 -0
- package/src/asset-workflow/asset-ledger-backend.ts +126 -0
- package/src/asset-workflow/asset-ledger.ts +156 -0
- package/src/asset-workflow/asset-materialization-report.ts +140 -0
- package/src/asset-workflow/asset-pack-manifest.ts +320 -0
- package/src/asset-workflow/asset-types.ts +142 -0
- package/src/asset-workflow/asset-workflow-quality.ts +61 -0
- package/src/asset-workflow/audio-preview-player.ts +193 -0
- package/src/asset-workflow/audio-waveform.ts +22 -0
- package/src/asset-workflow/cloud-asset-client.ts +263 -0
- package/src/asset-workflow/folder-preview.ts +345 -0
- package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
- package/src/asset-workflow/image-view-scale.ts +32 -0
- package/src/asset-workflow/import-contract.ts +124 -0
- package/src/asset-workflow/ledger-write-lock.ts +244 -0
- package/src/asset-workflow/model-inspection.ts +818 -0
- package/src/asset-workflow/pixi-spritesheet.ts +197 -0
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +139 -0
- package/src/asset-workflow/project-asset-operations.ts +154 -0
- package/src/asset-workflow/project-asset-roots.ts +68 -0
- package/src/asset-workflow/project-content.ts +288 -0
- package/src/asset-workflow/project-source-index.ts +550 -0
- package/src/asset-workflow/thumbnail-system.ts +256 -0
- package/src/authoring/active-adapter.ts +199 -0
- package/src/authoring/active-systems.ts +422 -0
- package/src/authoring/adapter-key.ts +18 -0
- package/src/authoring/authoring-asset-url.ts +27 -0
- package/src/authoring/bootstrap-state.ts +49 -0
- package/src/authoring/boundary-authoring-adapter.ts +184 -0
- package/src/authoring/breakpoint-state.ts +43 -0
- package/src/authoring/canvas-scene-guides.ts +76 -0
- package/src/authoring/component-instance-root.ts +171 -0
- package/src/authoring/composite-authoring-adapter.ts +2110 -0
- package/src/authoring/consumer-actions.ts +520 -0
- package/src/authoring/css-numeric-style.ts +97 -0
- package/src/authoring/design-time-layers.ts +849 -0
- package/src/authoring/design-time-mount-registry.ts +235 -0
- package/src/authoring/design-time-settle.ts +343 -0
- package/src/authoring/edit-mode-authoring.ts +619 -0
- package/src/authoring/eyedropper-session.ts +60 -0
- package/src/authoring/instance-source-menu-register.ts +13 -0
- package/src/authoring/instance-source-menu.ts +135 -0
- package/src/authoring/layered-pick.ts +183 -0
- package/src/authoring/live-gesture-lock.ts +50 -0
- package/src/authoring/live-object-transform.ts +62 -0
- package/src/authoring/mount-failure-report.ts +154 -0
- package/src/authoring/mounted-root-subjects.ts +144 -0
- package/src/authoring/no-authoring-adapter.ts +55 -0
- package/src/authoring/null-inspection-subjects.tsx +75 -0
- package/src/authoring/object3d-document-persistence.ts +106 -0
- package/src/authoring/object3d-document-session-registry.ts +120 -0
- package/src/authoring/object3d-document-session.ts +1326 -0
- package/src/authoring/object3d-gesture-controller.ts +113 -0
- package/src/authoring/panel-authoring.ts +121 -0
- package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
- package/src/authoring/prefab-instance-section-model.ts +55 -0
- package/src/authoring/project-authoring-session.ts +105 -0
- package/src/authoring/provenance.ts +99 -0
- package/src/authoring/quarks-particle-systems.ts +19 -0
- package/src/authoring/react-canvas-navigation.ts +255 -0
- package/src/authoring/react-design-canvas-style.ts +20 -0
- package/src/authoring/react-story-board.ts +937 -0
- package/src/authoring/selection-scope.ts +195 -0
- package/src/authoring/shell-document-ops.ts +169 -0
- package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
- package/src/authoring/shell-viewport-policy.ts +48 -0
- package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/authoring/stories-scope.ts +35 -0
- package/src/authoring/story-board-chrome-fit.ts +107 -0
- package/src/authoring/story-board-presentation.ts +111 -0
- package/src/authoring/three-projection-core.ts +226 -0
- package/src/authoring/viewport-pick-context.ts +40 -0
- package/src/authoring/viewport-raycast.ts +240 -0
- package/src/authoring/viewport-tool-context.ts +73 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +152 -0
- package/src/authoring/world-pan-state.ts +198 -0
- package/src/authoring/world-session-state.ts +101 -0
- package/src/authoring/write-pipe.ts +173 -0
- package/src/availability-tick.ts +66 -0
- package/src/bitmap-label.ts +120 -0
- package/src/blender-tab-metrics.ts +161 -0
- package/src/board-open-actions.ts +20 -0
- package/src/boot-routing.ts +391 -0
- package/src/build-identity.ts +15 -0
- package/src/bytes-codec.ts +62 -0
- package/src/camera-authoring.ts +175 -0
- package/src/cancellation-reason.ts +58 -0
- package/src/canvas-preview-frames.ts +482 -0
- package/src/capture-camera-pose.ts +77 -0
- package/src/chrome-registry.ts +159 -0
- package/src/chrome-slot-registry.ts +91 -0
- package/src/collaboration-client.ts +264 -0
- package/src/collaboration-presence.ts +41 -0
- package/src/command-dispatch.ts +19 -0
- package/src/command-listener.ts +2743 -0
- package/src/command-registry.ts +70 -0
- package/src/component-board-registry.ts +205 -0
- package/src/component-states-registry.ts +200 -0
- package/src/components/AgentPresentationNotice.tsx +75 -0
- package/src/components/AlignToolbar.tsx +204 -0
- package/src/components/AppRoot.tsx +394 -0
- package/src/components/ApplicationMenus.tsx +415 -0
- package/src/components/AssetBrowser.tsx +2843 -0
- package/src/components/AssetEditorShell.tsx +216 -0
- package/src/components/AssetInspectorToolSection.tsx +124 -0
- package/src/components/BoardRulers.tsx +354 -0
- package/src/components/CameraInfo.tsx +81 -0
- package/src/components/CanvasSceneViewport.tsx +556 -0
- package/src/components/CapabilityCoverageSection.tsx +185 -0
- package/src/components/CenterDocuments.tsx +22 -0
- package/src/components/ChromeSlot.tsx +20 -0
- package/src/components/CodeView.tsx +470 -0
- package/src/components/CollaborationHeaderControl.css +74 -0
- package/src/components/CompactInspectorCard.tsx +190 -0
- package/src/components/CompactInspectorShell.tsx +39 -0
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +270 -0
- package/src/components/DefaultEditorLayout.tsx +106 -0
- package/src/components/DocumentHeaderStrip.tsx +96 -0
- package/src/components/DocumentShelfRail.tsx +45 -0
- package/src/components/DocumentThumbnail.tsx +345 -0
- package/src/components/EditorLeaseGuard.tsx +372 -0
- package/src/components/ErrorBanner.tsx +123 -0
- package/src/components/FolderPreviewTile.tsx +159 -0
- package/src/components/GameHierarchy.tsx +3842 -0
- package/src/components/GameplaySessionTimeline.tsx +295 -0
- package/src/components/InspectionProjection.tsx +968 -0
- package/src/components/Inspector.tsx +233 -0
- package/src/components/InspectorCanvasPreview.tsx +35 -0
- package/src/components/InspectorFieldsSection.tsx +290 -0
- package/src/components/InspectorObjectPreview.tsx +57 -0
- package/src/components/InspectorStoriesSection.tsx +92 -0
- package/src/components/InspectorToolSection.tsx +96 -0
- package/src/components/InspectorTransformSection.tsx +245 -0
- package/src/components/LightExplorerPanel.tsx +433 -0
- package/src/components/MediaProperties.tsx +145 -0
- package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
- package/src/components/Object3DDocumentToolbar.css +100 -0
- package/src/components/Object3DDocumentToolbar.tsx +454 -0
- package/src/components/Object3DDocumentViewport.tsx +58 -0
- package/src/components/ProjectHeader.css +617 -0
- package/src/components/ProjectHeader.tsx +328 -0
- package/src/components/ProjectLayout.tsx +249 -0
- package/src/components/ReactCanvasControls.tsx +284 -0
- package/src/components/RootSelectionOverlay.tsx +3162 -0
- package/src/components/RootTextEditor.tsx +79 -0
- package/src/components/SaveStatus.tsx +70 -0
- package/src/components/StageHost.tsx +1954 -0
- package/src/components/StageOverlays.tsx +21 -0
- package/src/components/StartupErrorScreen.tsx +76 -0
- package/src/components/StartupLoadingScreen.tsx +65 -0
- package/src/components/StatsOverlay.tsx +78 -0
- package/src/components/SurfaceStateOverlay.tsx +24 -0
- package/src/components/ToolContributionSurfaces.tsx +65 -0
- package/src/components/ToolHost.tsx +370 -0
- package/src/components/ToolObject3DPreview.tsx +39 -0
- package/src/components/Toolbar.tsx +595 -0
- package/src/components/TransientHint.tsx +44 -0
- package/src/components/TransportStrip.tsx +174 -0
- package/src/components/VersionControlSection.tsx +470 -0
- package/src/components/VgaiLogo.css +83 -0
- package/src/components/VgaiLogo.tsx +35 -0
- package/src/components/ViewportControlsHint.tsx +60 -0
- package/src/components/ViewportFurniture.tsx +401 -0
- package/src/components/ViewportOverlay.tsx +145 -0
- package/src/components/ViewportOverlaysMenu.tsx +153 -0
- package/src/components/ViewportShadingMenu.tsx +300 -0
- package/src/components/ViewportViewMenu.tsx +101 -0
- package/src/components/WorkspaceDocumentSurface.tsx +204 -0
- package/src/components/WorkspaceUtilitySurface.tsx +22 -0
- package/src/components/WorktreeSwitcher.css +239 -0
- package/src/components/WorktreeSwitcher.tsx +890 -0
- package/src/components/account-documents.tsx +1162 -0
- package/src/components/asset-documents.tsx +812 -0
- package/src/components/asset-editor-persistence.ts +216 -0
- package/src/components/asset-selection-section.tsx +535 -0
- package/src/components/asset-thumbnails.tsx +312 -0
- package/src/components/asset-viewers/AudioViewer.tsx +201 -0
- package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
- package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
- package/src/components/asset-viewers/ImageViewer.tsx +300 -0
- package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
- package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
- package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
- package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
- package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
- package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
- package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
- package/src/components/asset-viewers/VideoViewer.tsx +101 -0
- package/src/components/asset-viewers/shader-source.ts +144 -0
- package/src/components/asset-workflow.css +731 -0
- package/src/components/board-guides.ts +150 -0
- package/src/components/compact-inspector.css +539 -0
- package/src/components/core-utilities.tsx +90 -0
- package/src/components/editor-notifications.css +40 -0
- package/src/components/engine-workspace.css +274 -0
- package/src/components/inspector-preview-section.tsx +238 -0
- package/src/components/inspector-property-grouping.ts +64 -0
- package/src/components/inspector-revert-label.ts +20 -0
- package/src/components/inspector-selection.ts +42 -0
- package/src/components/inspector-stories-gating.ts +171 -0
- package/src/components/inspector-transform-subject.ts +11 -0
- package/src/components/inspector-transform.ts +75 -0
- package/src/components/kind-documents.tsx +500 -0
- package/src/components/palette-action-publisher.tsx +140 -0
- package/src/components/primitives/DraftColorInput.tsx +74 -0
- package/src/components/product-shell.css +1829 -0
- package/src/components/project-tool-documents.tsx +410 -0
- package/src/components/scene-documents.tsx +223 -0
- package/src/components/stage-keyboard.tsx +37 -0
- package/src/components/stage-overlay-set.tsx +107 -0
- package/src/components/stage-presence-markers.ts +478 -0
- package/src/components/standard-viewport-dressing.ts +417 -0
- package/src/components/status-contributions.tsx +403 -0
- package/src/components/tool-documents.tsx +309 -0
- package/src/components/tool-schema-form.tsx +262 -0
- package/src/components/use-after-paint.ts +41 -0
- package/src/components/use-project-image-assets.ts +86 -0
- package/src/components/viewport-header-controls.css +90 -0
- package/src/components/viewport-surface-status.tsx +55 -0
- package/src/components/workspace-history.ts +32 -0
- package/src/components/workspace-static-panel-registry.tsx +152 -0
- package/src/components/workspace-surfaces.css +621 -0
- package/src/components/world-documents.tsx +586 -0
- package/src/components/world-overlay-gestures.ts +1694 -0
- package/src/composite-screenshot.ts +1641 -0
- package/src/console-sync.ts +131 -0
- package/src/constraint-helper.ts +338 -0
- package/src/content-entry-source-registry.ts +184 -0
- package/src/coverage/authoring-seam-evidence.ts +300 -0
- package/src/coverage/canvas-reveal.ts +192 -0
- package/src/coverage/design-time-surfaces.ts +101 -0
- package/src/coverage/live-seam-evidence.ts +11 -0
- package/src/coverage/ontology-invariants.ts +466 -0
- package/src/coverage/session-vitals.ts +501 -0
- package/src/coverage/system-seam-evidence.ts +72 -0
- package/src/crash-null-boundary.ts +36 -0
- package/src/creation-site-edit.ts +1479 -0
- package/src/creation-site-registry.ts +160 -0
- package/src/delegate-harness-registry.ts +143 -0
- package/src/document-context-registry.ts +177 -0
- package/src/document-open-registry.ts +200 -0
- package/src/document-preview-source.ts +20 -0
- package/src/document-renderer-session.ts +138 -0
- package/src/editor-api.ts +46 -0
- package/src/editor-chrome-capture.ts +139 -0
- package/src/editor-commands.ts +164 -0
- package/src/editor-console.ts +563 -0
- package/src/editor-current-view.ts +82 -0
- package/src/editor-document-probe.ts +881 -0
- package/src/editor-git-client.ts +115 -0
- package/src/editor-host-door.ts +462 -0
- package/src/editor-hotkeys.ts +842 -0
- package/src/editor-lease-view.ts +39 -0
- package/src/editor-lease.ts +415 -0
- package/src/editor-mode.ts +19 -0
- package/src/editor-notifications.ts +140 -0
- package/src/editor-presence.ts +563 -0
- package/src/editor-presentation-activity.ts +50 -0
- package/src/editor-presentation-notice.ts +42 -0
- package/src/editor-runtime.tsx +145 -0
- package/src/editor-server-response.ts +86 -0
- package/src/editor-session-attribution.ts +75 -0
- package/src/editor-session-mode.ts +54 -0
- package/src/editor-shell-store.ts +1304 -0
- package/src/editor-state-facets.ts +74 -0
- package/src/editor-styles.css +33 -0
- package/src/editor-view-presentation.ts +810 -0
- package/src/editor-viewport.ts +5302 -0
- package/src/entity-lod.ts +31 -0
- package/src/entity-object.ts +91 -0
- package/src/files/file-provider.ts +62 -0
- package/src/files/project-files.ts +264 -0
- package/src/finders/index.ts +136 -0
- package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
- package/src/frame/bridge.tsx +1408 -0
- package/src/frame/product.ts +77 -0
- package/src/gameplay-dom-recording.ts +319 -0
- package/src/gameplay-export-state.ts +14 -0
- package/src/gameplay-replay.ts +417 -0
- package/src/gameplay-session-time.ts +9 -0
- package/src/gameplay-sessions.ts +204 -0
- package/src/harness-chat-types.ts +253 -0
- package/src/hierarchy-component-marks.ts +298 -0
- package/src/hierarchy-drop.ts +91 -0
- package/src/hierarchy-expansion-state.ts +80 -0
- package/src/hierarchy-header-slot.ts +52 -0
- package/src/hierarchy-internals.ts +197 -0
- package/src/hierarchy-kind-icon.ts +217 -0
- package/src/hierarchy-mark-reader.ts +73 -0
- package/src/hierarchy-menu-registry.ts +67 -0
- package/src/hierarchy-node-rows.ts +307 -0
- package/src/hierarchy-panel-view.ts +280 -0
- package/src/hierarchy-projection.ts +76 -0
- package/src/hierarchy-row-cache.ts +243 -0
- package/src/hierarchy-row-model.ts +308 -0
- package/src/hierarchy-rows.ts +11 -0
- package/src/hierarchy-walk.ts +86 -0
- package/src/history/editor-session.ts +25 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-delegate.ts +163 -0
- package/src/history/history-limit-notices.ts +43 -0
- package/src/history/history-service.ts +1173 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +386 -0
- package/src/history/project-root-history-backends.ts +139 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +103 -0
- package/src/history/source-history-backend.ts +546 -0
- package/src/history/types.ts +124 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hotkeys.ts +188 -0
- package/src/inference-diagnostics.ts +69 -0
- package/src/initial-project.ts +80 -0
- package/src/inspection/active-subject.ts +578 -0
- package/src/inspection/active-surface.ts +142 -0
- package/src/inspection/compose.ts +1064 -0
- package/src/inspection/display.ts +170 -0
- package/src/inspection/document-subject.ts +109 -0
- package/src/inspection/game-subject.ts +85 -0
- package/src/inspection/model.ts +542 -0
- package/src/inspection/null-subject.ts +115 -0
- package/src/inspection/serialize.ts +357 -0
- package/src/inspection/use-active-inspection.ts +180 -0
- package/src/inspector-presentation.ts +201 -0
- package/src/inspector-section-registry.ts +221 -0
- package/src/instance-source-actions.ts +163 -0
- package/src/instanced-presentation.ts +164 -0
- package/src/js-heap.ts +71 -0
- package/src/key-actions.ts +91 -0
- package/src/keymap-presets.ts +401 -0
- package/src/layout-policy.ts +31 -0
- package/src/learn-links.ts +73 -0
- package/src/light-explorer-model.ts +134 -0
- package/src/live-canvas-frame.ts +55 -0
- package/src/live-document.ts +279 -0
- package/src/live-module-source.ts +230 -0
- package/src/live-session-registry.ts +220 -0
- package/src/live-transition.ts +633 -0
- package/src/manifest-project.ts +107 -0
- package/src/model-thumbnail.ts +523 -0
- package/src/native-selection-style.ts +202 -0
- package/src/object3d-document-write-policy.ts +137 -0
- package/src/packaged-runtime.ts +108 -0
- package/src/palettes/maya.palette.json +57 -0
- package/src/palettes/substance.palette.json +57 -0
- package/src/pasteboard-module.ts +81 -0
- package/src/performance-profiler.ts +367 -0
- package/src/performance-sources.ts +69 -0
- package/src/play-boot-phase.ts +145 -0
- package/src/presentation-surface.ts +248 -0
- package/src/project-adapter.ts +1143 -0
- package/src/project-asset-refresh.ts +26 -0
- package/src/project-declaration-refresh.ts +31 -0
- package/src/project-local-state.ts +118 -0
- package/src/project-manager.ts +243 -0
- package/src/project-module-changes.ts +163 -0
- package/src/project-module-split.ts +266 -0
- package/src/project-provenance.ts +115 -0
- package/src/project-ready.ts +42 -0
- package/src/project-session-reset.ts +67 -0
- package/src/project-shape.ts +68 -0
- package/src/project-tool-discovery.ts +66 -0
- package/src/project-tools.ts +107 -0
- package/src/project-work-types.ts +149 -0
- package/src/projection/three.ts +898 -0
- package/src/projection/types.ts +44 -0
- package/src/readiness.ts +113 -0
- package/src/reflection-probe-helper.ts +142 -0
- package/src/reported-play-state.ts +90 -0
- package/src/resolve-relative-specifier.ts +33 -0
- package/src/scene-document-plan.ts +315 -0
- package/src/scene-framing.ts +315 -0
- package/src/scene-live-open.ts +210 -0
- package/src/scene-view-fog.ts +89 -0
- package/src/scoped-game-css.ts +152 -0
- package/src/session-orphan-record.ts +193 -0
- package/src/session-tombstone.ts +126 -0
- package/src/settings/settings-provider.ts +82 -0
- package/src/settings-store.ts +345 -0
- package/src/shared-view-restore.ts +42 -0
- package/src/shell-store-door.ts +45 -0
- package/src/source-conflict.ts +122 -0
- package/src/spatial-handle-visuals.ts +332 -0
- package/src/stage-context.ts +341 -0
- package/src/stage-store-registry.ts +63 -0
- package/src/stale-chunk-recovery.ts +34 -0
- package/src/state-report-deferral.ts +73 -0
- package/src/storage/host-files-storage.ts +97 -0
- package/src/storage/http-storage.ts +174 -0
- package/src/storage/index.ts +75 -0
- package/src/storage/mem-storage.ts +149 -0
- package/src/storage/path-lock.ts +44 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/types.ts +116 -0
- package/src/stories/StoryComponentThumbnail.tsx +184 -0
- package/src/stories/StoryPreviewMount.tsx +306 -0
- package/src/stories/component-content-source.tsx +106 -0
- package/src/stories/component-states-source.ts +78 -0
- package/src/stories/compose-project-stories.ts +255 -0
- package/src/stories/pixi-story-model.ts +30 -0
- package/src/stories/prefabs-finder.ts +54 -0
- package/src/stories/prefabs-from-stories.ts +182 -0
- package/src/stories/project-story-discovery.ts +78 -0
- package/src/stories/project-story-regions.ts +24 -0
- package/src/stories/story-actions.ts +54 -0
- package/src/stories/story-capture-command.ts +124 -0
- package/src/stories/story-capture.ts +584 -0
- package/src/stories/story-declared-medium.ts +126 -0
- package/src/stories/story-discovery.ts +176 -0
- package/src/stories/story-document-openers.ts +36 -0
- package/src/stories/story-dom-runtime.ts +78 -0
- package/src/stories/story-grouping.ts +111 -0
- package/src/stories/story-lane.ts +88 -0
- package/src/stories/story-mount-turn.ts +27 -0
- package/src/stories/story-opener.ts +260 -0
- package/src/stories/story-pixi-preview.ts +408 -0
- package/src/stories/story-presentation.ts +215 -0
- package/src/stories/story-registry.ts +517 -0
- package/src/stories/story-three-preview.ts +807 -0
- package/src/stories/three-story-model.ts +96 -0
- package/src/story-three-preview-runtime.ts +56 -0
- package/src/surface-keyboard.ts +112 -0
- package/src/surface-state.ts +124 -0
- package/src/tab-bootstrap.js +628 -0
- package/src/tab-census.ts +198 -0
- package/src/tab-lifecycle-client.ts +196 -0
- package/src/theme-library.ts +844 -0
- package/src/theme-preference.ts +378 -0
- package/src/theme.css +3437 -0
- package/src/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/three-viewport/camera-fit.ts +41 -0
- package/src/three-viewport/interactive-renderer.ts +132 -0
- package/src/three-viewport/selection-brackets.ts +310 -0
- package/src/three-viewport/selection-outline.ts +183 -0
- package/src/three-viewport/skeleton-helper.ts +61 -0
- package/src/three-viewport/source-color.ts +167 -0
- package/src/three-viewport/studio-environment.ts +96 -0
- package/src/three-viewport-presentation.ts +23 -0
- package/src/tool-contribution-play.ts +74 -0
- package/src/tool-loader.ts +1798 -0
- package/src/transform-mode-request.ts +57 -0
- package/src/transient-hint.ts +78 -0
- package/src/trigger-volume-helper.ts +116 -0
- package/src/ui-source/adapter-region-includes.ts +241 -0
- package/src/ui-source/ensure-import.ts +132 -0
- package/src/ui-source/file-region-resolver.ts +302 -0
- package/src/ui-source/inspect.ts +775 -0
- package/src/ui-source/oid-transform.ts +1845 -0
- package/src/ui-source/plan-csf-story.ts +196 -0
- package/src/ui-source/plan-extract-component.ts +421 -0
- package/src/ui-source/plan-fork-component.ts +621 -0
- package/src/ui-source/plan-named-style.ts +95 -0
- package/src/ui-source/plan-source-edit.ts +353 -0
- package/src/ui-source/r3f-contract-resolver.ts +327 -0
- package/src/ui-source/r3f-diagnostic-index.ts +165 -0
- package/src/ui-source/r3f-environment-binding.ts +133 -0
- package/src/ui-source/r3f-joint-binding.ts +251 -0
- package/src/ui-source/r3f-lod-binding.ts +91 -0
- package/src/ui-source/r3f-particle-binding.ts +235 -0
- package/src/ui-source/r3f-physics-binding.ts +483 -0
- package/src/ui-source/r3f-project-contracts.ts +444 -0
- package/src/ui-source/relative-import-specifier.ts +34 -0
- package/src/ui-source/reparent-guard.ts +544 -0
- package/src/ui-source/source-edit-request.ts +97 -0
- package/src/ui-source/source-write-backend.ts +618 -0
- package/src/ui-source/syntactic-prop-specs.ts +296 -0
- package/src/ui-source/tier-source-write-backend.ts +279 -0
- package/src/ui-source/ts-ast.ts +171 -0
- package/src/ui-source/utility-class-support.ts +169 -0
- package/src/ui-source/write-component-default.ts +133 -0
- package/src/ui-source/writer.ts +1989 -0
- package/src/viewport-activation-timings.ts +840 -0
- package/src/viewport-authoring-policy.ts +186 -0
- package/src/viewport-controls-hint.ts +57 -0
- package/src/viewport-door.ts +237 -0
- package/src/viewport-shading-boundary.ts +12 -0
- package/src/vite-error-surface.ts +141 -0
- package/src/wait-until.ts +37 -0
- package/src/workspace-areas.ts +156 -0
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-available-documents.ts +127 -0
- package/src/workspace-core-utilities.ts +31 -0
- package/src/workspace-document-ids.ts +59 -0
- package/src/workspace-document-registry.ts +612 -0
- package/src/workspace-document-restore.ts +145 -0
- package/src/workspace-host-commands.ts +141 -0
- package/src/workspace-persistence-gate.ts +40 -0
- package/src/workspace-play-utilities.ts +44 -0
- package/src/workspace-presets.ts +425 -0
- package/src/workspace-regions.ts +294 -0
- package/src/workspace-state-persistence.ts +536 -0
- package/src/workspace-static-panels.ts +73 -0
- package/src/workspace-status-registry.ts +121 -0
- package/src/workspace-style.ts +239 -0
- package/src/workspace-utility-commands.ts +74 -0
- package/src/workspace-utility-registry.ts +263 -0
- package/src/workspace-viewport-rect.ts +97 -0
- package/src/world-adoption.ts +115 -0
- package/src/world-document-routing.ts +104 -0
- package/vite-plugin-creation-site-write.ts +184 -0
- package/vite-plugin-creation-site.ts +80 -0
- package/vite-plugin-game-static.ts +303 -0
- package/vite-plugin-module-doorways.ts +336 -0
- package/vite-plugin-product-contributions.ts +197 -0
- package/vite-plugin-project-game-static.ts +125 -0
- package/vite-plugin-project-jsx-js.ts +65 -0
- package/vite-plugin-project-root-absolute-assets.ts +359 -0
- package/vite-plugin-shared-react.ts +420 -0
- package/vite-plugin-shared-three.ts +203 -0
- package/vite-plugin-ui-oid.ts +2106 -0
|
@@ -0,0 +1,1064 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `composeInspectionSubject` — the ONE pure function that turns the editor's
|
|
3
|
+
* four inspection channels into an {@link InspectionSubject}
|
|
4
|
+
* (`inspection/model.ts`). Design: `docs/ARCHITECTURE-CORE.md` §Editor chrome
|
|
5
|
+
* ("The Inspection Model"); build ledger: `docs/WORK.md` §Inspection Model
|
|
6
|
+
* program, W1.
|
|
7
|
+
*
|
|
8
|
+
* The four channels it wraps, in render order:
|
|
9
|
+
* 1. the adapter's typed `InspectorProvider` descriptors — the loose
|
|
10
|
+
* (ungrouped) grid, then one section per named `group`;
|
|
11
|
+
* 2. `inspector-section-registry.ts`'s matched contributions — opaque custom
|
|
12
|
+
* bodies, in registration order;
|
|
13
|
+
* 3. the shell built-ins — the identity row, the live Preview section,
|
|
14
|
+
* Transform, Stories;
|
|
15
|
+
* 4. the verbs — the visibility eye (lifted OUT of the descriptor grid: the
|
|
16
|
+
* hierarchy already presents visibility as an eye, and the same fact must
|
|
17
|
+
* not reappear as a labelled checkbox) and the Asset Editor jump.
|
|
18
|
+
*
|
|
19
|
+
* It is a FUNCTION, not a store: no cache, no module state, no subscription.
|
|
20
|
+
* Every projection calls it during its own render, so the model is always as
|
|
21
|
+
* fresh as the render that asked for it.
|
|
22
|
+
*
|
|
23
|
+
* With NOTHING selected it composes the same subject shape out of the active
|
|
24
|
+
* adapter's own empty state (`inspection/null-subject.ts`) — W3, so a
|
|
25
|
+
* surface's empty inspector is its own subject and never another surface's.
|
|
26
|
+
*
|
|
27
|
+
* Replacement is resolved HERE and nowhere else, and it is POSITIVE SPACE: a
|
|
28
|
+
* contribution CLAIMS the section id it provides, and a built-in whose id is
|
|
29
|
+
* claimed is simply not composed. A projection sees one list of identified
|
|
30
|
+
* sections and never learns that a built-in existed.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
faArrowUpFromBracket,
|
|
35
|
+
faAtom,
|
|
36
|
+
faBookOpen,
|
|
37
|
+
faCamera,
|
|
38
|
+
faCube,
|
|
39
|
+
faDiagramProject,
|
|
40
|
+
faEye,
|
|
41
|
+
faEyeSlash,
|
|
42
|
+
faLightbulb,
|
|
43
|
+
faMusic,
|
|
44
|
+
faPalette,
|
|
45
|
+
faPerson,
|
|
46
|
+
faPuzzlePiece,
|
|
47
|
+
faSliders,
|
|
48
|
+
faUpDownLeftRight,
|
|
49
|
+
type IconDefinition,
|
|
50
|
+
} from '@fortawesome/free-solid-svg-icons';
|
|
51
|
+
import type {
|
|
52
|
+
AuthoringAdapter,
|
|
53
|
+
EditorNode,
|
|
54
|
+
StoryRef,
|
|
55
|
+
Transform,
|
|
56
|
+
TransformChannel,
|
|
57
|
+
TransformEditability,
|
|
58
|
+
} from '@volter/editor-project/adapter';
|
|
59
|
+
import { contentWorldBounds } from '@volter/editor-threejs/viewport/content-bounds';
|
|
60
|
+
import { createElement } from 'react';
|
|
61
|
+
import type * as THREE from 'three';
|
|
62
|
+
import {
|
|
63
|
+
applyAuthoringTransform,
|
|
64
|
+
beginAuthoringTransformEdit,
|
|
65
|
+
commitAuthoringTransformSource,
|
|
66
|
+
endAuthoringTransformEdit,
|
|
67
|
+
removeAuthoringInspectorField,
|
|
68
|
+
removeAuthoringTransform,
|
|
69
|
+
setAuthoringInspectorField,
|
|
70
|
+
} from '../authoring/consumer-actions';
|
|
71
|
+
import { authoringDestination, provenanceForNode } from '../authoring/provenance';
|
|
72
|
+
import { InspectorStoriesSection } from '../components/InspectorStoriesSection';
|
|
73
|
+
import { InspectorTransformSection } from '../components/InspectorTransformSection';
|
|
74
|
+
import {
|
|
75
|
+
InspectorCanvasPreviewBody,
|
|
76
|
+
InspectorComponentPreviewBody,
|
|
77
|
+
InspectorPreviewBody,
|
|
78
|
+
} from '../components/inspector-preview-section';
|
|
79
|
+
import { groupProperties } from '../components/inspector-property-grouping';
|
|
80
|
+
import { rotationDegrees, withTransformChannel } from '../components/inspector-transform';
|
|
81
|
+
import { transformDimensionsFor } from '../components/inspector-transform-subject';
|
|
82
|
+
import {
|
|
83
|
+
type ContentEntry,
|
|
84
|
+
type ContentEntrySource,
|
|
85
|
+
contentEntryForComponent,
|
|
86
|
+
} from '../content-entry-source-registry';
|
|
87
|
+
import {
|
|
88
|
+
type InspectorSectionContribution,
|
|
89
|
+
isSectionsProducer,
|
|
90
|
+
} from '../inspector-section-registry';
|
|
91
|
+
import { describeInstancedPresentation } from '../instanced-presentation';
|
|
92
|
+
import {
|
|
93
|
+
type FieldDescriptor,
|
|
94
|
+
GROUP_SECTION_ORDER,
|
|
95
|
+
groupSectionId,
|
|
96
|
+
type InspectionAction,
|
|
97
|
+
type InspectionFieldIo,
|
|
98
|
+
type InspectionSection,
|
|
99
|
+
type InspectionSubject,
|
|
100
|
+
type InspectionSurfaceKind,
|
|
101
|
+
inspectionAffinityFor,
|
|
102
|
+
PREVIEW_SECTION_ID,
|
|
103
|
+
PREVIEW_SECTION_ORDER,
|
|
104
|
+
PROPERTIES_SECTION_ID,
|
|
105
|
+
PROPERTIES_SECTION_ORDER,
|
|
106
|
+
STORIES_SECTION_ID,
|
|
107
|
+
STORIES_SECTION_ORDER,
|
|
108
|
+
TRANSFORM_SECTION_ID,
|
|
109
|
+
TRANSFORM_SECTION_ORDER,
|
|
110
|
+
} from './model';
|
|
111
|
+
import type { NullInspectionSubject } from './null-subject';
|
|
112
|
+
|
|
113
|
+
/** Above this many properties a group starts collapsed even when the document
|
|
114
|
+
* sets some of them — a 40-field library block is a wall, not a panel. */
|
|
115
|
+
const GROUP_AUTO_OPEN_MAX = 14;
|
|
116
|
+
|
|
117
|
+
/** Property groups are component blocks named by their owner (`RigidBody`,
|
|
118
|
+
* `Material`, `PlayerCharacter`…) — keyword-match the name to a legible
|
|
119
|
+
* icon; the tooltip carries the exact name either way. */
|
|
120
|
+
const GROUP_ICON_RULES: readonly (readonly [RegExp, IconDefinition])[] = [
|
|
121
|
+
[/physic|rigid|collid/i, faAtom],
|
|
122
|
+
[/material/i, faPalette],
|
|
123
|
+
[/light/i, faLightbulb],
|
|
124
|
+
[/camera/i, faCamera],
|
|
125
|
+
[/audio|sound|music/i, faMusic],
|
|
126
|
+
[/player|character|humanoid|person/i, faPerson],
|
|
127
|
+
[/anim|state|machine/i, faDiagramProject],
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
export function groupIcon(name: string): IconDefinition {
|
|
131
|
+
for (const [pattern, icon] of GROUP_ICON_RULES) {
|
|
132
|
+
if (pattern.test(name)) return icon;
|
|
133
|
+
}
|
|
134
|
+
return faPuzzlePiece;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
type PreviewRenderer = Extract<InspectionSection['body'], { readonly kind: 'preview' }>['render'];
|
|
138
|
+
|
|
139
|
+
function objectPreviewRenderer(input: {
|
|
140
|
+
readonly object: THREE.Object3D;
|
|
141
|
+
readonly displayName: string;
|
|
142
|
+
readonly previewKey: string;
|
|
143
|
+
readonly actions: readonly InspectionAction[];
|
|
144
|
+
}): PreviewRenderer {
|
|
145
|
+
return (mode) =>
|
|
146
|
+
createElement(InspectorPreviewBody, {
|
|
147
|
+
object: input.object,
|
|
148
|
+
displayName: input.displayName,
|
|
149
|
+
previewKey: input.previewKey,
|
|
150
|
+
actions: input.actions,
|
|
151
|
+
...(mode ? { mode } : {}),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function componentPreviewRenderer(input: {
|
|
156
|
+
readonly picture: { readonly source: ContentEntrySource; readonly entry: ContentEntry };
|
|
157
|
+
readonly previewKey: string;
|
|
158
|
+
readonly actions: readonly InspectionAction[];
|
|
159
|
+
}): PreviewRenderer {
|
|
160
|
+
return (mode) =>
|
|
161
|
+
createElement(InspectorComponentPreviewBody, {
|
|
162
|
+
source: input.picture.source,
|
|
163
|
+
entry: input.picture.entry,
|
|
164
|
+
previewKey: input.previewKey,
|
|
165
|
+
actions: input.actions,
|
|
166
|
+
...(mode ? { mode } : {}),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function canvasPreviewRenderer(input: {
|
|
171
|
+
readonly capture: (size: number) => Promise<string | null>;
|
|
172
|
+
readonly previewKey: string;
|
|
173
|
+
readonly actions: readonly InspectionAction[];
|
|
174
|
+
}): PreviewRenderer {
|
|
175
|
+
return (mode) =>
|
|
176
|
+
createElement(InspectorCanvasPreviewBody, {
|
|
177
|
+
capture: input.capture,
|
|
178
|
+
previewKey: input.previewKey,
|
|
179
|
+
actions: input.actions,
|
|
180
|
+
...(mode ? { mode } : {}),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function selectionPreviewRenderer(input: {
|
|
185
|
+
readonly object: THREE.Object3D | null;
|
|
186
|
+
readonly canvasCapture: ((size: number) => Promise<string | null>) | null | undefined;
|
|
187
|
+
readonly picture: { readonly source: ContentEntrySource; readonly entry: ContentEntry } | null;
|
|
188
|
+
readonly displayName: string;
|
|
189
|
+
readonly previewKey: string;
|
|
190
|
+
readonly actions: readonly InspectionAction[];
|
|
191
|
+
}): PreviewRenderer | null {
|
|
192
|
+
// A camera's preview is its view through the scene, owned by the viewport.
|
|
193
|
+
// An isolated model snapshot of the camera itself contains no image.
|
|
194
|
+
if ((input.object as THREE.Camera | null)?.isCamera) return null;
|
|
195
|
+
if (input.object) {
|
|
196
|
+
return objectPreviewRenderer({
|
|
197
|
+
object: input.object,
|
|
198
|
+
displayName: input.displayName,
|
|
199
|
+
previewKey: input.previewKey,
|
|
200
|
+
actions: input.actions,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (input.canvasCapture) {
|
|
204
|
+
return canvasPreviewRenderer({
|
|
205
|
+
capture: input.canvasCapture,
|
|
206
|
+
previewKey: input.previewKey,
|
|
207
|
+
actions: input.actions,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (input.picture) {
|
|
211
|
+
return componentPreviewRenderer({
|
|
212
|
+
picture: input.picture,
|
|
213
|
+
previewKey: input.previewKey,
|
|
214
|
+
actions: input.actions,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** `transform.position` -> `position`. The three paths the Transform section's
|
|
221
|
+
* write half publishes are the only ones its io is ever handed. */
|
|
222
|
+
function transformChannelOf(path: string): TransformChannel {
|
|
223
|
+
const channel = path.slice('transform.'.length);
|
|
224
|
+
if (channel !== 'position' && channel !== 'rotation' && channel !== 'scale') {
|
|
225
|
+
throw new Error(`The Transform section has no ${JSON.stringify(path)}.`);
|
|
226
|
+
}
|
|
227
|
+
return channel;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const IDENTITY_TRANSFORM: Transform = {
|
|
231
|
+
position: [0, 0, 0],
|
|
232
|
+
rotation: [0, 0, 0, 1],
|
|
233
|
+
scale: [1, 1, 1],
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/** The Stories section's content for this selection, or `null` when the
|
|
237
|
+
* selection has no stories concept at all. Resolved by the caller because
|
|
238
|
+
* the gating is an adapter-TOPOLOGY question (which child adapter owns this
|
|
239
|
+
* row) rather than a composition one. */
|
|
240
|
+
export interface ComposeStoriesInput {
|
|
241
|
+
readonly stories: readonly StoryRef[];
|
|
242
|
+
readonly activeStoryId: string;
|
|
243
|
+
readonly onApplyStory: (storyId: string | null) => void;
|
|
244
|
+
/** B3 — a catalog component node also gets the Isolate toggle. */
|
|
245
|
+
readonly isolation: { readonly isolated: boolean; readonly toggle: () => void } | null;
|
|
246
|
+
/** The provider's own display vocabulary (`StoriesProvider.title`) — an
|
|
247
|
+
* ingested game's contract scenes say "Scenes". Absent ⇒ "Stories". */
|
|
248
|
+
readonly title?: string;
|
|
249
|
+
/** The provider's own reason its list could not be read
|
|
250
|
+
* (`StoriesProvider.unavailable`), or `null` when it ran. */
|
|
251
|
+
readonly unavailableReason?: string | null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface ComposeInspectionInput {
|
|
255
|
+
readonly adapter: AuthoringAdapter;
|
|
256
|
+
/** The selected node, or `null` for the NO-SELECTION subject — which is
|
|
257
|
+
* the active surface's own empty state, never a shared blank. */
|
|
258
|
+
readonly nodeId: string | null;
|
|
259
|
+
/** The resolved hierarchy node, or `null` when the active adapter cannot
|
|
260
|
+
* resolve the selected id. */
|
|
261
|
+
readonly node: EditorNode | null;
|
|
262
|
+
/** The surface this subject belongs to — decides its presentation
|
|
263
|
+
* affinity, and nothing else here. Absent in a bounded host, which names
|
|
264
|
+
* no surface and offers no preference. */
|
|
265
|
+
readonly surface?: InspectionSurfaceKind | undefined;
|
|
266
|
+
/** The active adapter's answer to `describeSubject(null)`
|
|
267
|
+
* (`inspection/null-subject.ts`), resolved by the caller the same way
|
|
268
|
+
* `contributions` is. Read only when `nodeId` is `null`; `null` there
|
|
269
|
+
* means this surface honestly has no empty-state subject. */
|
|
270
|
+
readonly nullSubject?: NullInspectionSubject | null;
|
|
271
|
+
/** `matchedInspectorSections(node, adapter)`, in registration order. */
|
|
272
|
+
readonly contributions: readonly InspectorSectionContribution[];
|
|
273
|
+
/** How many OTHER things are selected alongside {@link nodeId} — see
|
|
274
|
+
* {@link InspectionSubject.alsoSelected}. The caller resolves it, because
|
|
275
|
+
* the selection lives on the store and this function is pure over one
|
|
276
|
+
* node. */
|
|
277
|
+
readonly alsoSelected?: number | undefined;
|
|
278
|
+
/**
|
|
279
|
+
* The live `Object3D` this node can be previewed FROM, or `null` when there
|
|
280
|
+
* is none. Present ⇒ the subject gets a Preview section; absent ⇒ it simply
|
|
281
|
+
* has no such section. It is the object rather than an id to look one up by,
|
|
282
|
+
* because "previewable" and "renders honestly" have to be the same fact: a
|
|
283
|
+
* part inside an asset document is a real object that is NOT in the shell's
|
|
284
|
+
* object map, and an id-keyed preview could only have shown a "no longer
|
|
285
|
+
* available" alert for a thing that is on screen.
|
|
286
|
+
*/
|
|
287
|
+
readonly previewObject: THREE.Object3D | null;
|
|
288
|
+
/** Live native Canvas/Pixi subtree capture. The mounted surface owns the
|
|
289
|
+
* renderer, so the caller supplies the capture door when one exists. */
|
|
290
|
+
readonly canvasPreview?: ((size: number) => Promise<string | null>) | null;
|
|
291
|
+
/** This node has an Asset Lab document to jump to. */
|
|
292
|
+
readonly assetDocument: { readonly open: () => void } | null;
|
|
293
|
+
readonly stories: ComposeStoriesInput | null;
|
|
294
|
+
/** Notify the shell that the adapter's truth changed (`notifyIngestEdit`). */
|
|
295
|
+
readonly onEdit: () => void;
|
|
296
|
+
/** See {@link OwnedInspectorRail} — present only while a document whose
|
|
297
|
+
* package owns its Properties rail is active. */
|
|
298
|
+
readonly rail?: OwnedInspectorRail | undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* A contribution's identified sections — the one place an
|
|
303
|
+
* `InspectorSectionContribution` becomes `InspectionSection`s.
|
|
304
|
+
*
|
|
305
|
+
* A single-section registration yields exactly one, with an opaque custom
|
|
306
|
+
* body. A PRODUCER (`InspectorSectionsProducer`) yields whatever the node
|
|
307
|
+
* has: the react/DOM adapter's descriptor groups are its Layout / Style /
|
|
308
|
+
* Position / Spacing blocks, and only the node can say how many there are.
|
|
309
|
+
* Either way what comes out is model data — identity, order, icon, body —
|
|
310
|
+
* and the projections cannot tell which shape produced it.
|
|
311
|
+
*/
|
|
312
|
+
export function contributionSections(
|
|
313
|
+
contribution: InspectorSectionContribution,
|
|
314
|
+
adapter: AuthoringAdapter,
|
|
315
|
+
nodeId: string | null,
|
|
316
|
+
node: EditorNode | null,
|
|
317
|
+
): readonly InspectionSection[] {
|
|
318
|
+
if (isSectionsProducer(contribution)) return contribution.sections(node, adapter);
|
|
319
|
+
const Section = contribution.Section;
|
|
320
|
+
return [
|
|
321
|
+
{
|
|
322
|
+
id: contribution.id,
|
|
323
|
+
title: contribution.title,
|
|
324
|
+
// A registration's icon may be a FUNCTION of the subject
|
|
325
|
+
// (`InspectorSectionIcon`) — Blender's Object Data tab draws the mark
|
|
326
|
+
// its object type earns. Resolved here, once per composition, so every
|
|
327
|
+
// projection downstream still receives a plain `IconDefinition`.
|
|
328
|
+
icon:
|
|
329
|
+
typeof contribution.icon === 'function'
|
|
330
|
+
? contribution.icon(node, adapter)
|
|
331
|
+
: contribution.icon,
|
|
332
|
+
order: contribution.order,
|
|
333
|
+
...(contribution.railGroup === undefined ? {} : { railGroup: contribution.railGroup }),
|
|
334
|
+
...(contribution.railDefault === undefined ? {} : { railDefault: contribution.railDefault }),
|
|
335
|
+
body: { kind: 'custom', render: () => createElement(Section, { adapter, nodeId }) },
|
|
336
|
+
},
|
|
337
|
+
];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* The positive-space replacement protocol, whole: a contribution claims the
|
|
342
|
+
* section ids it PROVIDES, and a built-in whose id is claimed is never
|
|
343
|
+
* composed. A section id is unique within a subject (the compact card keys
|
|
344
|
+
* its tabs by it), so the FIRST claim of an id wins — registration order, the
|
|
345
|
+
* same order everything else here reads.
|
|
346
|
+
*
|
|
347
|
+
* A contribution that loses EVERY claim renders nothing.
|
|
348
|
+
*/
|
|
349
|
+
function expandContributions(
|
|
350
|
+
contributions: readonly InspectorSectionContribution[],
|
|
351
|
+
adapter: AuthoringAdapter,
|
|
352
|
+
nodeId: string | null,
|
|
353
|
+
node: EditorNode | null,
|
|
354
|
+
): {
|
|
355
|
+
sections: InspectionSection[];
|
|
356
|
+
claimed: ReadonlySet<string>;
|
|
357
|
+
/** Which package each contributed section came out of, by section id —
|
|
358
|
+
* what {@link ComposeInspectionInput.rail} filters on. */
|
|
359
|
+
owners: ReadonlyMap<string, string | undefined>;
|
|
360
|
+
} {
|
|
361
|
+
const claimed = new Set<string>();
|
|
362
|
+
const owners = new Map<string, string | undefined>();
|
|
363
|
+
const sections: InspectionSection[] = [];
|
|
364
|
+
for (const contribution of contributions) {
|
|
365
|
+
const produced = contributionSections(contribution, adapter, nodeId, node).filter(
|
|
366
|
+
(section) => !claimed.has(section.id),
|
|
367
|
+
);
|
|
368
|
+
// A producer may also stand a built-in down without replacing it with a
|
|
369
|
+
// section of its own (the descriptor channel of a node with no loose
|
|
370
|
+
// fields).
|
|
371
|
+
const declared = (isSectionsProducer(contribution) ? (contribution.claims ?? []) : []).filter(
|
|
372
|
+
(id) => !claimed.has(id),
|
|
373
|
+
);
|
|
374
|
+
if (produced.length === 0 && declared.length === 0) continue;
|
|
375
|
+
for (const section of produced) {
|
|
376
|
+
claimed.add(section.id);
|
|
377
|
+
owners.set(section.id, contribution.owner);
|
|
378
|
+
}
|
|
379
|
+
for (const id of declared) claimed.add(id);
|
|
380
|
+
sections.push(...produced);
|
|
381
|
+
}
|
|
382
|
+
return { sections, claimed, owners };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* A DOCUMENT'S PACKAGE OWNS ITS PROPERTIES RAIL (owner ruling; WORK.md
|
|
387
|
+
* §Blender in the tab is Blender, "Inspection parity", I2 decision 1).
|
|
388
|
+
*
|
|
389
|
+
* `owner` is the package whose `workspace.document` contribution mounts the
|
|
390
|
+
* ACTIVE document and declared `export const inspectorRail = 'owned'`;
|
|
391
|
+
* `builtins` is what that document listed in `export const inspectorBuiltins`.
|
|
392
|
+
* Absent — the ordinary case — and composition is exactly as it was.
|
|
393
|
+
*/
|
|
394
|
+
export interface OwnedInspectorRail {
|
|
395
|
+
readonly owner: string;
|
|
396
|
+
readonly builtins: readonly string[];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Keep only what an OWNED rail may show: this package's own contributed
|
|
401
|
+
* sections, plus the built-ins the document named by id.
|
|
402
|
+
*
|
|
403
|
+
* WHY A FILTER AND NOT A BRANCH THROUGH THE WHOLE COMPOSER. Every built-in
|
|
404
|
+
* below is composed from live inputs — a preview capture, the transform
|
|
405
|
+
* channel's editability, the descriptor grid's io — and the shell's other
|
|
406
|
+
* doors resolve field paths against exactly those composed sections
|
|
407
|
+
* (`inspection/active-subject.ts`). Composing them and then standing them
|
|
408
|
+
* down keeps one code path for what a section IS, and makes the ownership
|
|
409
|
+
* rule one readable predicate instead of five `if (!rail)` guards. It is also
|
|
410
|
+
* the SMALLEST change: the rule is stated once, here.
|
|
411
|
+
*/
|
|
412
|
+
function applyOwnedRail(
|
|
413
|
+
sections: readonly InspectionSection[],
|
|
414
|
+
owners: ReadonlyMap<string, string | undefined>,
|
|
415
|
+
rail: OwnedInspectorRail | undefined,
|
|
416
|
+
): InspectionSection[] {
|
|
417
|
+
if (rail === undefined) return [...sections];
|
|
418
|
+
return sections.filter(
|
|
419
|
+
(section) => owners.get(section.id) === rail.owner || rail.builtins.includes(section.id),
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export interface ComposeNullInspectionInput {
|
|
424
|
+
/** The surface's own answer to `describeSubject(null)` — for an asset
|
|
425
|
+
* document that is the DOCUMENT itself
|
|
426
|
+
* (`inspection/document-subject.ts`). */
|
|
427
|
+
readonly nullSubject: NullInspectionSubject | null;
|
|
428
|
+
readonly surface?: InspectionSurfaceKind | undefined;
|
|
429
|
+
/** Contributions matching the empty selection, and the adapter they render
|
|
430
|
+
* against. Both are absent for a subject with no adapter in play — a
|
|
431
|
+
* document with no authoring of its own describes itself — and
|
|
432
|
+
* contributions without an adapter to render them are dropped rather than
|
|
433
|
+
* half-composed. */
|
|
434
|
+
readonly contributions?: readonly InspectorSectionContribution[];
|
|
435
|
+
readonly adapter?: AuthoringAdapter;
|
|
436
|
+
/** See {@link OwnedInspectorRail}. */
|
|
437
|
+
readonly rail?: OwnedInspectorRail | undefined;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* The NO-SELECTION subject: the surface's own empty state
|
|
442
|
+
* (`inspection/null-subject.ts`) plus whatever contributions match the null
|
|
443
|
+
* selection, in one identified section list. There is no descriptor channel,
|
|
444
|
+
* no transform and no preview — there is no node to have any of them — so
|
|
445
|
+
* this branch composes exactly what the surface itself offered and nothing
|
|
446
|
+
* that belongs to another surface.
|
|
447
|
+
*
|
|
448
|
+
* It DOES compose an identity row when the surface named its subject's type
|
|
449
|
+
* (`kindLabel`): an asset document is a thing with a name, a type and a
|
|
450
|
+
* provenance, and both projections render that row exactly as they render a
|
|
451
|
+
* selection's. A three scene's empty state names no type and gets its quiet
|
|
452
|
+
* headline instead. The row is read-only — nothing here renames a document.
|
|
453
|
+
*/
|
|
454
|
+
export function composeNullInspectionSubject(input: ComposeNullInspectionInput): InspectionSubject {
|
|
455
|
+
const { adapter, nullSubject } = input;
|
|
456
|
+
const {
|
|
457
|
+
sections: contributed,
|
|
458
|
+
claimed,
|
|
459
|
+
owners,
|
|
460
|
+
} = expandContributions(
|
|
461
|
+
adapter ? (input.contributions ?? []) : [],
|
|
462
|
+
// Only reachable with an adapter (`expandContributions` is handed an
|
|
463
|
+
// empty list otherwise), so the cast never dereferences undefined.
|
|
464
|
+
adapter as AuthoringAdapter,
|
|
465
|
+
null,
|
|
466
|
+
null,
|
|
467
|
+
);
|
|
468
|
+
// The claim protocol is the SAME on both branches. A surface's own
|
|
469
|
+
// no-selection section is a built-in like any other: a contribution
|
|
470
|
+
// claiming `environment` REPLACES the scene's environment block, it does
|
|
471
|
+
// not appear beside it. (W4 — the selected path filtered on `claimed` from
|
|
472
|
+
// W2; this one did not, so the first contribution to claim a null-subject
|
|
473
|
+
// id would have duplicated the section instead of replacing it.)
|
|
474
|
+
const sections = applyOwnedRail(
|
|
475
|
+
[
|
|
476
|
+
...(nullSubject?.sections ?? []).filter((section) => !claimed.has(section.id)),
|
|
477
|
+
...contributed,
|
|
478
|
+
].sort((a, b) => a.order - b.order),
|
|
479
|
+
owners,
|
|
480
|
+
input.rail,
|
|
481
|
+
);
|
|
482
|
+
return {
|
|
483
|
+
// The fallback identity is for a surface that named no empty-state subject
|
|
484
|
+
// and yet HAS an inspector: several contributions match on the active
|
|
485
|
+
// DOCUMENT rather than on a node (a story's arg knobs, the 3D board's
|
|
486
|
+
// Exhibit block), so "nothing selected, and here is the document's own block" is
|
|
487
|
+
// a real thing a human looks at. When there is no such block either, the
|
|
488
|
+
// box unmounts and the wire says so (`inspection/active-subject.ts`
|
|
489
|
+
// answers `NO_INSPECTION`) — this object is then composed and discarded,
|
|
490
|
+
// never shown and never serialized.
|
|
491
|
+
id: nullSubject?.id ?? 'nothing-selected',
|
|
492
|
+
title: nullSubject?.title ?? 'Nothing selected',
|
|
493
|
+
identity: nullSubject?.kindLabel
|
|
494
|
+
? {
|
|
495
|
+
rename: { readOnly: true, set: () => undefined },
|
|
496
|
+
kindLabel: nullSubject.kindLabel,
|
|
497
|
+
...(nullSubject.note ? { note: nullSubject.note } : {}),
|
|
498
|
+
}
|
|
499
|
+
: null,
|
|
500
|
+
...(nullSubject?.hint ? { hint: nullSubject.hint } : {}),
|
|
501
|
+
presentation: { preferred: inspectionAffinityFor(input.surface) },
|
|
502
|
+
quickActions: nullSubject?.quickActions ?? [],
|
|
503
|
+
related: [],
|
|
504
|
+
sections,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Give a CONTRIBUTED custom body the same `editable` the generic body it
|
|
510
|
+
* REPLACED would have carried.
|
|
511
|
+
*
|
|
512
|
+
* A contribution that claims `properties` / `group:<id>` is saying "I draw
|
|
513
|
+
* these descriptors with my own widgets" — not "these descriptors are no
|
|
514
|
+
* longer writable". But `editable` is what `inspection/active-subject.ts`'s
|
|
515
|
+
* `setActiveInspectionField` resolves a path against, so until this existed,
|
|
516
|
+
* claiming a channel silently closed the only control-API write door onto it.
|
|
517
|
+
* Measured on an ingested game's DOM HUD: every style and prop field answered
|
|
518
|
+
* "The active Inspector has no field at …", because
|
|
519
|
+
* `@vgai/dom/react-inspector-section.tsx` claims `PROPERTIES_SECTION_ID` and
|
|
520
|
+
* renders custom bodies — so the lane the editor's panels could author was
|
|
521
|
+
* unreachable to `vgai eval`'s `editor.setField`, the one door an agent has.
|
|
522
|
+
*
|
|
523
|
+
* The `io` handed in is the SAME one the generic bodies use and the SAME
|
|
524
|
+
* `adapter.inspector.set` the contributed widgets call, so this adds a door,
|
|
525
|
+
* never a second write path. A body that already declares its own `editable`
|
|
526
|
+
* (the Transform section) is left exactly as it is, and a contributed section
|
|
527
|
+
* with an id of its own owns no descriptors and gets nothing.
|
|
528
|
+
*/
|
|
529
|
+
function withDescriptorEditable(
|
|
530
|
+
section: InspectionSection,
|
|
531
|
+
io: InspectionFieldIo,
|
|
532
|
+
fieldsById: ReadonlyMap<string, readonly FieldDescriptor[]>,
|
|
533
|
+
): InspectionSection {
|
|
534
|
+
if (section.body.kind !== 'custom' || section.body.editable) return section;
|
|
535
|
+
const fields = fieldsById.get(section.id);
|
|
536
|
+
if (!fields || fields.length === 0) return section;
|
|
537
|
+
return { ...section, body: { ...section.body, editable: { fields, io } } };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export function composeInspectionSubject(input: ComposeInspectionInput): InspectionSubject {
|
|
541
|
+
const { adapter, nodeId, node, contributions, onEdit } = input;
|
|
542
|
+
if (nodeId === null) {
|
|
543
|
+
return composeNullInspectionSubject({
|
|
544
|
+
nullSubject: input.nullSubject ?? null,
|
|
545
|
+
surface: input.surface,
|
|
546
|
+
contributions,
|
|
547
|
+
adapter,
|
|
548
|
+
rail: input.rail,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const {
|
|
553
|
+
sections: contributed,
|
|
554
|
+
claimed,
|
|
555
|
+
owners,
|
|
556
|
+
} = expandContributions(contributions, adapter, nodeId, node);
|
|
557
|
+
|
|
558
|
+
const io: InspectionFieldIo = {
|
|
559
|
+
get: (path: string) => adapter.inspector?.get(nodeId, path),
|
|
560
|
+
// Present only when the adapter can show a value without writing it.
|
|
561
|
+
...(adapter.inspector?.preview
|
|
562
|
+
? {
|
|
563
|
+
preview: (path: string, value: unknown) => {
|
|
564
|
+
adapter.inspector?.preview?.(nodeId, path, value);
|
|
565
|
+
},
|
|
566
|
+
}
|
|
567
|
+
: {}),
|
|
568
|
+
set: (path: string, value: unknown) => {
|
|
569
|
+
if (!adapter.inspector) return undefined;
|
|
570
|
+
const result = setAuthoringInspectorField(adapter, nodeId, path, value);
|
|
571
|
+
onEdit();
|
|
572
|
+
return result;
|
|
573
|
+
},
|
|
574
|
+
/** Revert-to-default: drop the document's override and let whatever
|
|
575
|
+
* governs the property in its absence take over (Unity's revert arrow).
|
|
576
|
+
* The owning provider's own ack is passed straight through, exactly as
|
|
577
|
+
* `set` does — a removal moves bytes and answers for them. Absent when the
|
|
578
|
+
* adapter declares no removal door, so the door above it can refuse by
|
|
579
|
+
* name rather than write a value that leaves the prop in the file. */
|
|
580
|
+
...(adapter.inspector?.remove
|
|
581
|
+
? {
|
|
582
|
+
remove: (path: string) => {
|
|
583
|
+
const result = removeAuthoringInspectorField(adapter, nodeId, path);
|
|
584
|
+
onEdit();
|
|
585
|
+
return result;
|
|
586
|
+
},
|
|
587
|
+
}
|
|
588
|
+
: {}),
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// The full property set is DRIVEN BY the adapter's InspectorProvider — a
|
|
592
|
+
// generic descriptor list, not a hardcoded panel. 'name' is the identity
|
|
593
|
+
// row's field; everything else is a section body.
|
|
594
|
+
const declared = adapter.inspector?.properties(nodeId) ?? [];
|
|
595
|
+
const nameProperty = declared.find((p) => p.path === 'name');
|
|
596
|
+
const { ungrouped, groups } = groupProperties(declared.filter((p) => p.path !== 'name'));
|
|
597
|
+
|
|
598
|
+
// The hierarchy already presents visibility as the EYE toggle — the same
|
|
599
|
+
// fact must not reappear here as a labelled "Visible" checkbox (owner,
|
|
600
|
+
// 2026-08-06). Lift the UNGROUPED `visible` boolean out of the grid and
|
|
601
|
+
// make it a subject verb; a GROUPED `visible` (a component's own prop)
|
|
602
|
+
// stays in its group untouched.
|
|
603
|
+
const visibleField = ungrouped.find((p) => p.path === 'visible' && p.type === 'boolean');
|
|
604
|
+
const looseFields = visibleField ? ungrouped.filter((p) => p !== visibleField) : ungrouped;
|
|
605
|
+
/** What the SUBJECT itself exposes to a field write — see the `editable`
|
|
606
|
+
* spread at the end of this function for why `name` is in it. */
|
|
607
|
+
const subjectEditableFields: FieldDescriptor[] = [
|
|
608
|
+
...(nameProperty && nameProperty.readonly !== true ? [nameProperty] : []),
|
|
609
|
+
...(visibleField ? [visibleField] : []),
|
|
610
|
+
];
|
|
611
|
+
|
|
612
|
+
const quickActions: InspectionAction[] = [];
|
|
613
|
+
if (visibleField) {
|
|
614
|
+
// Unset reads as visible — `Object3D.visible` defaults true.
|
|
615
|
+
const isVisible = io.get(visibleField.path) !== false;
|
|
616
|
+
const title = isVisible ? 'Visible — click to hide' : 'Hidden — click to show';
|
|
617
|
+
quickActions.push({
|
|
618
|
+
id: 'inspector-visibility-toggle',
|
|
619
|
+
title,
|
|
620
|
+
icon: isVisible ? faEye : faEyeSlash,
|
|
621
|
+
placement: 'identity',
|
|
622
|
+
pressed: !isVisible,
|
|
623
|
+
disabled: visibleField.readonly === true,
|
|
624
|
+
run: async () => {
|
|
625
|
+
await io.set(visibleField.path, !isVisible);
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
if (input.assetDocument) {
|
|
630
|
+
quickActions.push({
|
|
631
|
+
id: 'open-asset-editor',
|
|
632
|
+
title: 'Open in Asset Editor',
|
|
633
|
+
label: 'Open in Asset Editor',
|
|
634
|
+
placement: 'preview',
|
|
635
|
+
run: input.assetDocument.open,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
// THERE IS NO MODEL DRILL FROM A PREFAB'S MESH (2026-09-19, M1). Both quick
|
|
639
|
+
// actions that stood here resolved a `geometry={build()}` prop to a
|
|
640
|
+
// TypeScript model module and opened or wrote one. A model is a `.blend`
|
|
641
|
+
// now (ARCHITECTURE-CORE §The project model, "A model is Blender data"), a
|
|
642
|
+
// prefab uses the glTF exported from one, and nothing records WHICH `.blend`
|
|
643
|
+
// produced a given glTF — so there is no `.blend`-targeted drill to replace
|
|
644
|
+
// them with, only a design that does not exist yet. Recorded in WORK.md.
|
|
645
|
+
const sourceCommit = adapter.transforms?.sourceCommit;
|
|
646
|
+
const sourceCommitAvailability = sourceCommit?.availability(nodeId);
|
|
647
|
+
if (sourceCommitAvailability?.available) {
|
|
648
|
+
quickActions.push({
|
|
649
|
+
id: 'commit-live-transform-source',
|
|
650
|
+
title: 'Commit this live transform to its authored source literal',
|
|
651
|
+
label: 'Commit transform to source',
|
|
652
|
+
icon: faArrowUpFromBracket,
|
|
653
|
+
placement: 'identity',
|
|
654
|
+
run: async () => {
|
|
655
|
+
await commitAuthoringTransformSource(adapter, nodeId);
|
|
656
|
+
onEdit();
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Which DESCRIPTORS each replaceable built-in section id owns. A
|
|
662
|
+
// contribution that CLAIMS one of these ids draws its own chrome over the
|
|
663
|
+
// very same descriptors, so this is what it must stay writable through —
|
|
664
|
+
// see {@link withDescriptorEditable}.
|
|
665
|
+
const descriptorFieldsById = new Map<string, readonly FieldDescriptor[]>();
|
|
666
|
+
descriptorFieldsById.set(PROPERTIES_SECTION_ID, looseFields);
|
|
667
|
+
for (const group of groups) {
|
|
668
|
+
descriptorFieldsById.set(groupSectionId(group.groupId), group.properties);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const sections: InspectionSection[] = [];
|
|
672
|
+
const creationSite = adapter.truth?.resolve(nodeId, 'position').site ?? null;
|
|
673
|
+
|
|
674
|
+
// The live PREVIEW — an ordinary section, first in the order, and present
|
|
675
|
+
// exactly when there is a real native visual authority behind it. A Three
|
|
676
|
+
// selection hands us its live Object3D. A Canvas selection hands us a
|
|
677
|
+
// renderer-backed capture of its live display subtree; a story-backed
|
|
678
|
+
// component remains the fallback where a mounted surface cannot expose a
|
|
679
|
+
// renderer. The
|
|
680
|
+
// `preview`-placed verbs (the Asset Editor jump) render inside either box,
|
|
681
|
+
// so they read the same in the card and the column.
|
|
682
|
+
const previewObject = input.previewObject;
|
|
683
|
+
// The COMPONENT's own picture, from whichever content source admits it
|
|
684
|
+
// (`content-entry-source-registry.ts`) — the fallback where a mounted canvas
|
|
685
|
+
// surface cannot expose a renderer. The shell asks about the component this
|
|
686
|
+
// node IS; what the picture is made of is the source's business.
|
|
687
|
+
//
|
|
688
|
+
// NARROWED 2026-09-18 (unit 11, edge 8): this used to resolve the node's
|
|
689
|
+
// ACTIVE story id against the project story registry, which is a registry
|
|
690
|
+
// question the host may not ask. The component's registered preview and its
|
|
691
|
+
// active story are the same picture whenever the active one is the component's
|
|
692
|
+
// default, which is what `StoriesProvider.active` reports for an unswitched
|
|
693
|
+
// node; a node switched to a non-default variant now shows the component's
|
|
694
|
+
// registered preview instead. Restoring the variant needs the CANVAS surface
|
|
695
|
+
// package that owns this branch (edge 10), not a second door here.
|
|
696
|
+
const componentName = node?.typeLabel ?? node?.label ?? nodeId;
|
|
697
|
+
const componentPicture =
|
|
698
|
+
input.surface === 'canvas' && previewObject === null && input.stories
|
|
699
|
+
? contentEntryForComponent({
|
|
700
|
+
name: componentName,
|
|
701
|
+
path: creationSite?.anchored === true ? creationSite.file : '',
|
|
702
|
+
surface: 'canvas',
|
|
703
|
+
})
|
|
704
|
+
: null;
|
|
705
|
+
const previewActions = quickActions.filter((action) => action.placement === 'preview');
|
|
706
|
+
const previewRender = selectionPreviewRenderer({
|
|
707
|
+
object: previewObject,
|
|
708
|
+
canvasCapture: input.canvasPreview,
|
|
709
|
+
picture: componentPicture,
|
|
710
|
+
displayName: node?.label ?? nodeId,
|
|
711
|
+
previewKey: nodeId,
|
|
712
|
+
actions: previewActions,
|
|
713
|
+
});
|
|
714
|
+
if (!claimed.has(PREVIEW_SECTION_ID) && previewRender !== null) {
|
|
715
|
+
sections.push({
|
|
716
|
+
id: PREVIEW_SECTION_ID,
|
|
717
|
+
title: 'Preview',
|
|
718
|
+
icon: faCube,
|
|
719
|
+
order: PREVIEW_SECTION_ORDER,
|
|
720
|
+
body: {
|
|
721
|
+
kind: 'preview',
|
|
722
|
+
render: previewRender,
|
|
723
|
+
},
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const transformDimensions = transformDimensionsFor(adapter, nodeId);
|
|
728
|
+
if (!claimed.has(TRANSFORM_SECTION_ID) && transformDimensions !== null) {
|
|
729
|
+
const transform = adapter.transforms?.get(nodeId) ?? IDENTITY_TRANSFORM;
|
|
730
|
+
// Measured on the ONE node the reader selected, which is what pays for the
|
|
731
|
+
// bounds walk `describeInstancedPresentation` needs (the hierarchy row's
|
|
732
|
+
// detail deliberately asks the cheap question instead). `contentWorldBounds`
|
|
733
|
+
// resolves instance matrices live — three's own cached object box would
|
|
734
|
+
// answer for whatever pose was first asked about.
|
|
735
|
+
const instanced = previewObject
|
|
736
|
+
? describeInstancedPresentation(previewObject, contentWorldBounds(previewObject))
|
|
737
|
+
: null;
|
|
738
|
+
const channelEditability = Object.fromEntries(
|
|
739
|
+
(['position', 'rotation', 'scale'] as const).map((channel) => [
|
|
740
|
+
channel,
|
|
741
|
+
adapter.transforms?.editability?.(nodeId, channel) ?? {
|
|
742
|
+
writable: adapter.capabilities.transform,
|
|
743
|
+
},
|
|
744
|
+
]),
|
|
745
|
+
) as Record<TransformChannel, TransformEditability>;
|
|
746
|
+
// The section's values in the units its ROWS use — degrees for rotation —
|
|
747
|
+
// spelled once so the read channel, the writable field descriptors and the
|
|
748
|
+
// write's own round trip can never quote three different numbers.
|
|
749
|
+
const transformData: Record<TransformChannel, [number, number, number]> = {
|
|
750
|
+
position: [...transform.position],
|
|
751
|
+
rotation: rotationDegrees(transform.rotation),
|
|
752
|
+
scale: [...transform.scale],
|
|
753
|
+
};
|
|
754
|
+
sections.push({
|
|
755
|
+
id: TRANSFORM_SECTION_ID,
|
|
756
|
+
title: 'Transform',
|
|
757
|
+
icon: faUpDownLeftRight,
|
|
758
|
+
order: TRANSFORM_SECTION_ORDER,
|
|
759
|
+
body: {
|
|
760
|
+
kind: 'custom',
|
|
761
|
+
// The section's own values, for the readers that cannot render one
|
|
762
|
+
// (`inspection/serialize.ts` — P18). Same `transform` the body below
|
|
763
|
+
// is handed, converted the same way its rows convert it: the rotation
|
|
764
|
+
// rows DISPLAY Euler XYZ degrees (`rotationDegrees`), so degrees is
|
|
765
|
+
// what the wire carries — a quaternion here would be a second answer
|
|
766
|
+
// to the question the panel already answers.
|
|
767
|
+
data: {
|
|
768
|
+
...transformData,
|
|
769
|
+
// The same per-channel verdict the rendered rows lock and explain
|
|
770
|
+
// themselves with. It belongs on the wire for the reason the values
|
|
771
|
+
// above do: a reader that cannot render the section still has to be
|
|
772
|
+
// able to ask "can I move this, and if not why not" — and for an
|
|
773
|
+
// ingest mount that answer IS the product surface (the creation-site
|
|
774
|
+
// gate's reason, verbatim).
|
|
775
|
+
editability: channelEditability,
|
|
776
|
+
// The same two facts the human is shown — the row's unit count and
|
|
777
|
+
// the world-anchored sentence — so an agent reading this section
|
|
778
|
+
// cannot conclude the transform means something the panel is warning
|
|
779
|
+
// it does not. Absent entirely for a node that is not an instanced
|
|
780
|
+
// draw, which is nearly every node.
|
|
781
|
+
...(instanced ? { instanced } : {}),
|
|
782
|
+
},
|
|
783
|
+
// The WRITE half of the three values above, through the same
|
|
784
|
+
// begin/apply/end `onCommit` performs. An agent could read this
|
|
785
|
+
// section and the sentence explaining whether it may move — and had no
|
|
786
|
+
// way to move it; for an ingest root, whose only authoring surface is
|
|
787
|
+
// the editor, that closed the lane entirely.
|
|
788
|
+
editable: {
|
|
789
|
+
fields: (['position', 'rotation', 'scale'] as const).map((channel) => {
|
|
790
|
+
const readonly =
|
|
791
|
+
!adapter.capabilities.transform || !channelEditability[channel].writable;
|
|
792
|
+
return {
|
|
793
|
+
path: `transform.${channel}`,
|
|
794
|
+
label: channel[0]!.toUpperCase() + channel.slice(1),
|
|
795
|
+
type: 'vec3' as const,
|
|
796
|
+
value: transformData[channel],
|
|
797
|
+
readonly,
|
|
798
|
+
// The SAME two declarations a property row carries, for the
|
|
799
|
+
// channel this section owns instead: the adapter's `removable`,
|
|
800
|
+
// and the io below saying the lane has a door at all. Both are
|
|
801
|
+
// read, neither inferred — `remove-inspection-field` refuses by
|
|
802
|
+
// name when either is missing rather than writing a value that
|
|
803
|
+
// would leave the attribute in the file.
|
|
804
|
+
//
|
|
805
|
+
// HOW MUCH `removable` CLAIMS IS THE ADAPTER'S TO SAY, and it is
|
|
806
|
+
// not the same on every lane — see `TransformEditability`
|
|
807
|
+
// (`@volter/editor-project/adapter/authoring`), which apportions it: a lane
|
|
808
|
+
// holding its own source text answers both halves (the callsite
|
|
809
|
+
// carries this channel AND there is a door), while one that
|
|
810
|
+
// holds only an oid → file:line index answers for the DOOR, and
|
|
811
|
+
// the removal's own ack reports whether a byte actually left. So
|
|
812
|
+
// `resettable` here means "this lane will attempt the drop and
|
|
813
|
+
// answer honestly for it", never "a byte is guaranteed to move".
|
|
814
|
+
...(!readonly &&
|
|
815
|
+
adapter.transforms?.remove &&
|
|
816
|
+
channelEditability[channel].removable === true
|
|
817
|
+
? { resettable: true }
|
|
818
|
+
: {}),
|
|
819
|
+
};
|
|
820
|
+
}),
|
|
821
|
+
io: {
|
|
822
|
+
get: (path) => transformData[transformChannelOf(path)],
|
|
823
|
+
// `endEdit` closes the gesture AND answers for whatever write it
|
|
824
|
+
// triggered (`TransformProvider.endEdit`), so the gesture's own ack
|
|
825
|
+
// is what this io returns — never a destination read off the
|
|
826
|
+
// surface afterwards.
|
|
827
|
+
set: (path, value) => {
|
|
828
|
+
if (!adapter.transforms) return undefined;
|
|
829
|
+
const next = withTransformChannel(
|
|
830
|
+
transform,
|
|
831
|
+
transformChannelOf(path),
|
|
832
|
+
value as number[],
|
|
833
|
+
);
|
|
834
|
+
beginAuthoringTransformEdit(adapter, nodeId);
|
|
835
|
+
applyAuthoringTransform(adapter, nodeId, next);
|
|
836
|
+
const ack = endAuthoringTransformEdit(adapter, nodeId);
|
|
837
|
+
onEdit();
|
|
838
|
+
return ack;
|
|
839
|
+
},
|
|
840
|
+
// Revert-to-default for a transform channel — the ONE door that
|
|
841
|
+
// can express byte-ABSENCE, because `set` above can only write
|
|
842
|
+
// three numbers. Present exactly when the adapter declares one, so
|
|
843
|
+
// the door above can refuse `REMOVAL_UNAVAILABLE` by name instead
|
|
844
|
+
// of a value write that pretends to be a revert; the provider's
|
|
845
|
+
// own ack is passed straight through, exactly as `set` does.
|
|
846
|
+
...(adapter.transforms?.remove
|
|
847
|
+
? {
|
|
848
|
+
remove: (path: string) => {
|
|
849
|
+
const ack = removeAuthoringTransform(adapter, nodeId, transformChannelOf(path));
|
|
850
|
+
onEdit();
|
|
851
|
+
return ack;
|
|
852
|
+
},
|
|
853
|
+
}
|
|
854
|
+
: {}),
|
|
855
|
+
},
|
|
856
|
+
},
|
|
857
|
+
render: () =>
|
|
858
|
+
createElement(InspectorTransformSection, {
|
|
859
|
+
dimensions: transformDimensions,
|
|
860
|
+
transform,
|
|
861
|
+
readOnly: !adapter.capabilities.transform,
|
|
862
|
+
channelEditability,
|
|
863
|
+
...(instanced?.note ? { note: instanced.note } : {}),
|
|
864
|
+
// The section's gesture IS the adapter's: one `beginEdit` when
|
|
865
|
+
// the hand (or the caret) takes the field, live `apply` per
|
|
866
|
+
// scrub move, and the ONE `endEdit` write on release/Enter — the
|
|
867
|
+
// same shape the gizmo drives, so a scrub or a typed number is
|
|
868
|
+
// one undo step and one remount.
|
|
869
|
+
onBegin: () => {
|
|
870
|
+
beginAuthoringTransformEdit(adapter, nodeId);
|
|
871
|
+
},
|
|
872
|
+
onPreview: (next: Transform) => {
|
|
873
|
+
applyAuthoringTransform(adapter, nodeId, next);
|
|
874
|
+
},
|
|
875
|
+
onCommit: (next: Transform) => {
|
|
876
|
+
applyAuthoringTransform(adapter, nodeId, next);
|
|
877
|
+
endAuthoringTransformEdit(adapter, nodeId);
|
|
878
|
+
onEdit();
|
|
879
|
+
},
|
|
880
|
+
}),
|
|
881
|
+
},
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// `properties` names the whole DESCRIPTOR CHANNEL — the loose grid plus the
|
|
886
|
+
// `group:<id>` blocks its named groups produce (see the constant's own
|
|
887
|
+
// doc) — so claiming it stands the channel down entirely.
|
|
888
|
+
if (!claimed.has(PROPERTIES_SECTION_ID)) {
|
|
889
|
+
// The loose grid is headed like every other section — "Properties", with
|
|
890
|
+
// its own glyph, in every projection. `visible` lives on the identity row
|
|
891
|
+
// as the eye, so it never earns this section by itself.
|
|
892
|
+
if (looseFields.length > 0) {
|
|
893
|
+
sections.push({
|
|
894
|
+
id: PROPERTIES_SECTION_ID,
|
|
895
|
+
title: 'Properties',
|
|
896
|
+
icon: faSliders,
|
|
897
|
+
order: PROPERTIES_SECTION_ORDER,
|
|
898
|
+
defaultOpen: true,
|
|
899
|
+
body: { kind: 'fields', fields: looseFields, io },
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
groups.forEach((group, index) => {
|
|
903
|
+
// A group is one owner's block (a component, a wrapper tag) and
|
|
904
|
+
// collapses like Transform does — the same affordance, because it is
|
|
905
|
+
// the same kind of thing. A wrapper like `<RigidBody>` can declare 40+
|
|
906
|
+
// props, so a block whose fields are almost all unset starts CLOSED: it
|
|
907
|
+
// stays discoverable without burying the props this document sets.
|
|
908
|
+
const authored = group.properties.filter((p) => !p.defaulted).length;
|
|
909
|
+
sections.push({
|
|
910
|
+
id: groupSectionId(group.groupId),
|
|
911
|
+
title: group.name,
|
|
912
|
+
icon: groupIcon(group.name),
|
|
913
|
+
order: GROUP_SECTION_ORDER + index,
|
|
914
|
+
description:
|
|
915
|
+
authored > 0
|
|
916
|
+
? `${authored} set of ${group.properties.length}`
|
|
917
|
+
: `${group.properties.length} properties`,
|
|
918
|
+
defaultOpen: authored > 0 && group.properties.length <= GROUP_AUTO_OPEN_MAX,
|
|
919
|
+
testId: `ingest-group-${group.groupId}`,
|
|
920
|
+
body: { kind: 'fields', fields: group.properties, io },
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
const stories = input.stories;
|
|
926
|
+
if (!claimed.has(STORIES_SECTION_ID) && stories) {
|
|
927
|
+
sections.push(composeStoriesSection(stories));
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
sections.push(
|
|
931
|
+
...contributed.map((section) => withDescriptorEditable(section, io, descriptorFieldsById)),
|
|
932
|
+
);
|
|
933
|
+
|
|
934
|
+
// STABLE — equal-ordered contributions keep registration order, which is
|
|
935
|
+
// how contributions that share `CONTRIBUTED_SECTION_ORDER` stay in the
|
|
936
|
+
// order their modules registered them (`Array.prototype.sort` is specified
|
|
937
|
+
// stable).
|
|
938
|
+
sections.sort((a, b) => a.order - b.order);
|
|
939
|
+
|
|
940
|
+
// A DOCUMENT WHOSE PACKAGE OWNS ITS RAIL shows only that package's sections
|
|
941
|
+
// (plus the built-ins it named). One statement of the rule, after everything
|
|
942
|
+
// has been composed — see {@link applyOwnedRail}.
|
|
943
|
+
const railed = applyOwnedRail(sections, owners, input.rail);
|
|
944
|
+
|
|
945
|
+
// Resolved per NODE, not off the active adapter: a composite session's
|
|
946
|
+
// active adapter deliberately declares no provenance of its own (its worlds'
|
|
947
|
+
// declarations differ), so the badge is the GOVERNING child's —
|
|
948
|
+
// `authoring/provenance.ts`. For a plain adapter this is the adapter's own
|
|
949
|
+
// declaration, unchanged.
|
|
950
|
+
const subjectProvenance = provenanceForNode(adapter, nodeId);
|
|
951
|
+
const subjectDestination = authoringDestination(adapter);
|
|
952
|
+
|
|
953
|
+
return {
|
|
954
|
+
id: nodeId,
|
|
955
|
+
title: node?.label ?? '',
|
|
956
|
+
// Identity belongs to the SUBJECT, so every selection has this row: the
|
|
957
|
+
// name, what kind of thing it is, and where it came from. Sections are
|
|
958
|
+
// FACETS of that subject and never stand in for it.
|
|
959
|
+
identity: {
|
|
960
|
+
rename: {
|
|
961
|
+
// Writable exactly when the adapter DECLARES a writable `name` field.
|
|
962
|
+
// An adapter that describes no `name` at all also drops the path in
|
|
963
|
+
// `inspector.set` (the react/DOM lanes return on any non-style path,
|
|
964
|
+
// and a root-group row answers only its manifest facts), so an
|
|
965
|
+
// editable box over one is a control that silently discards what you
|
|
966
|
+
// type — the row still SHOWS the name, it just cannot pretend to
|
|
967
|
+
// rewrite it.
|
|
968
|
+
readOnly: nameProperty === undefined || nameProperty.readonly === true,
|
|
969
|
+
set: (next: string) => io.set('name', next),
|
|
970
|
+
},
|
|
971
|
+
kindLabel: `${node?.typeLabel ?? node?.kind ?? ''}${
|
|
972
|
+
subjectProvenance ? ` · ${subjectProvenance.label}` : ''
|
|
973
|
+
}`,
|
|
974
|
+
// The same fact the line above formats, UNFORMATTED — the identity row
|
|
975
|
+
// draws the kind's glyph and a sentence cannot be drawn. See
|
|
976
|
+
// `InspectionIdentity.kind`.
|
|
977
|
+
...(node?.kind ? { kind: node.kind } : {}),
|
|
978
|
+
// WHERE this is written back. It used to head the Hierarchy panel as a
|
|
979
|
+
// breadcrumb row of its own; the Outliner's header is one row and names
|
|
980
|
+
// no file, and the datablock this belongs to is the one being inspected
|
|
981
|
+
// (`InspectionDocumentLine`). A composite session declares no single
|
|
982
|
+
// destination, so it gets no line — the same silence its `provenance`
|
|
983
|
+
// keeps, and for the same reason.
|
|
984
|
+
...(subjectDestination
|
|
985
|
+
? {
|
|
986
|
+
document: {
|
|
987
|
+
path: subjectDestination,
|
|
988
|
+
...(subjectProvenance?.detail ? { title: subjectProvenance.detail } : {}),
|
|
989
|
+
},
|
|
990
|
+
}
|
|
991
|
+
: {}),
|
|
992
|
+
// Only an ANCHORED truth earns a note. An unanchored resolution is the
|
|
993
|
+
// honest fallback the composite hands back for a subject its truth index
|
|
994
|
+
// does not reach (a react/DOM element, a synthetic group row): its
|
|
995
|
+
// `reason` is "no live object for this selection", which as a creation
|
|
996
|
+
// note reads "Source · no live object" over a perfectly mounted,
|
|
997
|
+
// editable element — a false provenance line. No anchor ⇒ no note.
|
|
998
|
+
note: creationSite?.anchored
|
|
999
|
+
? {
|
|
1000
|
+
// The label names WHICH truth this object came out of, because
|
|
1001
|
+
// "Source" over a level-data record would be false: no source
|
|
1002
|
+
// line mentions where a placed robot stands.
|
|
1003
|
+
text: `${creationSite.kind === 'data' ? 'Level data' : 'Source'} · ${creationSite.display}`,
|
|
1004
|
+
title:
|
|
1005
|
+
creationSite.kind === 'data'
|
|
1006
|
+
? `Placed by record ${creationSite.record} of ${creationSite.file}, the game's own level data`
|
|
1007
|
+
: `Constructed by a \`new\` expression at ${creationSite.file} line ${creationSite.line}, column ${creationSite.col}`,
|
|
1008
|
+
testId: 'inspector-creation-site',
|
|
1009
|
+
}
|
|
1010
|
+
: undefined,
|
|
1011
|
+
},
|
|
1012
|
+
presentation: { preferred: inspectionAffinityFor(input.surface) },
|
|
1013
|
+
// THE SUBJECT'S OWN WRITABLE FIELDS, which is the door `editor.setField`
|
|
1014
|
+
// resolves a path against.
|
|
1015
|
+
//
|
|
1016
|
+
// `name` is one of them, and it was missing. The identity row DRAWS it and
|
|
1017
|
+
// writes it through `io.set('name', …)` — the same call the hierarchy's
|
|
1018
|
+
// inline rename makes — but it was never listed here, so the one door an
|
|
1019
|
+
// agent has answered "The active Inspector has no field at \"name\"" for
|
|
1020
|
+
// every adapter in the estate. Measured 2026-09-21 on a Blender Model
|
|
1021
|
+
// document, where renaming an object is one of the macro adjustments the
|
|
1022
|
+
// document exists for and had no reachable door at all: the Outliner's
|
|
1023
|
+
// rename is a context menu, the Properties rail is the workbench's own
|
|
1024
|
+
// view, and neither is drivable.
|
|
1025
|
+
//
|
|
1026
|
+
// Same shape and same argument as `withDescriptorEditable` above — it adds
|
|
1027
|
+
// a door onto the write path that already exists, never a second one — and
|
|
1028
|
+
// the readonly descriptor is left out so a refusal stays a refusal (the
|
|
1029
|
+
// Blender Outliner returns `readonly: true` for a collection row).
|
|
1030
|
+
...(subjectEditableFields.length > 0
|
|
1031
|
+
? { editable: { fields: subjectEditableFields, io } }
|
|
1032
|
+
: {}),
|
|
1033
|
+
...(input.alsoSelected ? { alsoSelected: input.alsoSelected } : {}),
|
|
1034
|
+
quickActions,
|
|
1035
|
+
related: adapter.related?.links(nodeId) ?? [],
|
|
1036
|
+
sections: railed,
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* The Stories section from a resolved {@link ComposeStoriesInput}.
|
|
1042
|
+
*/
|
|
1043
|
+
export function composeStoriesSection(stories: ComposeStoriesInput): InspectionSection {
|
|
1044
|
+
return {
|
|
1045
|
+
id: STORIES_SECTION_ID,
|
|
1046
|
+
// The provider names its own states when it has a better word for them
|
|
1047
|
+
// (an ingested game's screens are "Scenes"); "Stories" is the default,
|
|
1048
|
+
// not a fixed heading.
|
|
1049
|
+
title: stories.title ?? 'Stories',
|
|
1050
|
+
icon: faBookOpen,
|
|
1051
|
+
order: STORIES_SECTION_ORDER,
|
|
1052
|
+
body: {
|
|
1053
|
+
kind: 'custom',
|
|
1054
|
+
render: () =>
|
|
1055
|
+
createElement(InspectorStoriesSection, {
|
|
1056
|
+
stories: stories.stories,
|
|
1057
|
+
activeStoryId: stories.activeStoryId,
|
|
1058
|
+
onApplyStory: stories.onApplyStory,
|
|
1059
|
+
isolation: stories.isolation,
|
|
1060
|
+
unavailableReason: stories.unavailableReason ?? null,
|
|
1061
|
+
}),
|
|
1062
|
+
},
|
|
1063
|
+
};
|
|
1064
|
+
}
|