@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,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The STORY lane of `vgai screenshot` — a `.stories.tsx`/`.stories.ts` file's
|
|
3
|
+
* CSF exports rendered in the live session's DOM and captured as one variant
|
|
4
|
+
* sheet (ARCHITECTURE-CORE §Agent surface, the look-verb decision).
|
|
5
|
+
*
|
|
6
|
+
* Nothing here is a second story runtime. The editor already discovers CSF
|
|
7
|
+
* files (`stories/story-discovery.ts`), composes them through Storybook's own
|
|
8
|
+
* portable-story API (`stories/compose-project-stories.ts`) and mounts one
|
|
9
|
+
* into an isolated `react-dom/client` root (`stories/StoryPreviewMount.ts`'s
|
|
10
|
+
* `mountIsolatedStory`) for the Stories panel. This lane DRIVES that machinery
|
|
11
|
+
* headlessly and photographs the result — the CSF module on disk stays the
|
|
12
|
+
* only story definition that exists, and a story looks the same through the
|
|
13
|
+
* CLI as it does in the panel because it is literally the same mount.
|
|
14
|
+
*
|
|
15
|
+
* The pixels come from the same leg the GAME lane composites with
|
|
16
|
+
* (`capturePlayComposite`): a story is DOM, so it rides the `foreignObject`
|
|
17
|
+
* half of that function exactly as a React HUD does. One capture path, so a
|
|
18
|
+
* story sheet and a game screenshot can never disagree about how DOM is
|
|
19
|
+
* rasterized.
|
|
20
|
+
*
|
|
21
|
+
* A CANVAS story is DOM (its own `<Application>` renders into a `<canvas>` the
|
|
22
|
+
* DOM leg composites), so it rides that leg — but it needed two things the leg
|
|
23
|
+
* did not do, and until they were added every `@pixi/react` story photographed
|
|
24
|
+
* BLANK: the capture has to wait for the Pixi renderer's first presented frame
|
|
25
|
+
* (`<Application>` commits its canvas before it has a renderer at all), and it
|
|
26
|
+
* has to read the pixels back through Pixi's own extraction (a WebGL canvas
|
|
27
|
+
* created without `preserveDrawingBuffer` — which a story's own `<Application>`
|
|
28
|
+
* is — has nothing left to `drawImage` by the time the compositor gets to it).
|
|
29
|
+
* Both live in `canvas-preview-frames.ts` with the measurement.
|
|
30
|
+
*
|
|
31
|
+
* EXCEPT a `three` story, which is not DOM at all: a prefab's CSF renders R3F
|
|
32
|
+
* JSX (`<group>`, `<mesh>`), and a DOM root turns those into inert custom
|
|
33
|
+
* elements — every 3D prefab's sheet came out pure white, silently (found
|
|
34
|
+
* live: a project whose 120+ prefab stories all photographed blank). So each
|
|
35
|
+
* export is offered to the off-screen R3F mount FIRST (`mountStoryObject3D`
|
|
36
|
+
* via `captureStoryComponentThumbnail` — the same machinery the Content tab's
|
|
37
|
+
* thumbnails and the 3D board already trust, packaged-runtime-aware), and
|
|
38
|
+
* only a story the R3F reconciler refuses ("is not part of the THREE
|
|
39
|
+
* namespace" — the qualification-rejection class that module documents) falls
|
|
40
|
+
* through to the DOM leg below. The known cost: a DOM story's `loaders` run
|
|
41
|
+
* once in the refused three attempt and again in the DOM mount — accepted,
|
|
42
|
+
* because the alternative (guessing a story's kind from its source) is a
|
|
43
|
+
* second story format, and loaders on DOM stories are rare.
|
|
44
|
+
*
|
|
45
|
+
* Isolation and cleanup: every export gets its OWN container element, mounted,
|
|
46
|
+
* captured, unmounted and removed before the next one — and the offscreen host
|
|
47
|
+
* is removed in a `finally`, so a story that throws mid-sheet leaves nothing
|
|
48
|
+
* behind in `document.body`. Per-export containers are not fussiness:
|
|
49
|
+
* `mountIsolatedStory` keys its react root by container in a `WeakMap` and
|
|
50
|
+
* DELETES the entry on unmount, so re-mounting into an already-unmounted
|
|
51
|
+
* container would call `createRoot` twice on the same node — react-dom's own
|
|
52
|
+
* documented warning case.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
import type { Application } from 'pixi.js';
|
|
56
|
+
import { reactStoryBoardLayout } from '../authoring/react-story-board';
|
|
57
|
+
import { drawBitmapLabel } from '../bitmap-label';
|
|
58
|
+
import { pixiCanvasFrame, withApplicationCollector } from '../canvas-preview-frames';
|
|
59
|
+
import { type CaptureOptions, capturePlayComposite } from '../composite-screenshot';
|
|
60
|
+
import { getCurrentProject } from '../project-manager';
|
|
61
|
+
import {
|
|
62
|
+
type ComposedProjectStory,
|
|
63
|
+
composeProjectStories,
|
|
64
|
+
ensureProjectAnnotations,
|
|
65
|
+
} from './compose-project-stories';
|
|
66
|
+
import { getProjectStoryRegions } from './project-story-regions';
|
|
67
|
+
import { mountIsolatedStory } from './StoryPreviewMount';
|
|
68
|
+
import { declaredStoryMedium } from './story-declared-medium';
|
|
69
|
+
import {
|
|
70
|
+
discoverProjectStories,
|
|
71
|
+
loadProjectPreviewAnnotations,
|
|
72
|
+
type StoryDiscoveryProject,
|
|
73
|
+
} from './story-discovery';
|
|
74
|
+
import { awaitPixiStoryFrame } from './story-pixi-preview';
|
|
75
|
+
import { storyBoardPresentation } from './story-presentation';
|
|
76
|
+
import { getProjectPreviewStories, whenProjectStoriesReady } from './story-registry';
|
|
77
|
+
import {
|
|
78
|
+
captureStoryComponentThumbnail,
|
|
79
|
+
isStoryThreeClassificationRefusal,
|
|
80
|
+
type StoryPreviewComponent,
|
|
81
|
+
supportsThreeStoryCapture,
|
|
82
|
+
} from './story-three-preview';
|
|
83
|
+
|
|
84
|
+
/** One CSF export, rendered and photographed. */
|
|
85
|
+
export interface StoryVariantImage {
|
|
86
|
+
/** The CSF export name — what `--story` narrows on, and the sheet label. */
|
|
87
|
+
name: string;
|
|
88
|
+
/** Storybook's human-facing story name; the CLI prints it, files use `name`. */
|
|
89
|
+
label: string;
|
|
90
|
+
base64: string;
|
|
91
|
+
mimeType: 'image/png';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** One story FILE's variant sheet — the unit this lane produces. */
|
|
95
|
+
export interface StoryVariantCapture {
|
|
96
|
+
/** Project-relative path of the CSF module that was photographed. */
|
|
97
|
+
modulePath: string;
|
|
98
|
+
/** Per-variant cell size in CSS pixels. */
|
|
99
|
+
width: number;
|
|
100
|
+
height: number;
|
|
101
|
+
variants: StoryVariantImage[];
|
|
102
|
+
contactSheet: { width: number; height: number; base64: string; mimeType: 'image/png' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface StoryCaptureOptions {
|
|
106
|
+
/** Project-relative CSF path, exactly as `/__editor/story-files` reports it. */
|
|
107
|
+
modulePath: string;
|
|
108
|
+
/** Narrow the sheet to ONE export (`--story <export>`). */
|
|
109
|
+
story?: string;
|
|
110
|
+
width: number;
|
|
111
|
+
height: number;
|
|
112
|
+
/**
|
|
113
|
+
* The rasterizer. Defaults to the game lane's own composite capture; a test
|
|
114
|
+
* injects a stub because jsdom has no canvas, which is also what lets the
|
|
115
|
+
* mount/cleanup contract be proven without a browser. The options are the
|
|
116
|
+
* composite's own — the Pixi lane below hands it a `canvasFrame` seam.
|
|
117
|
+
*/
|
|
118
|
+
capture?: (container: HTMLElement, options?: CaptureOptions) => Promise<{ base64: string }>;
|
|
119
|
+
/**
|
|
120
|
+
* The `three` leg (see the module doc comment). Defaults to
|
|
121
|
+
* `captureStoryComponentThumbnail`, gated on `supportsThreeStoryCapture()`
|
|
122
|
+
* (jsdom has no WebGL, so under tests the default leg is simply absent
|
|
123
|
+
* unless a stub is injected). Returns a `data:` URL; ANY rejection routes
|
|
124
|
+
* the story to the DOM leg.
|
|
125
|
+
*/
|
|
126
|
+
captureThree?: (
|
|
127
|
+
component: StoryPreviewComponent,
|
|
128
|
+
size: { width: number; height: number },
|
|
129
|
+
) => Promise<string>;
|
|
130
|
+
/** Free capture camera for THREE stories (`--azimuth/--elevation/
|
|
131
|
+
* --distance`), forwarded to the three leg's `captureObjectAssetPreview`.
|
|
132
|
+
* A selected story that lands on the DOM leg REFUSES it by name — a
|
|
133
|
+
* camera silently ignored is a wrong photograph presented as evidence. */
|
|
134
|
+
camera?: import('@volter/editor-sdk').AssetPreviewCameraChoice;
|
|
135
|
+
/** Clip pose for THREE stories (`--clip/--time`); DOM-leg stories refuse
|
|
136
|
+
* it by the same rule. */
|
|
137
|
+
pose?: import('@volter/editor-sdk').AssetPreviewPose;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Capture the UI Canvas as a composition of every declared DOM artboard.
|
|
141
|
+
* This uses the same portable-story mount and composite rasterizer as the
|
|
142
|
+
* live board, without opening or activating that document. */
|
|
143
|
+
export async function captureDomStoryBoardPreview(width: number, height: number): Promise<string> {
|
|
144
|
+
await whenProjectStoriesReady();
|
|
145
|
+
const regions = getProjectStoryRegions();
|
|
146
|
+
const stories = getProjectPreviewStories().filter(
|
|
147
|
+
(story) => declaredStoryMedium({ modulePath: story.modulePath, regions }).medium === 'dom',
|
|
148
|
+
);
|
|
149
|
+
const output = document.createElement('canvas');
|
|
150
|
+
output.width = width;
|
|
151
|
+
output.height = height;
|
|
152
|
+
const context = output.getContext('2d');
|
|
153
|
+
if (!context) throw new Error('UI Canvas preview could not create a 2D output context.');
|
|
154
|
+
if (stories.length === 0) return output.toDataURL('image/png');
|
|
155
|
+
const presentation = storyBoardPresentation(stories);
|
|
156
|
+
const resolution = getCurrentProject()?.config.resolution;
|
|
157
|
+
const fallback = { width: resolution?.width ?? 640, height: resolution?.height ?? 400 };
|
|
158
|
+
const images = new Map<string, HTMLImageElement>();
|
|
159
|
+
const sizes = new Map<string, { width: number; height: number }>();
|
|
160
|
+
for (const story of stories) {
|
|
161
|
+
const frame = presentation.resolve(story, fallback);
|
|
162
|
+
const [variant] = await renderStoryVariants(story.modulePath, [story], {
|
|
163
|
+
width: frame.width,
|
|
164
|
+
height: frame.height,
|
|
165
|
+
capture: capturePlayComposite,
|
|
166
|
+
cropToContent: frame.layout !== 'fullscreen',
|
|
167
|
+
layout: frame.layout,
|
|
168
|
+
transparent: true,
|
|
169
|
+
});
|
|
170
|
+
if (!variant) throw new Error(`No preview rendered for ${story.id}.`);
|
|
171
|
+
const image = new Image();
|
|
172
|
+
image.src = `data:${variant.mimeType};base64,${variant.base64}`;
|
|
173
|
+
await image.decode();
|
|
174
|
+
images.set(story.id, image);
|
|
175
|
+
sizes.set(story.id, { width: image.naturalWidth, height: image.naturalHeight });
|
|
176
|
+
}
|
|
177
|
+
const board = reactStoryBoardLayout(stories, presentation.index, (story) => sizes.get(story.id)!);
|
|
178
|
+
const scale = Math.min(width / board.width, height / board.height);
|
|
179
|
+
const left = (width - board.width * scale) / 2;
|
|
180
|
+
const top = (height - board.height * scale) / 2;
|
|
181
|
+
for (const [id, frame] of board.layouts) {
|
|
182
|
+
context.drawImage(
|
|
183
|
+
images.get(id)!,
|
|
184
|
+
left + frame.x * scale,
|
|
185
|
+
top + frame.y * scale,
|
|
186
|
+
frame.width * scale,
|
|
187
|
+
frame.height * scale,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return output.toDataURL('image/png');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* A neutral, OPAQUE backdrop behind each story.
|
|
195
|
+
*
|
|
196
|
+
* Storybook's own default backdrop is white and most CSF files that care paint
|
|
197
|
+
* their own (a decorator with `position:absolute; inset:0`), so this only shows
|
|
198
|
+
* through where a story declares nothing. Opaque rather than transparent for a
|
|
199
|
+
* mechanical reason as well: `capturePlayComposite` treats a canvas-less
|
|
200
|
+
* capture that comes back mostly transparent as a torn `foreignObject` frame
|
|
201
|
+
* and pays for two bounded retries before returning it.
|
|
202
|
+
*/
|
|
203
|
+
const STORY_BACKDROP = '#ffffff';
|
|
204
|
+
|
|
205
|
+
/** The sheet's gutter/letterbox colour — dark, so a white story cell reads as
|
|
206
|
+
* a cell rather than bleeding into the sheet. */
|
|
207
|
+
const SHEET_BACKDROP = '#14171c';
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Load one project CSF module and photograph each of its stories.
|
|
211
|
+
*
|
|
212
|
+
* Every refusal names the FILE, because the caller typed a path: a module the
|
|
213
|
+
* project's own story scan does not know, a module that fails to import, a
|
|
214
|
+
* module with no renderable stories, and a `--story` that names no export all
|
|
215
|
+
* fail loudly rather than producing a plausible empty sheet.
|
|
216
|
+
*/
|
|
217
|
+
export async function captureProjectStoryVariants(
|
|
218
|
+
project: StoryDiscoveryProject | null,
|
|
219
|
+
options: StoryCaptureOptions,
|
|
220
|
+
): Promise<StoryVariantCapture> {
|
|
221
|
+
const { modulePath, width, height } = options;
|
|
222
|
+
if (!project) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`No project is open, so '${modulePath}' cannot be loaded. Open the project first.`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const discovered = await discoverProjectStories(project);
|
|
229
|
+
const found = discovered.find((candidate) => candidate.modulePath === modulePath);
|
|
230
|
+
if (!found) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`'${modulePath}' is not a story file of this project. ` +
|
|
233
|
+
(discovered.length === 0
|
|
234
|
+
? 'This project has no *.stories.tsx / *.stories.ts under src/.'
|
|
235
|
+
: `This project's story files are: ${discovered.map((s) => s.modulePath).join(', ')}.`),
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// The Stories panel applies the project's `.storybook/preview` annotations
|
|
240
|
+
// through this same once-per-session latch before composing anything. Doing
|
|
241
|
+
// it here too means a capture taken before the panel has ever opened is
|
|
242
|
+
// composed identically to one taken after — not almost identically.
|
|
243
|
+
await ensureProjectAnnotations(await loadProjectPreviewAnnotations(project));
|
|
244
|
+
|
|
245
|
+
let mod: unknown;
|
|
246
|
+
try {
|
|
247
|
+
mod = await found.load();
|
|
248
|
+
} catch (err) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`'${modulePath}' failed to import — ${err instanceof Error ? err.message : String(err)}`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const composed = await composeProjectStories(modulePath, mod);
|
|
255
|
+
if (!composed.ok) throw new Error(composed.error);
|
|
256
|
+
|
|
257
|
+
const selected = selectStories(modulePath, composed.stories, options.story);
|
|
258
|
+
const capture = options.capture ?? capturePlayComposite;
|
|
259
|
+
const captureThree =
|
|
260
|
+
options.captureThree ??
|
|
261
|
+
(supportsThreeStoryCapture()
|
|
262
|
+
? (component: StoryPreviewComponent, size: { width: number; height: number }) =>
|
|
263
|
+
captureStoryComponentThumbnail(component, {
|
|
264
|
+
...size,
|
|
265
|
+
...(options.camera === undefined ? {} : { camera: options.camera }),
|
|
266
|
+
...(options.pose === undefined ? {} : { pose: options.pose }),
|
|
267
|
+
})
|
|
268
|
+
: undefined);
|
|
269
|
+
if ((options.camera || options.pose) && !captureThree) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`'${modulePath}': camera/clip parameters need the three capture leg, which this ` +
|
|
272
|
+
'environment cannot provide (no WebGL).',
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
const variants = await renderStoryVariants(modulePath, selected, {
|
|
276
|
+
width,
|
|
277
|
+
height,
|
|
278
|
+
capture,
|
|
279
|
+
...(captureThree ? { captureThree } : {}),
|
|
280
|
+
requireThree: Boolean(options.camera || options.pose),
|
|
281
|
+
});
|
|
282
|
+
const contactSheet = await composeVariantSheet(variants, width, height);
|
|
283
|
+
return { modulePath, width, height, variants, contactSheet };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** `--story <export>`, or every story in CSF export order. */
|
|
287
|
+
function selectStories(
|
|
288
|
+
modulePath: string,
|
|
289
|
+
stories: ComposedProjectStory[],
|
|
290
|
+
story: string | undefined,
|
|
291
|
+
): ComposedProjectStory[] {
|
|
292
|
+
if (story === undefined) return stories;
|
|
293
|
+
const match = stories.find((candidate) => candidate.name === story);
|
|
294
|
+
if (!match) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`'${modulePath}' exports no story named '${story}'. It exports: ` +
|
|
297
|
+
`${stories.map((candidate) => candidate.name).join(', ')}.`,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
return [match];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Mount each story offscreen, one at a time, and capture it.
|
|
305
|
+
*
|
|
306
|
+
* The host is `position: fixed` far off-screen with `contain: strict` — the
|
|
307
|
+
* containment is load-bearing, not decoration: layout containment makes the
|
|
308
|
+
* host the containing block for `position: fixed` descendants, so a HUD story
|
|
309
|
+
* that pins itself to the viewport lands inside the capture box instead of
|
|
310
|
+
* flashing across the editor the user is watching.
|
|
311
|
+
*/
|
|
312
|
+
export async function renderStoryVariants(
|
|
313
|
+
modulePath: string,
|
|
314
|
+
stories: readonly ComposedProjectStory[],
|
|
315
|
+
options: {
|
|
316
|
+
width: number;
|
|
317
|
+
height: number;
|
|
318
|
+
capture: (
|
|
319
|
+
container: HTMLElement,
|
|
320
|
+
captureOptions?: CaptureOptions,
|
|
321
|
+
) => Promise<{
|
|
322
|
+
base64: string;
|
|
323
|
+
}>;
|
|
324
|
+
captureThree?: (
|
|
325
|
+
component: StoryPreviewComponent,
|
|
326
|
+
size: { width: number; height: number },
|
|
327
|
+
) => Promise<string>;
|
|
328
|
+
/** Camera/clip parameters were requested: a story that cannot render on
|
|
329
|
+
* the three leg REFUSES by name instead of falling back to the DOM leg,
|
|
330
|
+
* which would silently ignore them. */
|
|
331
|
+
requireThree?: boolean;
|
|
332
|
+
/** Preserve the declared mount box for artboard/composition capture. */
|
|
333
|
+
cropToContent?: boolean;
|
|
334
|
+
layout?: string;
|
|
335
|
+
transparent?: boolean;
|
|
336
|
+
},
|
|
337
|
+
): Promise<StoryVariantImage[]> {
|
|
338
|
+
const { width, height, capture, captureThree, requireThree, cropToContent = true } = options;
|
|
339
|
+
const host = document.createElement('div');
|
|
340
|
+
host.setAttribute(
|
|
341
|
+
'style',
|
|
342
|
+
`position:fixed;left:-20000px;top:0;width:${width}px;height:${height}px;` +
|
|
343
|
+
// The neutral backdrop lives HERE, behind the per-story container it
|
|
344
|
+
// exactly covers — never inline on the container itself. That container
|
|
345
|
+
// is a game-CSS scope root (`mountIsolatedStory` marks it), and an
|
|
346
|
+
// inline background there outranks every rule in the game's own
|
|
347
|
+
// stylesheet: a dark-themed game would get its white HUD text painted
|
|
348
|
+
// onto the editor's white backdrop, correctly styled and unreadable.
|
|
349
|
+
// Behind the container the backdrop still shows through wherever the
|
|
350
|
+
// story paints nothing, which is all it was ever for.
|
|
351
|
+
`overflow:hidden;contain:strict;pointer-events:none;background:${options.transparent ? 'transparent' : STORY_BACKDROP};`,
|
|
352
|
+
);
|
|
353
|
+
document.body.appendChild(host);
|
|
354
|
+
const variants: StoryVariantImage[] = [];
|
|
355
|
+
try {
|
|
356
|
+
for (const story of stories) {
|
|
357
|
+
// The three leg first (see the module doc comment): a prefab story is
|
|
358
|
+
// R3F JSX that a DOM root can only render as a blank cell. ANY three
|
|
359
|
+
// failure — the reconciler's qualification rejection for a DOM story,
|
|
360
|
+
// or a genuine crash — falls through to the DOM leg, whose error
|
|
361
|
+
// boundary is the containment that names a broken story in its cell.
|
|
362
|
+
if (captureThree) {
|
|
363
|
+
const three = await captureThree(story.Component as StoryPreviewComponent, {
|
|
364
|
+
width,
|
|
365
|
+
height,
|
|
366
|
+
}).catch((error: unknown) => {
|
|
367
|
+
// The DOM-story classification refusal falls through silently by design; a THREE
|
|
368
|
+
// story that genuinely failed must be named here, or a broken prefab photographs
|
|
369
|
+
// as a blank cell with no trace (the whole-project-blank-sheet defect).
|
|
370
|
+
if (!isStoryThreeClassificationRefusal(error)) {
|
|
371
|
+
// biome-ignore lint/suspicious/noConsole: the loud half of the fall-through contract — this refusal used to be swallowed whole.
|
|
372
|
+
console.warn(
|
|
373
|
+
`[story-capture] three mount for '${story.name}' failed — falling back to the DOM leg:`,
|
|
374
|
+
error instanceof Error ? (error.stack ?? error.message) : String(error),
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
return null;
|
|
378
|
+
});
|
|
379
|
+
if (three === null && requireThree) {
|
|
380
|
+
throw new Error(
|
|
381
|
+
`story '${story.name}' did not render on the three leg — ` +
|
|
382
|
+
'--azimuth/--elevation/--distance/--clip/--time apply to three (R3F) stories ' +
|
|
383
|
+
'only, and falling back to the DOM leg would silently ignore them. Narrow to a ' +
|
|
384
|
+
'three story with --story <export>, or drop the camera/clip flags.',
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
if (three !== null) {
|
|
388
|
+
const comma = three.indexOf(',');
|
|
389
|
+
variants.push({
|
|
390
|
+
name: story.name,
|
|
391
|
+
label: story.label,
|
|
392
|
+
base64: comma >= 0 ? three.slice(comma + 1) : three,
|
|
393
|
+
mimeType: 'image/png',
|
|
394
|
+
});
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// A FRESH container per export — see the module doc comment for why
|
|
399
|
+
// reusing one would trip react-dom's double-`createRoot` warning.
|
|
400
|
+
const container = document.createElement('div');
|
|
401
|
+
container.setAttribute(
|
|
402
|
+
'style',
|
|
403
|
+
`position:relative;width:${width}px;height:${height}px;overflow:hidden;`,
|
|
404
|
+
);
|
|
405
|
+
if (options.layout === 'padded') container.style.padding = '16px';
|
|
406
|
+
if (options.layout === 'centered') {
|
|
407
|
+
container.style.display = 'flex';
|
|
408
|
+
container.style.alignItems = 'center';
|
|
409
|
+
container.style.justifyContent = 'center';
|
|
410
|
+
}
|
|
411
|
+
container.style.boxSizing = 'border-box';
|
|
412
|
+
host.replaceChildren(container);
|
|
413
|
+
// Every `Application` this story's mount creates is collected, so the two
|
|
414
|
+
// things a Pixi story's capture needs — a settled first frame, and a
|
|
415
|
+
// readback the compositor cannot clear — can both be keyed on the MOUNT
|
|
416
|
+
// rather than guessed from the story's source
|
|
417
|
+
// (`canvas-preview-frames.ts` owns both, with the measurement).
|
|
418
|
+
let storyApps: readonly Application[] = [];
|
|
419
|
+
const mounted = await withApplicationCollector(async (apps) => {
|
|
420
|
+
storyApps = apps;
|
|
421
|
+
const handle = await mountIsolatedStory(container, modulePath, story.name, story.Component);
|
|
422
|
+
await awaitStoryCommit(container);
|
|
423
|
+
await awaitPixiStoryFrame(apps);
|
|
424
|
+
return handle;
|
|
425
|
+
});
|
|
426
|
+
try {
|
|
427
|
+
// Mount in the truth, frame the subject: the host is the full mount
|
|
428
|
+
// box (the screen the story's anchors resolve against), the photograph
|
|
429
|
+
// crops to what painted. A widget crops to the widget; a full screen's
|
|
430
|
+
// painted union spans the frame and stays effectively full-frame.
|
|
431
|
+
const shot = await capture(host, {
|
|
432
|
+
canvasFrame: pixiCanvasFrame(storyApps),
|
|
433
|
+
cropToContent,
|
|
434
|
+
...(options.transparent ? { allowTransparent: true } : {}),
|
|
435
|
+
});
|
|
436
|
+
variants.push({
|
|
437
|
+
name: story.name,
|
|
438
|
+
label: story.label,
|
|
439
|
+
base64: shot.base64,
|
|
440
|
+
mimeType: 'image/png',
|
|
441
|
+
});
|
|
442
|
+
} finally {
|
|
443
|
+
mounted?.unmount();
|
|
444
|
+
// `unmount()` defers its real teardown to a microtask (React refuses a
|
|
445
|
+
// synchronous unmount from inside another root's commit); yield a full
|
|
446
|
+
// turn so it has landed before the container is detached.
|
|
447
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
448
|
+
container.remove();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
} finally {
|
|
452
|
+
host.remove();
|
|
453
|
+
}
|
|
454
|
+
return variants;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Wait for the isolated root's render to COMMIT before photographing it.
|
|
459
|
+
*
|
|
460
|
+
* `mountIsolatedStory` returns as soon as it has called `root.render()`, and
|
|
461
|
+
* React schedules that work rather than performing it inline — so capturing on
|
|
462
|
+
* the very next line photographs an empty container. That is not a
|
|
463
|
+
* hypothetical — it has been observed in practice,
|
|
464
|
+
* and it is the worst possible failure for a look verb, because a blank cell
|
|
465
|
+
* rasterizes into a perfectly plausible sheet.
|
|
466
|
+
*
|
|
467
|
+
* Polls for committed child nodes rather than sleeping a guessed duration, so
|
|
468
|
+
* the common case costs one turn. The bound is what a story that legitimately
|
|
469
|
+
* renders `null` falls through — an empty capture is then the truth, not a
|
|
470
|
+
* race.
|
|
471
|
+
*
|
|
472
|
+
* The FIRST child is not the last word: a story whose content arrives through
|
|
473
|
+
* `lazy()` + `<Suspense>` commits its wrapper immediately and its screen when
|
|
474
|
+
* the chunk lands — a translated port's scene UI is the shipped case, and the
|
|
475
|
+
* capture used to photograph the fallback. So after the first commit this
|
|
476
|
+
* waits for the subtree to go QUIET (no mutations for a settle window),
|
|
477
|
+
* bounded — a story that animates forever is photographed mid-animation at the
|
|
478
|
+
* ceiling rather than never.
|
|
479
|
+
*/
|
|
480
|
+
async function awaitStoryCommit(container: HTMLElement, maxTurns = 10): Promise<void> {
|
|
481
|
+
for (let turn = 0; turn < maxTurns; turn++) {
|
|
482
|
+
if (container.childNodes.length > 0) break;
|
|
483
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
484
|
+
}
|
|
485
|
+
if (container.childNodes.length === 0) return;
|
|
486
|
+
const QUIET_MS = 200;
|
|
487
|
+
const CEILING_MS = 5_000;
|
|
488
|
+
await new Promise<void>((resolve) => {
|
|
489
|
+
let settle: ReturnType<typeof setTimeout> | null = null;
|
|
490
|
+
let done = false;
|
|
491
|
+
const observer = new MutationObserver(() => {
|
|
492
|
+
if (settle !== null) clearTimeout(settle);
|
|
493
|
+
settle = setTimeout(finish, QUIET_MS);
|
|
494
|
+
});
|
|
495
|
+
const ceiling = setTimeout(finish, CEILING_MS);
|
|
496
|
+
function finish(): void {
|
|
497
|
+
if (done) return;
|
|
498
|
+
done = true;
|
|
499
|
+
observer.disconnect();
|
|
500
|
+
if (settle !== null) clearTimeout(settle);
|
|
501
|
+
clearTimeout(ceiling);
|
|
502
|
+
resolve();
|
|
503
|
+
}
|
|
504
|
+
observer.observe(container, {
|
|
505
|
+
subtree: true,
|
|
506
|
+
childList: true,
|
|
507
|
+
attributes: true,
|
|
508
|
+
characterData: true,
|
|
509
|
+
});
|
|
510
|
+
settle = setTimeout(finish, QUIET_MS);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Tile the captured variants into ONE labeled sheet — the same 2-column,
|
|
516
|
+
* bitmap-labeled geometry the Asset Lab's four-view contact sheet and its
|
|
517
|
+
* shot-set sheet already use (`asset-preview.ts`), so every sheet this repo
|
|
518
|
+
* hands an agent reads the same way. A single variant gets a single cell
|
|
519
|
+
* rather than a half-empty grid.
|
|
520
|
+
*/
|
|
521
|
+
export async function composeVariantSheet(
|
|
522
|
+
variants: readonly StoryVariantImage[],
|
|
523
|
+
width: number,
|
|
524
|
+
height: number,
|
|
525
|
+
options: { readonly labels?: boolean; readonly columns?: number } = {},
|
|
526
|
+
): Promise<{ width: number; height: number; base64: string; mimeType: 'image/png' }> {
|
|
527
|
+
const columns = Math.min(
|
|
528
|
+
Math.max(1, variants.length),
|
|
529
|
+
Math.max(1, Math.floor(options.columns ?? 2)),
|
|
530
|
+
);
|
|
531
|
+
const rows = Math.max(1, Math.ceil(variants.length / columns));
|
|
532
|
+
// Cells fit the variants, not the mount box: a cropped-to-content variant is
|
|
533
|
+
// its own size, so the cell is the largest variant (falling back to the
|
|
534
|
+
// mount box for an empty sheet) and each image draws centered at 1:1 —
|
|
535
|
+
// stretching a cropped widget to a screen-shaped cell would undo the crop.
|
|
536
|
+
const images: HTMLImageElement[] = [];
|
|
537
|
+
for (const variant of variants) {
|
|
538
|
+
const image = new Image();
|
|
539
|
+
image.src = `data:${variant.mimeType};base64,${variant.base64}`;
|
|
540
|
+
await image.decode();
|
|
541
|
+
images.push(image);
|
|
542
|
+
}
|
|
543
|
+
const cellWidth = Math.max(
|
|
544
|
+
1,
|
|
545
|
+
...images.map((one) => one.naturalWidth),
|
|
546
|
+
images.length ? 0 : width,
|
|
547
|
+
);
|
|
548
|
+
const cellHeight = Math.max(
|
|
549
|
+
1,
|
|
550
|
+
...images.map((one) => one.naturalHeight),
|
|
551
|
+
images.length ? 0 : height,
|
|
552
|
+
);
|
|
553
|
+
const sheetWidth = cellWidth * columns;
|
|
554
|
+
const sheetHeight = cellHeight * rows;
|
|
555
|
+
const canvas = document.createElement('canvas');
|
|
556
|
+
canvas.width = sheetWidth;
|
|
557
|
+
canvas.height = sheetHeight;
|
|
558
|
+
const context = canvas.getContext('2d');
|
|
559
|
+
if (!context) throw new Error('Unable to create the story variant sheet.');
|
|
560
|
+
context.fillStyle = SHEET_BACKDROP;
|
|
561
|
+
context.fillRect(0, 0, sheetWidth, sheetHeight);
|
|
562
|
+
|
|
563
|
+
for (const [index, variant] of variants.entries()) {
|
|
564
|
+
const x = (index % columns) * cellWidth;
|
|
565
|
+
const y = Math.floor(index / columns) * cellHeight;
|
|
566
|
+
const image = images[index]!;
|
|
567
|
+
context.drawImage(
|
|
568
|
+
image,
|
|
569
|
+
x + Math.floor((cellWidth - image.naturalWidth) / 2),
|
|
570
|
+
y + Math.floor((cellHeight - image.naturalHeight) / 2),
|
|
571
|
+
);
|
|
572
|
+
if (options.labels !== false)
|
|
573
|
+
drawBitmapLabel(context, variant.name.toUpperCase(), x, y, cellWidth, cellHeight);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const dataUrl = canvas.toDataURL('image/png');
|
|
577
|
+
const comma = dataUrl.indexOf(',');
|
|
578
|
+
return {
|
|
579
|
+
width: sheetWidth,
|
|
580
|
+
height: sheetHeight,
|
|
581
|
+
base64: comma >= 0 ? dataUrl.slice(comma + 1) : dataUrl,
|
|
582
|
+
mimeType: 'image/png',
|
|
583
|
+
};
|
|
584
|
+
}
|