@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,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSF STORY WRITES — the planner behind "save these args as a story",
|
|
3
|
+
* rename, and delete (the design ledger's CSF-writing order: stories are
|
|
4
|
+
* discovered, composed, framed and switched, and until this nothing WROTE
|
|
5
|
+
* one except Extract Component's emitted file).
|
|
6
|
+
*
|
|
7
|
+
* Pure text surgery over the module source, in the repo's writer idiom
|
|
8
|
+
* (`plan-extract-component.ts` is the emit-shape precedent): every refusal
|
|
9
|
+
* is a named sentence, and nothing here parses with a full AST — a CSF
|
|
10
|
+
* story export is a top-level `export const <Name>` with a braced
|
|
11
|
+
* initializer, and balanced-brace scanning over that shape is the whole
|
|
12
|
+
* grammar this file needs. Anything outside the shape refuses rather than
|
|
13
|
+
* guessing.
|
|
14
|
+
*
|
|
15
|
+
* IT STAYS IN THE HOST, and the reason is the same one unit 6 recorded for the
|
|
16
|
+
* harness and generation estates: its caller is the SERVER tier
|
|
17
|
+
* (`vite-plugin-ui-oid.ts`'s `POST /__ui-source/csf-story`), and the
|
|
18
|
+
* contribution points are page-side only — there is no server-side `.service`
|
|
19
|
+
* or route point for a package to register a handler into. It was moved out
|
|
20
|
+
* once and came straight back when the probe refused to boot: a grep over
|
|
21
|
+
* `src` and `server` had reported zero importers, and the one importer is a
|
|
22
|
+
* file at the package ROOT. (CLAUDE.md: never conclude "dead code" from a
|
|
23
|
+
* search that returned zero.)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** One planned edit: the whole next source, or the refusal. */
|
|
27
|
+
export type CsfStoryPlan =
|
|
28
|
+
| { ok: true; nextSource: string; summary: string }
|
|
29
|
+
| { ok: false; reason: string };
|
|
30
|
+
|
|
31
|
+
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
32
|
+
|
|
33
|
+
/** Find `export const <name>` at top level; returns [start, endAfterSemi]. */
|
|
34
|
+
function findStoryExport(source: string, name: string): [number, number] | null {
|
|
35
|
+
const re = new RegExp(`^export const ${name}\\b`, 'm');
|
|
36
|
+
const m = re.exec(source);
|
|
37
|
+
if (!m) return null;
|
|
38
|
+
const eq = source.indexOf('=', m.index);
|
|
39
|
+
if (eq < 0) return null;
|
|
40
|
+
// Balanced scan from the first brace after `=` (a story initializer is an
|
|
41
|
+
// object literal; a non-object initializer refuses at the caller).
|
|
42
|
+
const braceStart = source.indexOf('{', eq);
|
|
43
|
+
if (braceStart < 0) return null;
|
|
44
|
+
let depth = 0;
|
|
45
|
+
for (let i = braceStart; i < source.length; i++) {
|
|
46
|
+
const ch = source[i];
|
|
47
|
+
if (ch === '{') depth++;
|
|
48
|
+
else if (ch === '}') {
|
|
49
|
+
depth--;
|
|
50
|
+
if (depth === 0) {
|
|
51
|
+
let end = i + 1;
|
|
52
|
+
if (source[end] === ';') end++;
|
|
53
|
+
if (source[end] === '\n') end++;
|
|
54
|
+
return [m.index, end];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The file's story TYPE annotation, when it declares the common alias
|
|
62
|
+
* (`type Story = StoryObj<…>`); an un-annotated export is valid CSF, so
|
|
63
|
+
* absence simply drops the annotation. */
|
|
64
|
+
function storyTypeAlias(source: string): string | null {
|
|
65
|
+
const m = /^(?:export )?type (\w+) = StoryObj\b/m.exec(source);
|
|
66
|
+
return m?.[1] ?? null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Serialize args to a JS object literal. JSON-shaped values only — a
|
|
70
|
+
* function/symbol/undefined arg refuses BY KEY, because emitting source
|
|
71
|
+
* that silently drops an arg would author a different story than the one
|
|
72
|
+
* on screen. */
|
|
73
|
+
function serializeArgs(
|
|
74
|
+
args: Readonly<Record<string, unknown>>,
|
|
75
|
+
): CsfStoryPlan | { literal: string } {
|
|
76
|
+
const unserializable = Object.entries(args)
|
|
77
|
+
.filter(([, value]) => {
|
|
78
|
+
if (value === null || value === undefined) return value === undefined;
|
|
79
|
+
const t = typeof value;
|
|
80
|
+
if (t === 'function' || t === 'symbol' || t === 'bigint') return true;
|
|
81
|
+
try {
|
|
82
|
+
return JSON.stringify(value) === undefined;
|
|
83
|
+
} catch {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
.map(([key]) => key);
|
|
88
|
+
if (unserializable.length > 0) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
reason:
|
|
92
|
+
`args ${unserializable.join(', ')} cannot be written as literals (function/symbol/` +
|
|
93
|
+
'undefined values have no CSF source form) — set them in the story file by hand',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const body = Object.entries(args)
|
|
97
|
+
.map(([key, value]) => {
|
|
98
|
+
const keyText = IDENTIFIER.test(key) ? key : JSON.stringify(key);
|
|
99
|
+
return ` ${keyText}: ${JSON.stringify(value)},`;
|
|
100
|
+
})
|
|
101
|
+
.join('\n');
|
|
102
|
+
return { literal: body.length > 0 ? `{\n${body}\n }` : '{}' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Append `export const <name>(: Alias)? = { args: {…} };` to the module. */
|
|
106
|
+
export function planSaveStory(
|
|
107
|
+
source: string,
|
|
108
|
+
name: string,
|
|
109
|
+
args: Readonly<Record<string, unknown>>,
|
|
110
|
+
): CsfStoryPlan {
|
|
111
|
+
if (!IDENTIFIER.test(name)) {
|
|
112
|
+
return { ok: false, reason: `'${name}' is not a valid story export name (identifier)` };
|
|
113
|
+
}
|
|
114
|
+
if (findStoryExport(source, name) || new RegExp(`^export const ${name}\\b`, 'm').test(source)) {
|
|
115
|
+
return { ok: false, reason: `a story named '${name}' already exists in this file` };
|
|
116
|
+
}
|
|
117
|
+
const serialized = serializeArgs(args);
|
|
118
|
+
if ('ok' in serialized) return serialized;
|
|
119
|
+
const alias = storyTypeAlias(source);
|
|
120
|
+
const annotation = alias ? `: ${alias}` : '';
|
|
121
|
+
const argsPart = serialized.literal === '{}' ? '' : `\n args: ${serialized.literal},\n`;
|
|
122
|
+
const block = `\nexport const ${name}${annotation} = {${argsPart}};\n`;
|
|
123
|
+
const nextSource = source.endsWith('\n') ? source + block.slice(1) : source + block;
|
|
124
|
+
return { ok: true, nextSource, summary: `story '${name}' appended` };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Rename the export. Only the DECLARATION and `defaultStory: '<old>'`
|
|
129
|
+
* references move; any other in-file use of the identifier refuses by name
|
|
130
|
+
* (a broad textual rename would rewrite code this planner cannot see the
|
|
131
|
+
* meaning of).
|
|
132
|
+
*/
|
|
133
|
+
export function planRenameStory(source: string, oldName: string, newName: string): CsfStoryPlan {
|
|
134
|
+
if (!IDENTIFIER.test(newName)) {
|
|
135
|
+
return { ok: false, reason: `'${newName}' is not a valid story export name (identifier)` };
|
|
136
|
+
}
|
|
137
|
+
const span = findStoryExport(source, oldName);
|
|
138
|
+
if (!span) return { ok: false, reason: `no story export named '${oldName}' in this file` };
|
|
139
|
+
if (new RegExp(`^export const ${newName}\\b`, 'm').test(source)) {
|
|
140
|
+
return { ok: false, reason: `a story named '${newName}' already exists in this file` };
|
|
141
|
+
}
|
|
142
|
+
const outside = source.slice(0, span[0]) + source.slice(span[1]);
|
|
143
|
+
const otherUses = [...outside.matchAll(new RegExp(`\\b${oldName}\\b`, 'g'))].filter((m) => {
|
|
144
|
+
// string references to the story NAME (defaultStory: 'Old') are renamed
|
|
145
|
+
// too; bare identifier uses are the refusal.
|
|
146
|
+
const before = outside.slice(Math.max(0, (m.index ?? 0) - 1), m.index);
|
|
147
|
+
const after = outside.slice(
|
|
148
|
+
(m.index ?? 0) + oldName.length,
|
|
149
|
+
(m.index ?? 0) + oldName.length + 1,
|
|
150
|
+
);
|
|
151
|
+
const quoted = (before === "'" && after === "'") || (before === '"' && after === '"');
|
|
152
|
+
return !quoted;
|
|
153
|
+
});
|
|
154
|
+
if (otherUses.length > 0) {
|
|
155
|
+
return {
|
|
156
|
+
ok: false,
|
|
157
|
+
reason:
|
|
158
|
+
`'${oldName}' is referenced ${otherUses.length} more time(s) in this file beyond its ` +
|
|
159
|
+
'declaration — rename it in source, where those uses are visible',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
let nextSource =
|
|
163
|
+
source.slice(0, span[0]) +
|
|
164
|
+
source.slice(span[0], span[1]).replace(`export const ${oldName}`, `export const ${newName}`) +
|
|
165
|
+
source.slice(span[1]);
|
|
166
|
+
nextSource = nextSource
|
|
167
|
+
.replaceAll(`defaultStory: '${oldName}'`, `defaultStory: '${newName}'`)
|
|
168
|
+
.replaceAll(`defaultStory: "${oldName}"`, `defaultStory: "${newName}"`);
|
|
169
|
+
return { ok: true, nextSource, summary: `story '${oldName}' renamed to '${newName}'` };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Remove the export block. The LAST story refuses (an empty CSF file fails
|
|
173
|
+
* composition — delete the file instead), and a `defaultStory` reference
|
|
174
|
+
* refuses by name rather than leaving a dangling default. */
|
|
175
|
+
export function planDeleteStory(source: string, name: string): CsfStoryPlan {
|
|
176
|
+
const span = findStoryExport(source, name);
|
|
177
|
+
if (!span) return { ok: false, reason: `no story export named '${name}' in this file` };
|
|
178
|
+
const outside = source.slice(0, span[0]) + source.slice(span[1]);
|
|
179
|
+
if (!/^export const \w+/m.test(outside)) {
|
|
180
|
+
return {
|
|
181
|
+
ok: false,
|
|
182
|
+
reason:
|
|
183
|
+
`'${name}' is the last story in this file — an empty CSF file fails composition; ` +
|
|
184
|
+
'delete the file instead',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (outside.includes(`defaultStory: '${name}'`) || outside.includes(`defaultStory: "${name}"`)) {
|
|
188
|
+
return {
|
|
189
|
+
ok: false,
|
|
190
|
+
reason:
|
|
191
|
+
`'${name}' is this file's declared defaultStory — point vgai.defaultStory at another ` +
|
|
192
|
+
'story first',
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return { ok: true, nextSource: outside, summary: `story '${name}' deleted` };
|
|
196
|
+
}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `planComponentExtraction` — the PURE core of "Extract Component…": one
|
|
3
|
+
* already-read source string in, THREE exact artifacts out — a new component
|
|
4
|
+
* module, a portable CSF story beside it, and the callsite edit that replaces
|
|
5
|
+
* the extracted subtree with an instance. No I/O, no hashing, no transport —
|
|
6
|
+
* the same split `plan-fork-component.ts` uses, and for the same reason (the
|
|
7
|
+
* dev server reads with `node:fs`; this module stays dependency-free so it is
|
|
8
|
+
* probeable headlessly).
|
|
9
|
+
*
|
|
10
|
+
* WHAT AN EXTRACTION IS. The inverse of pasting inline: a subtree an author
|
|
11
|
+
* assembled in place becomes a reusable component under `src/prefabs/` — the
|
|
12
|
+
* scene/component/prefab taxonomy the template teaches — with a story so the
|
|
13
|
+
* component board can mount it in isolation, and the original site becomes an
|
|
14
|
+
* ordinary instance. There is deliberately NO parallel prefab format: the
|
|
15
|
+
* output is a plain component file plus plain portable CSF, both of which a
|
|
16
|
+
* developer could have written by hand (WORK.md's own constraint).
|
|
17
|
+
*
|
|
18
|
+
* IMPORTS TRAVEL; EVERYTHING ELSE REFUSES BY NAME. The subtree's free
|
|
19
|
+
* identifiers (`referencedIdentifiers`, the reparent guard's R1 machinery)
|
|
20
|
+
* are classified by where they bind:
|
|
21
|
+
*
|
|
22
|
+
* - an IMPORT binding travels — the new module gets its own import
|
|
23
|
+
* statement, with relative specifiers re-resolved from the new file's
|
|
24
|
+
* location;
|
|
25
|
+
* - a module-level declaration (a `const`/function in the world file)
|
|
26
|
+
* REFUSES, naming the declaration and its line — chasing and copying
|
|
27
|
+
* module locals is scope creep this planner deliberately does not have
|
|
28
|
+
* (the fork records the same boundary), and a silent copy would fork the
|
|
29
|
+
* value's identity;
|
|
30
|
+
* - a binding inside an enclosing function (a hook result, a mapped
|
|
31
|
+
* variable, a prop) REFUSES with the owning function's name — the same
|
|
32
|
+
* sentence shape R1 gives a reparent that would break lexical capture;
|
|
33
|
+
* - an unresolved name (a global — `Math`) is fine as-is.
|
|
34
|
+
*
|
|
35
|
+
* INSERTION SEMANTICS. When the extracted root is an INTRINSIC element
|
|
36
|
+
* (`<mesh>`, `<group>`), its `name`/`position`/`rotation`/`scale` attributes
|
|
37
|
+
* are LIFTED to the new callsite and the component's root spreads
|
|
38
|
+
* `{...props}` (typed `ThreeElements['<tag>']`, the template HeroBox's own
|
|
39
|
+
* shape) — placement belongs to the instance, so a second drop of the same
|
|
40
|
+
* prefab does not land inside the first. A component-tag root is carried
|
|
41
|
+
* verbatim with no lift and no props parameter: its prop surface is its own.
|
|
42
|
+
*
|
|
43
|
+
* REFUSALS ARE NAMED — every rejection returns a sentence the author can act
|
|
44
|
+
* on, and the planner refuses before anything is written.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import ts from 'typescript';
|
|
48
|
+
import { lineColToOffset } from './oid-transform';
|
|
49
|
+
import { relativeImportSpecifier } from './relative-import-specifier';
|
|
50
|
+
import {
|
|
51
|
+
bindingScopeOf,
|
|
52
|
+
type JsxElementNode,
|
|
53
|
+
jsxElementAt,
|
|
54
|
+
owningFunctionName,
|
|
55
|
+
referencedIdentifiers,
|
|
56
|
+
} from './reparent-guard';
|
|
57
|
+
import { parseAuthoringTsx } from './ts-ast';
|
|
58
|
+
|
|
59
|
+
const IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
60
|
+
const MAX_FALLBACK_SUFFIX = 99;
|
|
61
|
+
const LIFTED_ATTRS = ['name', 'position', 'rotation', 'scale'] as const;
|
|
62
|
+
|
|
63
|
+
export interface ExtractComponentRequest {
|
|
64
|
+
/** The file the subtree lives in, and its current bytes. */
|
|
65
|
+
readonly sourceFile: string;
|
|
66
|
+
readonly source: string;
|
|
67
|
+
/** The element's `OidEntry` position (1-based line, 0-based col). */
|
|
68
|
+
readonly line: number;
|
|
69
|
+
readonly col: number;
|
|
70
|
+
/** The preferred component name (the author's, or an instance `name`). */
|
|
71
|
+
readonly nameSeed?: string | undefined;
|
|
72
|
+
/** Project-relative directory the new files land in (`src/prefabs`). */
|
|
73
|
+
readonly prefabDir: string;
|
|
74
|
+
/** File NAMES already in that directory — collision refusals + name fallback. */
|
|
75
|
+
readonly siblingFiles?: readonly string[] | undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ExtractComponentPlan {
|
|
79
|
+
readonly newName: string;
|
|
80
|
+
/** `Lantern.tsx` / `Lantern.stories.tsx` — caller joins onto `prefabDir`. */
|
|
81
|
+
readonly componentFileName: string;
|
|
82
|
+
readonly storyFileName: string;
|
|
83
|
+
/** Project-relative paths, forward slashes. */
|
|
84
|
+
readonly componentFile: string;
|
|
85
|
+
readonly storyFile: string;
|
|
86
|
+
/** Complete bytes of the two new modules. */
|
|
87
|
+
readonly componentSource: string;
|
|
88
|
+
readonly storySource: string;
|
|
89
|
+
readonly callsiteFile: string;
|
|
90
|
+
readonly callsitePrevSource: string;
|
|
91
|
+
readonly callsiteNewSource: string;
|
|
92
|
+
/** The specifier the callsite's new import uses. */
|
|
93
|
+
readonly importSpecifier: string;
|
|
94
|
+
/** The extracted root's tag, for hints. */
|
|
95
|
+
readonly tag: string;
|
|
96
|
+
/** History label for the CALLSITE edit (the new files are outside history). */
|
|
97
|
+
readonly historyLabel: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type ExtractComponentPlanResult =
|
|
101
|
+
| { readonly ok: true; readonly plan: ExtractComponentPlan }
|
|
102
|
+
| { readonly ok: false; readonly reason: string };
|
|
103
|
+
|
|
104
|
+
function refuse(reason: string): ExtractComponentPlanResult {
|
|
105
|
+
return { ok: false, reason };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ------------------------------------------------------------------- paths
|
|
109
|
+
|
|
110
|
+
function normalizePath(path: string): string {
|
|
111
|
+
return path.replace(/\\/g, '/');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function directoryOf(path: string): string {
|
|
115
|
+
const clean = normalizePath(path);
|
|
116
|
+
const cut = clean.lastIndexOf('/');
|
|
117
|
+
return cut < 0 ? '' : clean.slice(0, cut);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Resolve `./x`/`../x` against a directory, collapsing the dot segments. */
|
|
121
|
+
function resolveRelative(fromDir: string, specifier: string): string {
|
|
122
|
+
const parts = [...fromDir.split('/').filter(Boolean)];
|
|
123
|
+
for (const segment of normalizePath(specifier).split('/')) {
|
|
124
|
+
if (segment === '' || segment === '.') continue;
|
|
125
|
+
if (segment === '..') parts.pop();
|
|
126
|
+
else parts.push(segment);
|
|
127
|
+
}
|
|
128
|
+
return parts.join('/');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ------------------------------------------------------------------- naming
|
|
132
|
+
|
|
133
|
+
function pascalCase(seed: string): string {
|
|
134
|
+
const words = seed.match(/[A-Za-z0-9]+/g) ?? [];
|
|
135
|
+
const joined = words.map((w) => w[0]!.toUpperCase() + w.slice(1)).join('');
|
|
136
|
+
return /^[0-9]/.test(joined) ? '' : joined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The component name: the seed PascalCased when usable, else `Extracted<Tag>`,
|
|
140
|
+
* suffixed past any sibling module already claiming the basename. */
|
|
141
|
+
function pickName(
|
|
142
|
+
seed: string | undefined,
|
|
143
|
+
tag: string,
|
|
144
|
+
siblingFiles: readonly string[],
|
|
145
|
+
): string | null {
|
|
146
|
+
const taken = new Set(
|
|
147
|
+
siblingFiles.map((f) => f.replace(/\.(stories\.)?(tsx|ts|jsx|js)$/, '').toLowerCase()),
|
|
148
|
+
);
|
|
149
|
+
const base =
|
|
150
|
+
(seed && IDENTIFIER_RE.test(pascalCase(seed)) && pascalCase(seed)) ||
|
|
151
|
+
(/^[A-Z]/.test(tag) && IDENTIFIER_RE.test(tag) && `${tag}Part`) ||
|
|
152
|
+
`Extracted${pascalCase(tag) || 'Node'}`;
|
|
153
|
+
if (!taken.has(base.toLowerCase())) return base;
|
|
154
|
+
for (let i = 2; i <= MAX_FALLBACK_SUFFIX; i++) {
|
|
155
|
+
if (!taken.has(`${base}${i}`.toLowerCase())) return `${base}${i}`;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ------------------------------------------------------------ import carrying
|
|
161
|
+
|
|
162
|
+
/** The import statement binding `name` in `sf`, rebuilt for a module living at
|
|
163
|
+
* `newFile` — or `null` when `name` is not import-bound. Only the bindings the
|
|
164
|
+
* subtree actually uses are carried, so an import of five names carries one. */
|
|
165
|
+
function carriedImportFor(
|
|
166
|
+
sf: ts.SourceFile,
|
|
167
|
+
name: string,
|
|
168
|
+
callsiteFile: string,
|
|
169
|
+
newFile: string,
|
|
170
|
+
): { specifier: string; piece: 'default' | 'namespace' | 'named' } | null {
|
|
171
|
+
for (const statement of sf.statements) {
|
|
172
|
+
if (!ts.isImportDeclaration(statement) || !statement.importClause) continue;
|
|
173
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
174
|
+
const clause = statement.importClause;
|
|
175
|
+
let piece: 'default' | 'namespace' | 'named' | null = null;
|
|
176
|
+
if (clause.name?.text === name) piece = 'default';
|
|
177
|
+
const bindings = clause.namedBindings;
|
|
178
|
+
if (bindings && ts.isNamespaceImport(bindings) && bindings.name.text === name) {
|
|
179
|
+
piece = 'namespace';
|
|
180
|
+
} else if (bindings && ts.isNamedImports(bindings)) {
|
|
181
|
+
if (bindings.elements.some((e) => e.name.text === name)) piece = 'named';
|
|
182
|
+
}
|
|
183
|
+
if (!piece) continue;
|
|
184
|
+
let specifier = statement.moduleSpecifier.text;
|
|
185
|
+
if (specifier.startsWith('.')) {
|
|
186
|
+
const absolute = resolveRelative(directoryOf(callsiteFile), specifier);
|
|
187
|
+
specifier = relativeImportSpecifier(newFile, absolute);
|
|
188
|
+
}
|
|
189
|
+
return { specifier, piece };
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// --------------------------------------------------------------- subtree text
|
|
195
|
+
|
|
196
|
+
function openingTagOf(node: JsxElementNode): ts.JsxOpeningElement | ts.JsxSelfClosingElement {
|
|
197
|
+
return ts.isJsxElement(node) ? node.openingElement : node;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Dedent by the root's own line indent, then re-indent for the return body. */
|
|
201
|
+
function reindent(text: string, originalIndent: string, newIndent: string): string {
|
|
202
|
+
return text
|
|
203
|
+
.split('\n')
|
|
204
|
+
.map((line, i) => {
|
|
205
|
+
if (i === 0) return `${newIndent}${line}`;
|
|
206
|
+
const stripped = line.startsWith(originalIndent) ? line.slice(originalIndent.length) : line;
|
|
207
|
+
return stripped.trim() === '' ? '' : `${newIndent}${stripped}`;
|
|
208
|
+
})
|
|
209
|
+
.join('\n');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// -------------------------------------------------------------------- plan
|
|
213
|
+
|
|
214
|
+
export function planComponentExtraction(
|
|
215
|
+
request: ExtractComponentRequest,
|
|
216
|
+
): ExtractComponentPlanResult {
|
|
217
|
+
const source = request.source;
|
|
218
|
+
const sf = parseAuthoringTsx(request.sourceFile, source);
|
|
219
|
+
const offset = lineColToOffset(source, request.line, request.col);
|
|
220
|
+
const element = jsxElementAt(sf, offset);
|
|
221
|
+
if (!element) {
|
|
222
|
+
return refuse(
|
|
223
|
+
`${request.sourceFile}:${request.line} no longer holds a JSX element — ` +
|
|
224
|
+
'the file changed since the hierarchy was indexed.',
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
const opening = openingTagOf(element);
|
|
228
|
+
const tag = opening.tagName.getText(sf);
|
|
229
|
+
|
|
230
|
+
// R1 over the whole subtree: classify every free identifier.
|
|
231
|
+
const carried = new Map<
|
|
232
|
+
string,
|
|
233
|
+
{ specifier: string; piece: 'default' | 'namespace' | 'named' }
|
|
234
|
+
>();
|
|
235
|
+
for (const [name, site] of referencedIdentifiers(element)) {
|
|
236
|
+
const scope = bindingScopeOf(name, element);
|
|
237
|
+
if (scope === null) continue; // a global — travels as-is
|
|
238
|
+
if (ts.isSourceFile(scope)) {
|
|
239
|
+
const asImport = carriedImportFor(
|
|
240
|
+
sf,
|
|
241
|
+
name,
|
|
242
|
+
normalizePath(request.sourceFile),
|
|
243
|
+
`${request.prefabDir}/x.tsx`,
|
|
244
|
+
);
|
|
245
|
+
if (asImport) {
|
|
246
|
+
carried.set(name, asImport);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const line = sf.getLineAndCharacterOfPosition(site.getStart(sf)).line + 1;
|
|
250
|
+
return refuse(
|
|
251
|
+
`the subtree references \`${name}\` (line ${line}), declared in this module — ` +
|
|
252
|
+
'extraction only carries imports; move the declaration to its own module first, ' +
|
|
253
|
+
'or inline the value.',
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return refuse(
|
|
257
|
+
`the subtree references \`${name}\`, bound inside ${owningFunctionName(element, sf)} — ` +
|
|
258
|
+
'a value local to the enclosing component cannot travel to a new file. ' +
|
|
259
|
+
'Inline it or lift it to a prop first.',
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Name + collision.
|
|
264
|
+
const siblings = request.siblingFiles ?? [];
|
|
265
|
+
const attrs = opening.attributes.properties;
|
|
266
|
+
const authoredName = attrs.find(
|
|
267
|
+
(p): p is ts.JsxAttribute =>
|
|
268
|
+
ts.isJsxAttribute(p) &&
|
|
269
|
+
ts.isIdentifier(p.name) &&
|
|
270
|
+
p.name.text === 'name' &&
|
|
271
|
+
!!p.initializer &&
|
|
272
|
+
ts.isStringLiteral(p.initializer),
|
|
273
|
+
);
|
|
274
|
+
const newName = pickName(
|
|
275
|
+
request.nameSeed ?? (authoredName?.initializer as ts.StringLiteral | undefined)?.text,
|
|
276
|
+
tag,
|
|
277
|
+
siblings,
|
|
278
|
+
);
|
|
279
|
+
if (!newName) return refuse('every candidate component name is taken in the prefab folder.');
|
|
280
|
+
|
|
281
|
+
const componentFileName = `${newName}.tsx`;
|
|
282
|
+
const storyFileName = `${newName}.stories.tsx`;
|
|
283
|
+
const componentFile = `${request.prefabDir}/${componentFileName}`;
|
|
284
|
+
const storyFile = `${request.prefabDir}/${storyFileName}`;
|
|
285
|
+
const collision = siblings.find(
|
|
286
|
+
(f) =>
|
|
287
|
+
f.toLowerCase() === componentFileName.toLowerCase() ||
|
|
288
|
+
f.toLowerCase() === storyFileName.toLowerCase(),
|
|
289
|
+
);
|
|
290
|
+
if (collision) {
|
|
291
|
+
return refuse(
|
|
292
|
+
`${request.prefabDir}/${collision} already exists — extraction will not overwrite.`,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Root-attribute lift (intrinsic roots only): placement belongs to the instance.
|
|
297
|
+
const intrinsicRoot = /^[a-z]/.test(tag);
|
|
298
|
+
const lifted: string[] = [];
|
|
299
|
+
let rootTagWithoutLifted: { start: number; end: number; text: string } | null = null;
|
|
300
|
+
if (intrinsicRoot) {
|
|
301
|
+
const removals: Array<{ start: number; end: number }> = [];
|
|
302
|
+
for (const property of attrs) {
|
|
303
|
+
if (!ts.isJsxAttribute(property) || !ts.isIdentifier(property.name)) continue;
|
|
304
|
+
if (!(LIFTED_ATTRS as readonly string[]).includes(property.name.text)) continue;
|
|
305
|
+
lifted.push(property.getText(sf));
|
|
306
|
+
removals.push({ start: property.getStart(sf), end: property.getEnd() });
|
|
307
|
+
}
|
|
308
|
+
const tagStart = opening.getStart(sf);
|
|
309
|
+
const tagEnd = opening.getEnd();
|
|
310
|
+
let text = '';
|
|
311
|
+
let cursor = tagStart;
|
|
312
|
+
for (const removal of removals.sort((a, b) => a.start - b.start)) {
|
|
313
|
+
// Take the span before the attr, trimming the whitespace that preceded it.
|
|
314
|
+
text += source.slice(cursor, removal.start).replace(/[ \t\n]+$/, ' ');
|
|
315
|
+
cursor = removal.end;
|
|
316
|
+
}
|
|
317
|
+
text += source.slice(cursor, tagEnd);
|
|
318
|
+
// Consecutive removals each left one space behind; collapse the run (only
|
|
319
|
+
// single-line runs — a multi-line attribute list keeps its own layout).
|
|
320
|
+
text = text.replace(/[ \t]{2,}/g, ' ').replace(/[ \t]+>/g, '>');
|
|
321
|
+
// Spread the instance's props last, so a callsite override wins.
|
|
322
|
+
const closer = ts.isJsxSelfClosingElement(opening) ? /\s*\/>$/ : />$/;
|
|
323
|
+
text = text.replace(
|
|
324
|
+
closer,
|
|
325
|
+
ts.isJsxSelfClosingElement(opening) ? ' {...props} />' : ' {...props}>',
|
|
326
|
+
);
|
|
327
|
+
rootTagWithoutLifted = { start: tagStart, end: tagEnd, text };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// The component module.
|
|
331
|
+
const elementStart = element.getStart(sf);
|
|
332
|
+
const elementEnd = element.getEnd();
|
|
333
|
+
const lineStart = source.lastIndexOf('\n', elementStart - 1) + 1;
|
|
334
|
+
const indent = /^[ \t]*/.exec(source.slice(lineStart, elementStart))?.[0] ?? '';
|
|
335
|
+
let subtree = source.slice(elementStart, elementEnd);
|
|
336
|
+
if (rootTagWithoutLifted) {
|
|
337
|
+
subtree = rootTagWithoutLifted.text + source.slice(rootTagWithoutLifted.end, elementEnd);
|
|
338
|
+
}
|
|
339
|
+
const body = reindent(subtree, indent, ' ');
|
|
340
|
+
|
|
341
|
+
const importLines = new Map<
|
|
342
|
+
string,
|
|
343
|
+
{ defaults: string[]; named: string[]; namespaces: string[] }
|
|
344
|
+
>();
|
|
345
|
+
for (const [name, entry] of carried) {
|
|
346
|
+
const bucket = importLines.get(entry.specifier) ?? { defaults: [], named: [], namespaces: [] };
|
|
347
|
+
if (entry.piece === 'default') bucket.defaults.push(name);
|
|
348
|
+
else if (entry.piece === 'namespace') bucket.namespaces.push(name);
|
|
349
|
+
else bucket.named.push(name);
|
|
350
|
+
importLines.set(entry.specifier, bucket);
|
|
351
|
+
}
|
|
352
|
+
const imports: string[] = [];
|
|
353
|
+
if (intrinsicRoot) {
|
|
354
|
+
imports.push(`import type { ThreeElements } from '@react-three/fiber';`);
|
|
355
|
+
}
|
|
356
|
+
for (const [specifier, bucket] of [...importLines.entries()].sort()) {
|
|
357
|
+
for (const namespace of bucket.namespaces) {
|
|
358
|
+
imports.push(`import * as ${namespace} from '${specifier}';`);
|
|
359
|
+
}
|
|
360
|
+
const pieces: string[] = [];
|
|
361
|
+
if (bucket.defaults.length > 0) pieces.push(bucket.defaults[0]!);
|
|
362
|
+
if (bucket.named.length > 0) pieces.push(`{ ${bucket.named.sort().join(', ')} }`);
|
|
363
|
+
if (pieces.length > 0) imports.push(`import ${pieces.join(', ')} from '${specifier}';`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const componentSourceFinal = `${imports.length > 0 ? `${imports.join('\n')}\n\n` : ''}export function ${newName}(${intrinsicRoot ? `props: ThreeElements['${tag}']` : ''}) {
|
|
367
|
+
return (
|
|
368
|
+
${body}
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
`;
|
|
372
|
+
|
|
373
|
+
// The story (the template HeroBox story, transcribed).
|
|
374
|
+
const title = newName.replace(/([a-z0-9])([A-Z])/g, '$1 $2');
|
|
375
|
+
const storySource = `import type { Meta, StoryObj } from '@storybook/react';
|
|
376
|
+
import { ${newName} } from './${newName}';
|
|
377
|
+
|
|
378
|
+
const meta = {
|
|
379
|
+
title: '${title}',
|
|
380
|
+
component: ${newName},
|
|
381
|
+
} satisfies Meta<typeof ${newName}>;
|
|
382
|
+
|
|
383
|
+
export default meta;
|
|
384
|
+
|
|
385
|
+
type Story = StoryObj<typeof meta>;
|
|
386
|
+
|
|
387
|
+
export const Default: Story = {
|
|
388
|
+
parameters: { vgai: { default: true } },
|
|
389
|
+
};
|
|
390
|
+
`;
|
|
391
|
+
|
|
392
|
+
// The callsite: the subtree becomes an instance, and the import arrives.
|
|
393
|
+
const importSpecifier = relativeImportSpecifier(normalizePath(request.sourceFile), componentFile);
|
|
394
|
+
const instanceAttrs = lifted.length > 0 ? ` ${lifted.join(' ')}` : '';
|
|
395
|
+
const instance = `<${newName}${instanceAttrs} />`;
|
|
396
|
+
let callsiteNewSource = source.slice(0, elementStart) + instance + source.slice(elementEnd);
|
|
397
|
+
const importStatement = `import { ${newName} } from '${importSpecifier}';\n`;
|
|
398
|
+
const lastImport = [...sf.statements].reverse().find(ts.isImportDeclaration);
|
|
399
|
+
const importAt = lastImport ? lastImport.getEnd() + 1 : 0;
|
|
400
|
+
callsiteNewSource =
|
|
401
|
+
callsiteNewSource.slice(0, importAt) + importStatement + callsiteNewSource.slice(importAt);
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
ok: true,
|
|
405
|
+
plan: {
|
|
406
|
+
newName,
|
|
407
|
+
componentFileName,
|
|
408
|
+
storyFileName,
|
|
409
|
+
componentFile,
|
|
410
|
+
storyFile,
|
|
411
|
+
componentSource: componentSourceFinal,
|
|
412
|
+
storySource,
|
|
413
|
+
callsiteFile: normalizePath(request.sourceFile),
|
|
414
|
+
callsitePrevSource: source,
|
|
415
|
+
callsiteNewSource,
|
|
416
|
+
importSpecifier,
|
|
417
|
+
tag,
|
|
418
|
+
historyLabel: `Extract ${newName}`,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|