@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,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* H2, third half — honest gizmo refusal.
|
|
3
|
+
*
|
|
4
|
+
* WHY THE TRIGGER IS TOOL ACTIVATION AND NOT DRAG-START. The design record
|
|
5
|
+
* says "attempting to drag a refused node shows a transient hint". Read
|
|
6
|
+
* against the code, a literal drag-start trigger is unreachable: a fully
|
|
7
|
+
* refused selection never gets a gizmo in the first place —
|
|
8
|
+
* `EditorViewport.syncFromStore` computes `selectedTransformWritable` and
|
|
9
|
+
* calls `this.detach()` (editor-viewport.ts, the `attach`/`detach` branch), so
|
|
10
|
+
* `TransformControls` fires no `dragging-changed`: there is nothing attached
|
|
11
|
+
* to drag. THAT detach is the exact spot where the silence lives.
|
|
12
|
+
*
|
|
13
|
+
* The earliest point at which the user has made a real, unambiguous attempt to
|
|
14
|
+
* transform — as opposed to merely selecting something — is therefore ARMING
|
|
15
|
+
* the move/rotate/scale tool (W/E/R, the toolbar's mode buttons, the command
|
|
16
|
+
* palette's Translate/Rotate/Scale Mode actions) while that refused selection
|
|
17
|
+
* is live. That is the second trigger the brief explicitly allows, and
|
|
18
|
+
* it is what this module implements. Every UI mode-switch call site routes
|
|
19
|
+
* through {@link requestTransformMode} instead of `store.setTransformMode`.
|
|
20
|
+
* (The programmatic `command-listener` path deliberately does not: an
|
|
21
|
+
* automated `set-transform-mode` command is not a user attempt, and a headless
|
|
22
|
+
* caller has no viewport to read a hint in.)
|
|
23
|
+
*
|
|
24
|
+
* The hint text is the adapter's own `reason`, verbatim — the same sentence
|
|
25
|
+
* the hierarchy lock tooltip and the inspector field show (doctrine rule 2).
|
|
26
|
+
* When an adapter refuses without giving a reason we show NOTHING rather than
|
|
27
|
+
* invent a sentence to paraphrase it with.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { getActiveAuthoring } from './authoring/active-adapter';
|
|
31
|
+
import type { EditorShellStore, TransformMode } from './editor-shell-store';
|
|
32
|
+
import { transformLockSummary } from './hierarchy-row-model';
|
|
33
|
+
import { showTransientHint } from './transient-hint';
|
|
34
|
+
|
|
35
|
+
/** Set the transform mode, and NAME the refusal if the live selection is one
|
|
36
|
+
* the active adapter will not write. */
|
|
37
|
+
export function requestTransformMode(store: EditorShellStore, mode: TransformMode): void {
|
|
38
|
+
store.setTransformMode(mode);
|
|
39
|
+
// Arming SELECT is not an attempt to transform anything — it is the tool
|
|
40
|
+
// that transforms nothing (`TransformMode`'s own note) — so the refusal
|
|
41
|
+
// below, whose whole trigger is "a real, unambiguous attempt to transform",
|
|
42
|
+
// has nothing to report about it.
|
|
43
|
+
if (mode === 'select') return;
|
|
44
|
+
const ids = [...store.selectedEntityIds];
|
|
45
|
+
if (ids.length === 0) return;
|
|
46
|
+
const editability = getActiveAuthoring(store).transforms?.editability;
|
|
47
|
+
if (!editability) return;
|
|
48
|
+
let reason: string | undefined;
|
|
49
|
+
for (const id of ids) {
|
|
50
|
+
const summary = transformLockSummary((channel) => editability(id, channel));
|
|
51
|
+
// Anything movable in the selection means the gesture is not refused —
|
|
52
|
+
// the gizmo attaches and moves what it can.
|
|
53
|
+
if (!summary.locked) return;
|
|
54
|
+
reason ??= summary.reason;
|
|
55
|
+
}
|
|
56
|
+
if (reason) showTransientHint(reason);
|
|
57
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's one transient, non-blocking hint channel (H2).
|
|
3
|
+
*
|
|
4
|
+
* There was no toast/notification/status-message mechanism in the editor when
|
|
5
|
+
* this landed (grepped: `toast`, `notification`, `statusMessage`, `hint` —
|
|
6
|
+
* only unrelated hits), so this is the minimal one. Deliberately: one hint at
|
|
7
|
+
* a time (a new one replaces the old), auto-dismissing, carrying nothing but
|
|
8
|
+
* a string it did not write. Blocking dialogs (`window.alert`/`prompt`) are
|
|
9
|
+
* banned in editor UI.
|
|
10
|
+
*
|
|
11
|
+
* Publisher lives here (plain module state, no React) so non-React callers —
|
|
12
|
+
* hotkeys, the action registry — can raise a hint without importing the
|
|
13
|
+
* component. `components/TransientHint.tsx` renders it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const TRANSIENT_HINT_MS = 3000;
|
|
17
|
+
|
|
18
|
+
export interface TransientHint {
|
|
19
|
+
/** Monotonic — lets the renderer restart its animation on a repeat message. */
|
|
20
|
+
readonly id: number;
|
|
21
|
+
readonly message: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let current: TransientHint | null = null;
|
|
25
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
26
|
+
let nextId = 1;
|
|
27
|
+
const listeners = new Set<() => void>();
|
|
28
|
+
|
|
29
|
+
function emit(): void {
|
|
30
|
+
for (const listener of listeners) listener();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Show `message` for {@link TRANSIENT_HINT_MS}, replacing any live hint. */
|
|
34
|
+
export function showTransientHint(message: string): void {
|
|
35
|
+
current = { id: nextId++, message };
|
|
36
|
+
if (timer) clearTimeout(timer);
|
|
37
|
+
timer = setTimeout(() => {
|
|
38
|
+
current = null;
|
|
39
|
+
timer = null;
|
|
40
|
+
emit();
|
|
41
|
+
}, TRANSIENT_HINT_MS);
|
|
42
|
+
emit();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function dismissTransientHint(): void {
|
|
46
|
+
if (timer) clearTimeout(timer);
|
|
47
|
+
timer = null;
|
|
48
|
+
current = null;
|
|
49
|
+
emit();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const subscribeTransientHint = (listener: () => void): (() => void) => {
|
|
53
|
+
listeners.add(listener);
|
|
54
|
+
return () => {
|
|
55
|
+
listeners.delete(listener);
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const transientHint = (): TransientHint | null => current;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A drag that ended with NOTHING accepting it says so.
|
|
63
|
+
*
|
|
64
|
+
* `dropEffect === 'none'` at `dragend` is the browser's own word that no drop
|
|
65
|
+
* target took the payload: the card was released over editor chrome (a tab
|
|
66
|
+
* strip, the toolbar, a console row) instead of the viewport or a hierarchy
|
|
67
|
+
* row. That used to be perfectly silent — no hint, no console line, nothing
|
|
68
|
+
* changed — and a verifier lost a whole round to it before checking
|
|
69
|
+
* `elementFromPoint` (2026-09-02). A human just sees "the drop did nothing".
|
|
70
|
+
*/
|
|
71
|
+
export function reportUnacceptedAssetDrop(event: { dataTransfer: DataTransfer | null }): void {
|
|
72
|
+
if (event.dataTransfer?.dropEffect !== 'none') return;
|
|
73
|
+
// Surface-neutral on purpose: the source cannot know whether the scene is
|
|
74
|
+
// three or canvas, and a 2D tester told 'the 3D view' would be misled.
|
|
75
|
+
showTransientHint(
|
|
76
|
+
'Nothing accepted that drop — release it over the scene view or a hierarchy row.',
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { EDITOR_LAYER } from '@volter/editor-threejs/viewport/editor-layers';
|
|
2
|
+
import { triggerVolumeOf } from '@volter/editor-threejs/adapter/trigger-volume';
|
|
3
|
+
import { setUserData } from '@volter/editor-threejs/ecs/user-data';
|
|
4
|
+
import * as THREE from 'three';
|
|
5
|
+
|
|
6
|
+
const RING = new THREE.Color(0x4fd1c5);
|
|
7
|
+
const RING_SELECTED = new THREE.Color(0x9ff0e8);
|
|
8
|
+
const SEGMENTS = 64;
|
|
9
|
+
/** How far the corner ticks rise off the ring, in world units. */
|
|
10
|
+
const TICK_HEIGHT = 0.6;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Editor-only visualization for a project-owned trigger volume — the group
|
|
14
|
+
* whose radius decides where a hazard fires or a quest arrives. The game's
|
|
15
|
+
* source says nothing about being authored; it just carries the radius in
|
|
16
|
+
* `userData`, and this helper draws it: a ground circle in the source's local
|
|
17
|
+
* XZ plane that extends upward at ±X/±Z, which is what such a volume tests.
|
|
18
|
+
*
|
|
19
|
+
* Same contract as `ReflectionProbeHelper`: it follows the live Object3D but
|
|
20
|
+
* owns every line and material it draws, sits on EDITOR_LAYER, and therefore
|
|
21
|
+
* never enters the game's tree or its runtime camera.
|
|
22
|
+
*/
|
|
23
|
+
export class TriggerVolumeHelper extends THREE.Group {
|
|
24
|
+
private signature = '';
|
|
25
|
+
private selected = false;
|
|
26
|
+
|
|
27
|
+
constructor(readonly source: THREE.Object3D) {
|
|
28
|
+
super();
|
|
29
|
+
this.name = 'Trigger Volume Helper';
|
|
30
|
+
this.matrixAutoUpdate = false;
|
|
31
|
+
this.renderOrder = 998;
|
|
32
|
+
setUserData(this, 'editorHelper', true);
|
|
33
|
+
setUserData(this, 'editorHelperType', 'trigger-volumes');
|
|
34
|
+
this.layers.set(EDITOR_LAYER);
|
|
35
|
+
this.refresh(true);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setSelected(selected: boolean): void {
|
|
39
|
+
if (this.selected === selected) return;
|
|
40
|
+
this.selected = selected;
|
|
41
|
+
this.refresh(true);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
update(): void {
|
|
45
|
+
this.refresh(false);
|
|
46
|
+
this.source.updateWorldMatrix(true, false);
|
|
47
|
+
this.matrix.copy(this.source.matrixWorld);
|
|
48
|
+
this.matrixWorldNeedsUpdate = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
dispose(): void {
|
|
52
|
+
this.clearVisuals();
|
|
53
|
+
this.removeFromParent();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private refresh(force: boolean): void {
|
|
57
|
+
const volume = triggerVolumeOf(this.source);
|
|
58
|
+
if (!volume) return;
|
|
59
|
+
const radius = volume.radius;
|
|
60
|
+
const next = String(radius);
|
|
61
|
+
if (!force && next === this.signature) return;
|
|
62
|
+
this.signature = next;
|
|
63
|
+
this.clearVisuals();
|
|
64
|
+
|
|
65
|
+
const color = this.selected ? RING_SELECTED : RING;
|
|
66
|
+
const opacity = this.selected ? 1 : 0.62;
|
|
67
|
+
|
|
68
|
+
const ring: number[] = [];
|
|
69
|
+
for (let i = 0; i < SEGMENTS; i += 1) {
|
|
70
|
+
const angle = (i / SEGMENTS) * Math.PI * 2;
|
|
71
|
+
ring.push(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);
|
|
72
|
+
}
|
|
73
|
+
const ringGeometry = new THREE.BufferGeometry();
|
|
74
|
+
ringGeometry.setAttribute('position', new THREE.Float32BufferAttribute(ring, 3));
|
|
75
|
+
const loop = new THREE.LineLoop(ringGeometry, this.lineMaterial(color, opacity));
|
|
76
|
+
loop.renderOrder = this.renderOrder;
|
|
77
|
+
this.add(loop);
|
|
78
|
+
|
|
79
|
+
const ticks: number[] = [];
|
|
80
|
+
for (const [x, z] of [
|
|
81
|
+
[radius, 0],
|
|
82
|
+
[-radius, 0],
|
|
83
|
+
[0, radius],
|
|
84
|
+
[0, -radius],
|
|
85
|
+
] as const) {
|
|
86
|
+
ticks.push(x, 0, z, x, TICK_HEIGHT, z);
|
|
87
|
+
}
|
|
88
|
+
const tickGeometry = new THREE.BufferGeometry();
|
|
89
|
+
tickGeometry.setAttribute('position', new THREE.Float32BufferAttribute(ticks, 3));
|
|
90
|
+
const tickLines = new THREE.LineSegments(tickGeometry, this.lineMaterial(color, opacity));
|
|
91
|
+
tickLines.renderOrder = this.renderOrder;
|
|
92
|
+
this.add(tickLines);
|
|
93
|
+
|
|
94
|
+
this.traverse((object) => object.layers.set(EDITOR_LAYER));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private lineMaterial(color: THREE.Color, opacity: number): THREE.LineBasicMaterial {
|
|
98
|
+
return new THREE.LineBasicMaterial({
|
|
99
|
+
color,
|
|
100
|
+
depthTest: false,
|
|
101
|
+
transparent: true,
|
|
102
|
+
opacity,
|
|
103
|
+
toneMapped: false,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private clearVisuals(): void {
|
|
108
|
+
for (const child of [...this.children]) {
|
|
109
|
+
child.removeFromParent();
|
|
110
|
+
const line = child as THREE.LineSegments;
|
|
111
|
+
line.geometry?.dispose();
|
|
112
|
+
const materials = Array.isArray(line.material) ? line.material : [line.material];
|
|
113
|
+
for (const material of materials) material?.dispose();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A SYNCHRONOUS, source-only read of what a project's `vgai.adapter.ts`
|
|
5
|
+
* declares about which files belong to which SURFACE: each region's `include`
|
|
6
|
+
* globs, and the additional surfaces it `mounts` — stated either as parameters
|
|
7
|
+
* layered onto the `'manifest-roots'` rule (`regionIncludes`, what every
|
|
8
|
+
* first-party project uses) or inside an explicit `regions` list (the
|
|
9
|
+
* full-replacement form). Pure — the dev server adds
|
|
10
|
+
* the filesystem and a cache (`server/adapter-region-includes.ts`); the
|
|
11
|
+
* browser-hosted Content index reads the same file through its storage backend.
|
|
12
|
+
*
|
|
13
|
+
* WHY A STATIC READ AND NOT AN IMPORT. The adapter module's contract
|
|
14
|
+
* (`@volter/editor-project/adapter/adapter-module`, ARCHITECTURE-CORE §The editor protocol) is
|
|
15
|
+
* that its TOP LEVEL is "a STATICALLY EVALUABLE BINDING TABLE, readable without
|
|
16
|
+
* booting the game" — plain data, closures only inside `observation[].answer`.
|
|
17
|
+
* This module takes the contract at its word for the one consumer that cannot
|
|
18
|
+
* take any other route: the OID stamping transform runs in a Vite `transform`
|
|
19
|
+
* hook, synchronously, per project `.tsx`, long before any browser-tier adapter
|
|
20
|
+
* load has happened (and in `vite build`, where none ever will). The same
|
|
21
|
+
* placement test ARCHITECTURE-CORE applies to manifest-vs-adapter fields —
|
|
22
|
+
* "readable WITHOUT EVALUATING ANY MODULE … e.g. Vite config time" — is what
|
|
23
|
+
* makes a static read of a statically-evaluable table the right instrument here
|
|
24
|
+
* rather than a second declaration home.
|
|
25
|
+
*
|
|
26
|
+
* It reads those properties and reads them literally. Anything it cannot read
|
|
27
|
+
* literally is reported as {@link AdapterRegionIncludes.unreadable} — never
|
|
28
|
+
* silently treated as "no includes", because a silently dropped declaration is
|
|
29
|
+
* indistinguishable from a declaration that worked, which is the whole failure
|
|
30
|
+
* class this rung exists to end. `packages/editor/src/project-adapter.ts` (the
|
|
31
|
+
* real loader, which IMPORTS the module) stays the validation authority: this
|
|
32
|
+
* read never accepts anything that loader would reject, it only declines to
|
|
33
|
+
* answer.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** One ADDITIONAL mounted surface a region declares — see
|
|
37
|
+
* `@volter/editor-project/adapter/adapter-module`'s `AdapterRegion.mounts`. */
|
|
38
|
+
export interface AdapterRegionMountDeclaration {
|
|
39
|
+
readonly surface: 'three' | 'canvas' | 'dom';
|
|
40
|
+
readonly include: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Region id -> the `include` globs it declares, and the additional surfaces it
|
|
44
|
+
* mounts. */
|
|
45
|
+
export interface AdapterRegionIncludes {
|
|
46
|
+
readonly byRegionId: ReadonlyMap<string, readonly string[]>;
|
|
47
|
+
/** Region id -> the surfaces it mounts BESIDE its manifest root's own
|
|
48
|
+
* adapter, each with the files that render on it. Read here for the same
|
|
49
|
+
* reason `include` is: it is the one place a root's second surface can be
|
|
50
|
+
* stated, and every OID/HMR/Content reader answers from the same table. */
|
|
51
|
+
readonly mountsByRegionId: ReadonlyMap<string, readonly AdapterRegionMountDeclaration[]>;
|
|
52
|
+
/**
|
|
53
|
+
* `true` when the file HAS a `regions` binding this static read could not
|
|
54
|
+
* evaluate (a spread, an imported constant, a computed value). The caller
|
|
55
|
+
* reports it; it never reads as "declares nothing".
|
|
56
|
+
*/
|
|
57
|
+
readonly unreadable: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const EMPTY_REGION_INCLUDES: AdapterRegionIncludes = {
|
|
61
|
+
byRegionId: new Map(),
|
|
62
|
+
mountsByRegionId: new Map(),
|
|
63
|
+
unreadable: false,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const UNREADABLE_REGION_INCLUDES: AdapterRegionIncludes = {
|
|
67
|
+
byRegionId: new Map(),
|
|
68
|
+
mountsByRegionId: new Map(),
|
|
69
|
+
unreadable: true,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** The game's adapter module lives beside `vgai.project.json`, by contract. */
|
|
73
|
+
export const ADAPTER_MODULE_FILENAME = 'vgai.adapter.ts';
|
|
74
|
+
|
|
75
|
+
function stringLiteralOf(node: ts.Node | undefined): string | undefined {
|
|
76
|
+
return node && ts.isStringLiteralLike(node) ? node.text : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function stringArrayOf(node: ts.Node | undefined): string[] | undefined {
|
|
80
|
+
if (!node || !ts.isArrayLiteralExpression(node)) return undefined;
|
|
81
|
+
const out: string[] = [];
|
|
82
|
+
for (const element of node.elements) {
|
|
83
|
+
const value = stringLiteralOf(element);
|
|
84
|
+
if (value === undefined) return undefined;
|
|
85
|
+
out.push(value);
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function propertyOf(object: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined {
|
|
91
|
+
for (const member of object.properties) {
|
|
92
|
+
if (!ts.isPropertyAssignment(member)) continue;
|
|
93
|
+
const key = ts.isIdentifier(member.name)
|
|
94
|
+
? member.name.text
|
|
95
|
+
: (stringLiteralOf(member.name) ?? null);
|
|
96
|
+
if (key === name) return member.initializer;
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* What the module's default export gives this reader.
|
|
103
|
+
*
|
|
104
|
+
* The three outcomes are deliberately distinct, because two of them used to
|
|
105
|
+
* collapse into one and that collapse WAS a silent hole: `export default def`
|
|
106
|
+
* (a hoisted `const def = defineAdapter({…})`) and `defineAdapter(config)`
|
|
107
|
+
* both evaluate fine in `project-adapter.ts` — which validates the exported
|
|
108
|
+
* VALUE — while this reader saw "not a readable literal" and answered "no
|
|
109
|
+
* includes, nothing to report". The loader would honor an `include` this tier
|
|
110
|
+
* had silently dropped, which is exactly the disagreement the module docblock
|
|
111
|
+
* promises cannot happen. Both are now `'unreadable'`.
|
|
112
|
+
*/
|
|
113
|
+
type DefinitionRead =
|
|
114
|
+
/** A statically readable argument object (possibly with no `regions`). */
|
|
115
|
+
| { readonly kind: 'literal'; readonly literal: ts.ObjectLiteralExpression }
|
|
116
|
+
/** A zero-argument call — `nativeAdapter()`. The legitimate empty. */
|
|
117
|
+
| { readonly kind: 'empty' }
|
|
118
|
+
/** No default export at all: nothing here worth flagging; the loader is the
|
|
119
|
+
* validation authority and an absent adapter is the native default. */
|
|
120
|
+
| { readonly kind: 'absent' }
|
|
121
|
+
/** A default export this reader cannot evaluate but the loader can. */
|
|
122
|
+
| { readonly kind: 'unreadable' };
|
|
123
|
+
|
|
124
|
+
function readDefinition(source: ts.SourceFile): DefinitionRead {
|
|
125
|
+
for (const statement of source.statements) {
|
|
126
|
+
if (!ts.isExportAssignment(statement) || statement.isExportEquals) continue;
|
|
127
|
+
const call = statement.expression;
|
|
128
|
+
if (!ts.isCallExpression(call)) return { kind: 'unreadable' };
|
|
129
|
+
const arg = call.arguments[0];
|
|
130
|
+
if (arg === undefined) return { kind: 'empty' };
|
|
131
|
+
return ts.isObjectLiteralExpression(arg)
|
|
132
|
+
? { kind: 'literal', literal: arg }
|
|
133
|
+
: { kind: 'unreadable' };
|
|
134
|
+
}
|
|
135
|
+
return { kind: 'absent' };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Read `regions[].include` out of one adapter source. Pure — the fs half is
|
|
139
|
+
* `server/adapter-region-includes.ts`, so this is directly testable, and
|
|
140
|
+
* `adapter-region-includes-conformance.test.ts` pins it against what
|
|
141
|
+
* `parseAdapterDefinition` yields from EVALUATING the same source. */
|
|
142
|
+
export function parseAdapterRegionIncludes(code: string): AdapterRegionIncludes {
|
|
143
|
+
const source = ts.createSourceFile('vgai.adapter.ts', code, ts.ScriptTarget.Latest, true);
|
|
144
|
+
const definition = readDefinition(source);
|
|
145
|
+
if (definition.kind === 'unreadable') return UNREADABLE_REGION_INCLUDES;
|
|
146
|
+
if (definition.kind !== 'literal') return EMPTY_REGION_INCLUDES;
|
|
147
|
+
const regions = propertyOf(definition.literal, 'regions');
|
|
148
|
+
const overlay = propertyOf(definition.literal, 'regionIncludes');
|
|
149
|
+
const listed = regions !== undefined && !ts.isStringLiteralLike(regions);
|
|
150
|
+
// The two forms may not be combined — `defineAdapter` rejects that pairing by
|
|
151
|
+
// name, so the module never loads. This tier declines to answer rather than
|
|
152
|
+
// reporting half of an invalid table.
|
|
153
|
+
if (listed && overlay !== undefined) return UNREADABLE_REGION_INCLUDES;
|
|
154
|
+
// Absent, or the `'manifest-roots'` RULE: the derivation runs, and the
|
|
155
|
+
// declared PARAMETERS beside it are what a native project states (decision,
|
|
156
|
+
// 2026-08-17). Neither present is a real answer — pure reach — not a gap.
|
|
157
|
+
if (!listed) return overlay ? layeredIncludes(overlay) : EMPTY_REGION_INCLUDES;
|
|
158
|
+
if (!ts.isArrayLiteralExpression(regions)) return UNREADABLE_REGION_INCLUDES;
|
|
159
|
+
|
|
160
|
+
const byRegionId = new Map<string, readonly string[]>();
|
|
161
|
+
const mountsByRegionId = new Map<string, readonly AdapterRegionMountDeclaration[]>();
|
|
162
|
+
for (const element of regions.elements) {
|
|
163
|
+
if (!ts.isObjectLiteralExpression(element)) return UNREADABLE_REGION_INCLUDES;
|
|
164
|
+
const id = stringLiteralOf(propertyOf(element, 'id'));
|
|
165
|
+
if (id === undefined) return UNREADABLE_REGION_INCLUDES;
|
|
166
|
+
const include = propertyOf(element, 'include');
|
|
167
|
+
if (include !== undefined) {
|
|
168
|
+
const globs = stringArrayOf(include);
|
|
169
|
+
if (globs === undefined) return UNREADABLE_REGION_INCLUDES;
|
|
170
|
+
byRegionId.set(id, globs);
|
|
171
|
+
}
|
|
172
|
+
const mounts = propertyOf(element, 'mounts');
|
|
173
|
+
if (mounts === undefined) continue;
|
|
174
|
+
const declared = mountArrayOf(mounts);
|
|
175
|
+
if (declared === undefined) return UNREADABLE_REGION_INCLUDES;
|
|
176
|
+
mountsByRegionId.set(id, declared);
|
|
177
|
+
}
|
|
178
|
+
return { byRegionId, mountsByRegionId, unreadable: false };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Read the LAYERED form: `regionIncludes`, the per-region parameters the
|
|
183
|
+
* `'manifest-roots'` rule takes (`@volter/editor-project/adapter/adapter-module`'s
|
|
184
|
+
* {@link AdapterDefinition.regionIncludes}, decision 2026-08-17).
|
|
185
|
+
*
|
|
186
|
+
* Statically readable BY CONSTRUCTION — it is an object literal of object
|
|
187
|
+
* literals of string arrays, sitting beside the `regions` string in the same
|
|
188
|
+
* argument object — which is exactly why the decision put the parameters here
|
|
189
|
+
* rather than behind a builder call. Anything that is NOT that literal shape
|
|
190
|
+
* (a spread, an imported table, a computed key) is `unreadable`, on the same
|
|
191
|
+
* terms as every other shape this reader declines: the loader would honor it,
|
|
192
|
+
* so silence about it would be the drift this module exists to prevent.
|
|
193
|
+
*/
|
|
194
|
+
function layeredIncludes(node: ts.Expression): AdapterRegionIncludes {
|
|
195
|
+
if (!ts.isObjectLiteralExpression(node)) return UNREADABLE_REGION_INCLUDES;
|
|
196
|
+
const byRegionId = new Map<string, readonly string[]>();
|
|
197
|
+
const mountsByRegionId = new Map<string, readonly AdapterRegionMountDeclaration[]>();
|
|
198
|
+
for (const member of node.properties) {
|
|
199
|
+
if (!ts.isPropertyAssignment(member)) return UNREADABLE_REGION_INCLUDES;
|
|
200
|
+
const id = ts.isIdentifier(member.name) ? member.name.text : stringLiteralOf(member.name);
|
|
201
|
+
const overlay = overlayOf(member.initializer);
|
|
202
|
+
if (id === undefined || overlay === undefined) return UNREADABLE_REGION_INCLUDES;
|
|
203
|
+
if (overlay.include) byRegionId.set(id, overlay.include);
|
|
204
|
+
if (overlay.mounts) mountsByRegionId.set(id, overlay.mounts);
|
|
205
|
+
}
|
|
206
|
+
return { byRegionId, mountsByRegionId, unreadable: false };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** One `regionIncludes` entry, read literally. `undefined` for anything this
|
|
210
|
+
* reader cannot evaluate — never a partial answer. */
|
|
211
|
+
function overlayOf(
|
|
212
|
+
node: ts.Expression,
|
|
213
|
+
): { include?: string[]; mounts?: AdapterRegionMountDeclaration[] } | undefined {
|
|
214
|
+
if (!ts.isObjectLiteralExpression(node)) return undefined;
|
|
215
|
+
const includeNode = propertyOf(node, 'include');
|
|
216
|
+
const include = includeNode === undefined ? undefined : stringArrayOf(includeNode);
|
|
217
|
+
if (includeNode !== undefined && include === undefined) return undefined;
|
|
218
|
+
const mountsNode = propertyOf(node, 'mounts');
|
|
219
|
+
const mounts = mountsNode === undefined ? undefined : mountArrayOf(mountsNode);
|
|
220
|
+
if (mountsNode !== undefined && mounts === undefined) return undefined;
|
|
221
|
+
return { ...(include ? { include } : {}), ...(mounts ? { mounts } : {}) };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Read a region's `mounts` array literally. `undefined` — not an empty list —
|
|
225
|
+
* for anything this reader cannot evaluate, so the caller reports it rather
|
|
226
|
+
* than silently dropping a surface the real loader will honor. */
|
|
227
|
+
function mountArrayOf(node: ts.Expression): AdapterRegionMountDeclaration[] | undefined {
|
|
228
|
+
if (!ts.isArrayLiteralExpression(node)) return undefined;
|
|
229
|
+
const out: AdapterRegionMountDeclaration[] = [];
|
|
230
|
+
for (const element of node.elements) {
|
|
231
|
+
if (!ts.isObjectLiteralExpression(element)) return undefined;
|
|
232
|
+
const surface = stringLiteralOf(propertyOf(element, 'surface'));
|
|
233
|
+
if (surface !== 'three' && surface !== 'canvas' && surface !== 'dom') return undefined;
|
|
234
|
+
const include = propertyOf(element, 'include');
|
|
235
|
+
if (include === undefined) return undefined;
|
|
236
|
+
const globs = stringArrayOf(include);
|
|
237
|
+
if (globs === undefined || globs.length === 0) return undefined;
|
|
238
|
+
out.push({ surface, include: globs });
|
|
239
|
+
}
|
|
240
|
+
return out;
|
|
241
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ensureNamedImport` — make a named import available in a module, idempotently.
|
|
3
|
+
*
|
|
4
|
+
* Why this is not in `writer.ts`: that file is deliberately parser-free ("pure
|
|
5
|
+
* string ops", see its header), and an import list is the one region of a source
|
|
6
|
+
* file where a scan-based guess is genuinely unsafe — a string, a comment, or a
|
|
7
|
+
* type-only clause all look like the thing being matched. This is a real parse.
|
|
8
|
+
*
|
|
9
|
+
* The one caller is the `create`/`create-sibling` struct op
|
|
10
|
+
* (`plan-source-edit.ts`): an inserted element whose tag is a COMPONENT is broken
|
|
11
|
+
* source until its import exists, so the import rides the same plan (and therefore
|
|
12
|
+
* the same single history transaction) as the insert itself.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as ts from 'typescript';
|
|
16
|
+
import { parseAuthoringTsx } from './ts-ast';
|
|
17
|
+
import type { StructEditResult } from './writer';
|
|
18
|
+
|
|
19
|
+
/** Insert `name` into the module's named imports from `moduleSpecifier`, adding
|
|
20
|
+
* the whole import statement when the module is not imported yet. Already
|
|
21
|
+
* present ⇒ `changed: false` with the code untouched (the idempotent case every
|
|
22
|
+
* drop after the first takes). */
|
|
23
|
+
export function ensureNamedImport(
|
|
24
|
+
code: string,
|
|
25
|
+
name: string,
|
|
26
|
+
moduleSpecifier: string,
|
|
27
|
+
): StructEditResult {
|
|
28
|
+
const sf = parseAuthoringTsx('module.tsx', code);
|
|
29
|
+
const imports = sf.statements.filter(ts.isImportDeclaration);
|
|
30
|
+
|
|
31
|
+
for (const declaration of imports) {
|
|
32
|
+
if (!ts.isStringLiteral(declaration.moduleSpecifier)) continue;
|
|
33
|
+
if (declaration.moduleSpecifier.text !== moduleSpecifier) continue;
|
|
34
|
+
// A type-only import of the same module cannot host a VALUE import; leave it
|
|
35
|
+
// alone and let the new-statement path below add a separate one.
|
|
36
|
+
if (declaration.importClause?.isTypeOnly) continue;
|
|
37
|
+
const bindings = declaration.importClause?.namedBindings;
|
|
38
|
+
if (!bindings || !ts.isNamedImports(bindings)) continue;
|
|
39
|
+
if (bindings.elements.some((element) => element.name.text === name)) {
|
|
40
|
+
return { code, changed: false };
|
|
41
|
+
}
|
|
42
|
+
// Keep the clause alphabetical — the order every formatter in this repo's
|
|
43
|
+
// toolchain writes, so an inserted name does not read as a stray.
|
|
44
|
+
const after = bindings.elements.find((element) => element.name.text > name);
|
|
45
|
+
if (after) {
|
|
46
|
+
const at = after.getStart(sf);
|
|
47
|
+
return { code: `${code.slice(0, at)}${name}, ${code.slice(at)}`, changed: true };
|
|
48
|
+
}
|
|
49
|
+
const last = bindings.elements[bindings.elements.length - 1];
|
|
50
|
+
const at = last ? last.getEnd() : bindings.getEnd() - 1;
|
|
51
|
+
const separator = last ? ', ' : '';
|
|
52
|
+
return { code: `${code.slice(0, at)}${separator}${name}${code.slice(at)}`, changed: true };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const statement = `import { ${name} } from ${quoteOf(sf, code)}${moduleSpecifier}${quoteOf(sf, code)}${semicolonOf(imports, code)}`;
|
|
56
|
+
const lastImport = imports[imports.length - 1];
|
|
57
|
+
if (!lastImport) return { code: `${statement}\n${code}`, changed: true };
|
|
58
|
+
const at = lastImport.getEnd();
|
|
59
|
+
return { code: `${code.slice(0, at)}\n${statement}${code.slice(at)}`, changed: true };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Ensure either ecosystem-native import spelling. Component discovery knows
|
|
63
|
+
* whether a project component is a default or named export; preserving that
|
|
64
|
+
* fact avoids inventing a registry or wrapper merely to place it. */
|
|
65
|
+
export function ensureValueImport(
|
|
66
|
+
code: string,
|
|
67
|
+
name: string,
|
|
68
|
+
moduleSpecifier: string,
|
|
69
|
+
kind: 'default' | 'named' = 'named',
|
|
70
|
+
): StructEditResult {
|
|
71
|
+
if (kind === 'named') return ensureNamedImport(code, name, moduleSpecifier);
|
|
72
|
+
const sf = parseAuthoringTsx('module.tsx', code);
|
|
73
|
+
const imports = sf.statements.filter(ts.isImportDeclaration);
|
|
74
|
+
for (const declaration of imports) {
|
|
75
|
+
if (!importsFrom(declaration, moduleSpecifier)) continue;
|
|
76
|
+
const defaultBinding = defaultBindingStatus(declaration.importClause, name);
|
|
77
|
+
if (defaultBinding === 'same') return { code, changed: false };
|
|
78
|
+
// A module may have only one default binding per import declaration. If
|
|
79
|
+
// this file already chose another local name, import the same ecosystem
|
|
80
|
+
// value through standard ESM's named-default spelling instead of emitting
|
|
81
|
+
// a second default clause.
|
|
82
|
+
if (defaultBinding === 'different') {
|
|
83
|
+
const statement = `import { default as ${name} } from ${quoteOf(sf, code)}${moduleSpecifier}${quoteOf(sf, code)}${semicolonOf(imports, code)}`;
|
|
84
|
+
const at = declaration.getEnd();
|
|
85
|
+
return { code: `${code.slice(0, at)}\n${statement}${code.slice(at)}`, changed: true };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const statement = `import ${name} from ${quoteOf(sf, code)}${moduleSpecifier}${quoteOf(sf, code)}${semicolonOf(imports, code)}`;
|
|
89
|
+
const lastImport = imports[imports.length - 1];
|
|
90
|
+
if (!lastImport) return { code: `${statement}\n${code}`, changed: true };
|
|
91
|
+
const at = lastImport.getEnd();
|
|
92
|
+
return { code: `${code.slice(0, at)}\n${statement}${code.slice(at)}`, changed: true };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function importsFrom(declaration: ts.ImportDeclaration, moduleSpecifier: string): boolean {
|
|
96
|
+
return (
|
|
97
|
+
ts.isStringLiteral(declaration.moduleSpecifier) &&
|
|
98
|
+
declaration.moduleSpecifier.text === moduleSpecifier
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function defaultBindingStatus(
|
|
103
|
+
clause: ts.ImportClause | undefined,
|
|
104
|
+
name: string,
|
|
105
|
+
): 'same' | 'different' | 'absent' {
|
|
106
|
+
if (!clause || clause.isTypeOnly) return 'absent';
|
|
107
|
+
if (clause.name?.text === name) return 'same';
|
|
108
|
+
const named = clause.namedBindings;
|
|
109
|
+
if (
|
|
110
|
+
named &&
|
|
111
|
+
ts.isNamedImports(named) &&
|
|
112
|
+
named.elements.some(
|
|
113
|
+
(element) => element.propertyName?.text === 'default' && element.name.text === name,
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
return 'same';
|
|
117
|
+
}
|
|
118
|
+
return clause.name ? 'different' : 'absent';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Match the file's own import spelling rather than imposing one. */
|
|
122
|
+
function quoteOf(sf: ts.SourceFile, code: string): string {
|
|
123
|
+
const first = sf.statements.find(ts.isImportDeclaration);
|
|
124
|
+
if (!first) return "'";
|
|
125
|
+
return code[first.moduleSpecifier.getStart(sf)] === '"' ? '"' : "'";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function semicolonOf(imports: readonly ts.ImportDeclaration[], code: string): string {
|
|
129
|
+
const last = imports[imports.length - 1];
|
|
130
|
+
if (!last) return ';';
|
|
131
|
+
return code.slice(0, last.getEnd()).trimEnd().endsWith(';') ? ';' : '';
|
|
132
|
+
}
|