@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,842 @@
|
|
|
1
|
+
import { collectAllNodeIds } from './authoring/active-adapter';
|
|
2
|
+
import {
|
|
3
|
+
copyAuthoringNodes,
|
|
4
|
+
cutAuthoringNodes,
|
|
5
|
+
duplicateAuthoringNode,
|
|
6
|
+
groupAuthoringNodes,
|
|
7
|
+
pasteAuthoringNodes,
|
|
8
|
+
removeAuthoringNode,
|
|
9
|
+
removeManyAuthoringNodes,
|
|
10
|
+
setAuthoringSelection,
|
|
11
|
+
unwrapAuthoringNode,
|
|
12
|
+
wrapAuthoringNode,
|
|
13
|
+
} from './authoring/consumer-actions';
|
|
14
|
+
import { resolvePanelAuthoring } from './authoring/panel-authoring';
|
|
15
|
+
import {
|
|
16
|
+
currentSelectionScopeId,
|
|
17
|
+
enterSelectedScope,
|
|
18
|
+
exitSelectionScope,
|
|
19
|
+
} from './authoring/selection-scope';
|
|
20
|
+
import { activeSelectionIds, saveActiveAuthoring } from './authoring/shell-document-ops';
|
|
21
|
+
import { resetRootPan } from './authoring/world-pan-state';
|
|
22
|
+
import {
|
|
23
|
+
boxEditForId,
|
|
24
|
+
computeArrowNudgePatch,
|
|
25
|
+
readSpacingValues,
|
|
26
|
+
rectForId,
|
|
27
|
+
} from './components/world-overlay-gestures';
|
|
28
|
+
import { openCommandPalette } from './editor-commands';
|
|
29
|
+
import { editorConsole } from './editor-console';
|
|
30
|
+
import type { EditorShellStore } from './editor-shell-store';
|
|
31
|
+
import type { EditorViewport } from './editor-viewport';
|
|
32
|
+
import type { HistoryCommands } from './history/history-commands';
|
|
33
|
+
import {
|
|
34
|
+
allowsAppUndoWhileEditable,
|
|
35
|
+
getActiveScope,
|
|
36
|
+
type HotkeyBinding,
|
|
37
|
+
installHotkeys,
|
|
38
|
+
isEditableTarget,
|
|
39
|
+
registerHotkeys,
|
|
40
|
+
setActiveScope,
|
|
41
|
+
} from './hotkeys';
|
|
42
|
+
import { type KeyActionScope, registerKeyAction } from './key-actions';
|
|
43
|
+
import {
|
|
44
|
+
type EditorKeyActionId,
|
|
45
|
+
keyChordsFor,
|
|
46
|
+
shortcutFor,
|
|
47
|
+
subscribeEditorKeymap,
|
|
48
|
+
} from './keymap-presets';
|
|
49
|
+
import { stopAllLiveSessions } from './live-session-registry';
|
|
50
|
+
import { requestTransformMode } from './transform-mode-request';
|
|
51
|
+
import { showTransientHint } from './transient-hint';
|
|
52
|
+
import { saveActiveWorkspaceDocument } from './workspace-document-registry';
|
|
53
|
+
import { toggleWorkspaceFocus } from './workspace-host-commands';
|
|
54
|
+
import { cycleEditorWorkspace } from './workspace-presets';
|
|
55
|
+
import { toggleConsoleUtility } from './workspace-utility-commands';
|
|
56
|
+
|
|
57
|
+
// 'inspector' belongs here: the inspector shows the SELECTED NODE, so a user
|
|
58
|
+
// who just edited a field expects node-scoped clipboard/duplicate to still
|
|
59
|
+
// answer (a human pressed Ctrl+D after typing a color and read the silence as
|
|
60
|
+
// "duplicate is broken" — runhuman pass 41). Focused text inputs stay safe:
|
|
61
|
+
// isEditableTarget refuses there, and clipboard keys keep their native-DOM
|
|
62
|
+
// `when` guard.
|
|
63
|
+
const AUTHORING_SELECTION_SCOPES = ['viewport', 'hierarchy', 'inspector'] as const;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* D4 (spec27 §6 D4, §8 "Keyboard / undo / pan-zoom" row) — RECONCILED by the
|
|
67
|
+
* `overlay-panzoom` unit (D-E's own reconciliation note).
|
|
68
|
+
*
|
|
69
|
+
* Space-pan is BUILT: `RootSelectionOverlay.tsx`'s own Space-held +
|
|
70
|
+
* pointer-drag handling (its `spaceHeldRef`/`panDragRef`), writing the shared
|
|
71
|
+
* `world-pan-state.ts` singleton, which `design-time-layers.ts` applies to
|
|
72
|
+
* every world-layer host IN LOCKSTEP with the overlay's own wrapper — a pure
|
|
73
|
+
* CSS `transform: translate()` on both. Not registered here in
|
|
74
|
+
* `editor-hotkeys.ts`'s `registerHotkeys` array because that registry is a
|
|
75
|
+
* single-fire-per-keydown dispatcher with no "held" concept (see
|
|
76
|
+
* `hotkeys.ts`); Space-pan needs a literal hold state, so it follows the
|
|
77
|
+
* SAME precedent this file's own vertex-snap `V`-key handling below already
|
|
78
|
+
* set (a raw `window` keydown/keyup pair) — just owned by the overlay
|
|
79
|
+
* component instead of this file, since the gesture IS a pointer-drag over
|
|
80
|
+
* the overlay's own interaction layer, not a stateless action like
|
|
81
|
+
* duplicate/delete/nudge above.
|
|
82
|
+
*
|
|
83
|
+
* Zoom-fit/selection is a stated EXCLUDE — evidence checked (not assumed):
|
|
84
|
+
* the 3D viewport (`world-root-stage.ts`/`editor-viewport.ts`) already has a
|
|
85
|
+
* real zoom substrate (Three.js `OrbitControls`, numpad view presets), so
|
|
86
|
+
* zoom-fit there is pure duplication, not a gap. The react/DOM world
|
|
87
|
+
* surface's `RectProvider.rect(id)` is `getBoundingClientRect()`-derived and
|
|
88
|
+
* host-relative (`react-world-authoring-adapter.ts`'s `toHostRelative`) —
|
|
89
|
+
* UNLIKE pan (a pure translation, which cancels out of that subtraction with
|
|
90
|
+
* zero consumer changes, per `world-pan-state.ts`'s own soundness doc
|
|
91
|
+
* comment), a host `scale()` bakes the zoom factor into every rect AND every
|
|
92
|
+
* screen-space gesture delta (`toHostLocal`), so it would require an
|
|
93
|
+
* explicit zoom-aware conversion at all ~14 host-relative chrome consumers
|
|
94
|
+
* (selection/hover box, 8 handles, rotate, labels, spacing bands, measure
|
|
95
|
+
* lines, snap guides, badges, parent outline, drag-ghost, reorder marker,
|
|
96
|
+
* empty-container hints, align-toolbar anchor, marquee) and the 3
|
|
97
|
+
* screen-delta gesture patches (`computeResizePatch`/`computeMovePatch`/
|
|
98
|
+
* `computeSpacingPatch`) — a foundation rebase against the ~2360-test unit
|
|
99
|
+
* suite + the full 27x e2e series (all pinned at the implicit `scale=1`
|
|
100
|
+
* frame) that is disproportionate to one keyboard-row affordance the
|
|
101
|
+
* visual-edit reference itself only had live in its own demo host, not its
|
|
102
|
+
* reusable library. No dead keybinding is registered for it.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
// The five action functions below (`duplicateSelection` … `nudgeSelection`)
|
|
106
|
+
// are exported for two reasons: `registerEditorShellHotkeys` wires them to
|
|
107
|
+
// their keybindings below, AND `editor-hotkeys.test.ts` unit-tests the
|
|
108
|
+
// capability-gate behavior (structure/boxEdit absent -> no-op) directly
|
|
109
|
+
// against them, without needing a full `EditorViewport`/canvas fixture.
|
|
110
|
+
|
|
111
|
+
/** Entity clipboard shortcuts yield to ordinary DOM text selection. Editable
|
|
112
|
+
* controls already yield in `hotkeys.ts`; this covers selectable labels/logs
|
|
113
|
+
* inside the hierarchy or viewport scopes. */
|
|
114
|
+
export function shouldHandleEditorClipboard(): boolean {
|
|
115
|
+
return (globalThis.getSelection?.()?.toString() ?? '').length === 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* THE SUBJECT THESE ACTIONS ACT ON — the ACTIVE DOCUMENT's selection, which is
|
|
120
|
+
* the same place they already take their adapter from.
|
|
121
|
+
*
|
|
122
|
+
* `store.selectedEntityIds` is the SHELL store's set, and for a document whose
|
|
123
|
+
* adapter owns its own selection it is a different set entirely: every one of
|
|
124
|
+
* these functions resolved its adapter through `resolvePanelAuthoring` and then
|
|
125
|
+
* read the ids from the shell, so on such a document they saw an empty
|
|
126
|
+
* selection and returned at the guard — a silent no-op with nothing to report.
|
|
127
|
+
*
|
|
128
|
+
* MEASURED 2026-09-21 on the Blender Model document, whose Outliner adapter
|
|
129
|
+
* publishes its selection in Blender's own row ids: with the Sphere selected
|
|
130
|
+
* and highlighted in the panel, `vgai.edit.delete` ran, found
|
|
131
|
+
* `store.selectedEntityIds` empty and deleted nothing — X and Shift+D were
|
|
132
|
+
* dead for every object on that document. `activeSelectionIds` is what
|
|
133
|
+
* `editor.status()` already reports and what the hierarchy panel already
|
|
134
|
+
* draws, so reading it here makes the three agree instead of two against one.
|
|
135
|
+
*/
|
|
136
|
+
function actionSelectionIds(store: EditorShellStore): string[] {
|
|
137
|
+
return activeSelectionIds(store);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function copySelection(store: EditorShellStore): Promise<void> {
|
|
141
|
+
const ids = actionSelectionIds(store);
|
|
142
|
+
if (ids.length === 0) return;
|
|
143
|
+
await copyAuthoringNodes(resolvePanelAuthoring(store).adapter, ids);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function cutSelection(store: EditorShellStore): Promise<void> {
|
|
147
|
+
const ids = actionSelectionIds(store);
|
|
148
|
+
if (ids.length === 0) return;
|
|
149
|
+
await cutAuthoringNodes(resolvePanelAuthoring(store).adapter, ids);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Ordinary Paste creates siblings: the current selection's native parent is
|
|
153
|
+
* the destination. With no selection, the adapter's document root is used. */
|
|
154
|
+
export async function pasteSelection(store: EditorShellStore): Promise<void> {
|
|
155
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
156
|
+
const selectedId = actionSelectionIds(store)[0];
|
|
157
|
+
const parentId = selectedId ? (adapter.hierarchy.node(selectedId)?.parentId ?? null) : null;
|
|
158
|
+
await pasteAuthoringNodes(adapter, parentId);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* D4 (spec27 §6 D4 "Ctrl+D duplicate, Del/Backspace delete — route through
|
|
163
|
+
* adapter.structure.* (capability-gated)"). `store.selectedEntityIds` is the
|
|
164
|
+
* shared, adapter-routed selection set (every adapter's own `selection.set`
|
|
165
|
+
* funnels into it); the ACTION goes through the active adapter's
|
|
166
|
+
* `StructureProvider`, and is a capability-gated no-op when that adapter has
|
|
167
|
+
* none (e.g. the ingested `react-dom` adapter, spec:308-309).
|
|
168
|
+
*
|
|
169
|
+
* There is no fork here: every adapter is reached the same way, through its
|
|
170
|
+
* `StructureProvider`.
|
|
171
|
+
*
|
|
172
|
+
* Each id's ack is AWAITED before the next fires, exactly as `deleteSelection`
|
|
173
|
+
* awaits `remove`: for a source lane every duplicate in the loop rewrites the
|
|
174
|
+
* SAME file, and two un-awaited writes each read it before either wrote back,
|
|
175
|
+
* so whichever landed second silently loses the first. That is why the id and
|
|
176
|
+
* the ack come back together (`StructuralIdWrite`) — a synchronous id with a
|
|
177
|
+
* `void`ed write would leave this loop nothing to await. Awaiting a lane whose
|
|
178
|
+
* duplicate is in-memory (`ack: undefined`) is a harmless no-op.
|
|
179
|
+
*/
|
|
180
|
+
/**
|
|
181
|
+
* SERIALIZED ACROSS PRESSES. One invocation already awaits each id's write,
|
|
182
|
+
* but a BURST of Ctrl+D presses ran concurrently: every press read the same
|
|
183
|
+
* pre-write selection and the same stale source, so five presses produced five
|
|
184
|
+
* copies with one name at one position, stacked (runhuman pass 66 —
|
|
185
|
+
* "they are all stacked somewhere together... the names are HeroBox 2, 2, 2").
|
|
186
|
+
* Queuing makes each press start from what the previous one produced, which is
|
|
187
|
+
* also what a human means by pressing it again: a chain of copies, each named
|
|
188
|
+
* and offset from the last.
|
|
189
|
+
*/
|
|
190
|
+
let duplicateQueue: Promise<void> = Promise.resolve();
|
|
191
|
+
|
|
192
|
+
export function duplicateSelection(store: EditorShellStore): Promise<void> {
|
|
193
|
+
const queued = duplicateQueue.then(() => runDuplicateSelection(store));
|
|
194
|
+
// The queue never inherits a rejection — one failed duplicate must not
|
|
195
|
+
// strand every later press (the commit-queue rule in the canvas lane).
|
|
196
|
+
duplicateQueue = queued.then(
|
|
197
|
+
() => undefined,
|
|
198
|
+
() => undefined,
|
|
199
|
+
);
|
|
200
|
+
return queued;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function runDuplicateSelection(store: EditorShellStore): Promise<void> {
|
|
204
|
+
const selection = actionSelectionIds(store);
|
|
205
|
+
if (selection.length === 0) return;
|
|
206
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
207
|
+
const structure = adapter.structure;
|
|
208
|
+
if (!structure) return;
|
|
209
|
+
// Immediate acknowledgement: a source-lane duplicate takes a visible moment
|
|
210
|
+
// (write + remount), and the silence in between reads as a dead hotkey — a
|
|
211
|
+
// human pressed Ctrl+D repeatedly and ended up with three copies (runhuman
|
|
212
|
+
// pass 41). The hint is published BEFORE the first await and replaced by the
|
|
213
|
+
// done line when the writes land.
|
|
214
|
+
const label =
|
|
215
|
+
selection.length === 1
|
|
216
|
+
? (adapter.hierarchy.node(selection[0] as string)?.label ?? 'selection')
|
|
217
|
+
: `${selection.length} objects`;
|
|
218
|
+
showTransientHint(`Duplicating ${label}…`);
|
|
219
|
+
for (const id of selection) await duplicateAuthoringNode(adapter, id).ack;
|
|
220
|
+
showTransientHint(`Duplicated ${label}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* D4.R2 (spec27 §6 D4 reopen) — a multi-node override delete prefers the
|
|
225
|
+
* batched `structure.removeMany` (ONE undo entry for the whole selection,
|
|
226
|
+
* matching the first-party `store.deleteEntities` batched-undo semantics
|
|
227
|
+
* below) when the active adapter implements it; falls back to the per-id
|
|
228
|
+
* `structure.remove` loop for any adapter that doesn't (N undo entries).
|
|
229
|
+
* `await`ed the same way `remove` is (harmless no-op for a synchronous/
|
|
230
|
+
* in-memory implementer) — see `StructureProvider.removeMany`'s doc comment.
|
|
231
|
+
*
|
|
232
|
+
* SOURCE-CORRUPTION FIX (bug-panel reopen, post-D4.R2) + DELETE-ORDER-
|
|
233
|
+
* RESIDUAL FIX (bug-panel follow-up, post-50f90a6d): the per-id fallback loop
|
|
234
|
+
* used to fire `structure.remove(id)` for every id in RAW selection order —
|
|
235
|
+
* document/top-first, the same order a marquee/DFS walk produces — with NO
|
|
236
|
+
* `await` between iterations. For an adapter whose per-op target offset is
|
|
237
|
+
* resolved from an index that is only refreshed by a real reparse (never
|
|
238
|
+
* between same-file writes — `ReactRootAuthoringAdapter`'s `OidStore.index`),
|
|
239
|
+
* that is a real, default-gesture-reachable source-corruption hazard:
|
|
240
|
+
* deleting an EARLIER element first shifts every LATER element's still-stale
|
|
241
|
+
* `{line, col}` out from under it, and firing every write in the same tick
|
|
242
|
+
* can race the underlying network request, letting a later write silently
|
|
243
|
+
* clobber an earlier one. Both mechanisms are fixed for the fallback loop,
|
|
244
|
+
* together (see the CORRECTNESS INVARIANT comment on
|
|
245
|
+
* `ReactRootAuthoringAdapter.structure`'s per-id `remove` for the full
|
|
246
|
+
* soundness argument, including its own remaining caveat):
|
|
247
|
+
* 1. sort `ids` into REVERSE DOCUMENT ORDER (bottom-most/deepest first) —
|
|
248
|
+
* derived from the SAME DFS pre-order `collectAllNodeIds` walks (the
|
|
249
|
+
* marquee/select-all candidate order), reversed, so a selected
|
|
250
|
+
* descendant is always deleted before a selected ancestor;
|
|
251
|
+
* 2. `await` each `structure.remove(id)` before starting the next, so
|
|
252
|
+
* write N only ever begins once write N-1 has landed on disk.
|
|
253
|
+
* A synchronous/in-memory `remove` (e.g. `UIAuthoringAdapter`'s) is
|
|
254
|
+
* unaffected — awaiting a non-promise return is a harmless no-op, and its own
|
|
255
|
+
* `removeMany` batches instead of ever reaching this loop. This fallback loop
|
|
256
|
+
* is sound ONLY when `collectAllNodeIds`' walk order matches true SOURCE
|
|
257
|
+
* order — `ReactRootAuthoringAdapter` now implements `removeMany` (a single
|
|
258
|
+
* shared-snapshot batched delete, order-independent by construction) exactly
|
|
259
|
+
* so THIS loop is never reached for it; it stays here, documented and tested,
|
|
260
|
+
* as the honest degrade for any override adapter that has no batched delete.
|
|
261
|
+
*/
|
|
262
|
+
export async function deleteSelection(store: EditorShellStore): Promise<void> {
|
|
263
|
+
const ids = actionSelectionIds(store);
|
|
264
|
+
if (ids.length === 0) return;
|
|
265
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
266
|
+
const structure = adapter.structure;
|
|
267
|
+
if (!structure) return;
|
|
268
|
+
if (structure.removeMany) {
|
|
269
|
+
await removeManyAuthoringNodes(adapter, ids);
|
|
270
|
+
} else {
|
|
271
|
+
// Reverse DOCUMENT order (bottom-most/deepest first) — see this
|
|
272
|
+
// function's own doc comment. `collectAllNodeIds` walks the adapter's
|
|
273
|
+
// hierarchy in the same DFS pre-order (parent-before-child, top-first)
|
|
274
|
+
// a marquee/select-all uses; an id outside that walk (should not
|
|
275
|
+
// happen — every selected id came FROM this adapter's own hierarchy)
|
|
276
|
+
// sorts last (rank -1) as a defensive fallback, never a throw.
|
|
277
|
+
const rank = new Map(collectAllNodeIds(adapter).map((id, i) => [id, i]));
|
|
278
|
+
const ordered = [...ids].sort((a, b) => (rank.get(b) ?? -1) - (rank.get(a) ?? -1));
|
|
279
|
+
for (const id of ordered) await removeAuthoringNode(adapter, id);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** D4 — `Ctrl+Alt+G` wrap / `Ctrl+Shift+G` unwrap (spec §6 D4, §5 "wrap/
|
|
284
|
+
* unwrap ... via `structure.*`"): no first-party equivalent exists (a 3D
|
|
285
|
+
* entity can't be "wrapped"), so this is override-only, capability-gated on
|
|
286
|
+
* the OPTIONAL `structure.wrap`/`structure.unwrap` (absent for the
|
|
287
|
+
* ingested `react-dom` adapter and the first-party three adapter alike —
|
|
288
|
+
* both correctly no-op). Applies to every selected id, same multi-select
|
|
289
|
+
* loop `duplicateSelection`/`deleteSelection` use. */
|
|
290
|
+
/** Awaited per id for the same reason `deleteSelection` awaits `remove`: two
|
|
291
|
+
* in-flight structural writes against the SAME source file each read it before
|
|
292
|
+
* either wrote back, and whichever lands second silently loses the first. The
|
|
293
|
+
* awaited value is that edit's own `{destination, persisted}` ack. */
|
|
294
|
+
export async function wrapSelection(store: EditorShellStore): Promise<void> {
|
|
295
|
+
const ids = actionSelectionIds(store);
|
|
296
|
+
if (ids.length === 0) return;
|
|
297
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
298
|
+
const structure = adapter.structure;
|
|
299
|
+
if (!structure?.wrap) return;
|
|
300
|
+
for (const id of ids) await wrapAuthoringNode(adapter, id);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export async function unwrapSelection(store: EditorShellStore): Promise<void> {
|
|
304
|
+
const ids = actionSelectionIds(store);
|
|
305
|
+
if (ids.length === 0) return;
|
|
306
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
307
|
+
const structure = adapter.structure;
|
|
308
|
+
if (!structure?.unwrap) return;
|
|
309
|
+
for (const id of ids) await unwrapAuthoringNode(adapter, id);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Group the current selection as one real spatial parent when its adapter
|
|
313
|
+
* supports lossless grouping. One provider call owns one undo entry, and its
|
|
314
|
+
* ack is awaited the same way `wrapSelection` awaits `wrap` — a caller that
|
|
315
|
+
* awaits this function has awaited the byte. */
|
|
316
|
+
export async function groupSelection(store: EditorShellStore): Promise<void> {
|
|
317
|
+
const ids = actionSelectionIds(store);
|
|
318
|
+
if (ids.length === 0) return;
|
|
319
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
320
|
+
if (!adapter.structure?.group) return;
|
|
321
|
+
await groupAuthoringNodes(adapter, ids).ack;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* D4.c (spec §6 D4 "arrow-nudge (↑↓←→, Shift=×10) ... writes margins/
|
|
326
|
+
* offsets"): nudges every currently-selected id whose owner exposes
|
|
327
|
+
* `boxEdit` (capability-gated — silently skips ids that don't, e.g. a 3D
|
|
328
|
+
* entity or an ingested-world node with no spatial write path). Position/
|
|
329
|
+
* margin math is the pure, independently-tested `computeArrowNudgePatch`
|
|
330
|
+
* (`world-overlay-gestures.ts`); this function only resolves the per-id
|
|
331
|
+
* rect/position/margin inputs it needs and brackets the write in a single
|
|
332
|
+
* `begin`/`apply`/`end` (one undo step per nudged node, per B1/D-1).
|
|
333
|
+
*/
|
|
334
|
+
export function nudgeSelection(store: EditorShellStore, dx: number, dy: number): void {
|
|
335
|
+
const ids = actionSelectionIds(store);
|
|
336
|
+
if (ids.length === 0) return;
|
|
337
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
338
|
+
for (const id of ids) {
|
|
339
|
+
const boxEdit = boxEditForId(adapter, id);
|
|
340
|
+
if (!boxEdit) continue;
|
|
341
|
+
const rect = rectForId(adapter, id);
|
|
342
|
+
if (!rect) continue;
|
|
343
|
+
const position = adapter.inspector?.get(id, 'style.position');
|
|
344
|
+
const isPositioned = position === 'absolute' || position === 'fixed';
|
|
345
|
+
const spacing = readSpacingValues(adapter, id);
|
|
346
|
+
const patch = computeArrowNudgePatch(dx, dy, isPositioned, rect, {
|
|
347
|
+
left: spacing.marginLeft,
|
|
348
|
+
top: spacing.marginTop,
|
|
349
|
+
});
|
|
350
|
+
if (Object.keys(patch).length === 0) continue;
|
|
351
|
+
boxEdit.begin(id);
|
|
352
|
+
boxEdit.apply(id, patch);
|
|
353
|
+
boxEdit.end(id);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
// The keymap seam
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
// No binding below spells a literal key. Each names a LOGICAL ACTION and
|
|
361
|
+
// `keymap-presets.ts` supplies the chords the active keymap assigns it, so
|
|
362
|
+
// `vgai` and `blender` are two tables rather than two code paths. See that
|
|
363
|
+
// module's header for the ruling, the collision resolutions, and the boundary
|
|
364
|
+
// against play-mode input isolation (which this seam does not touch).
|
|
365
|
+
|
|
366
|
+
/** How the FRAME reads a binding's scope when it turns one into a `when`
|
|
367
|
+
* clause (`key-actions.ts`): the viewport is the stage, the
|
|
368
|
+
* selection panels are the editor's own parts, global is everywhere in it. */
|
|
369
|
+
function frameScopeOf(scope: HotkeyBinding['scope']): KeyActionScope {
|
|
370
|
+
const scopes = Array.isArray(scope) ? scope : [scope];
|
|
371
|
+
if (scopes.includes('global')) return 'global';
|
|
372
|
+
if (scopes.length === 1 && scopes[0] === 'viewport') return 'stage';
|
|
373
|
+
return 'panel';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* The action ids one `build()` pass registered, collected so the pass's
|
|
378
|
+
* disposal can take them back out. `bind` is the one place an action's body
|
|
379
|
+
* is written, so it is also the one place the frame's table is filled; the
|
|
380
|
+
* collector is how a call inside the build closure reaches the registration
|
|
381
|
+
* that owns it without every call site threading it.
|
|
382
|
+
*/
|
|
383
|
+
let keyActionCollector: (() => void)[] | null = null;
|
|
384
|
+
|
|
385
|
+
/** Expand one logical action into the dispatcher's per-chord binding rows,
|
|
386
|
+
* and record what the action DOES in the chord-independent table the frame
|
|
387
|
+
* dispatches through (`key-actions.ts`). */
|
|
388
|
+
function bind(
|
|
389
|
+
action: EditorKeyActionId,
|
|
390
|
+
binding: Omit<HotkeyBinding, 'key' | 'code' | 'mod' | 'shift' | 'alt'> & {
|
|
391
|
+
/** This binding's `when`, in the form the FRAME can ask — no event. Set
|
|
392
|
+
* it wherever `when` ignores its argument; see `KeyActionEntry.enabled`. */
|
|
393
|
+
frameEnabled?: () => boolean;
|
|
394
|
+
},
|
|
395
|
+
): HotkeyBinding[] {
|
|
396
|
+
const { frameEnabled, ...row } = binding;
|
|
397
|
+
keyActionCollector?.push(
|
|
398
|
+
registerKeyAction({
|
|
399
|
+
id: action,
|
|
400
|
+
run: (event) => row.action(event ?? new KeyboardEvent('keydown')),
|
|
401
|
+
scope: frameScopeOf(row.scope),
|
|
402
|
+
...(frameEnabled ? { enabled: frameEnabled } : {}),
|
|
403
|
+
}),
|
|
404
|
+
);
|
|
405
|
+
return keyChordsFor(action).map((chord) => ({ ...row, ...chord }));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Register a binding set that REBUILDS on a keymap switch — the "live, no
|
|
410
|
+
* reload" half of the setting. Nothing else re-registers: the actions are the
|
|
411
|
+
* same objects, only their chords move.
|
|
412
|
+
*
|
|
413
|
+
* The action TABLE is filled by the same pass, and it is filled whoever owns
|
|
414
|
+
* the keyboard: under the frame the chord rows below go unread (the host
|
|
415
|
+
* dispatcher is never installed — `hotkeys.ts`) while the table is what every
|
|
416
|
+
* `vgai.*` VS Code command dispatches into.
|
|
417
|
+
*/
|
|
418
|
+
function registerKeymapBindings(build: () => HotkeyBinding[]): () => void {
|
|
419
|
+
const runBuild = (): { rows: HotkeyBinding[]; actions: (() => void)[] } => {
|
|
420
|
+
const actions: (() => void)[] = [];
|
|
421
|
+
const previous = keyActionCollector;
|
|
422
|
+
keyActionCollector = actions;
|
|
423
|
+
try {
|
|
424
|
+
return { rows: build(), actions };
|
|
425
|
+
} finally {
|
|
426
|
+
keyActionCollector = previous;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
let pass = runBuild();
|
|
430
|
+
let dispose = registerHotkeys(pass.rows);
|
|
431
|
+
const disposePass = (): void => {
|
|
432
|
+
dispose();
|
|
433
|
+
for (const drop of pass.actions) drop();
|
|
434
|
+
};
|
|
435
|
+
const unsubscribe = subscribeEditorKeymap(() => {
|
|
436
|
+
disposePass();
|
|
437
|
+
pass = runBuild();
|
|
438
|
+
dispose = registerHotkeys(pass.rows);
|
|
439
|
+
});
|
|
440
|
+
return () => {
|
|
441
|
+
unsubscribe();
|
|
442
|
+
disposePass();
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Shell-lifetime bindings: undo/redo, save, clipboard, delete, command palette.
|
|
448
|
+
* These do not need a Three viewport. Canvas-only projects (Bubbo isolation
|
|
449
|
+
* documents, first-party Pixi worlds) never mount a three stage at all, so they
|
|
450
|
+
* only get these keys if THIS function is registered from the editor shell.
|
|
451
|
+
*
|
|
452
|
+
* `mod+Z` matches Ctrl OR Cmd (`hotkeys.ts`); on Mac the menu prints ⌘Z and
|
|
453
|
+
* both chords fire the same action.
|
|
454
|
+
*/
|
|
455
|
+
export function registerEditorShellHotkeys(
|
|
456
|
+
store: EditorShellStore,
|
|
457
|
+
history: HistoryCommands,
|
|
458
|
+
): () => void {
|
|
459
|
+
// NARRATED undo/redo: the hint names what just happened ("Undid Paste 3
|
|
460
|
+
// Entities"). Undoing a paste empties the selection (the pasted ids die
|
|
461
|
+
// with it), and the silent no-gizmo viewport that follows read as "a Play
|
|
462
|
+
// preview I had to click out of" to a human (runhuman passes 52/53).
|
|
463
|
+
const narratedUndo = (): void => {
|
|
464
|
+
const label = history.getSnapshot().undoLabel;
|
|
465
|
+
void history.undo().then((done) => {
|
|
466
|
+
if (done && label) showTransientHint(`Undid ${label}`);
|
|
467
|
+
});
|
|
468
|
+
};
|
|
469
|
+
const narratedRedo = (): void => {
|
|
470
|
+
const label = history.getSnapshot().redoLabel;
|
|
471
|
+
void history.redo().then((done) => {
|
|
472
|
+
if (done && label) showTransientHint(`Redid ${label}`);
|
|
473
|
+
});
|
|
474
|
+
};
|
|
475
|
+
const unregister = registerKeymapBindings(() => [
|
|
476
|
+
// --- Global (fire regardless of active panel) ---
|
|
477
|
+
// H2: one project-scoped async command queue owns shell undo/redo. Ctrl+Y
|
|
478
|
+
// mirrors Ctrl+Shift+Z (Windows convention) — both are chords of the one
|
|
479
|
+
// `edit.redo` action in the keymap table.
|
|
480
|
+
// Undo/redo also fire inside fields that opted into app undo
|
|
481
|
+
// (`APP_UNDO_PASSTHROUGH_ATTR` — per-keystroke-committing numeric inputs,
|
|
482
|
+
// where native text undo is a dead key); every other editable keeps the
|
|
483
|
+
// guard so typing retains the browser's own undo.
|
|
484
|
+
...bind('edit.redo', {
|
|
485
|
+
scope: 'global',
|
|
486
|
+
allowInEditable: true,
|
|
487
|
+
when: (e) => !isEditableTarget(e.target) || allowsAppUndoWhileEditable(e.target),
|
|
488
|
+
action: narratedRedo,
|
|
489
|
+
}),
|
|
490
|
+
...bind('edit.undo', {
|
|
491
|
+
scope: 'global',
|
|
492
|
+
allowInEditable: true,
|
|
493
|
+
when: (e) => !isEditableTarget(e.target) || allowsAppUndoWhileEditable(e.target),
|
|
494
|
+
action: narratedUndo,
|
|
495
|
+
}),
|
|
496
|
+
...bind('edit.copy', {
|
|
497
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
498
|
+
when: shouldHandleEditorClipboard,
|
|
499
|
+
frameEnabled: shouldHandleEditorClipboard,
|
|
500
|
+
action: () => void copySelection(store),
|
|
501
|
+
}),
|
|
502
|
+
...bind('edit.cut', {
|
|
503
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
504
|
+
when: shouldHandleEditorClipboard,
|
|
505
|
+
frameEnabled: shouldHandleEditorClipboard,
|
|
506
|
+
action: () => void cutSelection(store),
|
|
507
|
+
}),
|
|
508
|
+
...bind('edit.paste', {
|
|
509
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
510
|
+
when: shouldHandleEditorClipboard,
|
|
511
|
+
frameEnabled: shouldHandleEditorClipboard,
|
|
512
|
+
action: () => void pasteSelection(store),
|
|
513
|
+
}),
|
|
514
|
+
...bind('edit.duplicate', {
|
|
515
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
516
|
+
action: () => void duplicateSelection(store),
|
|
517
|
+
}),
|
|
518
|
+
// D4 — wrap/unwrap (override-only, capability-gated — see
|
|
519
|
+
// `wrapSelection`/`unwrapSelection`'s doc comments).
|
|
520
|
+
...bind('edit.group', {
|
|
521
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
522
|
+
action: () => void groupSelection(store),
|
|
523
|
+
}),
|
|
524
|
+
...bind('edit.wrap', {
|
|
525
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
526
|
+
action: () => wrapSelection(store),
|
|
527
|
+
}),
|
|
528
|
+
...bind('edit.unwrap', {
|
|
529
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
530
|
+
action: () => unwrapSelection(store),
|
|
531
|
+
}),
|
|
532
|
+
// D4 — arrow-nudge (↑↓←→, Shift=×10 — see `nudgeSelection`'s doc comment).
|
|
533
|
+
...bind('edit.nudgeUp', { scope: 'viewport', action: () => nudgeSelection(store, 0, -1) }),
|
|
534
|
+
...bind('edit.nudgeUpCoarse', {
|
|
535
|
+
scope: 'viewport',
|
|
536
|
+
action: () => nudgeSelection(store, 0, -10),
|
|
537
|
+
}),
|
|
538
|
+
...bind('edit.nudgeDown', { scope: 'viewport', action: () => nudgeSelection(store, 0, 1) }),
|
|
539
|
+
...bind('edit.nudgeDownCoarse', {
|
|
540
|
+
scope: 'viewport',
|
|
541
|
+
action: () => nudgeSelection(store, 0, 10),
|
|
542
|
+
}),
|
|
543
|
+
...bind('edit.nudgeLeft', { scope: 'viewport', action: () => nudgeSelection(store, -1, 0) }),
|
|
544
|
+
...bind('edit.nudgeLeftCoarse', {
|
|
545
|
+
scope: 'viewport',
|
|
546
|
+
action: () => nudgeSelection(store, -10, 0),
|
|
547
|
+
}),
|
|
548
|
+
...bind('edit.nudgeRight', { scope: 'viewport', action: () => nudgeSelection(store, 1, 0) }),
|
|
549
|
+
...bind('edit.nudgeRightCoarse', {
|
|
550
|
+
scope: 'viewport',
|
|
551
|
+
action: () => nudgeSelection(store, 10, 0),
|
|
552
|
+
}),
|
|
553
|
+
...bind('edit.selectAll', {
|
|
554
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
555
|
+
action: () => {
|
|
556
|
+
const a = resolvePanelAuthoring(store).adapter;
|
|
557
|
+
setAuthoringSelection(a, collectAllNodeIds(a));
|
|
558
|
+
},
|
|
559
|
+
}),
|
|
560
|
+
// Blender's Alt+A. Unbound in the vgai table, where Escape is the deselect
|
|
561
|
+
// (and carries the scope-exit verb with it); this one only clears.
|
|
562
|
+
...bind('edit.deselectAll', {
|
|
563
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
564
|
+
action: () => setAuthoringSelection(resolvePanelAuthoring(store).adapter, []),
|
|
565
|
+
}),
|
|
566
|
+
...bind('edit.save', {
|
|
567
|
+
scope: 'global',
|
|
568
|
+
allowInEditable: true,
|
|
569
|
+
action: () => {
|
|
570
|
+
void saveActiveWorkspaceDocument().then((handled) => {
|
|
571
|
+
// Surfaces with no document-local editor fall back to the ACTIVE
|
|
572
|
+
// authoring adapter's own persistence provider.
|
|
573
|
+
if (handled === null) void saveActiveAuthoring(store);
|
|
574
|
+
});
|
|
575
|
+
},
|
|
576
|
+
}),
|
|
577
|
+
// D4 (spec27 §8 "space-pan" row) — Ctrl+0, the conventional "reset
|
|
578
|
+
// zoom/pan to 100%" shortcut (browsers/design tools alike), resets the
|
|
579
|
+
// DOM/react overlay's shared pan translate (`world-pan-state.ts`) back to
|
|
580
|
+
// `(0, 0)`. A no-op when already at rest or when the active surface has
|
|
581
|
+
// no pan-capable world (there is nothing for it to affect either way —
|
|
582
|
+
// `resetRootPan()` itself no-ops at `(0,0)`).
|
|
583
|
+
...bind('view.resetPan', { scope: 'viewport', action: () => resetRootPan() }),
|
|
584
|
+
...bind('edit.enterScope', {
|
|
585
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
586
|
+
// While playing, Enter belongs to the GAME. The dispatcher preventDefaults
|
|
587
|
+
// every matched binding, so with this bound unconditionally the editor
|
|
588
|
+
// swallowed Enter during play and a game's own Enter binding could never
|
|
589
|
+
// fire — third-person's 'restart' is Enter, and its SQUAD DOWN card says
|
|
590
|
+
// 'PRESS ENTER TO REDEPLOY': a tester pressed it and 'Enter then redeploy
|
|
591
|
+
// doesn't work' (runhuman pass 122; an earlier reproduction had already
|
|
592
|
+
// measured Enter and Escape both exiting Play). Same gate shape as
|
|
593
|
+
// Escape's below, for the same reason.
|
|
594
|
+
when: () => store.playState === 'stopped',
|
|
595
|
+
frameEnabled: () => store.playState === 'stopped',
|
|
596
|
+
action: () => {
|
|
597
|
+
// The PANEL adapter, not the composite: `selection-scope.ts` keys its
|
|
598
|
+
// stack by adapter identity and the Hierarchy's breadcrumb/entry both
|
|
599
|
+
// resolve this one, so all three gestures name the same scope.
|
|
600
|
+
enterSelectedScope(resolvePanelAuthoring(store).adapter);
|
|
601
|
+
},
|
|
602
|
+
}),
|
|
603
|
+
...bind('edit.delete', {
|
|
604
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
605
|
+
action: () => void deleteSelection(store),
|
|
606
|
+
}),
|
|
607
|
+
...bind('edit.exitScopeOrDeselect', {
|
|
608
|
+
scope: AUTHORING_SELECTION_SCOPES,
|
|
609
|
+
// While playing, Escape is owned by play-mode (stop on Escape) — don't also
|
|
610
|
+
// clear the editor selection out from under the running game. The `when`
|
|
611
|
+
// gate (NOT an action-internal no-op) is load-bearing: the dispatcher
|
|
612
|
+
// preventDefaults every MATCHED binding, and play-mode's own bubble-phase
|
|
613
|
+
// Escape guard (`shouldEscapeStopPlay`) reads `defaultPrevented` as
|
|
614
|
+
// "an overlay consumed this Escape" — an action no-op here would still
|
|
615
|
+
// preventDefault and poison that signal, leaving Escape unable to stop play.
|
|
616
|
+
when: () => currentSelectionScopeId() !== null || store.playState === 'stopped',
|
|
617
|
+
// AND UNDER THE FRAME THAT FALLTHROUGH DOES NOT EXIST, which is why the two
|
|
618
|
+
// gates differ (found by walk 2's beat 17, 2026-09-20). VS Code's keybinding
|
|
619
|
+
// service preventDefaults every rule whose `when` RESOLVES, before the command
|
|
620
|
+
// it names decides anything — so with a `vgai.edit.exitScopeOrDeselect` rule on
|
|
621
|
+
// Escape, play-mode's `window` listener never sees an Escape that is not already
|
|
622
|
+
// `defaultPrevented`. A `frameEnabled` that returned false during play therefore
|
|
623
|
+
// produced neither outcome: the command refused ("the editor has no live handler
|
|
624
|
+
// for it right now") and the keystroke was consumed, so Escape did nothing at all
|
|
625
|
+
// while play ran and only the transport's ■ could end an immersive Play.
|
|
626
|
+
// The frame's Escape is this ONE action, and it does what the gesture means: leave
|
|
627
|
+
// the current scope, or — with none to leave — leave PLAY.
|
|
628
|
+
frameEnabled: () => true,
|
|
629
|
+
action: () => {
|
|
630
|
+
// Same one resolution as the Enter binding above — with the composite
|
|
631
|
+
// here, `exitSelectionScope` saw a FOREIGN adapter and dropped the whole
|
|
632
|
+
// stack instead of popping one level.
|
|
633
|
+
const adapter = resolvePanelAuthoring(store).adapter;
|
|
634
|
+
if (exitSelectionScope(adapter)) return;
|
|
635
|
+
// Nothing to pop. Standalone this is only ever reached with play stopped
|
|
636
|
+
// (the `when` above), so it is the deselect it always was; under the frame
|
|
637
|
+
// it is also the Escape that ends a run, through the registry every lane
|
|
638
|
+
// registers into — the same door `stop` reaches, never play-mode by name
|
|
639
|
+
// (the editor does not import `@vgai/game`).
|
|
640
|
+
if (store.playState !== 'stopped') {
|
|
641
|
+
stopAllLiveSessions();
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
store.select(null);
|
|
645
|
+
},
|
|
646
|
+
}),
|
|
647
|
+
...bind('view.commandPalette', {
|
|
648
|
+
scope: 'global',
|
|
649
|
+
// THE PALETTE IS THE WORKBENCH'S. The frame hands over an opener that
|
|
650
|
+
// runs `workbench.action.showCommands`. (This action's own CHORD is
|
|
651
|
+
// unbound — ⌘K is VS Code's chord prefix, U6 — so this path is the
|
|
652
|
+
// command, run from the palette itself or from a keybinding a person
|
|
653
|
+
// made.)
|
|
654
|
+
action: () => {
|
|
655
|
+
if (!openCommandPalette()) {
|
|
656
|
+
editorConsole.warn(
|
|
657
|
+
'The command palette has not handed over its opener yet — run "Volter Editor: Open Workspace" first, or press the workbench\'s own Show All Commands.',
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
}),
|
|
662
|
+
...bind('view.toggleConsole', {
|
|
663
|
+
scope: 'global',
|
|
664
|
+
action: () => toggleConsoleUtility(),
|
|
665
|
+
}),
|
|
666
|
+
// W5 (§6.2 focus/maximize mode) — Ctrl/Cmd+Shift+F. SHORTCUT DECISION,
|
|
667
|
+
// recorded: the benchmarks are Unity's Shift+Space (maximize view under
|
|
668
|
+
// cursor) and VS Code zen's Ctrl+K Z chord — but this registry is
|
|
669
|
+
// single-fire and chord-less, and Space already participates in the
|
|
670
|
+
// overlay's space-pan HOLD gesture (`RootSelectionOverlay`), so both
|
|
671
|
+
// benchmarks' literal keys would collide. mod+shift+F ("Focus") is free
|
|
672
|
+
// in this registry and unclaimed by Chromium on an app page.
|
|
673
|
+
...bind('view.focusMode', {
|
|
674
|
+
scope: 'global',
|
|
675
|
+
action: () => toggleWorkspaceFocus(),
|
|
676
|
+
}),
|
|
677
|
+
// NAMED WORKSPACES' cycle pair (ARCHITECTURE-CORE §Editor chrome asks for
|
|
678
|
+
// "a cycle shortcut"). Ctrl/Cmd+PageDown / PageUp is BLENDER'S OWN
|
|
679
|
+
// workspace cycle — the vocabulary rule (docs/BLENDER-PARITY.md) applies
|
|
680
|
+
// to the modeling half of the editor, and both keys are unclaimed here,
|
|
681
|
+
// so both keymaps keep them.
|
|
682
|
+
...bind('workspace.cycleNext', {
|
|
683
|
+
scope: 'global',
|
|
684
|
+
action: () => {
|
|
685
|
+
cycleEditorWorkspace(1);
|
|
686
|
+
},
|
|
687
|
+
}),
|
|
688
|
+
...bind('workspace.cyclePrevious', {
|
|
689
|
+
scope: 'global',
|
|
690
|
+
action: () => {
|
|
691
|
+
cycleEditorWorkspace(-1);
|
|
692
|
+
},
|
|
693
|
+
}),
|
|
694
|
+
]);
|
|
695
|
+
installHotkeys();
|
|
696
|
+
return unregister;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Three-viewport bindings (the gizmo-mode trio, numpad views, vertex snap).
|
|
701
|
+
* Only the 3D Scene document has an `EditorViewport`; canvas isolation
|
|
702
|
+
* documents do not. Which keys the trio lands on is the active KEYMAP's call
|
|
703
|
+
* (`keymap-presets.ts`): W/E/R under `vgai`, G/R/S under `blender`.
|
|
704
|
+
*/
|
|
705
|
+
export function registerEditorViewportHotkeys(
|
|
706
|
+
store: EditorShellStore,
|
|
707
|
+
viewport: EditorViewport,
|
|
708
|
+
canvas: HTMLCanvasElement,
|
|
709
|
+
): () => void {
|
|
710
|
+
const unregister = registerKeymapBindings(() => [
|
|
711
|
+
// --- Viewport-scoped (only when viewport/canvas is active) ---
|
|
712
|
+
...bind('transform.select', {
|
|
713
|
+
scope: 'viewport',
|
|
714
|
+
action: () => {
|
|
715
|
+
if (!viewport.isFlying) requestTransformMode(store, 'select');
|
|
716
|
+
},
|
|
717
|
+
}),
|
|
718
|
+
...bind('transform.combined', {
|
|
719
|
+
scope: 'viewport',
|
|
720
|
+
action: () => {
|
|
721
|
+
if (!viewport.isFlying) requestTransformMode(store, 'combined');
|
|
722
|
+
},
|
|
723
|
+
}),
|
|
724
|
+
...bind('transform.translate', {
|
|
725
|
+
scope: 'viewport',
|
|
726
|
+
action: () => {
|
|
727
|
+
if (!viewport.isFlying) requestTransformMode(store, 'translate');
|
|
728
|
+
},
|
|
729
|
+
}),
|
|
730
|
+
...bind('transform.rotate', {
|
|
731
|
+
scope: 'viewport',
|
|
732
|
+
action: () => {
|
|
733
|
+
if (!viewport.isFlying) requestTransformMode(store, 'rotate');
|
|
734
|
+
},
|
|
735
|
+
}),
|
|
736
|
+
...bind('transform.scale', {
|
|
737
|
+
scope: 'viewport',
|
|
738
|
+
action: () => {
|
|
739
|
+
if (!viewport.isFlying) requestTransformMode(store, 'scale');
|
|
740
|
+
},
|
|
741
|
+
}),
|
|
742
|
+
// Snap has a key and says what it did: a human build session asked "how
|
|
743
|
+
// do you toggle the snap" with the magnet button on screen the whole time.
|
|
744
|
+
// The hint names the ACTIVE keymap's key, never a remembered literal.
|
|
745
|
+
// Holding Ctrl/⌘ during a drag snaps temporarily (editor-viewport.ts).
|
|
746
|
+
...bind('viewport.toggleSnap', {
|
|
747
|
+
scope: 'viewport',
|
|
748
|
+
action: () => {
|
|
749
|
+
store.toggleSnap();
|
|
750
|
+
const { translate, rotate, scale } = store.snapValues;
|
|
751
|
+
const toggles = `${shortcutFor('viewport.toggleSnap') ?? ''} toggles; hold Ctrl while dragging to snap once`;
|
|
752
|
+
showTransientHint(
|
|
753
|
+
store.snapEnabled
|
|
754
|
+
? `Snap on — ${translate} units, ${rotate}°, ×${scale} (${toggles})`
|
|
755
|
+
: `Snap off (${toggles})`,
|
|
756
|
+
);
|
|
757
|
+
},
|
|
758
|
+
}),
|
|
759
|
+
...bind('viewport.frameSelection', {
|
|
760
|
+
scope: 'viewport',
|
|
761
|
+
action: () => store.focusOnSelection(),
|
|
762
|
+
}),
|
|
763
|
+
...bind('viewport.cyclePivot', {
|
|
764
|
+
scope: 'viewport',
|
|
765
|
+
action: () => {
|
|
766
|
+
const pivotModes = ['active-element', 'median-point', 'individual-origins'] as const;
|
|
767
|
+
const idx = pivotModes.indexOf(store.pivotMode);
|
|
768
|
+
store.setPivotMode(pivotModes[(idx + 1) % pivotModes.length]!);
|
|
769
|
+
},
|
|
770
|
+
}),
|
|
771
|
+
...bind('view.top', { scope: 'viewport', action: () => viewport.setViewPreset('top') }),
|
|
772
|
+
...bind('view.front', { scope: 'viewport', action: () => viewport.setViewPreset('front') }),
|
|
773
|
+
...bind('view.right', { scope: 'viewport', action: () => viewport.setViewPreset('right') }),
|
|
774
|
+
...bind('view.perspective', {
|
|
775
|
+
scope: 'viewport',
|
|
776
|
+
action: () => viewport.setViewPreset('perspective'),
|
|
777
|
+
}),
|
|
778
|
+
...bind('viewport.snapToFloor', {
|
|
779
|
+
scope: 'viewport',
|
|
780
|
+
action: () => viewport.snapSelectionToFloor(),
|
|
781
|
+
}),
|
|
782
|
+
]);
|
|
783
|
+
|
|
784
|
+
const onPointerDown = () => setActiveScope('viewport');
|
|
785
|
+
canvas.addEventListener('pointerdown', onPointerDown);
|
|
786
|
+
installHotkeys();
|
|
787
|
+
|
|
788
|
+
// Vertex snap: HOLD to activate. Not a `registerHotkeys` row because that
|
|
789
|
+
// dispatcher is single-fire-per-keydown with no held concept, so this pair
|
|
790
|
+
// reads the same keymap entry directly rather than a literal key.
|
|
791
|
+
const isVertexSnapKey = (e: KeyboardEvent): boolean =>
|
|
792
|
+
keyChordsFor('viewport.vertexSnapHold').some((chord) => chord.key === e.key.toLowerCase());
|
|
793
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
794
|
+
if (isVertexSnapKey(e) && !e.metaKey && !e.ctrlKey && !e.repeat) {
|
|
795
|
+
// THE STAGE'S OWN SCOPE, which is what keeps this from being a second
|
|
796
|
+
// keyboard owner: it fires only while the viewport holds the editor's
|
|
797
|
+
// scope, the same fact the frame publishes as `vgai.stage.focused`.
|
|
798
|
+
if (getActiveScope() !== 'viewport') return;
|
|
799
|
+
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
|
|
800
|
+
store.setVertexSnapActive(true);
|
|
801
|
+
viewport.activateVertexSnap();
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
const onKeyUp = (e: KeyboardEvent) => {
|
|
805
|
+
if (isVertexSnapKey(e) && !e.metaKey && !e.ctrlKey) {
|
|
806
|
+
store.setVertexSnapActive(false);
|
|
807
|
+
viewport.deactivateVertexSnap();
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
// THE ONE KEY LISTENER THE EDITOR STILL INSTALLS, and it is not a second
|
|
811
|
+
// keyboard owner: a keybinding rule fires on keydown and has no HELD
|
|
812
|
+
// concept, so vertex-snap hold has no workbench counterpart to become. It
|
|
813
|
+
// consumes nothing — no `preventDefault`, no action table — reads only
|
|
814
|
+
// whether a chord is DOWN, and fires only while the viewport holds the
|
|
815
|
+
// editor's own scope. That is the stage-local gesture WORK.md U6 left open,
|
|
816
|
+
// living beside `editor-viewport.ts`'s fly keys and snap-hold, which are
|
|
817
|
+
// gated the same way.
|
|
818
|
+
window.addEventListener('keydown', onKeyDown);
|
|
819
|
+
window.addEventListener('keyup', onKeyUp);
|
|
820
|
+
|
|
821
|
+
return () => {
|
|
822
|
+
unregister();
|
|
823
|
+
canvas.removeEventListener('pointerdown', onPointerDown);
|
|
824
|
+
window.removeEventListener('keydown', onKeyDown);
|
|
825
|
+
window.removeEventListener('keyup', onKeyUp);
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/** Test/legacy wrapper: shell bindings plus the Three viewport set. */
|
|
830
|
+
export function registerEditorHotkeys(
|
|
831
|
+
store: EditorShellStore,
|
|
832
|
+
viewport: EditorViewport,
|
|
833
|
+
canvas: HTMLCanvasElement,
|
|
834
|
+
history: HistoryCommands,
|
|
835
|
+
): () => void {
|
|
836
|
+
const unregisterShell = registerEditorShellHotkeys(store, history);
|
|
837
|
+
const unregisterViewport = registerEditorViewportHotkeys(store, viewport, canvas);
|
|
838
|
+
return () => {
|
|
839
|
+
unregisterShell();
|
|
840
|
+
unregisterViewport();
|
|
841
|
+
};
|
|
842
|
+
}
|