@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,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-demand reconstruction of a canvas + DOM recording.
|
|
3
|
+
*
|
|
4
|
+
* rrweb is an intermediate representation here, not the delivery UI. A read
|
|
5
|
+
* seeks the recorded canvas video and DOM timeline offscreen, puts that video
|
|
6
|
+
* frame back into rrweb's canvas placeholder, then calls the ordinary game
|
|
7
|
+
* compositor. The same primitive can feed a later batch video encoder without
|
|
8
|
+
* making live gameplay pay the DOM rasterization cost.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
AudioSampleSink,
|
|
13
|
+
AudioSampleSource,
|
|
14
|
+
BufferTarget,
|
|
15
|
+
CanvasSource,
|
|
16
|
+
Input,
|
|
17
|
+
Output,
|
|
18
|
+
Quality,
|
|
19
|
+
UrlSource,
|
|
20
|
+
WEBM,
|
|
21
|
+
WebMOutputFormat,
|
|
22
|
+
} from 'mediabunny';
|
|
23
|
+
import { EventType, type eventWithTime, Replayer } from 'rrweb';
|
|
24
|
+
import { BASE } from './api/base';
|
|
25
|
+
import {
|
|
26
|
+
type CompositeCapture,
|
|
27
|
+
capturePlayComposite,
|
|
28
|
+
createImageSnapshotCache,
|
|
29
|
+
drawPlayCompositeFrame,
|
|
30
|
+
} from './composite-screenshot';
|
|
31
|
+
import type { GameplayReplayMetadata, ReplayRect } from './gameplay-dom-recording';
|
|
32
|
+
|
|
33
|
+
interface ReplayManifest extends GameplayReplayMetadata {
|
|
34
|
+
readonly status: 'complete';
|
|
35
|
+
readonly format: 'canvas-dom';
|
|
36
|
+
readonly capture: {
|
|
37
|
+
readonly durationMs: number;
|
|
38
|
+
readonly audio: boolean;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface GameplayReplayCapture extends CompositeCapture {
|
|
43
|
+
readonly replayPath: string;
|
|
44
|
+
readonly positionMs: number;
|
|
45
|
+
readonly width: number;
|
|
46
|
+
readonly height: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface CanvasLayout {
|
|
50
|
+
readonly rect: ReplayRect;
|
|
51
|
+
readonly drawingWidth: number;
|
|
52
|
+
readonly drawingHeight: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function replayFileUrl(path: string, file: string): string {
|
|
56
|
+
const query = new URLSearchParams({ replay: path, file });
|
|
57
|
+
return `${BASE}/recording/replay-file?${query}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function fetchedText(path: string, file: string): Promise<string> {
|
|
61
|
+
const response = await fetch(replayFileUrl(path, file));
|
|
62
|
+
if (!response.ok) throw new Error(`could not load replay ${file}: HTTP ${response.status}`);
|
|
63
|
+
return response.text();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function blobDataUrl(blob: Blob): Promise<string> {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const reader = new FileReader();
|
|
69
|
+
reader.onload = () => resolve(String(reader.result));
|
|
70
|
+
reader.onerror = () => reject(reader.error ?? new Error('could not read replay asset'));
|
|
71
|
+
reader.readAsDataURL(blob);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function loadEvents(path: string): Promise<eventWithTime[]> {
|
|
76
|
+
let text = await fetchedText(path, 'events.json');
|
|
77
|
+
const names = new Set(
|
|
78
|
+
text.match(/https:\/\/replay-assets\.invalid\/\d{6}\.(?:png|jpg|webp|gif|svg)/g) ?? [],
|
|
79
|
+
);
|
|
80
|
+
const replacements = await Promise.all(
|
|
81
|
+
[...names].map(async (url) => {
|
|
82
|
+
const name = url.split('/').at(-1)!;
|
|
83
|
+
const response = await fetch(replayFileUrl(path, `assets/${name}`));
|
|
84
|
+
if (!response.ok) throw new Error(`could not load replay asset ${name}`);
|
|
85
|
+
return [url, await blobDataUrl(await response.blob())] as const;
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
for (const [url, dataUrl] of replacements) text = text.replaceAll(url, dataUrl);
|
|
89
|
+
const events = JSON.parse(text) as unknown;
|
|
90
|
+
if (!Array.isArray(events) || events.length === 0) {
|
|
91
|
+
throw new Error('replay contains no DOM events');
|
|
92
|
+
}
|
|
93
|
+
return events as eventWithTime[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function domOffset(manifest: ReplayManifest, events: readonly eventWithTime[], videoMs: number) {
|
|
97
|
+
return Math.max(0, manifest.videoStartEpochMs + videoMs - events[0]!.timestamp);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function layoutAt(
|
|
101
|
+
manifest: ReplayManifest,
|
|
102
|
+
events: readonly eventWithTime[],
|
|
103
|
+
videoMs: number,
|
|
104
|
+
): CanvasLayout {
|
|
105
|
+
const epochMs = manifest.videoStartEpochMs + videoMs;
|
|
106
|
+
let layout: CanvasLayout = {
|
|
107
|
+
rect: manifest.canvas,
|
|
108
|
+
drawingWidth: manifest.canvas.drawingWidth,
|
|
109
|
+
drawingHeight: manifest.canvas.drawingHeight,
|
|
110
|
+
};
|
|
111
|
+
for (const event of events) {
|
|
112
|
+
if (event.timestamp > epochMs) break;
|
|
113
|
+
if (
|
|
114
|
+
event.type === EventType.Custom &&
|
|
115
|
+
event.data.tag === 'vgai-canvas-layout' &&
|
|
116
|
+
event.data.payload &&
|
|
117
|
+
typeof event.data.payload === 'object' &&
|
|
118
|
+
'rect' in event.data.payload &&
|
|
119
|
+
'drawingWidth' in event.data.payload &&
|
|
120
|
+
'drawingHeight' in event.data.payload
|
|
121
|
+
) {
|
|
122
|
+
layout = event.data.payload as CanvasLayout;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return layout;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function seekVideo(video: HTMLVideoElement, seconds: number): Promise<void> {
|
|
129
|
+
await new Promise<void>((resolve, reject) => {
|
|
130
|
+
const cleanup = () => {
|
|
131
|
+
clearTimeout(timeout);
|
|
132
|
+
video.removeEventListener('loadeddata', onLoaded);
|
|
133
|
+
video.removeEventListener('error', onError);
|
|
134
|
+
};
|
|
135
|
+
const onLoaded = () => {
|
|
136
|
+
cleanup();
|
|
137
|
+
resolve();
|
|
138
|
+
};
|
|
139
|
+
const onError = () => {
|
|
140
|
+
cleanup();
|
|
141
|
+
reject(new Error('replay video failed to load'));
|
|
142
|
+
};
|
|
143
|
+
const timeout = setTimeout(() => {
|
|
144
|
+
cleanup();
|
|
145
|
+
reject(new Error('replay video load timed out'));
|
|
146
|
+
}, 15_000);
|
|
147
|
+
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) onLoaded();
|
|
148
|
+
else {
|
|
149
|
+
video.addEventListener('loadeddata', onLoaded, { once: true });
|
|
150
|
+
video.addEventListener('error', onError, { once: true });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
if (Math.abs(video.currentTime - seconds) < 0.001) return;
|
|
154
|
+
await new Promise<void>((resolve, reject) => {
|
|
155
|
+
const cleanup = () => {
|
|
156
|
+
clearTimeout(timeout);
|
|
157
|
+
video.removeEventListener('seeked', onSeeked);
|
|
158
|
+
video.removeEventListener('error', onError);
|
|
159
|
+
};
|
|
160
|
+
const onSeeked = () => {
|
|
161
|
+
cleanup();
|
|
162
|
+
resolve();
|
|
163
|
+
};
|
|
164
|
+
const onError = () => {
|
|
165
|
+
cleanup();
|
|
166
|
+
reject(new Error('replay video seek failed'));
|
|
167
|
+
};
|
|
168
|
+
const timeout = setTimeout(() => {
|
|
169
|
+
cleanup();
|
|
170
|
+
reject(new Error('replay video seek timed out'));
|
|
171
|
+
}, 15_000);
|
|
172
|
+
video.addEventListener('seeked', onSeeked, { once: true });
|
|
173
|
+
video.addEventListener('error', onError, { once: true });
|
|
174
|
+
video.currentTime = seconds;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function settleReplayDocument(document: Document): Promise<void> {
|
|
179
|
+
if (document.fonts.size > 0 && document.fonts.status !== 'loaded') {
|
|
180
|
+
await Promise.race([
|
|
181
|
+
document.fonts.ready.then(() => undefined),
|
|
182
|
+
new Promise<void>((resolve) => setTimeout(resolve, 3_000)),
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
185
|
+
await Promise.all(Array.from(document.images, (image) => image.decode().catch(() => undefined)));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Reconstruct and rasterize one moment without presenting a replay player. */
|
|
189
|
+
async function withGameplayReplay<T>(
|
|
190
|
+
replayPath: string,
|
|
191
|
+
visit: (reader: {
|
|
192
|
+
manifest: ReplayManifest;
|
|
193
|
+
render(positionMs: number): Promise<HTMLElement>;
|
|
194
|
+
}) => Promise<T>,
|
|
195
|
+
): Promise<T> {
|
|
196
|
+
const [manifestText, events] = await Promise.all([
|
|
197
|
+
fetchedText(replayPath, 'manifest.json'),
|
|
198
|
+
loadEvents(replayPath),
|
|
199
|
+
]);
|
|
200
|
+
const manifest = JSON.parse(manifestText) as ReplayManifest;
|
|
201
|
+
if (
|
|
202
|
+
manifest.status !== 'complete' ||
|
|
203
|
+
manifest.format !== 'canvas-dom' ||
|
|
204
|
+
manifest.version !== 1 ||
|
|
205
|
+
!manifest.capture ||
|
|
206
|
+
typeof manifest.capture.durationMs !== 'number'
|
|
207
|
+
) {
|
|
208
|
+
throw new Error('replay manifest is incomplete or unsupported');
|
|
209
|
+
}
|
|
210
|
+
const host = document.createElement('div');
|
|
211
|
+
// Reconstructing an older capture during Play must not record the
|
|
212
|
+
// reconstruction itself into the new capture's DOM event stream.
|
|
213
|
+
host.setAttribute('data-vgai-replay-block', '');
|
|
214
|
+
Object.assign(host.style, {
|
|
215
|
+
position: 'fixed',
|
|
216
|
+
left: '-100000px',
|
|
217
|
+
top: '0',
|
|
218
|
+
width: `${manifest.viewport.width}px`,
|
|
219
|
+
height: `${manifest.viewport.height}px`,
|
|
220
|
+
overflow: 'hidden',
|
|
221
|
+
pointerEvents: 'none',
|
|
222
|
+
});
|
|
223
|
+
const mount = document.createElement('div');
|
|
224
|
+
host.append(mount);
|
|
225
|
+
document.body.append(host);
|
|
226
|
+
const video = document.createElement('video');
|
|
227
|
+
video.muted = true;
|
|
228
|
+
video.preload = 'auto';
|
|
229
|
+
video.playsInline = true;
|
|
230
|
+
video.src = replayFileUrl(replayPath, 'video');
|
|
231
|
+
host.append(video);
|
|
232
|
+
|
|
233
|
+
let player: Replayer | undefined;
|
|
234
|
+
try {
|
|
235
|
+
player = new Replayer(events, {
|
|
236
|
+
root: mount,
|
|
237
|
+
showWarning: false,
|
|
238
|
+
showDebug: false,
|
|
239
|
+
mouseTail: false,
|
|
240
|
+
UNSAFE_replayCanvas: true,
|
|
241
|
+
insertStyleRules: ['[data-vgai-replay-video] { visibility: hidden !important; }'],
|
|
242
|
+
});
|
|
243
|
+
const replay = player;
|
|
244
|
+
return await visit({
|
|
245
|
+
manifest,
|
|
246
|
+
async render(positionMs) {
|
|
247
|
+
const bounded = Math.max(0, Math.min(positionMs, manifest.capture.durationMs));
|
|
248
|
+
await seekVideo(video, bounded / 1000);
|
|
249
|
+
replay.pause(domOffset(manifest, events, bounded));
|
|
250
|
+
await settleReplayDocument(replay.iframe.contentDocument!);
|
|
251
|
+
|
|
252
|
+
const canvasNode = replay.getMirror().getNode(manifest.canvas.nodeId);
|
|
253
|
+
const surfaceNode = replay.getMirror().getNode(manifest.surfaceNodeId);
|
|
254
|
+
if (!canvasNode || (canvasNode as Element).tagName !== 'CANVAS') {
|
|
255
|
+
throw new Error('replay canvas placeholder is unavailable');
|
|
256
|
+
}
|
|
257
|
+
if (
|
|
258
|
+
!surfaceNode ||
|
|
259
|
+
typeof (surfaceNode as HTMLElement).getBoundingClientRect !== 'function'
|
|
260
|
+
) {
|
|
261
|
+
throw new Error('replay game surface is unavailable');
|
|
262
|
+
}
|
|
263
|
+
const canvas = canvasNode as HTMLCanvasElement;
|
|
264
|
+
const surface = surfaceNode as HTMLElement;
|
|
265
|
+
canvas.setAttribute('data-vgai-root-surface', 'true');
|
|
266
|
+
const layout = layoutAt(manifest, events, bounded);
|
|
267
|
+
canvas.width = layout.drawingWidth;
|
|
268
|
+
canvas.height = layout.drawingHeight;
|
|
269
|
+
const context = canvas.getContext('2d');
|
|
270
|
+
if (!context) throw new Error('replay canvas has no 2D context');
|
|
271
|
+
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
272
|
+
canvas.style.setProperty('visibility', 'visible', 'important');
|
|
273
|
+
|
|
274
|
+
return surface;
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
} finally {
|
|
278
|
+
video.pause();
|
|
279
|
+
player?.destroy();
|
|
280
|
+
video.removeAttribute('src');
|
|
281
|
+
video.load();
|
|
282
|
+
host.remove();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** One reconstructed screenshot; no player or live game mutation. */
|
|
287
|
+
export async function captureGameplayReplay(
|
|
288
|
+
replayPath: string,
|
|
289
|
+
positionMs: number,
|
|
290
|
+
): Promise<GameplayReplayCapture> {
|
|
291
|
+
return withGameplayReplay(replayPath, async ({ manifest, render }) => {
|
|
292
|
+
const bounded = Math.max(0, Math.min(positionMs, manifest.capture.durationMs));
|
|
293
|
+
const surface = await render(bounded);
|
|
294
|
+
const capture = await capturePlayComposite(surface);
|
|
295
|
+
const rect = surface.getBoundingClientRect();
|
|
296
|
+
return {
|
|
297
|
+
...capture,
|
|
298
|
+
replayPath,
|
|
299
|
+
positionMs: bounded,
|
|
300
|
+
width: Math.max(1, Math.round(rect.width)),
|
|
301
|
+
height: Math.max(1, Math.round(rect.height)),
|
|
302
|
+
};
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface GameplayReplayExportOptions {
|
|
307
|
+
replayPath: string;
|
|
308
|
+
fps?: number;
|
|
309
|
+
startMs?: number;
|
|
310
|
+
endMs?: number;
|
|
311
|
+
name?: string;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Encode recorded pixels and UI after capture. The live simulation is never stepped. */
|
|
315
|
+
export async function exportGameplayReplay(options: GameplayReplayExportOptions) {
|
|
316
|
+
const fps = options.fps ?? 30;
|
|
317
|
+
if (!Number.isInteger(fps) || fps < 1 || fps > 60)
|
|
318
|
+
throw new Error('replay export fps must be an integer from 1 to 60');
|
|
319
|
+
return withGameplayReplay(options.replayPath, async ({ manifest, render }) => {
|
|
320
|
+
const startMs = options.startMs ?? 0;
|
|
321
|
+
const endMs = options.endMs ?? manifest.capture.durationMs;
|
|
322
|
+
if (
|
|
323
|
+
!Number.isFinite(startMs) ||
|
|
324
|
+
!Number.isFinite(endMs) ||
|
|
325
|
+
startMs < 0 ||
|
|
326
|
+
endMs <= startMs ||
|
|
327
|
+
endMs > manifest.capture.durationMs ||
|
|
328
|
+
endMs - startMs > 300_000
|
|
329
|
+
) {
|
|
330
|
+
throw new Error('replay export requires a valid recorded interval of at most five minutes');
|
|
331
|
+
}
|
|
332
|
+
const start = startMs / 1000;
|
|
333
|
+
const end = endMs / 1000;
|
|
334
|
+
const frames = Math.ceil((end - start) * fps);
|
|
335
|
+
const surface = await render(startMs);
|
|
336
|
+
const canvas = document.createElement('canvas');
|
|
337
|
+
const rect = surface.getBoundingClientRect();
|
|
338
|
+
canvas.width = Math.max(1, Math.round(rect.width));
|
|
339
|
+
canvas.height = Math.max(1, Math.round(rect.height));
|
|
340
|
+
const size = { width: canvas.width, height: canvas.height };
|
|
341
|
+
const target = new BufferTarget();
|
|
342
|
+
const output = new Output({ target, format: new WebMOutputFormat() });
|
|
343
|
+
const source = new CanvasSource(canvas, {
|
|
344
|
+
codec: 'vp9',
|
|
345
|
+
quality: new Quality({ bitrate: 3_000_000 }),
|
|
346
|
+
});
|
|
347
|
+
output.addVideoTrack(source, { frameRate: fps });
|
|
348
|
+
const input = new Input({
|
|
349
|
+
source: new UrlSource(replayFileUrl(options.replayPath, 'video')),
|
|
350
|
+
formats: [WEBM],
|
|
351
|
+
});
|
|
352
|
+
try {
|
|
353
|
+
const track = await input.getPrimaryAudioTrack();
|
|
354
|
+
if (manifest.capture.audio && !track) throw new Error('recorded audio track is missing');
|
|
355
|
+
const audio = track
|
|
356
|
+
? new AudioSampleSource({ codec: 'opus', quality: new Quality({ bitrate: 128_000 }) })
|
|
357
|
+
: null;
|
|
358
|
+
if (audio) output.addAudioTrack(audio);
|
|
359
|
+
await output.start();
|
|
360
|
+
const encodeAudio = async () => {
|
|
361
|
+
if (!audio || !track) return;
|
|
362
|
+
for await (const sample of new AudioSampleSink(track).samples(start, end)) {
|
|
363
|
+
try {
|
|
364
|
+
const from = Math.max(0, Math.ceil((start - sample.timestamp) * sample.sampleRate));
|
|
365
|
+
const to = Math.min(
|
|
366
|
+
sample.numberOfFrames,
|
|
367
|
+
Math.ceil((end - sample.timestamp) * sample.sampleRate),
|
|
368
|
+
);
|
|
369
|
+
if (to <= from) continue;
|
|
370
|
+
const trimmed = sample.trim(from, to);
|
|
371
|
+
try {
|
|
372
|
+
trimmed.setTimestamp(Math.max(0, trimmed.timestamp - start));
|
|
373
|
+
await audio.add(trimmed);
|
|
374
|
+
} finally {
|
|
375
|
+
trimmed.close();
|
|
376
|
+
}
|
|
377
|
+
} finally {
|
|
378
|
+
sample.close();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
audio.close();
|
|
382
|
+
};
|
|
383
|
+
const encodeVideo = async () => {
|
|
384
|
+
const snapshots = createImageSnapshotCache();
|
|
385
|
+
for (let frame = 0; frame < frames; frame++) {
|
|
386
|
+
const at = frame / fps;
|
|
387
|
+
await drawPlayCompositeFrame(await render(startMs + at * 1000), canvas, {
|
|
388
|
+
size,
|
|
389
|
+
snapshots,
|
|
390
|
+
});
|
|
391
|
+
await source.add(at, Math.min(1 / fps, end - start - at), {
|
|
392
|
+
keyFrame: frame % (fps * 2) === 0,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
source.close();
|
|
396
|
+
};
|
|
397
|
+
// The muxer applies cross-track backpressure. Feeding all audio before
|
|
398
|
+
// starting video deadlocks once its interleave queue fills.
|
|
399
|
+
await Promise.all([encodeAudio(), encodeVideo()]);
|
|
400
|
+
await output.finalize();
|
|
401
|
+
if (!target.buffer) throw new Error('replay encoder produced no output');
|
|
402
|
+
return {
|
|
403
|
+
blob: new Blob([target.buffer], { type: 'video/webm' }),
|
|
404
|
+
frames,
|
|
405
|
+
fps,
|
|
406
|
+
durationMs: endMs - startMs,
|
|
407
|
+
...size,
|
|
408
|
+
audio: track !== null,
|
|
409
|
+
};
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (output.state !== 'finalized') await output.cancel();
|
|
412
|
+
throw error;
|
|
413
|
+
} finally {
|
|
414
|
+
input.dispose();
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Map the editor's Play-relative cursor onto a recording that may have
|
|
2
|
+
* started later than Play. Both starts are wall-clock milliseconds. */
|
|
3
|
+
export function gameplayRecordingMediaTimeMs(
|
|
4
|
+
sessionStartedAt: number,
|
|
5
|
+
recordingStartedAt: number | null,
|
|
6
|
+
cursorMs: number,
|
|
7
|
+
): number {
|
|
8
|
+
return Math.max(0, sessionStartedAt + cursorMs - (recordingStartedAt ?? sessionStartedAt));
|
|
9
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/** Editor-owned durable Gameplay Session catalog and shared analytics cursor. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ToolContributionRecording,
|
|
5
|
+
ToolContributionRecordingSnapshot,
|
|
6
|
+
ToolGameplaySession,
|
|
7
|
+
ToolGameplaySessions,
|
|
8
|
+
ToolGameplaySessionsSnapshot,
|
|
9
|
+
} from '@volter/editor-sdk/contributions';
|
|
10
|
+
import { editorServerJson } from './editor-server-response';
|
|
11
|
+
|
|
12
|
+
let snapshot: ToolGameplaySessionsSnapshot = {
|
|
13
|
+
sessions: [],
|
|
14
|
+
selectedSessionId: null,
|
|
15
|
+
selectedSession: null,
|
|
16
|
+
cursorMs: 0,
|
|
17
|
+
liveEdgeMs: 0,
|
|
18
|
+
loading: true,
|
|
19
|
+
error: null,
|
|
20
|
+
};
|
|
21
|
+
let followingLiveEdge = true;
|
|
22
|
+
let poll: number | null = null;
|
|
23
|
+
let requestedSessionId: string | null = null;
|
|
24
|
+
let refreshInFlight: Promise<void> | null = null;
|
|
25
|
+
let refreshQueued = false;
|
|
26
|
+
const listeners = new Set<() => void>();
|
|
27
|
+
|
|
28
|
+
export interface GameplaySessionSelection {
|
|
29
|
+
readonly sessionId: string | null;
|
|
30
|
+
readonly latestAppeared: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Resolve one catalog refresh without confusing a deliberate history choice
|
|
34
|
+
* with the arrival of a new Play interval. A newly-created latest session gets
|
|
35
|
+
* focus exactly once; after it is known, the tester may select history and the
|
|
36
|
+
* one-second poll preserves that choice. */
|
|
37
|
+
export function resolveGameplaySessionSelection(
|
|
38
|
+
sessions: readonly Pick<ToolGameplaySession, 'id'>[],
|
|
39
|
+
previousSessions: readonly Pick<ToolGameplaySession, 'id'>[],
|
|
40
|
+
requestedId: string | null,
|
|
41
|
+
): GameplaySessionSelection {
|
|
42
|
+
const latest = sessions[0] ?? null;
|
|
43
|
+
const latestAppeared =
|
|
44
|
+
latest !== null &&
|
|
45
|
+
previousSessions.length > 0 &&
|
|
46
|
+
!previousSessions.some((session) => session.id === latest.id);
|
|
47
|
+
if (latestAppeared) return { sessionId: latest.id, latestAppeared: true };
|
|
48
|
+
if (requestedId && sessions.some((session) => session.id === requestedId)) {
|
|
49
|
+
return { sessionId: requestedId, latestAppeared: false };
|
|
50
|
+
}
|
|
51
|
+
return { sessionId: latest?.id ?? null, latestAppeared: false };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function publish(next: ToolGameplaySessionsSnapshot): void {
|
|
55
|
+
snapshot = next;
|
|
56
|
+
for (const listener of listeners) listener();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The catalog the session derives from the project's own play logs. */
|
|
60
|
+
async function fetchSessionCatalog(): Promise<ToolGameplaySession[]> {
|
|
61
|
+
const response = await fetch('/__editor/gameplay-sessions');
|
|
62
|
+
const data = await editorServerJson<{ sessions: ToolGameplaySession[] }>(
|
|
63
|
+
response,
|
|
64
|
+
'Could not load Gameplay Sessions',
|
|
65
|
+
);
|
|
66
|
+
return data.sessions;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function fetchSessionDetail(id: string): Promise<ToolGameplaySession | null> {
|
|
70
|
+
return editorServerJson<{ session: ToolGameplaySession }>(
|
|
71
|
+
await fetch(`/__editor/gameplay-sessions/${encodeURIComponent(id)}`),
|
|
72
|
+
'Could not load the selected Gameplay Session',
|
|
73
|
+
).then((detail) => detail.session);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function refreshOnce(): Promise<void> {
|
|
77
|
+
try {
|
|
78
|
+
const data = { sessions: await fetchSessionCatalog() };
|
|
79
|
+
const selection = resolveGameplaySessionSelection(
|
|
80
|
+
data.sessions,
|
|
81
|
+
snapshot.sessions,
|
|
82
|
+
requestedSessionId,
|
|
83
|
+
);
|
|
84
|
+
const selectedId = selection.sessionId;
|
|
85
|
+
if (selection.latestAppeared) followingLiveEdge = true;
|
|
86
|
+
requestedSessionId = selectedId;
|
|
87
|
+
const selected = selectedId ? await fetchSessionDetail(selectedId) : null;
|
|
88
|
+
if (requestedSessionId !== selectedId) {
|
|
89
|
+
refreshQueued = true;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const sessions = data.sessions.map((session) =>
|
|
93
|
+
session.id === selected?.id ? selected : session,
|
|
94
|
+
);
|
|
95
|
+
const liveEdgeMs = selected?.durationMs ?? 0;
|
|
96
|
+
const cursorMs = followingLiveEdge ? liveEdgeMs : Math.min(snapshot.cursorMs, liveEdgeMs);
|
|
97
|
+
publish({
|
|
98
|
+
sessions,
|
|
99
|
+
selectedSessionId: selectedId,
|
|
100
|
+
selectedSession: selected,
|
|
101
|
+
cursorMs,
|
|
102
|
+
liveEdgeMs,
|
|
103
|
+
loading: false,
|
|
104
|
+
error: null,
|
|
105
|
+
});
|
|
106
|
+
} catch (error) {
|
|
107
|
+
publish({
|
|
108
|
+
...snapshot,
|
|
109
|
+
loading: false,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function refresh(): Promise<void> {
|
|
116
|
+
if (refreshInFlight) {
|
|
117
|
+
refreshQueued = true;
|
|
118
|
+
return refreshInFlight;
|
|
119
|
+
}
|
|
120
|
+
refreshInFlight = refreshOnce().finally(() => {
|
|
121
|
+
refreshInFlight = null;
|
|
122
|
+
if (refreshQueued) {
|
|
123
|
+
refreshQueued = false;
|
|
124
|
+
void refresh();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return refreshInFlight;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function subscribe(listener: () => void): () => void {
|
|
131
|
+
listeners.add(listener);
|
|
132
|
+
if (listeners.size === 1) {
|
|
133
|
+
void refresh();
|
|
134
|
+
poll = window.setInterval(() => void refresh(), 1_000);
|
|
135
|
+
}
|
|
136
|
+
return () => {
|
|
137
|
+
listeners.delete(listener);
|
|
138
|
+
if (listeners.size === 0 && poll !== null) {
|
|
139
|
+
window.clearInterval(poll);
|
|
140
|
+
poll = null;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function select(sessionId: string): void {
|
|
146
|
+
const selected = snapshot.sessions.find((session) => session.id === sessionId);
|
|
147
|
+
if (!selected) return;
|
|
148
|
+
requestedSessionId = selected.id;
|
|
149
|
+
followingLiveEdge = true;
|
|
150
|
+
publish({
|
|
151
|
+
...snapshot,
|
|
152
|
+
selectedSessionId: selected.id,
|
|
153
|
+
selectedSession: selected,
|
|
154
|
+
cursorMs: selected.durationMs,
|
|
155
|
+
liveEdgeMs: selected.durationMs,
|
|
156
|
+
});
|
|
157
|
+
void refresh();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function seek(realTimeMs: number): void {
|
|
161
|
+
const cursorMs = Math.max(0, Math.min(realTimeMs, snapshot.liveEdgeMs));
|
|
162
|
+
followingLiveEdge = snapshot.liveEdgeMs - cursorMs < 250;
|
|
163
|
+
publish({ ...snapshot, cursorMs });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const toolGameplaySessions: ToolGameplaySessions = {
|
|
167
|
+
getSnapshot: () => snapshot,
|
|
168
|
+
subscribe,
|
|
169
|
+
select,
|
|
170
|
+
seek,
|
|
171
|
+
refresh,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* THE LIVE RECORDING's bounded visual index, as `workspace.analytics` tools
|
|
176
|
+
* and the analytics header (`GameplaySessionTimeline`) read it — the
|
|
177
|
+
* `ToolContributionRecording` transport of the SDK contract. The recorder
|
|
178
|
+
* (Play's `gameplay-recording.ts`) PUBLISHES here; the full WebM remains the
|
|
179
|
+
* recording artifact. Null while nothing records.
|
|
180
|
+
*/
|
|
181
|
+
let _recordingPreview: ToolContributionRecordingSnapshot | null = null;
|
|
182
|
+
const _recordingListeners = new Set<() => void>();
|
|
183
|
+
|
|
184
|
+
export const toolContributionRecording: ToolContributionRecording = {
|
|
185
|
+
getSnapshot: () => _recordingPreview,
|
|
186
|
+
subscribe(listener) {
|
|
187
|
+
_recordingListeners.add(listener);
|
|
188
|
+
return () => _recordingListeners.delete(listener);
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/** The recorder's publish: the current preview, or null once it stops. */
|
|
193
|
+
export function publishToolContributionRecording(
|
|
194
|
+
snapshot: ToolContributionRecordingSnapshot | null,
|
|
195
|
+
): void {
|
|
196
|
+
_recordingPreview = snapshot;
|
|
197
|
+
for (const listener of _recordingListeners) {
|
|
198
|
+
try {
|
|
199
|
+
listener();
|
|
200
|
+
} catch {
|
|
201
|
+
// A project-owned observer may not break the editor-owned recorder.
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|