@volter/editor-game 0.5.65 → 0.5.67
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/contributions/audio-unlock.service.ts +2 -2
- package/contributions/autoplay.service.ts +1 -1
- package/contributions/bridge.command.ts +3 -3
- package/contributions/canvas/component-board.service.ts +16 -0
- package/contributions/canvas/design-time-mount.service.ts +45 -0
- package/contributions/canvas-story-capture.service.ts +12 -0
- package/contributions/edit-mode-audio.service.ts +2 -2
- package/contributions/edit-mode-networking.service.ts +1 -1
- package/contributions/gameplay.command.ts +4 -4
- package/contributions/generation.service.ts +3 -3
- package/contributions/generations.status.tsx +1 -1
- package/contributions/godot.palette.json +99 -0
- package/contributions/godot.style.ts +82 -0
- package/contributions/godot.view.ts +75 -0
- package/contributions/ingest.service.ts +4 -4
- package/contributions/instances.command.ts +1 -1
- package/contributions/navmesh.menu.ts +1 -1
- package/contributions/network-observer.service.ts +14 -0
- package/contributions/play.command.ts +11 -5
- package/contributions/react/component-board.service.ts +1 -1
- package/contributions/react/design-time-mount.service.ts +1 -1
- package/contributions/react/react-inspector.service.ts +2 -2
- package/contributions/scene-document.service.ts +5 -5
- package/contributions/state-watch.menu.ts +1 -1
- package/contributions/state-watch.utility.tsx +1 -1
- package/contributions/team-playtest.service.ts +4 -4
- package/contributions/three/component-board.service.ts +1 -1
- package/contributions/three/component-verbs.command.ts +8 -8
- package/contributions/three/three-authoring.service.ts +8 -5
- package/contributions/three-story-capture.service.ts +12 -0
- package/contributions/unity.palette.json +102 -0
- package/contributions/unity.style.ts +72 -0
- package/contributions/unity.view.ts +82 -0
- package/contributions/unreal.palette.json +105 -0
- package/contributions/unreal.style.ts +66 -0
- package/contributions/unreal.view.ts +93 -0
- package/dist-node/serving.mjs +16 -0
- package/package.json +33 -16
- package/serving/index.ts +27 -0
- package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
- package/src/asset-budget/asset-budget-model.ts +4 -4
- package/src/asset-budget/optimize-apply.ts +7 -7
- package/src/audio/AudioDebuggerPanel.tsx +1 -1
- package/src/bridge/dispatch.ts +16 -16
- package/src/bridge/live-frames.ts +1 -1
- package/src/bridge/screenshot.ts +6 -6
- package/src/build/BuildProfilesPanel.tsx +3 -3
- package/src/canvas/canvas-board/CanvasBoardDocument.tsx +676 -0
- package/src/canvas/canvas-board/canvas-board-model.ts +407 -0
- package/src/canvas/canvas-board/canvas-component-board.ts +56 -0
- package/src/canvas/canvas-design-mount.ts +524 -0
- package/src/canvas/design-time-canvas-mount.ts +79 -0
- package/src/coverage/live-authoring-surface.ts +6 -6
- package/src/coverage/live-project-verbs.ts +2 -2
- package/src/coverage/native-system-coverage.ts +6 -6
- package/src/coverage/root-coverage.ts +1 -1
- package/src/coverage/session-coverage.ts +5 -5
- package/src/design-system-stories/ApplicationChrome.stories.tsx +5 -5
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +13 -53
- package/src/edit-mode/edit-mode-audio.ts +4 -4
- package/src/edit-mode/edit-mode-networking.ts +2 -2
- package/src/game-document/GameCaptureFrameButton.tsx +2 -2
- package/src/game-document/GameDocument.tsx +2 -2
- package/src/game-document/GamePanel.tsx +8 -8
- package/src/game-document/InstanceInspectorPicker.tsx +2 -2
- package/src/game-document/crowd-debug.ts +1 -1
- package/src/game-document/device-preview.ts +10 -11
- package/src/game-document/physics-debug.ts +1 -1
- package/src/generation/GenerationActivity.tsx +6 -8
- package/src/generation/generation-documents.tsx +8 -8
- package/src/generation/generation-jobs.ts +2 -2
- package/src/generation/generation-presentation.ts +1 -1
- package/src/host/adapter-reach.ts +1 -1
- package/src/host/adapter-runtime-bindings.ts +97 -6
- package/src/host/api/configurations.ts +2 -2
- package/src/host/authoring/babylon-authoring-adapter.ts +8 -8
- package/src/host/authoring/contract-hierarchy-authoring.ts +1 -1
- package/src/host/authoring/contract-scenes-stories.ts +1 -1
- package/src/host/authoring/creation-site-related.ts +2 -2
- package/src/host/authoring/ephemeral-persistence.ts +1 -1
- package/src/host/authoring/gesture-persist.ts +2 -2
- package/src/host/authoring/ingest-data-writer.ts +1 -1
- package/src/host/authoring/ingest-source-persistence.ts +8 -8
- package/src/host/authoring/mount-isolated-pixi-screen.ts +1 -1
- package/src/host/authoring/mounted-authoring.ts +4 -4
- package/src/host/authoring/phaser-live-authoring-adapter.ts +4 -4
- package/src/host/authoring/pixi-authoring-adapter.ts +42 -17
- package/src/host/authoring/pixi-creation-site-write-target.ts +2 -2
- package/src/host/authoring/pixi-live-write-target.ts +7 -7
- package/src/host/authoring/pixi-source-identity.ts +3 -3
- package/src/host/authoring/pixi-source-write-target.ts +1614 -0
- package/src/host/authoring/pixi-still-presentation.ts +2 -2
- package/src/host/authoring/pixi-structure-history.ts +3 -3
- package/src/host/authoring/pixi-transform-channels.ts +16 -14
- package/src/host/authoring/source-persistence-backend.ts +6 -6
- package/src/host/authoring/struct-write-pipe.ts +3 -3
- package/src/host/binding-resolver.ts +8 -9
- package/src/host/browser-transpile.ts +2 -2
- package/src/host/canvas-entry-runtime.ts +59 -48
- package/src/host/canvas-preview-frames.ts +482 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +1 -1
- package/src/host/components/HeaderTelemetry.tsx +9 -16
- package/src/host/components/PixiIsolationSceneContent.tsx +17 -16
- package/src/host/components/ThreeIsolationSceneContent.tsx +5 -5
- package/src/host/components/frame-debugger-model.ts +2 -2
- package/src/host/components/header-telemetry-model.ts +2 -2
- package/src/host/components/scene-document.tsx +29 -61
- package/src/host/components/utility-view-state.ts +1 -1
- package/src/host/components/world-root-stage-binding.tsx +17 -26
- package/src/host/components/world-root-stage.ts +130 -63
- package/src/host/coverage/capability-coverage.ts +13 -1
- package/src/host/coverage/game-contract-seam-evidence.ts +1 -1
- package/src/host/coverage/project-verb-coverage.ts +2 -17
- package/src/host/coverage/system-adapter-coverage.ts +4 -5
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +14 -8
- package/src/host/document-preview-three.ts +2 -2
- package/src/host/entry-adjudication.ts +6 -6
- package/src/host/game-css-scope-transform.ts +4 -0
- package/src/host/game-module-access.ts +1 -1
- package/src/host/game-realm-page.ts +16 -8
- package/src/host/game-realm-reclaim.ts +1 -1
- package/src/host/gameplay-export.ts +25 -14
- package/src/host/gameplay-recording.ts +5 -5
- package/src/host/gated-globals.ts +6 -6
- package/src/host/history/json-history-resource.ts +3 -3
- package/src/host/instance-extract-actions.ts +1 -1
- package/src/host/instance-fork-actions.ts +1 -1
- package/src/host/projection/dom.ts +2 -2
- package/src/host/projection/pixi.ts +25 -3
- package/src/host/r3f-entry-runtime.ts +65 -34
- package/src/host/react-mount-runtime.ts +8 -49
- package/src/host/realm-services.ts +2 -2
- package/src/host/roots/canvas-root.tsx +361 -0
- package/src/host/roots/module-root.ts +1 -1
- package/src/host/roots/r3f-root.tsx +473 -0
- package/src/host/roots/react-root.ts +9 -43
- package/src/host/scene-view-drawability.ts +2 -2
- package/src/host/served-bundle-runtime-modules.ts +1 -19
- package/src/host/server-log-bridge.ts +3 -3
- package/src/host/stories/mounted-story-viewport-source.ts +1 -1
- package/src/host/stories/pixi-story-model.ts +30 -0
- package/src/host/stories/story-media-captures.ts +46 -0
- package/src/host/stories/story-media-presence.ts +3 -3
- package/src/host/stories/story-pixi-preview.ts +408 -0
- package/src/host/stories/story-three-preview.ts +806 -0
- package/src/host/stories/three-story-captures.ts +35 -0
- package/src/host/story-three-preview-runtime.ts +56 -0
- package/src/host/three-ingest-runtime.ts +1 -1
- package/src/host/use-active-performance-source.ts +3 -3
- package/src/host/viewport-pose-memory.ts +1 -1
- package/src/host/viewport-root-presentation.ts +6 -5
- package/src/ingest/active-ingest.ts +2 -2
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
- package/src/ingest/authoring/ingest-root-adapter.ts +13 -13
- package/src/ingest/capture-wait-report.ts +1 -1
- package/src/ingest/deferred-ingest-play.ts +11 -10
- package/src/ingest/discovery-public-ingest.ts +2 -2
- package/src/ingest/ingest-boot-viewport.ts +6 -8
- package/src/ingest/ingest-canvas-scene-document.tsx +17 -16
- package/src/ingest/ingest-canvas-scene.ts +5 -8
- package/src/ingest/ingest-evidence-hook.ts +1 -1
- package/src/ingest/ingest-frame-snapshot.ts +1 -1
- package/src/ingest/ingest-play-control.ts +4 -4
- package/src/ingest/ingest-render-debug.ts +11 -11
- package/src/ingest/ingest-siblings.ts +16 -28
- package/src/ingest/module-mode.ts +18 -19
- package/src/ingest/mount-canvas-ingest-root.ts +27 -27
- package/src/ingest/mount-coverage.ts +4 -4
- package/src/ingest/mount-dom-ingest-root.ts +16 -16
- package/src/ingest/mount-ingest-root.ts +14 -14
- package/src/ingest/mount-three-ingest-root.ts +12 -12
- package/src/ingest/resolve-canvas.ts +1 -1
- package/src/ingest/served-html-boot.ts +1 -1
- package/src/ingest/surface-canvas.ts +1 -1
- package/src/ingest/unmount-ingest-root.ts +7 -7
- package/src/navmesh/navmesh-handler.ts +24 -16
- package/src/network/NetworkInspectorPanel.tsx +449 -14
- package/src/network/network-inspector-model.ts +14 -2
- package/src/play/play-log-events.ts +1 -1
- package/src/play/play-mode.ts +99 -142
- package/src/play/play-recording.ts +2 -2
- package/src/play/react-play-live-authoring.ts +3 -3
- package/src/play/run-selection.ts +93 -0
- package/src/play-bar/PlayBar.tsx +20 -39
- package/src/profiler/FrameDebuggerPanel.tsx +1 -1
- package/src/profiler/PerformancePanel.tsx +4 -4
- package/src/react/design-time-react-mount.ts +37 -85
- package/src/react/dom-authoring-adapter.ts +11 -11
- package/src/react/react-inspector-section.tsx +13 -13
- package/src/react/react-world-authoring-adapter.ts +31 -143
- package/src/react/story-documents/story-documents.tsx +18 -22
- package/src/react/ui-board-document.tsx +10 -11
- package/src/react/ui-component-board.ts +5 -5
- package/src/runtime/adapter/audio-meter.ts +21 -0
- package/src/runtime/adapter/first-party-audio-system.ts +230 -0
- package/src/runtime/adapter/ingest/contract-debug-adapter.ts +114 -0
- package/src/runtime/adapter/ingest/contract-system-adapters.ts +256 -0
- package/src/runtime/adapter/ingest/merge-debug-adapters.ts +197 -0
- package/src/runtime/adapter/ingest/observation-debug-adapter.ts +162 -0
- package/src/runtime/adapter/ingest/upstream-pin.ts +51 -0
- package/src/runtime/adapter/native-debug-module.ts +498 -0
- package/src/runtime/audio/bus-mixer.ts +161 -0
- package/src/runtime/audio/pose-guard.ts +80 -0
- package/src/runtime/core/frame-pacing.ts +126 -0
- package/src/runtime/core/game-loop.ts +225 -0
- package/src/runtime/core/game-scoped-slot.ts +28 -0
- package/src/runtime/core/seeded-random.ts +162 -0
- package/src/runtime/core/sim-clock.ts +391 -0
- package/src/runtime/core/system-runner.ts +269 -0
- package/src/runtime/core/types.ts +104 -0
- package/src/runtime/create-runtime.ts +1128 -0
- package/src/runtime/debug-bridge.ts +570 -0
- package/src/runtime/debug-registry.ts +899 -0
- package/src/runtime/dev/chrome-trace.ts +153 -0
- package/src/runtime/dev/instruments.ts +403 -0
- package/src/runtime/dev/logger.ts +119 -0
- package/src/runtime/dev/performance-profiler.ts +367 -0
- package/src/runtime/dev/register-render-vitals.ts +276 -0
- package/src/runtime/dev/render-census.ts +354 -0
- package/src/runtime/dev/render-debug-adapter.ts +218 -0
- package/src/runtime/dev/render-memory.ts +226 -0
- package/src/runtime/dev/render-vitals.ts +338 -0
- package/src/runtime/dev/static-batch-advisor.ts +188 -0
- package/src/runtime/dev/webgl-frame-capture.ts +366 -0
- package/src/runtime/dev/webgl-gpu-timer.ts +53 -0
- package/src/runtime/dev-build.ts +47 -0
- package/src/runtime/game.ts +1636 -0
- package/src/runtime/gameplay-rng-trap.ts +135 -0
- package/src/runtime/host-context.ts +64 -0
- package/src/runtime/input-router.ts +169 -0
- package/src/runtime/mount-manifest.ts +480 -0
- package/src/runtime/pixi/authoring.ts +706 -0
- package/src/runtime/pixi/ingest.ts +116 -0
- package/src/runtime/pixi/physics-registry.ts +49 -0
- package/src/runtime/pixi/render-pass-bracket.ts +117 -0
- package/src/runtime/pixi/scene-capture.ts +179 -0
- package/src/runtime/pixi/system-adapters.ts +69 -0
- package/src/runtime/playtest.ts +22 -0
- package/src/runtime/presentation.ts +141 -0
- package/src/runtime/render-control.ts +642 -0
- package/src/runtime/render-seed.ts +77 -0
- package/src/runtime/run-ticks-settled.ts +73 -0
- package/src/runtime/setup/setup-audio.ts +72 -0
- package/src/services/audio-pose-guard.ts +2 -2
- package/src/services/game-audio.ts +152 -0
- package/src/services/game-network.ts +657 -0
- package/src/services/game-physics.ts +334 -0
- package/src/state-watch/StateWatchPanel.tsx +1 -1
- package/src/three/authoring/camera-runtime-inspector-section.tsx +4 -3
- package/src/three/authoring/constraint-inspector-section.tsx +8 -7
- package/src/three/authoring/model-asset-inspector-section.tsx +14 -17
- package/src/three/authoring/oid-source-persistence.ts +13 -13
- package/src/three/authoring/r3f-design-session.ts +79 -61
- package/src/three/authoring/r3f-source-authoring-adapter.ts +112 -108
- package/src/three/authoring/reflection-probe-inspector-section.tsx +5 -4
- package/src/three/authoring/spatial-joint-handles.ts +1 -1
- package/src/three/authoring/spatial-lod-handles.ts +1 -1
- package/src/three/authoring/spatial-particle-handles.ts +1 -1
- package/src/three/authoring/three-authoring-adapter.ts +33 -33
- package/src/three/component-verbs/extract-menu.ts +8 -7
- package/src/three/component-verbs/fork-menu.ts +8 -7
- package/src/three/component-verbs/internals-menu.ts +3 -3
- package/src/three/drei-asset-caches.ts +13 -0
- package/src/three/story-documents/three-story-documents.tsx +24 -34
- package/src/three/three-board/ThreeBoardDocument.tsx +25 -25
- package/src/three/three-board/board-scene.ts +7 -7
- package/src/three/three-board/three-component-board.ts +5 -5
- package/contributions/react/pasteboard.action.ts +0 -41
- package/contributions/xstate-behavior.action.ts +0 -42
- package/contributions/xstate-behavior.document.tsx +0 -73
- package/contributions/xstate-behavior.inspector.tsx +0 -28
- package/contributions/xstate-behavior.menu.ts +0 -23
- package/src/react/pasteboard-materialize.ts +0 -154
- package/src/services/game-audio-unlock.ts +0 -48
- package/src/xstate/XStateBehaviorSection.tsx +0 -130
- package/src/xstate/XStateMachineInspector.tsx +0 -566
- package/src/xstate/character-animation-machine.fixture.ts +0 -74
- package/src/xstate/live-behaviors.ts +0 -106
- package/src/xstate/use-live-actor-state.ts +0 -44
- package/src/xstate/xstate-graph.ts +0 -235
- package/src/xstate/xstate-layout.ts +0 -76
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* the owner has retired AND no borrow is outstanding.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { disposeStoryObject3D, type MountedStoryObject3D } from '
|
|
19
|
+
import { disposeStoryObject3D, type MountedStoryObject3D } from './story-three-preview';
|
|
20
20
|
|
|
21
21
|
export interface MountedStoryViewportSource {
|
|
22
22
|
readonly build: () => {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, headless facts about a CANVAS story: whether a mounted story holds
|
|
3
|
+
* Pixi content. The exact counterpart of `three-story-model.ts`'s
|
|
4
|
+
* `mountedStoryHasThreeContent`, kept pure and off the React tree.
|
|
5
|
+
*
|
|
6
|
+
* Membership is DECLARED (`story-declared-medium.ts`). This module does not
|
|
7
|
+
* classify a story by mounting it.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Container } from 'pixi.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Whether a mounted story's stage actually holds Pixi content.
|
|
14
|
+
*
|
|
15
|
+
* A story can mount a `<Application>` successfully and render nothing into it —
|
|
16
|
+
* a gate whose condition never became true, a component that returns `null`.
|
|
17
|
+
* That is a successful mount with nothing in it, and a surface shows a story
|
|
18
|
+
* that rendered SOMETHING; "the mount succeeded" alone is not the test. Exactly
|
|
19
|
+
* the rule (and exactly the expression) `mountedStoryHasThreeContent` states
|
|
20
|
+
* for the three surface.
|
|
21
|
+
*/
|
|
22
|
+
export function mountedStoryHasPixiContent(stage: Container): boolean {
|
|
23
|
+
if (stage.children.length === 0) return false;
|
|
24
|
+
// An empty wrapper (`<pixiContainer>` around a still-loading piece) is
|
|
25
|
+
// a child and not content. Isolation and IsolatedPiece both land pixels
|
|
26
|
+
// after an async asset load; a 0×0 stage is the same "gate never true"
|
|
27
|
+
// case the header names.
|
|
28
|
+
const bounds = stage.getBounds();
|
|
29
|
+
return bounds.width > 1 && bounds.height > 1;
|
|
30
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The captures the kit's canvas medium provides: story thumbnails
|
|
3
|
+
* (`@volter/editor-sdk/kit/story-thumbnails`) for a canvas story in an
|
|
4
|
+
* offscreen Pixi application (the three.js leg is `three-story-captures.ts`), and
|
|
5
|
+
* Pixi's same-frame canvas pixels (`@volter/editor-sdk/kit/canvas-frames`), for
|
|
6
|
+
* the canvases its live surfaces present and the ones a photographed mount
|
|
7
|
+
* creates. Each loads its renderer on first use.
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
type CanvasFrame,
|
|
11
|
+
registerCanvasFrameSource,
|
|
12
|
+
registerCanvasMountObserver,
|
|
13
|
+
} from '@volter/editor-sdk/kit/canvas-frames';
|
|
14
|
+
import { registerStoryThumbnailCapture } from '@volter/editor-sdk/kit/story-thumbnails';
|
|
15
|
+
|
|
16
|
+
export function registerStoryMediaCaptures(): () => void {
|
|
17
|
+
const stopCanvas = registerStoryThumbnailCapture('canvas', async (component, options) => {
|
|
18
|
+
const { capturePixiStoryThumbnail } = await import('./story-pixi-preview');
|
|
19
|
+
return capturePixiStoryThumbnail(component as Parameters<typeof capturePixiStoryThumbnail>[0], options);
|
|
20
|
+
});
|
|
21
|
+
const stopFrames = registerCanvasFrameSource(async (canvas) =>
|
|
22
|
+
(await import('../canvas-preview-frames')).presentedPixiFrame(canvas),
|
|
23
|
+
);
|
|
24
|
+
// Every `Application` a photographed mount creates is collected, so the two
|
|
25
|
+
// things a Pixi canvas's capture needs — a settled first frame, and a readback
|
|
26
|
+
// the compositor cannot clear — are keyed on the MOUNT
|
|
27
|
+
// (`canvas-preview-frames.ts` owns both, with the measurement).
|
|
28
|
+
const stopObserver = registerCanvasMountObserver({
|
|
29
|
+
observe: async (task) => {
|
|
30
|
+
const { pixiCanvasFrame, withApplicationCollector } = await import('../canvas-preview-frames');
|
|
31
|
+
const { awaitPixiStoryFrame } = await import('./story-pixi-preview');
|
|
32
|
+
let apps: Parameters<typeof awaitPixiStoryFrame>[0] = [];
|
|
33
|
+
const result = await withApplicationCollector(async (created) => {
|
|
34
|
+
apps = created;
|
|
35
|
+
return task();
|
|
36
|
+
});
|
|
37
|
+
const frame: CanvasFrame = pixiCanvasFrame(apps);
|
|
38
|
+
return { result, settled: () => awaitPixiStoryFrame(apps), frame };
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return () => {
|
|
42
|
+
stopCanvas();
|
|
43
|
+
stopFrames();
|
|
44
|
+
stopObserver();
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
* pass publishes.
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
|
-
import { getProjectStoryRegions } from '@volter/editor-
|
|
36
|
+
import { getProjectStoryRegions } from '@volter/editor-sdk/kit/stories/project-story-regions';
|
|
37
37
|
import {
|
|
38
38
|
__resetUndeclaredStoryMediumReportsForTest,
|
|
39
39
|
declaredStoryMedium,
|
|
40
40
|
reportUndeclaredStoryMedium,
|
|
41
41
|
type StoryMedium,
|
|
42
|
-
} from '@volter/editor-
|
|
43
|
-
import { getProjectStoryModules, subscribeProjectStoryModules } from '@volter/editor-
|
|
42
|
+
} from '@volter/editor-sdk/kit/stories/story-declared-medium';
|
|
43
|
+
import { getProjectStoryModules, subscribeProjectStoryModules } from '@volter/editor-sdk/kit/stories/story-registry';
|
|
44
44
|
|
|
45
45
|
/** Which component boards the project's stories call for. */
|
|
46
46
|
export interface StoryMediaPresence {
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Off-screen CANVAS/Pixi story preview — mount a composed CSF story that
|
|
3
|
+
* produces Pixi content and hand back the `PIXI.Container` it rendered into,
|
|
4
|
+
* plus the `Application` that owns its clock.
|
|
5
|
+
*
|
|
6
|
+
* The canvas-surface sibling of `story-three-preview.ts`, and it exists for the
|
|
7
|
+
* same reason: portable CSF is a prefab's explicit "here is how this renders",
|
|
8
|
+
* and a surface that wants to SHOW a prefab (the canvas board, and the
|
|
9
|
+
* qualification both boards key membership on) needs that render as an object,
|
|
10
|
+
* not as a picture.
|
|
11
|
+
*
|
|
12
|
+
* ## Why the mount goes through react-dom, not through a Pixi root
|
|
13
|
+
*
|
|
14
|
+
* A 2D prefab story mounts its OWN `@pixi/react` `<Application>` (see any 2D prefab's colocated
|
|
15
|
+
* story under an example project's `prefabs` directory): unlike the three lane, where the
|
|
16
|
+
* board supplies the scene and the story renders bare JSX into it, nothing
|
|
17
|
+
* supplies a canvas-surface stage, so the story carries one and renders
|
|
18
|
+
* anywhere react-dom renders. That is the story's own declaration and this
|
|
19
|
+
* module does not second-guess it — it mounts the story exactly as written, in
|
|
20
|
+
* a detached DOM host, and then ADOPTS what came out.
|
|
21
|
+
*
|
|
22
|
+
* ## What comes out, and what the caller may do with it
|
|
23
|
+
*
|
|
24
|
+
* `host` is the detached element the story rendered into and `stage` is the
|
|
25
|
+
* Application's own root container — the REAL objects, with the story's real
|
|
26
|
+
* textures on them. A caller that wants to DISPLAY the story reparents the
|
|
27
|
+
* host into its own DOM (the `2D` board does exactly this, one host per
|
|
28
|
+
* frame); nothing here rasterizes, copies, or re-creates anything, which is
|
|
29
|
+
* what keeps the composed story render the sole visual authority (the
|
|
30
|
+
* anti-shim rule) and what makes a `scaleMode: 'nearest'` sheet magnify
|
|
31
|
+
* nearest wherever it ends up — the filtering belongs to the texture's own
|
|
32
|
+
* source, and adopting the real render carries it along.
|
|
33
|
+
*
|
|
34
|
+
* The mount arrives STOPPED: `Application.init` defaults to `autoStart: true`,
|
|
35
|
+
* so the story's own `requestAnimationFrame` loop would run on wall time in a
|
|
36
|
+
* detached host nobody is looking at. {@link MountedStoryPixi.ticker} is that
|
|
37
|
+
* clock, handed to the caller so a surface that wants the cycle to PLAY drives
|
|
38
|
+
* it from its own frame instead — one clock per surface, none on wall time.
|
|
39
|
+
*
|
|
40
|
+
* ## Mounts are SERIAL, and one bad story never takes the rest down
|
|
41
|
+
*
|
|
42
|
+
* Same physics as the three lane, same queue (`story-mount-turn.ts`): a story's
|
|
43
|
+
* loaders touch process-global state (Pixi's `Assets` cache and the
|
|
44
|
+
* `@pixi/react` component catalogue are both module-global), so overlapping two
|
|
45
|
+
* mounts interleaves their setup. A story that crashes while rendering is
|
|
46
|
+
* caught by {@link CrashNullBoundary} and rejects in milliseconds with its own
|
|
47
|
+
* error rather than burning the ceiling below — the exact measurement the three
|
|
48
|
+
* lane records, and the reason a board over a project of non-canvas stories
|
|
49
|
+
* finishes at all.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
import type { Application, Container } from 'pixi.js';
|
|
53
|
+
import { createElement } from 'react';
|
|
54
|
+
import type { Root } from 'react-dom/client';
|
|
55
|
+
import { withApplicationCollector } from '../canvas-preview-frames';
|
|
56
|
+
import { CrashNullBoundary } from '@volter/editor-sdk/kit/crash-null-boundary';
|
|
57
|
+
import { mountedStoryHasPixiContent } from './pixi-story-model';
|
|
58
|
+
import { resolveStoryDomRuntime } from '@volter/editor-sdk/kit/stories/story-dom-runtime';
|
|
59
|
+
import { runInStoryMountTurn } from '@volter/editor-sdk/kit/stories/story-mount-turn';
|
|
60
|
+
import type { StoryPreviewComponent } from '@volter/editor-sdk/kit/stories/story-preview-component';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* How long the mount waits for the story's `Application` to finish initializing
|
|
64
|
+
* AND for its stage to hold content. Both are legitimately slow: `init()`
|
|
65
|
+
* creates a real renderer, and a prefab story commonly gates its children on
|
|
66
|
+
* its atlas (`SpritesReady`), so the content arrives when the sheet does.
|
|
67
|
+
* Matches the three lane's own first-commit ceiling.
|
|
68
|
+
*/
|
|
69
|
+
const PIXI_MOUNT_TIMEOUT_MS = 10_000;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Event-loop turns the mount waits for the story's FIRST DOM commit, and for a
|
|
73
|
+
* `<canvas>` to appear in it. This is the cheap NO: a story that renders DOM
|
|
74
|
+
* and no canvas is not a canvas story, and it must answer in a few turns rather
|
|
75
|
+
* than at the ceiling above — otherwise every board build over a UI-only
|
|
76
|
+
* project would pay ten seconds per story.
|
|
77
|
+
*/
|
|
78
|
+
const COMMIT_TURNS = 12;
|
|
79
|
+
|
|
80
|
+
/** Where the detached host sits: far off-screen, contained, inert. `contain:
|
|
81
|
+
* strict` makes the host the containing block for `position: fixed`
|
|
82
|
+
* descendants, so a story that pins itself cannot flash across the editor —
|
|
83
|
+
* the same containment `story-capture.ts` documents for its own host. */
|
|
84
|
+
const HOST_STYLE =
|
|
85
|
+
'position:fixed;left:-20000px;top:0;width:512px;height:512px;' +
|
|
86
|
+
'overflow:hidden;contain:strict;pointer-events:none;visibility:hidden;';
|
|
87
|
+
|
|
88
|
+
/** One mounted canvas story. */
|
|
89
|
+
export interface MountedStoryPixi {
|
|
90
|
+
/**
|
|
91
|
+
* The detached element the story rendered into, with its `<canvas>` inside.
|
|
92
|
+
*
|
|
93
|
+
* A DOM surface that wants to SHOW this story reparents THIS element — never
|
|
94
|
+
* the canvas alone. React owns the host's children: moving a react-rendered
|
|
95
|
+
* node out from under its own root leaves `root.unmount()` removing a child
|
|
96
|
+
* of a parent it no longer has, which throws. The host itself is ours, so it
|
|
97
|
+
* travels freely and {@link dispose} still removes it from wherever it ended
|
|
98
|
+
* up. Restyle it on adoption; {@link HOST_STYLE} only parks it off-screen.
|
|
99
|
+
*/
|
|
100
|
+
readonly host: HTMLElement;
|
|
101
|
+
/** The Application's own root container — the story's real display tree.
|
|
102
|
+
* Reparent it; never copy it. */
|
|
103
|
+
readonly stage: Container;
|
|
104
|
+
/** The story's clock, already STOPPED. A surface that wants the story's
|
|
105
|
+
* cycle to play calls `ticker.update(elapsedMs)` from its own frame. */
|
|
106
|
+
readonly ticker: Application['ticker'];
|
|
107
|
+
/**
|
|
108
|
+
* The Application the story built — its own renderer included.
|
|
109
|
+
*
|
|
110
|
+
* A displaying surface keeps that renderer (it is what draws the adopted
|
|
111
|
+
* host's canvas) and therefore keeps its WebGL context. That is the real
|
|
112
|
+
* ceiling on how many stories one surface may show at once: a browser allows
|
|
113
|
+
* on the order of sixteen contexts per page and drops the OLDEST when that
|
|
114
|
+
* runs out. `app.screen` is the mounting canvas the story's own
|
|
115
|
+
* `<Application>` declared; presentation surfaces may crop it to stage
|
|
116
|
+
* content unless the story explicitly requests a fullscreen layout.
|
|
117
|
+
*/
|
|
118
|
+
readonly app: Application;
|
|
119
|
+
/** Tear the isolated react root down and destroy the story's display tree and
|
|
120
|
+
* clock. Runs the story's own effect cleanups. Idempotent. */
|
|
121
|
+
dispose(): void;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** A mount function valid inside an already-acquired story turn — the canvas
|
|
125
|
+
* sibling of `story-three-preview.ts`'s `StoryMountInTurn`. */
|
|
126
|
+
export type PixiStoryMountInTurn = (
|
|
127
|
+
Component: StoryPreviewComponent,
|
|
128
|
+
props?: Record<string, unknown>,
|
|
129
|
+
) => Promise<MountedStoryPixi>;
|
|
130
|
+
|
|
131
|
+
/** Run one compound canvas-story operation (a whole board build) with no other
|
|
132
|
+
* story mount, of any medium, interleaving it. */
|
|
133
|
+
export function withPixiStoryMountTurn<T>(
|
|
134
|
+
task: (mount: PixiStoryMountInTurn) => Promise<T>,
|
|
135
|
+
): Promise<T> {
|
|
136
|
+
return runInStoryMountTurn(() => task(mountStoryPixiInTurn));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Mount one canvas story off-screen, taking a turn of its own. */
|
|
140
|
+
export function mountStoryPixi(
|
|
141
|
+
Component: StoryPreviewComponent,
|
|
142
|
+
props: Record<string, unknown> = {},
|
|
143
|
+
): Promise<MountedStoryPixi> {
|
|
144
|
+
return withPixiStoryMountTurn((mount) => mount(Component, props));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Dispose a standalone mount in the same shared turn domain. Compound callers
|
|
148
|
+
* already holding a turn dispose their own mounts inside it. */
|
|
149
|
+
export function disposeStoryPixi(mounted: MountedStoryPixi): Promise<void> {
|
|
150
|
+
return runInStoryMountTurn(async () => mounted.dispose());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface PixiStoryThumbnailOptions {
|
|
154
|
+
readonly width?: number;
|
|
155
|
+
readonly height?: number;
|
|
156
|
+
readonly props?: Record<string, unknown>;
|
|
157
|
+
/** Storybook's standard layout policy. Only `fullscreen` makes the
|
|
158
|
+
* mounting Application's declared canvas part of the preview. */
|
|
159
|
+
readonly layout?: 'centered' | 'padded' | 'fullscreen';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Photograph a canvas prefab from its portable CSF story.
|
|
164
|
+
*
|
|
165
|
+
* This is the Pixi sibling of `captureStoryComponentThumbnail`: the story is
|
|
166
|
+
* mounted through its own real `<Application>`, its paused stage is rendered
|
|
167
|
+
* once without advancing the ticker, and Pixi's extraction API reads the
|
|
168
|
+
* result back. The requested rectangle is presentation only; it never becomes
|
|
169
|
+
* a second authored size or a component-specific thumbnail asset.
|
|
170
|
+
*/
|
|
171
|
+
export async function capturePixiStoryThumbnail(
|
|
172
|
+
Component: StoryPreviewComponent,
|
|
173
|
+
options: PixiStoryThumbnailOptions = {},
|
|
174
|
+
): Promise<string> {
|
|
175
|
+
const mounted = await mountStoryPixi(Component, options.props ?? {});
|
|
176
|
+
try {
|
|
177
|
+
const width = options.width ?? 128;
|
|
178
|
+
const height = options.height ?? 96;
|
|
179
|
+
mounted.app.renderer.render({ container: mounted.stage });
|
|
180
|
+
|
|
181
|
+
const bounds = mounted.stage.getBounds();
|
|
182
|
+
const fullscreen = options.layout === 'fullscreen';
|
|
183
|
+
const padding = 8;
|
|
184
|
+
// Both frames are the STORY's own `Rectangle` — `screen.clone()` is the
|
|
185
|
+
// app's class, whatever graph the story mounted in. A story's
|
|
186
|
+
// `<Application>` comes from its own module graph (the project's under the
|
|
187
|
+
// packaged runtime), and this rectangle is handed straight back to that
|
|
188
|
+
// app's extractor; constructing the shell's class here was the one value
|
|
189
|
+
// in this path that crossed.
|
|
190
|
+
const frame = mounted.app.screen.clone();
|
|
191
|
+
if (!fullscreen) {
|
|
192
|
+
frame.x = bounds.x - padding;
|
|
193
|
+
frame.y = bounds.y - padding;
|
|
194
|
+
frame.width = Math.max(1, bounds.width + padding * 2);
|
|
195
|
+
frame.height = Math.max(1, bounds.height + padding * 2);
|
|
196
|
+
}
|
|
197
|
+
// Match the target aspect by adding transparent breathing room. Never
|
|
198
|
+
// stretch a sprite to make a rectangular thumbnail.
|
|
199
|
+
const targetAspect = width / height;
|
|
200
|
+
if (!fullscreen && frame.width / frame.height < targetAspect) {
|
|
201
|
+
const nextWidth = frame.height * targetAspect;
|
|
202
|
+
frame.x -= (nextWidth - frame.width) / 2;
|
|
203
|
+
frame.width = nextWidth;
|
|
204
|
+
} else if (!fullscreen && frame.width / frame.height > targetAspect) {
|
|
205
|
+
const nextHeight = frame.width / targetAspect;
|
|
206
|
+
frame.y -= (nextHeight - frame.height) / 2;
|
|
207
|
+
frame.height = nextHeight;
|
|
208
|
+
}
|
|
209
|
+
const background = mounted.app.renderer.background;
|
|
210
|
+
const source = (await mounted.app.renderer.extract.canvas({
|
|
211
|
+
target: mounted.stage,
|
|
212
|
+
frame,
|
|
213
|
+
...(fullscreen && background.alpha > 0 ? { clearColor: background.colorRgba } : {}),
|
|
214
|
+
})) as HTMLCanvasElement;
|
|
215
|
+
const output = document.createElement('canvas');
|
|
216
|
+
output.width = width;
|
|
217
|
+
output.height = height;
|
|
218
|
+
const context = output.getContext('2d');
|
|
219
|
+
if (!context) throw new Error('Canvas story preview could not create a 2D readback context.');
|
|
220
|
+
|
|
221
|
+
const scale = Math.min(width / source.width, height / source.height);
|
|
222
|
+
const drawWidth = source.width * scale;
|
|
223
|
+
const drawHeight = source.height * scale;
|
|
224
|
+
context.drawImage(
|
|
225
|
+
source,
|
|
226
|
+
(width - drawWidth) / 2,
|
|
227
|
+
(height - drawHeight) / 2,
|
|
228
|
+
drawWidth,
|
|
229
|
+
drawHeight,
|
|
230
|
+
);
|
|
231
|
+
return output.toDataURL('image/png');
|
|
232
|
+
} finally {
|
|
233
|
+
await disposeStoryPixi(mounted);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const nextTurn = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));
|
|
238
|
+
|
|
239
|
+
/** The mount body for callers that already own the story turn. */
|
|
240
|
+
async function mountStoryPixiInTurn(
|
|
241
|
+
StoryComponent: StoryPreviewComponent,
|
|
242
|
+
props: Record<string, unknown> = {},
|
|
243
|
+
): Promise<MountedStoryPixi> {
|
|
244
|
+
// Loaders first (Storybook 9's portable-story contract): a story's `loaded`
|
|
245
|
+
// data is empty until `.load()` has run, and a canvas prefab's loader is
|
|
246
|
+
// where it fetches its atlas.
|
|
247
|
+
if (typeof StoryComponent.load === 'function') await StoryComponent.load();
|
|
248
|
+
|
|
249
|
+
// In the packaged editor the CSF component and @pixi/react both belong to
|
|
250
|
+
// the project's Vite graph. Mount them with that graph's react-dom too;
|
|
251
|
+
// using the prebuilt editor bundle's createRoot crosses React dispatchers.
|
|
252
|
+
const storyRuntime = await resolveStoryDomRuntime();
|
|
253
|
+
|
|
254
|
+
const host = document.createElement('div');
|
|
255
|
+
host.setAttribute('style', HOST_STYLE);
|
|
256
|
+
document.body.appendChild(host);
|
|
257
|
+
|
|
258
|
+
let root: Root | null = null;
|
|
259
|
+
const teardown = (): void => {
|
|
260
|
+
root?.unmount();
|
|
261
|
+
root = null;
|
|
262
|
+
host.remove();
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
return await withApplicationCollector(async (created) => {
|
|
267
|
+
let renderError: unknown = null;
|
|
268
|
+
root = storyRuntime.createRoot(host);
|
|
269
|
+
root.render(
|
|
270
|
+
createElement(
|
|
271
|
+
CrashNullBoundary,
|
|
272
|
+
{
|
|
273
|
+
onCaught: (error: unknown) => {
|
|
274
|
+
renderError ??= error;
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
createElement(StoryComponent, props),
|
|
278
|
+
),
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// The cheap NO: a committed story with no canvas in it is not a canvas
|
|
282
|
+
// story, and says so in a few turns.
|
|
283
|
+
for (let turn = 0; turn < COMMIT_TURNS; turn++) {
|
|
284
|
+
if (renderError) break;
|
|
285
|
+
if (host.querySelector('canvas')) break;
|
|
286
|
+
await nextTurn();
|
|
287
|
+
}
|
|
288
|
+
if (renderError) throw describeMountFailure(renderError);
|
|
289
|
+
if (!host.querySelector('canvas')) {
|
|
290
|
+
throw new Error('The story rendered no canvas — it does not mount Pixi content.');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// A canvas is present, so an `Application.init()` is in flight (or has
|
|
294
|
+
// already landed). From here the ceiling is the real one: init creates a
|
|
295
|
+
// renderer, and the story's own content may be gated on its atlas.
|
|
296
|
+
const deadline = Date.now() + PIXI_MOUNT_TIMEOUT_MS;
|
|
297
|
+
let app: Application | undefined;
|
|
298
|
+
while (Date.now() < deadline) {
|
|
299
|
+
if (renderError) throw describeMountFailure(renderError);
|
|
300
|
+
app ??= created[0];
|
|
301
|
+
if (app && mountedStoryHasPixiContent(app.stage)) break;
|
|
302
|
+
await nextTurn();
|
|
303
|
+
}
|
|
304
|
+
if (!app) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
'The story rendered a canvas but never initialized a Pixi Application within ' +
|
|
307
|
+
`${PIXI_MOUNT_TIMEOUT_MS / 1000}s — it is not a canvas story, or its renderer ` +
|
|
308
|
+
'could not be created here.',
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Content time does not run on wall time at design time. The story's own
|
|
313
|
+
// rAF loop stops here and the clock is handed to whoever presents it.
|
|
314
|
+
app.stop();
|
|
315
|
+
app.ticker.lastTime = 0;
|
|
316
|
+
|
|
317
|
+
let disposed = false;
|
|
318
|
+
return {
|
|
319
|
+
host,
|
|
320
|
+
stage: app.stage,
|
|
321
|
+
ticker: app.ticker,
|
|
322
|
+
app,
|
|
323
|
+
dispose(): void {
|
|
324
|
+
if (disposed) return;
|
|
325
|
+
disposed = true;
|
|
326
|
+
// React, and react ONLY. Unmounting the story's `<Application>` is
|
|
327
|
+
// what tears the Application down: `@pixi/react`'s own unmount path
|
|
328
|
+
// (`helpers/unmountRoot`) renders `null` into the stage — running
|
|
329
|
+
// every `useTick` cleanup in order — and only then destroys the app
|
|
330
|
+
// and forgets its canvas. Destroying anything here as well is a
|
|
331
|
+
// double free of a ticker its cleanups are still using.
|
|
332
|
+
teardown();
|
|
333
|
+
},
|
|
334
|
+
} satisfies MountedStoryPixi;
|
|
335
|
+
});
|
|
336
|
+
} catch (error) {
|
|
337
|
+
teardown();
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ------------------------------------------------- the capture lane's frame
|
|
343
|
+
|
|
344
|
+
/** How long {@link awaitPixiStoryFrame} waits. Same ceiling, same reasons, as
|
|
345
|
+
* the mount above: a real renderer is created and a prefab's content commonly
|
|
346
|
+
* waits on its atlas. */
|
|
347
|
+
const FIRST_FRAME_TIMEOUT_MS = PIXI_MOUNT_TIMEOUT_MS;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Wait until every collected `Application` has PRESENTED a frame with content
|
|
351
|
+
* in it — the readiness test a capture of a canvas story owes itself.
|
|
352
|
+
*
|
|
353
|
+
* MEASURED DEFECT (this is why the function exists): `vgai screenshot
|
|
354
|
+
* <file>.stories.tsx` photographed a `@pixi/react` story blank. The capture
|
|
355
|
+
* lane's own readiness test is "the container has committed child nodes", and
|
|
356
|
+
* `<Application>` commits its `<canvas>` on the FIRST commit — before
|
|
357
|
+
* `Application.init()` has even created a renderer, and long before an atlas
|
|
358
|
+
* the story gates its children on has resolved. So the sheet was rasterized
|
|
359
|
+
* from an empty canvas and looked like a perfectly plausible blank frame, which
|
|
360
|
+
* is the worst failure a look verb has.
|
|
361
|
+
*
|
|
362
|
+
* Two conditions, because either alone still lies: a renderer that has drawn an
|
|
363
|
+
* empty stage has presented a frame, and a stage with children whose renderer
|
|
364
|
+
* has not run yet has no pixels.
|
|
365
|
+
*
|
|
366
|
+
* Resolves (rather than throwing) at the ceiling: a story that genuinely never
|
|
367
|
+
* draws must still be photographed, and an honestly blank cell is the truth
|
|
368
|
+
* about it.
|
|
369
|
+
*/
|
|
370
|
+
export async function awaitPixiStoryFrame(
|
|
371
|
+
apps: readonly Application[],
|
|
372
|
+
now: () => number = Date.now,
|
|
373
|
+
): Promise<void> {
|
|
374
|
+
if (apps.length === 0) return;
|
|
375
|
+
const presented = new Set<Application>();
|
|
376
|
+
const subscriptions = apps.map((app) => {
|
|
377
|
+
const observer = {
|
|
378
|
+
postrender: () => {
|
|
379
|
+
presented.add(app);
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
app.renderer?.runners.postrender.add(observer);
|
|
383
|
+
return { app, observer };
|
|
384
|
+
});
|
|
385
|
+
try {
|
|
386
|
+
const deadline = now() + FIRST_FRAME_TIMEOUT_MS;
|
|
387
|
+
while (now() < deadline) {
|
|
388
|
+
const ready = apps.every(
|
|
389
|
+
(app) =>
|
|
390
|
+
app.stage !== undefined && mountedStoryHasPixiContent(app.stage) && presented.has(app),
|
|
391
|
+
);
|
|
392
|
+
if (ready) return;
|
|
393
|
+
await nextTurn();
|
|
394
|
+
}
|
|
395
|
+
} finally {
|
|
396
|
+
for (const { app, observer } of subscriptions) {
|
|
397
|
+
app.renderer?.runners.postrender.remove(observer);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function describeMountFailure(error: unknown): Error {
|
|
403
|
+
return new Error(
|
|
404
|
+
'mountStoryPixi: the story threw while rendering — it does not mount Pixi content, or its ' +
|
|
405
|
+
`component crashed. Original error: ${error instanceof Error ? error.message : String(error)}`,
|
|
406
|
+
{ cause: error },
|
|
407
|
+
);
|
|
408
|
+
}
|