@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,968 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PROJECTIONS of the inspection model — Layer 3, and the only place a
|
|
3
|
+
* composed {@link InspectionSubject} becomes pixels.
|
|
4
|
+
*
|
|
5
|
+
* ONE box, two layouts (`inspection/model.ts`):
|
|
6
|
+
* - `column` — the DEFAULT: the subject's full inspector as a slim vertical
|
|
7
|
+
* column. Identity row (name, visibility eye, a LABELED open-for-edit
|
|
8
|
+
* button), then a persistent SECTION-ICON STRIP (one glyph per
|
|
9
|
+
* `subject.sections`, 1:1) that signposts what the subject HAS and jumps
|
|
10
|
+
* to a section, then every section stacked and collapsible;
|
|
11
|
+
* - `card` — the space-tight fallback: a MINI card (preview, name, the same
|
|
12
|
+
* icon strip, the open-for-edit) that the viewport host
|
|
13
|
+
* (`components/CompactInspectorCard.tsx`) can collapse at the floor to a
|
|
14
|
+
* single-line PILL. Clicking any collapsed form restores the column.
|
|
15
|
+
*
|
|
16
|
+
* "The box should actually be an identical thing regardless of what viewport
|
|
17
|
+
* it's on. The only thing that differs is the sections. The sections only
|
|
18
|
+
* differ because the inspector's structured data is different." (owner.) So
|
|
19
|
+
* everything above the sections — the identity row, the name, the kind, the
|
|
20
|
+
* verbs on it, the icon strip — reads the SAME model fields in both layouts;
|
|
21
|
+
* the only differences below are the layout's own arrangement.
|
|
22
|
+
*
|
|
23
|
+
* Two actions stay unambiguous because only ONE is an unlabeled gesture:
|
|
24
|
+
* clicking the box (any collapsed form, the mini card's preview, a strip
|
|
25
|
+
* glyph) EXPANDS/RESTORES the column — an in-place zoom on the SAME subject —
|
|
26
|
+
* while OPEN-FOR-EDIT is ALWAYS a labeled control that navigates to the
|
|
27
|
+
* subject's DEFINITION (a DIFFERENT subject). The definition link is the
|
|
28
|
+
* subject's own `related` link (`inspection/model.ts` {@link SubjectLink},
|
|
29
|
+
* produced by each adapter's `related` provider), surfaced here as the labeled
|
|
30
|
+
* Edit button only when the subject carries one.
|
|
31
|
+
*
|
|
32
|
+
* The projections are DUMB: they read identity, verbs, related links, and each
|
|
33
|
+
* section's id/title/icon/order/body off the model, and they never learn what
|
|
34
|
+
* a section contains, which contribution replaced which built-in, or which
|
|
35
|
+
* surface produced the subject. `surface` appears here for exactly one reason
|
|
36
|
+
* — it is the key the presentation preference is stored under — and a host
|
|
37
|
+
* that names none simply offers no switch.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
faArrowUpRightFromSquare,
|
|
42
|
+
faCube,
|
|
43
|
+
faWindowMinimize,
|
|
44
|
+
faXmark,
|
|
45
|
+
} from '@fortawesome/free-solid-svg-icons';
|
|
46
|
+
import {
|
|
47
|
+
Button,
|
|
48
|
+
DraftTextInput,
|
|
49
|
+
EditorIcon,
|
|
50
|
+
IconButton,
|
|
51
|
+
Panel,
|
|
52
|
+
SectionHeader,
|
|
53
|
+
spaceVar,
|
|
54
|
+
themeVars,
|
|
55
|
+
} from '@volter/editor-sdk/widgets';
|
|
56
|
+
import {
|
|
57
|
+
Fragment,
|
|
58
|
+
type KeyboardEvent as ReactKeyboardEvent,
|
|
59
|
+
type ReactNode,
|
|
60
|
+
type UIEvent as ReactUIEvent,
|
|
61
|
+
useCallback,
|
|
62
|
+
useRef,
|
|
63
|
+
useState,
|
|
64
|
+
} from 'react';
|
|
65
|
+
import { hierarchyKindIcon } from '../hierarchy-kind-icon';
|
|
66
|
+
import { setActiveScope } from '../hotkeys';
|
|
67
|
+
import {
|
|
68
|
+
actionsPlacedAt,
|
|
69
|
+
type InspectionAction,
|
|
70
|
+
type InspectionPresentation,
|
|
71
|
+
type InspectionPreviewMode,
|
|
72
|
+
type InspectionSection,
|
|
73
|
+
type InspectionSubject,
|
|
74
|
+
type InspectionSurfaceKind,
|
|
75
|
+
type SubjectLink,
|
|
76
|
+
} from '../inspection/model';
|
|
77
|
+
import { compactInspectorTabs, setInspectorPresentationOverride } from '../inspector-presentation';
|
|
78
|
+
import { useCompactInspectorHost } from './CompactInspectorShell';
|
|
79
|
+
import { InspectorFieldsSection } from './InspectorFieldsSection';
|
|
80
|
+
import { useAfterPaint } from './use-after-paint';
|
|
81
|
+
|
|
82
|
+
/** The body of one composed section, rendered by whichever layout asked for
|
|
83
|
+
* it: a `fields` body is the generic descriptor grid, a `custom` body is an
|
|
84
|
+
* opaque block that renders itself, a `preview` body is the subject's live
|
|
85
|
+
* square view. No projection looks past this. */
|
|
86
|
+
function sectionBody(section: InspectionSection): ReactNode {
|
|
87
|
+
return section.body.kind === 'fields' ? (
|
|
88
|
+
<InspectorFieldsSection fields={section.body.fields} io={section.body.io} />
|
|
89
|
+
) : (
|
|
90
|
+
section.body.render()
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A subject verb as an icon button (the `identity` placement — today the
|
|
95
|
+
* visibility eye, beside the name field). */
|
|
96
|
+
function QuickActionIconButton({ action }: { readonly action: InspectionAction }) {
|
|
97
|
+
return (
|
|
98
|
+
<IconButton
|
|
99
|
+
data-testid={action.id}
|
|
100
|
+
aria-label={action.title}
|
|
101
|
+
title={action.title}
|
|
102
|
+
aria-pressed={action.pressed}
|
|
103
|
+
disabled={action.disabled}
|
|
104
|
+
onClick={action.run}
|
|
105
|
+
>
|
|
106
|
+
{action.icon && <EditorIcon icon={action.icon} />}
|
|
107
|
+
</IconButton>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The subject's DEFINITION link — the one whose `id` carries the `definition:`
|
|
113
|
+
* marker its producing adapter stamped (mirroring the `creation-site:`
|
|
114
|
+
* convention), never a POSITIONAL guess. `SubjectLink` has no definition field,
|
|
115
|
+
* so identifying "the definition" by list order would silently promote whatever
|
|
116
|
+
* link an adapter happened to list first (a state machine, say). The marker is
|
|
117
|
+
* the contract; the first link that carries it is the open-for-edit target,
|
|
118
|
+
* and its absence is an honest "no definition to open" — no `[0]` fallback.
|
|
119
|
+
*/
|
|
120
|
+
function definitionLink(subject: InspectionSubject): SubjectLink | null {
|
|
121
|
+
return subject.related.find((link) => link.id.startsWith('definition:')) ?? null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The related links that are NEITHER construction-site provenance (which rides
|
|
125
|
+
* in `identity.note`) NOR the promoted `definition:` link — the rest of the
|
|
126
|
+
* derivation chain (an asset document's state machine and the like). Filtered
|
|
127
|
+
* by kind, so a demoted definition can never reappear here and a non-definition
|
|
128
|
+
* link is never hidden by position. */
|
|
129
|
+
function otherRelatedLinks(subject: InspectionSubject): readonly SubjectLink[] {
|
|
130
|
+
return subject.related.filter(
|
|
131
|
+
(link) => !link.id.startsWith('creation-site:') && !link.id.startsWith('definition:'),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The LABELED open-for-edit control (icon + text) — the ONE unlabeled-gesture
|
|
137
|
+
* exception's counterweight: it navigates to the subject's DEFINITION (a
|
|
138
|
+
* DIFFERENT subject whose edits change every instance), so it must never be
|
|
139
|
+
* the primary click. Its text is the link's own title ("Open Crate"), which is
|
|
140
|
+
* the honest name of the real `SubjectLink.open` verb it runs.
|
|
141
|
+
*/
|
|
142
|
+
function EditDefinitionButton({ link }: { readonly link: SubjectLink }) {
|
|
143
|
+
return (
|
|
144
|
+
<Button
|
|
145
|
+
variant="secondary"
|
|
146
|
+
size="compact"
|
|
147
|
+
data-testid="inspector-open-definition"
|
|
148
|
+
title={link.title}
|
|
149
|
+
onClick={link.open}
|
|
150
|
+
style={{ display: 'inline-flex', alignItems: 'center', gap: 4, flexShrink: 0 }}
|
|
151
|
+
>
|
|
152
|
+
<EditorIcon icon={faArrowUpRightFromSquare} />
|
|
153
|
+
<span
|
|
154
|
+
style={{
|
|
155
|
+
maxWidth: 120,
|
|
156
|
+
overflow: 'hidden',
|
|
157
|
+
textOverflow: 'ellipsis',
|
|
158
|
+
whiteSpace: 'nowrap',
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
{link.title}
|
|
162
|
+
</span>
|
|
163
|
+
</Button>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** The DERIVATION chain beyond the definition — an asset document's state
|
|
168
|
+
* machine and the like. The definition itself is promoted to the identity
|
|
169
|
+
* row's Edit button, so this row renders only what is left. */
|
|
170
|
+
function RelatedSubjects({ subject }: { readonly subject: InspectionSubject }) {
|
|
171
|
+
const rest = otherRelatedLinks(subject);
|
|
172
|
+
if (rest.length === 0) return null;
|
|
173
|
+
return (
|
|
174
|
+
<div
|
|
175
|
+
data-testid="inspector-related-subjects"
|
|
176
|
+
style={{ display: 'flex', flexWrap: 'wrap', gap: 4, padding: '6px 8px' }}
|
|
177
|
+
>
|
|
178
|
+
{rest.map((link) => (
|
|
179
|
+
<Button key={link.id} variant="ghost" onClick={link.open} title={link.title}>
|
|
180
|
+
{link.title}
|
|
181
|
+
</Button>
|
|
182
|
+
))}
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The SECTION-ICON STRIP — one glyph per `subject.sections`, 1:1
|
|
189
|
+
* (`compactInspectorTabs`). It signposts what the subject HAS and jumps to a
|
|
190
|
+
* section; it is NOT a tab that opens one section into a floating panel. In the
|
|
191
|
+
* column `onJump` scrolls the section into view; on the mini card there are no
|
|
192
|
+
* stacked sections, so `onJump` restores the column.
|
|
193
|
+
*/
|
|
194
|
+
function SectionIconStrip({
|
|
195
|
+
subject,
|
|
196
|
+
onJump,
|
|
197
|
+
}: {
|
|
198
|
+
readonly subject: InspectionSubject;
|
|
199
|
+
readonly onJump: (sectionId: string) => void;
|
|
200
|
+
}) {
|
|
201
|
+
const tabs = compactInspectorTabs(subject);
|
|
202
|
+
// The strip is a JUMP AID — a table of contents. It earns its row only
|
|
203
|
+
// when there is real navigation to do (a dense entity subject: preview,
|
|
204
|
+
// transform, property groups, stories…) AND its glyphs can be told
|
|
205
|
+
// apart. Three sections need no navigator, and identical icons signpost
|
|
206
|
+
// nothing — the 1:1 derivation stands (owner, 2026-08-07); an
|
|
207
|
+
// uninformative row earns no pixels (owner-sighted, 2026-08-23).
|
|
208
|
+
if (tabs.length < 5 || new Set(tabs.map((tab) => tab.icon)).size < 3) return null;
|
|
209
|
+
return (
|
|
210
|
+
<div
|
|
211
|
+
className="vgai-inspector-icon-strip"
|
|
212
|
+
role="toolbar"
|
|
213
|
+
aria-label="Sections"
|
|
214
|
+
data-testid="inspector-section-strip"
|
|
215
|
+
>
|
|
216
|
+
{tabs.map((tab) => (
|
|
217
|
+
<IconButton
|
|
218
|
+
key={tab.id}
|
|
219
|
+
size="compact"
|
|
220
|
+
data-testid={`section-icon-${tab.id}`}
|
|
221
|
+
aria-label={tab.title}
|
|
222
|
+
title={tab.title}
|
|
223
|
+
onClick={() => onJump(tab.id)}
|
|
224
|
+
>
|
|
225
|
+
<EditorIcon icon={tab.icon} />
|
|
226
|
+
</IconButton>
|
|
227
|
+
))}
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** The subject's live preview at `thumbnail` fill, or a quiet kind glyph when
|
|
233
|
+
* the subject has none (react/DOM/no-Object3D subjects). The picture follows a
|
|
234
|
+
* frame after selection (`components/use-after-paint.ts`), so the glyph holds
|
|
235
|
+
* the geometry until it lands and nothing reflows. Inert: the host owns the
|
|
236
|
+
* single click. */
|
|
237
|
+
function SubjectThumbnail({
|
|
238
|
+
render,
|
|
239
|
+
glyph,
|
|
240
|
+
subjectKey,
|
|
241
|
+
}: {
|
|
242
|
+
readonly render: ((mode?: InspectionPreviewMode) => ReactNode) | null;
|
|
243
|
+
readonly glyph: InspectionSection['icon'];
|
|
244
|
+
readonly subjectKey: string;
|
|
245
|
+
}) {
|
|
246
|
+
const picture = useAfterPaint(subjectKey) ? render : null;
|
|
247
|
+
return picture ? (
|
|
248
|
+
<div aria-hidden="true" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
|
|
249
|
+
{picture('thumbnail')}
|
|
250
|
+
</div>
|
|
251
|
+
) : (
|
|
252
|
+
<EditorIcon icon={glyph} />
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** The editable-or-readonly name field, styled the same either way (a readonly
|
|
257
|
+
* `<input>` invites typing that vanishes, so it stands in as plain text).
|
|
258
|
+
*
|
|
259
|
+
* ITS INK IS THE FIELD'S, NOT A READ-ONLY TONE. The stand-in took
|
|
260
|
+
* `content.dim` (150) where every string Blender draws in its Properties
|
|
261
|
+
* editor inks #e5e5e5 — measured on `properties-object.png`'s datablock row
|
|
262
|
+
* (y 142..178, full width: ceiling 229 with 245 px, nothing above it), the
|
|
263
|
+
* row this one is the counterpart to. A WRONG MEMBER, not a wrong value:
|
|
264
|
+
* read-only is said by the absent caret and the absent hover, not by a
|
|
265
|
+
* quieter ink, and the field already restates every other part of the real
|
|
266
|
+
* control's paint (`compact-inspector.css`'s
|
|
267
|
+
* `[data-testid="ingest-name"][data-readonly="true"]`). */
|
|
268
|
+
function SubjectNameField({ subject }: { readonly subject: InspectionSubject }) {
|
|
269
|
+
const identity = subject.identity;
|
|
270
|
+
if (!identity) return null;
|
|
271
|
+
const summary = [identity.kindLabel, identity.note?.text].filter(Boolean).join(' — ');
|
|
272
|
+
if (identity.rename.readOnly) {
|
|
273
|
+
return (
|
|
274
|
+
<div
|
|
275
|
+
data-testid="ingest-name"
|
|
276
|
+
data-readonly="true"
|
|
277
|
+
{...(summary ? { title: summary } : {})}
|
|
278
|
+
style={{
|
|
279
|
+
flex: 1,
|
|
280
|
+
minWidth: 0,
|
|
281
|
+
fontSize: 'var(--vgai-font-md)',
|
|
282
|
+
fontWeight: 600,
|
|
283
|
+
color: themeVars.content.primary,
|
|
284
|
+
whiteSpace: 'nowrap',
|
|
285
|
+
overflow: 'hidden',
|
|
286
|
+
textOverflow: 'ellipsis',
|
|
287
|
+
}}
|
|
288
|
+
>
|
|
289
|
+
{subject.title}
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
return (
|
|
294
|
+
<DraftTextInput
|
|
295
|
+
data-testid="ingest-name"
|
|
296
|
+
aria-label="Name"
|
|
297
|
+
value={subject.title}
|
|
298
|
+
onCommit={(next) => {
|
|
299
|
+
if (next.trim() !== '') identity.rename.set(next);
|
|
300
|
+
}}
|
|
301
|
+
data-emphasis="strong"
|
|
302
|
+
{...(summary ? { title: summary } : {})}
|
|
303
|
+
style={{ flex: 1, minWidth: 0 }}
|
|
304
|
+
/>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* The subject's identity ROW — name, the identity verbs (the eye), and the
|
|
310
|
+
* labeled open-for-edit button when the subject carries a definition — over a
|
|
311
|
+
* dim kind/provenance line. One component, read by both layouts; a subject
|
|
312
|
+
* with no identity row shows its headline and quiet hint instead.
|
|
313
|
+
*/
|
|
314
|
+
function SubjectIdentity({ subject }: { readonly subject: InspectionSubject }) {
|
|
315
|
+
const identity = subject.identity;
|
|
316
|
+
if (!identity) {
|
|
317
|
+
return (
|
|
318
|
+
<div style={{ padding: 12 }}>
|
|
319
|
+
<div style={{ fontSize: 'var(--vgai-font-md)', fontWeight: 600 }}>{subject.title}</div>
|
|
320
|
+
{subject.hint && (
|
|
321
|
+
<div
|
|
322
|
+
data-testid="inspector-subject-hint"
|
|
323
|
+
style={{
|
|
324
|
+
marginTop: 4,
|
|
325
|
+
fontSize: 'var(--vgai-font-base)',
|
|
326
|
+
color: themeVars.content.dim,
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
{subject.hint}
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
const definition = definitionLink(subject);
|
|
336
|
+
return (
|
|
337
|
+
<div
|
|
338
|
+
className="vgai-inspector-identity"
|
|
339
|
+
// Blender's datablock row — the counterpart to this one — is 21px: its
|
|
340
|
+
// 18px name field plus a hair. An 8px inset all round made ours 37, the
|
|
341
|
+
// tallest thing in the column and the loudest place it stopped reading
|
|
342
|
+
// as Properties. The inset is now the 2px step vertically (the optional
|
|
343
|
+
// second lines below still need a floor) and stays 8px across.
|
|
344
|
+
style={{
|
|
345
|
+
padding: `${spaceVar[1]} ${spaceVar[4]}`,
|
|
346
|
+
borderBottom: '1px solid var(--vgai-structural-divider)',
|
|
347
|
+
}}
|
|
348
|
+
>
|
|
349
|
+
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
|
350
|
+
<SubjectNameField subject={subject} />
|
|
351
|
+
{/* The kind rides the name row — two lines said one thing
|
|
352
|
+
(owner-sighted, 2026-08-23). */}
|
|
353
|
+
{identity.kindLabel && (
|
|
354
|
+
<span
|
|
355
|
+
data-testid="inspector-kind-label"
|
|
356
|
+
style={{
|
|
357
|
+
fontSize: 'var(--vgai-font-base)',
|
|
358
|
+
color: themeVars.content.dim,
|
|
359
|
+
whiteSpace: 'nowrap',
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
{identity.kindLabel}
|
|
363
|
+
</span>
|
|
364
|
+
)}
|
|
365
|
+
{actionsPlacedAt(subject, 'identity').map((action) => (
|
|
366
|
+
<QuickActionIconButton key={action.id} action={action} />
|
|
367
|
+
))}
|
|
368
|
+
{definition && <EditDefinitionButton link={definition} />}
|
|
369
|
+
</div>
|
|
370
|
+
{/* WHERE this is written. The Hierarchy panel used to head itself with
|
|
371
|
+
this path; Blender's Outliner header is one row and names no file,
|
|
372
|
+
and the datablock row — this one — is where the file belongs. The
|
|
373
|
+
seam word rides `kindLabel` one line above, so this is the path
|
|
374
|
+
alone. */}
|
|
375
|
+
{identity.document && (
|
|
376
|
+
<div
|
|
377
|
+
data-testid="inspector-document-path"
|
|
378
|
+
data-document-path={identity.document.path}
|
|
379
|
+
{...(identity.document.title ? { title: identity.document.title } : {})}
|
|
380
|
+
style={{
|
|
381
|
+
marginTop: spaceVar[1],
|
|
382
|
+
fontSize: 'var(--vgai-font-base)',
|
|
383
|
+
color: themeVars.content.muted,
|
|
384
|
+
whiteSpace: 'nowrap',
|
|
385
|
+
overflow: 'hidden',
|
|
386
|
+
textOverflow: 'ellipsis',
|
|
387
|
+
direction: 'rtl',
|
|
388
|
+
textAlign: 'left',
|
|
389
|
+
}}
|
|
390
|
+
>
|
|
391
|
+
{/* `direction: rtl` ellipsizes at the HEAD, so a long path keeps its
|
|
392
|
+
filename — the half a person reads. The bidi isolate keeps the
|
|
393
|
+
slashes in their authored order inside it. */}
|
|
394
|
+
<bdi>{identity.document.path}</bdi>
|
|
395
|
+
</div>
|
|
396
|
+
)}
|
|
397
|
+
{/* WHAT ELSE THE GIZMO IS HOLDING. The fields below belong to the one
|
|
398
|
+
node named above; a drag moves the whole selection. Saying so is the
|
|
399
|
+
difference between a panel that is scoped and a panel that is wrong
|
|
400
|
+
(`InspectionSubject.alsoSelected`). */}
|
|
401
|
+
{subject.alsoSelected ? (
|
|
402
|
+
<div
|
|
403
|
+
data-testid="inspector-also-selected"
|
|
404
|
+
style={{ marginTop: 6, fontSize: 'var(--vgai-font-base)', color: themeVars.content.dim }}
|
|
405
|
+
>
|
|
406
|
+
{`${subject.alsoSelected + 1} selected — fields edit this one, the gizmo moves all`}
|
|
407
|
+
</div>
|
|
408
|
+
) : null}
|
|
409
|
+
{identity.note?.text && (
|
|
410
|
+
<div
|
|
411
|
+
data-testid="inspector-subject-note"
|
|
412
|
+
style={{ marginTop: 6, fontSize: 'var(--vgai-font-base)', color: themeVars.content.dim }}
|
|
413
|
+
>
|
|
414
|
+
{identity.note.text}
|
|
415
|
+
</div>
|
|
416
|
+
)}
|
|
417
|
+
</div>
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* The PROPERTIES presentation's identity — Blender's Properties editor opens
|
|
423
|
+
* with TWO rows, transcribed from the reference frames at 2x rather than
|
|
424
|
+
* described from memory, and neither is a title bar:
|
|
425
|
+
*
|
|
426
|
+
* - the BREADCRUMB row: a 14 px datablock glyph, the chain it belongs to, and
|
|
427
|
+
* ONE trailing control. `properties-data-edit.png`: the glyph's bounding box
|
|
428
|
+
* is x 80..107 / y 74..101 (28 device px = 14 CSS) and the trailing pin's is
|
|
429
|
+
* x 559..586 at the same size, on the panel's own fill with no well.
|
|
430
|
+
* WHAT THE CHAIN OWNS IS THE CONTEXT CHAIN, NOT A FILE (measured
|
|
431
|
+
* 2026-09-18 on all three tab frames): each link is `glyph + NAME`, the
|
|
432
|
+
* datablock's own name in PLAIN ink, innermost last, `>` between —
|
|
433
|
+
* `[▣] Cube` on the Object and Modifier tabs, `[▣] Cube > [▽] Cube` on
|
|
434
|
+
* Data. Blender has no file anywhere on this row. Ours carried
|
|
435
|
+
* `identity.document.path` here, which is the one fact the reference
|
|
436
|
+
* never shows; the path is now the row's TOOLTIP (and still the
|
|
437
|
+
* `data-document-path` attribute an agent reads), because the Model
|
|
438
|
+
* document's source path has NO other home in our chrome — the editor tab
|
|
439
|
+
* renders the bare title with no `title` attribute, and the window title
|
|
440
|
+
* is the PROJECT's. A reachable fact is not deleted to match a
|
|
441
|
+
* picture; it is moved off the row the picture owns.
|
|
442
|
+
* INK, measured on every frame: the glyph, the name and the `>` are all
|
|
443
|
+
* 229/229/229 — PLAIN UI ink, never the tab's category colour. Only the
|
|
444
|
+
* RAIL is coloured (`#73a1ff` modifiers, `#00d3a2` data). The pin alone
|
|
445
|
+
* is dimmer, 155. Ours inked the breadcrumb glyph `dim` (150) and let the
|
|
446
|
+
* datablock well's glyph fall through to its category (`#73a1ff`), which
|
|
447
|
+
* is the loudest thing on the row and is nowhere in the reference.
|
|
448
|
+
* - the DATABLOCK row: one 20 CSS px composite spanning the panel's inset
|
|
449
|
+
* width — an icon WELL in `widget.menu` (x 76..135 = 30 CSS wide, #272727),
|
|
450
|
+
* a 1 px divider, the name TEXT WELL in `widget.field` (x 138..550,
|
|
451
|
+
* #1c1c1c), and a trailing pushbutton in `widget.regular` (x 553..590,
|
|
452
|
+
* #535353), the whole thing outlined in #3c3c3c at x 74..75 / 591..592 and
|
|
453
|
+
* y 140..141 / 178..179.
|
|
454
|
+
*
|
|
455
|
+
* What we DO NOT draw, by the standing rule that a control writing nowhere is
|
|
456
|
+
* not drawn: Blender's well carries a `⌄` because it PICKS a datablock and its
|
|
457
|
+
* trailing button is the fake-user shield — this editor has neither verb, so
|
|
458
|
+
* the well is the kind glyph alone and the trailing slot carries the subject's
|
|
459
|
+
* own identity verbs (the visibility eye) instead. That eye's home in the
|
|
460
|
+
* reference is the Outliner, which our Hierarchy row already has; this is the
|
|
461
|
+
* shortcut, on the row whose trailing slot the reference spends the same way.
|
|
462
|
+
*
|
|
463
|
+
* Every fact the narrow column's row shows is still here — name, kind, the
|
|
464
|
+
* document path, the identity verbs, the definition link, the multi-selection
|
|
465
|
+
* warning and the note — rearranged into the reference's two rows.
|
|
466
|
+
* {@link SubjectIdentity} is untouched, so the narrow column (every other
|
|
467
|
+
* skin's default) is pixel-identical.
|
|
468
|
+
*/
|
|
469
|
+
function PropertiesIdentity({ subject }: { readonly subject: InspectionSubject }) {
|
|
470
|
+
const identity = subject.identity;
|
|
471
|
+
if (!identity) return <SubjectIdentity subject={subject} />;
|
|
472
|
+
const definition = definitionLink(subject);
|
|
473
|
+
/**
|
|
474
|
+
* THE SUBJECT'S KIND GLYPH — not the open tab's, and not the rail's first.
|
|
475
|
+
*
|
|
476
|
+
* MEASURED, and it settles a reading this row has had wrong twice. Blender's
|
|
477
|
+
* breadcrumb leads with the DATABLOCK the editor is about, on every tab:
|
|
478
|
+
* crop the first breadcrumb glyph (28x28 device at x 80..107, y 74..101)
|
|
479
|
+
* out of `properties-object.png`, `properties-modifier.png` and
|
|
480
|
+
* `properties-data-edit.png` and the three are byte-identical — 0 of 784
|
|
481
|
+
* pixels differ — while the open tab is Object, Modifier and Data in turn.
|
|
482
|
+
* On the Modifier tab Blender draws the object square, NOT the wrench.
|
|
483
|
+
*
|
|
484
|
+
* The row first derived this from `sections[0].icon`, which is RAIL ORDER;
|
|
485
|
+
* that looked right only while Data happened to be first, and the rail unit
|
|
486
|
+
* that put Modifiers above Data (Blender's order) broke it. The fix then
|
|
487
|
+
* was the ACTIVE TAB's icon, which is right for the rail's own active plate
|
|
488
|
+
* and wrong here — it agrees with the frames on Object and Data only
|
|
489
|
+
* because those two tabs' glyphs coincide with their datablock's, and
|
|
490
|
+
* disagrees on the one tab that discriminates.
|
|
491
|
+
*
|
|
492
|
+
* `hierarchy-kind-icon.ts` is the host's single kind-name-to-glyph table
|
|
493
|
+
* and the Outliner's rows already read it, so the breadcrumb and the well
|
|
494
|
+
* now read the same table for the same subject. Fallback stays the first
|
|
495
|
+
* section's glyph, for a subject whose adapter names no kind.
|
|
496
|
+
*/
|
|
497
|
+
const glyph = identity.kind
|
|
498
|
+
? hierarchyKindIcon(identity.kind, firstSectionGlyph(subject))
|
|
499
|
+
: firstSectionGlyph(subject);
|
|
500
|
+
const chain = subject.title || identity.kindLabel || '';
|
|
501
|
+
const chainTitle = [identity.kindLabel, identity.document?.path, identity.document?.title]
|
|
502
|
+
.filter(Boolean)
|
|
503
|
+
.join(' — ');
|
|
504
|
+
return (
|
|
505
|
+
<div className="vgai-inspector-identity" data-vgai-identity-layout="properties">
|
|
506
|
+
<div className="vgai-inspector-breadcrumb">
|
|
507
|
+
<EditorIcon icon={glyph} tone="primary" />
|
|
508
|
+
{/* A NAME, not a path: it ellipsizes at the TAIL like every other
|
|
509
|
+
name in the chrome. The head-ellipsis (`direction: rtl`) this row
|
|
510
|
+
used to carry existed for the path, which is now the tooltip. */}
|
|
511
|
+
<span
|
|
512
|
+
className="vgai-inspector-breadcrumb-chain"
|
|
513
|
+
data-testid="inspector-document-path"
|
|
514
|
+
{...(identity.document ? { 'data-document-path': identity.document.path } : {})}
|
|
515
|
+
{...(chainTitle ? { title: chainTitle } : {})}
|
|
516
|
+
>
|
|
517
|
+
<bdi>{chain}</bdi>
|
|
518
|
+
</span>
|
|
519
|
+
{actionsPlacedAt(subject, 'identity').map((action) => (
|
|
520
|
+
<QuickActionIconButton key={action.id} action={action} />
|
|
521
|
+
))}
|
|
522
|
+
</div>
|
|
523
|
+
{
|
|
524
|
+
<div className="vgai-inspector-datablock">
|
|
525
|
+
<span
|
|
526
|
+
className="vgai-inspector-datablock-kind"
|
|
527
|
+
data-testid="inspector-kind-label"
|
|
528
|
+
{...(identity.kindLabel ? { title: identity.kindLabel } : {})}
|
|
529
|
+
>
|
|
530
|
+
{/* PLAIN ink, not the category's: Blender's well glyph measures 229
|
|
531
|
+
on every frame, the same white as the breadcrumb's. The category
|
|
532
|
+
colour lives in the RAIL and nowhere else in this body. */}
|
|
533
|
+
<EditorIcon icon={glyph} tone="primary" />
|
|
534
|
+
</span>
|
|
535
|
+
<SubjectNameField subject={subject} />
|
|
536
|
+
{definition && <EditDefinitionButton link={definition} />}
|
|
537
|
+
</div>
|
|
538
|
+
}
|
|
539
|
+
{subject.alsoSelected ? (
|
|
540
|
+
<div
|
|
541
|
+
data-testid="inspector-also-selected"
|
|
542
|
+
style={{
|
|
543
|
+
marginTop: spaceVar[3],
|
|
544
|
+
fontSize: 'var(--vgai-font-base)',
|
|
545
|
+
color: themeVars.content.dim,
|
|
546
|
+
}}
|
|
547
|
+
>
|
|
548
|
+
{`${subject.alsoSelected + 1} selected — fields edit this one, the gizmo moves all`}
|
|
549
|
+
</div>
|
|
550
|
+
) : null}
|
|
551
|
+
{identity.note?.text && (
|
|
552
|
+
<div
|
|
553
|
+
data-testid="inspector-subject-note"
|
|
554
|
+
style={{
|
|
555
|
+
marginTop: spaceVar[3],
|
|
556
|
+
fontSize: 'var(--vgai-font-base)',
|
|
557
|
+
color: themeVars.content.dim,
|
|
558
|
+
}}
|
|
559
|
+
>
|
|
560
|
+
{identity.note.text}
|
|
561
|
+
</div>
|
|
562
|
+
)}
|
|
563
|
+
</div>
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** One section under its own header — title AND icon, unconditionally. The
|
|
568
|
+
* disclosure triangle is the STACK's affordance (`collapsible`). The wrapper
|
|
569
|
+
* carries `data-inspector-section` so the icon strip can scroll to it. */
|
|
570
|
+
function ProjectedSection({ section }: { readonly section: InspectionSection }) {
|
|
571
|
+
return (
|
|
572
|
+
<div data-testid={section.testId} data-inspector-section={section.id}>
|
|
573
|
+
<SectionHeader
|
|
574
|
+
label={section.title}
|
|
575
|
+
icon={section.icon}
|
|
576
|
+
description={section.description}
|
|
577
|
+
collapsible
|
|
578
|
+
defaultOpen={section.defaultOpen ?? true}
|
|
579
|
+
>
|
|
580
|
+
{sectionBody(section)}
|
|
581
|
+
</SectionHeader>
|
|
582
|
+
</div>
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* The NARROW COLUMN (default): identity row, the section-icon strip, the
|
|
588
|
+
* derivation chain, then every section stacked and collapsible. A strip glyph
|
|
589
|
+
* scrolls its section into view within this column.
|
|
590
|
+
*/
|
|
591
|
+
export function NarrowColumn({ subject }: { readonly subject: InspectionSubject }) {
|
|
592
|
+
const sectionsRef = useRef<HTMLDivElement>(null);
|
|
593
|
+
const jumpToSection = (sectionId: string) => {
|
|
594
|
+
const target = sectionsRef.current?.querySelector(`[data-inspector-section="${sectionId}"]`);
|
|
595
|
+
target?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
596
|
+
};
|
|
597
|
+
return (
|
|
598
|
+
<div style={{ pointerEvents: 'auto' }}>
|
|
599
|
+
<SubjectIdentity subject={subject} />
|
|
600
|
+
<SectionIconStrip subject={subject} onJump={jumpToSection} />
|
|
601
|
+
<RelatedSubjects subject={subject} />
|
|
602
|
+
<div ref={sectionsRef}>
|
|
603
|
+
{subject.sections.map((section) => (
|
|
604
|
+
<Fragment key={section.id}>
|
|
605
|
+
<ProjectedSection section={section} />
|
|
606
|
+
</Fragment>
|
|
607
|
+
))}
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Which tab each subject last showed under the PROPERTIES presentation, and
|
|
615
|
+
* the last tab shown anywhere — Blender keeps the Properties editor on the
|
|
616
|
+
* same tab across selections when the new object has it, and that is the
|
|
617
|
+
* behaviour a modeler's hands expect: pick Material, click through objects,
|
|
618
|
+
* still on Material. Keyed by subject id, bounded like the scroll memory.
|
|
619
|
+
*/
|
|
620
|
+
const propertiesTabMemory = new Map<string, string>();
|
|
621
|
+
let lastPropertiesTab: string | null = null;
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* The PROPERTIES column: identity row and derivation chain as the narrow
|
|
625
|
+
* column has them, then the sections TABBED — a vertical icon rail down the
|
|
626
|
+
* left edge, one section body at a time under a non-collapsible header (a
|
|
627
|
+
* triangle would offer to hide the only thing in the box). Every section
|
|
628
|
+
* earns a rail glyph, 1:1 with `subject.sections` — this rail is the
|
|
629
|
+
* NAVIGATION, not a table of contents, so the strip's "five sections or no
|
|
630
|
+
* strip" economy does not apply.
|
|
631
|
+
*/
|
|
632
|
+
export function PropertiesColumn({ subject }: { readonly subject: InspectionSubject }) {
|
|
633
|
+
const tabs = compactInspectorTabs(subject);
|
|
634
|
+
const remembered = propertiesTabMemory.get(subject.id) ?? lastPropertiesTab;
|
|
635
|
+
const [chosen, setChosen] = useState<string | null>(null);
|
|
636
|
+
// WHAT THE RAIL OPENS ON, in order: the tab this person clicked, the one
|
|
637
|
+
// they last used, THE TAB A SECTION DECLARED AS THE DEFAULT, and only then
|
|
638
|
+
// the first tab. The declaration exists because Blender's Properties editor
|
|
639
|
+
// does not open on its first tab: `SpaceProperties.context` is stored per
|
|
640
|
+
// screen in the startup file, and at factory settings
|
|
641
|
+
// `bpy.data.screens['Layout']` reads OBJECT while the rail's first tab is
|
|
642
|
+
// Render (measured against Blender 5.2 LTS, walk 5 parity row 3 — ours
|
|
643
|
+
// opened the cube on the Render tab). A rail whose sections declare nothing
|
|
644
|
+
// opens on its first tab exactly as before.
|
|
645
|
+
const activeId =
|
|
646
|
+
(chosen && tabs.some((tab) => tab.id === chosen) ? chosen : null) ??
|
|
647
|
+
(remembered && tabs.some((tab) => tab.id === remembered) ? remembered : null) ??
|
|
648
|
+
tabs.find((tab) => tab.railDefault)?.id ??
|
|
649
|
+
tabs[0]?.id ??
|
|
650
|
+
null;
|
|
651
|
+
const active = subject.sections.find((section) => section.id === activeId) ?? null;
|
|
652
|
+
const choose = (id: string) => {
|
|
653
|
+
setChosen(id);
|
|
654
|
+
if (propertiesTabMemory.size > 200) propertiesTabMemory.clear();
|
|
655
|
+
propertiesTabMemory.set(subject.id, id);
|
|
656
|
+
lastPropertiesTab = id;
|
|
657
|
+
};
|
|
658
|
+
return (
|
|
659
|
+
// The class carries no paint: it is the one named handle the rail needs
|
|
660
|
+
// to reach the bottom of the panel (`compact-inspector.css`, beside the
|
|
661
|
+
// rail's own `min-height: 100%`). Without a name this wrapper sizes to
|
|
662
|
+
// its content and the rail stops under the last tab.
|
|
663
|
+
<div className="vgai-inspector-properties-fill" style={{ pointerEvents: 'auto' }}>
|
|
664
|
+
{/* THE RAIL IS THE AREA'S FULL LEFT EDGE, and the identity rows sit
|
|
665
|
+
BESIDE it, not above. Measured on `properties-object.png` at 2x: the
|
|
666
|
+
rail column runs x 2..57 from the body's top (y=47) to the frame's
|
|
667
|
+
bottom, and the breadcrumb's own glyph starts at x=80 — everything
|
|
668
|
+
the editor draws is to the right of the rail. Ours used to stack the
|
|
669
|
+
identity across the full width and start the rail under it. */}
|
|
670
|
+
<div className="vgai-inspector-properties" data-testid="inspector-properties">
|
|
671
|
+
<div
|
|
672
|
+
className="vgai-inspector-properties-rail"
|
|
673
|
+
role="tablist"
|
|
674
|
+
aria-orientation="vertical"
|
|
675
|
+
aria-label="Sections"
|
|
676
|
+
>
|
|
677
|
+
{tabs.map((tab, index) => (
|
|
678
|
+
<Fragment key={tab.id}>
|
|
679
|
+
{/* THE RAIL HAS GROUPS, and Blender's draws ground between them:
|
|
680
|
+
`ED_buttons_tabs_list` (`space_buttons.cc:201-255`) calls
|
|
681
|
+
`add_spacer()` between the tool tab, the scene group, the
|
|
682
|
+
collection tab, the object group and Texture, appending a
|
|
683
|
+
`BCONTEXT_SEPARATOR` the rail renders as a gap rather than a
|
|
684
|
+
tab. Measured on `modeling-edit-none.png` down x=2852: 1 CSS
|
|
685
|
+
px of hairline between cells of ONE group, 8.5 across a group
|
|
686
|
+
boundary. Sections that name no group form one trailing group
|
|
687
|
+
— a subject with no `railGroup` anywhere draws no separator
|
|
688
|
+
at all, which is every non-Blender rail. */}
|
|
689
|
+
{index > 0 && tab.railGroup !== tabs[index - 1]?.railGroup ? (
|
|
690
|
+
<div
|
|
691
|
+
className="vgai-inspector-properties-rail-separator"
|
|
692
|
+
data-testid="properties-tab-separator"
|
|
693
|
+
role="presentation"
|
|
694
|
+
/>
|
|
695
|
+
) : null}
|
|
696
|
+
<IconButton
|
|
697
|
+
size="compact"
|
|
698
|
+
role="tab"
|
|
699
|
+
aria-selected={tab.id === activeId}
|
|
700
|
+
data-testid={`properties-tab-${tab.id}`}
|
|
701
|
+
aria-label={tab.title}
|
|
702
|
+
title={tab.title}
|
|
703
|
+
onClick={() => choose(tab.id)}
|
|
704
|
+
>
|
|
705
|
+
{/* A tab glyph, not body text: Blender's Properties tabs are
|
|
706
|
+
14px in a 28px strip, and the default `sm` rendered these at
|
|
707
|
+
10 — a rail of specks beside the reference. */}
|
|
708
|
+
<EditorIcon icon={tab.icon} size="xl" />
|
|
709
|
+
</IconButton>
|
|
710
|
+
</Fragment>
|
|
711
|
+
))}
|
|
712
|
+
</div>
|
|
713
|
+
<div className="vgai-inspector-properties-body" role="tabpanel">
|
|
714
|
+
<PropertiesIdentity subject={subject} />
|
|
715
|
+
<RelatedSubjects subject={subject} />
|
|
716
|
+
{/* NO HEADER OVER THE ACTIVE TAB. Blender's Properties editor never
|
|
717
|
+
heads a tab's content with the tab's own name — measured on three
|
|
718
|
+
frames at 2x: `properties-object.png` runs header → breadcrumb →
|
|
719
|
+
datablock row → the `Transform` CARD; `properties-data-edit.png`
|
|
720
|
+
runs the same three rows → the `Vertex Groups` card;
|
|
721
|
+
`modeling.png`'s Modifier tab → `Add Modifier`. The rail already
|
|
722
|
+
says which editor you are in, and a slab repeating it was the
|
|
723
|
+
full-bleed bar at the top of our column. The sections BELOW keep
|
|
724
|
+
their own headers, because in the reference they are the cards. */}
|
|
725
|
+
{active ? (
|
|
726
|
+
<div data-testid={active.testId} data-inspector-section={active.id}>
|
|
727
|
+
{sectionBody(active)}
|
|
728
|
+
</div>
|
|
729
|
+
) : null}
|
|
730
|
+
{/* Sections that host publishing contributions stay live behind the
|
|
731
|
+
active tab (`InspectionSection.keepMounted`). */}
|
|
732
|
+
{subject.sections
|
|
733
|
+
.filter((section) => section.keepMounted && section.id !== activeId)
|
|
734
|
+
.map((section) => (
|
|
735
|
+
<div
|
|
736
|
+
key={section.id}
|
|
737
|
+
data-inspector-section={section.id}
|
|
738
|
+
data-inspector-section-hidden="true"
|
|
739
|
+
style={{ display: 'none' }}
|
|
740
|
+
>
|
|
741
|
+
{sectionBody(section)}
|
|
742
|
+
</div>
|
|
743
|
+
))}
|
|
744
|
+
</div>
|
|
745
|
+
</div>
|
|
746
|
+
</div>
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/** The mini card's preview, or a kind glyph for a no-preview subject. */
|
|
751
|
+
function firstSectionGlyph(subject: InspectionSubject) {
|
|
752
|
+
return subject.sections[0]?.icon ?? faCube;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* The MINI card — the space-tight fallback: the subject's preview (or a kind
|
|
757
|
+
* glyph) above its name and eye, the same section-icon strip, and the labeled
|
|
758
|
+
* open-for-edit when the subject carries a definition. Clicking the preview or
|
|
759
|
+
* a strip glyph EXPANDS the column (an in-place zoom on the same subject);
|
|
760
|
+
* `minimize` (when the viewport host provides one) drops to the pill.
|
|
761
|
+
*
|
|
762
|
+
* `surface` is the key the expand preference is written under; a bounded host
|
|
763
|
+
* that names none renders the card without the expand affordance.
|
|
764
|
+
*/
|
|
765
|
+
export function MiniInspectorCard({
|
|
766
|
+
subject,
|
|
767
|
+
surface,
|
|
768
|
+
}: {
|
|
769
|
+
readonly subject: InspectionSubject;
|
|
770
|
+
readonly surface?: InspectionSurfaceKind | undefined;
|
|
771
|
+
}) {
|
|
772
|
+
const host = useCompactInspectorHost();
|
|
773
|
+
const previewSection = subject.sections.find((section) => section.body.kind === 'preview');
|
|
774
|
+
const previewBody = previewSection?.body.kind === 'preview' ? previewSection.body : null;
|
|
775
|
+
const definition = definitionLink(subject);
|
|
776
|
+
const expand = surface ? () => setInspectorPresentationOverride(surface, 'column') : null;
|
|
777
|
+
const inIdentity = subject.identity !== null;
|
|
778
|
+
return (
|
|
779
|
+
<div
|
|
780
|
+
className="vgai-mini-inspector"
|
|
781
|
+
data-testid="inspector-panel"
|
|
782
|
+
data-vgai-inspector-presentation="card"
|
|
783
|
+
onPointerDown={() => setActiveScope('inspector')}
|
|
784
|
+
>
|
|
785
|
+
{host && (
|
|
786
|
+
<IconButton
|
|
787
|
+
size="compact"
|
|
788
|
+
className="vgai-mini-inspector-minimize"
|
|
789
|
+
data-testid="inspector-minimize"
|
|
790
|
+
aria-label="Minimize inspector"
|
|
791
|
+
title="Minimize inspector"
|
|
792
|
+
onClick={host.minimize}
|
|
793
|
+
>
|
|
794
|
+
<EditorIcon icon={faWindowMinimize} />
|
|
795
|
+
</IconButton>
|
|
796
|
+
)}
|
|
797
|
+
{/* The preview (or kind glyph) is the primary EXPAND gesture — clicking
|
|
798
|
+
the box restores the column. */}
|
|
799
|
+
<div
|
|
800
|
+
className="vgai-mini-inspector-preview"
|
|
801
|
+
data-testid="inspector-mini-preview"
|
|
802
|
+
{...(expand
|
|
803
|
+
? {
|
|
804
|
+
role: 'button',
|
|
805
|
+
tabIndex: 0,
|
|
806
|
+
'aria-label': `Expand inspector for ${subject.title}`,
|
|
807
|
+
title: `Expand inspector for ${subject.title}`,
|
|
808
|
+
onClick: expand,
|
|
809
|
+
onKeyDown: (event: ReactKeyboardEvent) => {
|
|
810
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
|
811
|
+
event.preventDefault();
|
|
812
|
+
expand();
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
}
|
|
816
|
+
: {})}
|
|
817
|
+
>
|
|
818
|
+
<SubjectThumbnail
|
|
819
|
+
render={previewBody ? previewBody.render : null}
|
|
820
|
+
glyph={firstSectionGlyph(subject)}
|
|
821
|
+
subjectKey={subject.id}
|
|
822
|
+
/>
|
|
823
|
+
</div>
|
|
824
|
+
{inIdentity ? (
|
|
825
|
+
<div className="vgai-mini-inspector-identity">
|
|
826
|
+
<SubjectNameField subject={subject} />
|
|
827
|
+
{actionsPlacedAt(subject, 'identity').map((action) => (
|
|
828
|
+
<QuickActionIconButton key={action.id} action={action} />
|
|
829
|
+
))}
|
|
830
|
+
</div>
|
|
831
|
+
) : (
|
|
832
|
+
<div className="vgai-mini-inspector-identity">
|
|
833
|
+
<div style={{ flex: 1, minWidth: 0, fontSize: 'var(--vgai-font-md)', fontWeight: 600 }}>
|
|
834
|
+
{subject.title}
|
|
835
|
+
</div>
|
|
836
|
+
</div>
|
|
837
|
+
)}
|
|
838
|
+
<SectionIconStrip subject={subject} onJump={() => expand?.()} />
|
|
839
|
+
{definition && (
|
|
840
|
+
<div className="vgai-mini-inspector-actions">
|
|
841
|
+
<EditDefinitionButton link={definition} />
|
|
842
|
+
</div>
|
|
843
|
+
)}
|
|
844
|
+
</div>
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* The PILL — the floor: a single line carrying the subject's thumb-glyph, its
|
|
850
|
+
* name, and an expand affordance. The whole lozenge restores the column;
|
|
851
|
+
* `onClear` is the minimized state's only route to deselect (there is no
|
|
852
|
+
* viewport gesture reaching a minimized box).
|
|
853
|
+
*/
|
|
854
|
+
export function InspectorPill({
|
|
855
|
+
subject,
|
|
856
|
+
onExpand,
|
|
857
|
+
onClear,
|
|
858
|
+
}: {
|
|
859
|
+
readonly subject: InspectionSubject;
|
|
860
|
+
readonly onExpand: () => void;
|
|
861
|
+
readonly onClear?: (() => void) | undefined;
|
|
862
|
+
}) {
|
|
863
|
+
const previewSection = subject.sections.find((section) => section.body.kind === 'preview');
|
|
864
|
+
const previewBody = previewSection?.body.kind === 'preview' ? previewSection.body : null;
|
|
865
|
+
return (
|
|
866
|
+
<div className="vgai-inspector-pill" data-testid="inspector-pill">
|
|
867
|
+
{/* The role-annotated div is the sanctioned clickable-card idiom — a raw
|
|
868
|
+
native control here trips the design-system product-chrome scan. */}
|
|
869
|
+
<div
|
|
870
|
+
role="button"
|
|
871
|
+
tabIndex={0}
|
|
872
|
+
className="vgai-inspector-pill-open"
|
|
873
|
+
aria-label={`Expand inspector for ${subject.title}`}
|
|
874
|
+
title={`Expand inspector for ${subject.title}`}
|
|
875
|
+
onClick={onExpand}
|
|
876
|
+
onKeyDown={(event) => {
|
|
877
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
|
878
|
+
event.preventDefault();
|
|
879
|
+
onExpand();
|
|
880
|
+
}
|
|
881
|
+
}}
|
|
882
|
+
>
|
|
883
|
+
<span className="vgai-inspector-pill-thumb" aria-hidden="true">
|
|
884
|
+
{previewBody ? (
|
|
885
|
+
previewBody.render('thumbnail')
|
|
886
|
+
) : (
|
|
887
|
+
<EditorIcon icon={firstSectionGlyph(subject)} />
|
|
888
|
+
)}
|
|
889
|
+
</span>
|
|
890
|
+
<span className="vgai-inspector-pill-name">{subject.title}</span>
|
|
891
|
+
</div>
|
|
892
|
+
{onClear && (
|
|
893
|
+
<IconButton
|
|
894
|
+
data-testid="inspector-fab-clear-selection"
|
|
895
|
+
aria-label="Clear selection"
|
|
896
|
+
title="Clear selection"
|
|
897
|
+
onClick={onClear}
|
|
898
|
+
>
|
|
899
|
+
<EditorIcon icon={faXmark} />
|
|
900
|
+
</IconButton>
|
|
901
|
+
)}
|
|
902
|
+
</div>
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export interface InspectionProjectionProps {
|
|
907
|
+
readonly subject: InspectionSubject;
|
|
908
|
+
/** Which layout to render. The live shell reads this off the composed
|
|
909
|
+
* subject through `inspection/display.ts`; it is never re-derived here. */
|
|
910
|
+
readonly presentation: InspectionPresentation;
|
|
911
|
+
/** The surface this projection is showing, when it is the live shell's —
|
|
912
|
+
* the key the presentation-switch affordances write their override under.
|
|
913
|
+
* Omitted in bounded design-system hosts, which offer no switch. */
|
|
914
|
+
readonly surface?: InspectionSurfaceKind;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Column scroll memory across the RE-PROJECTION remount. A source write's HMR
|
|
919
|
+
* re-projection drops the selection for ~a beat (the measured, unpinned drop
|
|
920
|
+
* `react-world-authoring-adapter.ts#scheduleSelectionRepair` documents), which
|
|
921
|
+
* unmounts this panel entirely; the repair restores the SAME subject and the
|
|
922
|
+
* fresh mount landed at the top — measured live: the column node is REPLACED
|
|
923
|
+
* and `scrollTop` resets to 0 within 600ms of any style write, which the
|
|
924
|
+
* blind walker read as "every Fill edit scrolls the inspector back to top"
|
|
925
|
+
* (Fill just sits below the fold). Keyed by subject id, so a genuinely new
|
|
926
|
+
* selection still opens at the top while the same subject keeps its place.
|
|
927
|
+
*/
|
|
928
|
+
const columnScrollMemory = new Map<string, number>();
|
|
929
|
+
|
|
930
|
+
/** ONE subject, projected. */
|
|
931
|
+
export function InspectionProjectionView({
|
|
932
|
+
subject,
|
|
933
|
+
presentation,
|
|
934
|
+
surface,
|
|
935
|
+
}: InspectionProjectionProps) {
|
|
936
|
+
const subjectId = subject.id;
|
|
937
|
+
const restoreScroll = useCallback(
|
|
938
|
+
(el: HTMLDivElement | null) => {
|
|
939
|
+
if (el) el.scrollTop = columnScrollMemory.get(subjectId) ?? 0;
|
|
940
|
+
},
|
|
941
|
+
[subjectId],
|
|
942
|
+
);
|
|
943
|
+
if (presentation === 'card') {
|
|
944
|
+
return <MiniInspectorCard subject={subject} {...(surface ? { surface } : {})} />;
|
|
945
|
+
}
|
|
946
|
+
return (
|
|
947
|
+
<Panel
|
|
948
|
+
name="Inspector"
|
|
949
|
+
hideHeader
|
|
950
|
+
className="vgai-content-frost"
|
|
951
|
+
data-testid="inspector-panel"
|
|
952
|
+
data-vgai-inspector-presentation={presentation}
|
|
953
|
+
onPointerDown={() => setActiveScope('inspector')}
|
|
954
|
+
style={{ overflowY: 'auto' }}
|
|
955
|
+
ref={restoreScroll}
|
|
956
|
+
onScroll={(e: ReactUIEvent<HTMLDivElement>) => {
|
|
957
|
+
if (columnScrollMemory.size > 200) columnScrollMemory.clear();
|
|
958
|
+
columnScrollMemory.set(subjectId, e.currentTarget.scrollTop);
|
|
959
|
+
}}
|
|
960
|
+
>
|
|
961
|
+
{presentation === 'properties' ? (
|
|
962
|
+
<PropertiesColumn subject={subject} />
|
|
963
|
+
) : (
|
|
964
|
+
<NarrowColumn subject={subject} />
|
|
965
|
+
)}
|
|
966
|
+
</Panel>
|
|
967
|
+
);
|
|
968
|
+
}
|