@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
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E3/E4 demo + test fixture — mirrors `characterAnimationMachine`
|
|
3
|
-
* (`examples/third-person/src/components.ts`) EXACTLY (same states, events,
|
|
4
|
-
* guards, `meta.animation`). Not a re-export: that machine is a module-
|
|
5
|
-
* private `const` in the example game (never exported), and
|
|
6
|
-
* `examples/third-person` is outside `packages/editor`'s tsconfig `include`
|
|
7
|
-
* (no path alias reaches it — confirmed: `packages/editor/tsconfig*.json`
|
|
8
|
-
* only maps the runtime packages), so the editor package cannot
|
|
9
|
-
* import it directly without either widening the example's public surface or
|
|
10
|
-
* adding a new cross-package path alias — both bigger changes than this lean
|
|
11
|
-
* inspector unit warrants. A representative fixture machine is
|
|
12
|
-
* spec-sanctioned for exactly this case ("import it or a representative
|
|
13
|
-
* fixture machine").
|
|
14
|
-
*
|
|
15
|
-
* Keep this in sync with `examples/third-person/src/components.ts` by hand if
|
|
16
|
-
* that machine's shape ever changes — there is no structural coupling
|
|
17
|
-
* enforcing it (a comment-only contract, same as any other fixture mirroring
|
|
18
|
-
* real game code).
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { assign, setup as xstateSetup } from 'xstate';
|
|
22
|
-
|
|
23
|
-
export const characterAnimationMachine = xstateSetup({
|
|
24
|
-
types: {
|
|
25
|
-
context: {} as { speed: number; grounded: boolean },
|
|
26
|
-
events: {} as { type: 'UPDATE'; speed: number; grounded: boolean } | { type: 'JUMP' },
|
|
27
|
-
},
|
|
28
|
-
}).createMachine({
|
|
29
|
-
id: 'character-animation',
|
|
30
|
-
initial: 'idle',
|
|
31
|
-
context: { speed: 0, grounded: true },
|
|
32
|
-
on: {
|
|
33
|
-
UPDATE: {
|
|
34
|
-
actions: assign({
|
|
35
|
-
speed: ({ event }) => event.speed,
|
|
36
|
-
grounded: ({ event }) => event.grounded,
|
|
37
|
-
}),
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
states: {
|
|
41
|
-
idle: {
|
|
42
|
-
meta: { animation: { clip: 'idle', loop: true, crossfade: { duration: 0.2 } } },
|
|
43
|
-
on: { JUMP: 'airborne' },
|
|
44
|
-
always: { guard: ({ context }) => context.speed > 0.1, target: 'locomotion' },
|
|
45
|
-
},
|
|
46
|
-
locomotion: {
|
|
47
|
-
meta: {
|
|
48
|
-
animation: {
|
|
49
|
-
blendTree: {
|
|
50
|
-
type: '1D',
|
|
51
|
-
parameter: 'speed',
|
|
52
|
-
children: [
|
|
53
|
-
{ clip: 'walk', threshold: 1 },
|
|
54
|
-
{ clip: 'run', threshold: 5 },
|
|
55
|
-
],
|
|
56
|
-
},
|
|
57
|
-
crossfade: { duration: 0.2 },
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
on: { JUMP: 'airborne' },
|
|
61
|
-
always: { guard: ({ context }) => context.speed <= 0.1, target: 'idle' },
|
|
62
|
-
},
|
|
63
|
-
airborne: {
|
|
64
|
-
meta: { animation: { clip: 'jump', loop: false, crossfade: { duration: 0.1 } } },
|
|
65
|
-
always: [
|
|
66
|
-
{
|
|
67
|
-
guard: ({ context }) => context.grounded && context.speed > 0.1,
|
|
68
|
-
target: 'locomotion',
|
|
69
|
-
},
|
|
70
|
-
{ guard: ({ context }) => context.grounded, target: 'idle' },
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
});
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XState BEHAVIOR — the live debugger's reads over the editor's HIERARCHY
|
|
3
|
-
* DOOR (`editorHost().hierarchy`), and the one subject the Behavior document
|
|
4
|
-
* draws. The graph is a derived live debugger over a real actor; it is never
|
|
5
|
-
* an animation view or a second persisted machine.
|
|
6
|
-
*
|
|
7
|
-
* NO fabricated hierarchy rows, NO demo machine anywhere: the shipped
|
|
8
|
-
* `character-animation-machine.fixture.ts` is a TEST/story fixture only
|
|
9
|
-
* (`packages/editor/test/xstate-fixture-ban.test.ts` pins that), and every
|
|
10
|
-
* open path here requires an actual live actor — with none, nothing renders
|
|
11
|
-
* and nothing can be opened. The starter template has no XState machine, so a
|
|
12
|
-
* template session shows no XState UI at all.
|
|
13
|
-
*
|
|
14
|
-
* `Open Source` (§4.3 mentions it MAY exist): deliberately NOT built —
|
|
15
|
-
* neither XState nor the inspection carries machine→source-file identity, and
|
|
16
|
-
* fabricating one (e.g. grepping for the machine id) would violate §4.3's
|
|
17
|
-
* "no ids decoded for magic words". When the engine grows a real
|
|
18
|
-
* machine→module provenance, the section is where it lands.
|
|
19
|
-
*
|
|
20
|
-
* Editing stays a code change: the graph view is inspect-only.
|
|
21
|
-
*
|
|
22
|
-
* THE DOCUMENT IS ONE. A contributed `workspace.document` is registered under
|
|
23
|
-
* its own contribution id, so the editor holds exactly one Behavior tab and
|
|
24
|
-
* this module holds WHICH live actor it shows. Before the move into this
|
|
25
|
-
* package the host opened a document per node (`behavior:xstate:<nodeId>`);
|
|
26
|
-
* a package reaches the center through the contribution point, which has no
|
|
27
|
-
* per-subject form, and one Behavior surface that follows the actor you asked
|
|
28
|
-
* for is the honest shape of that point.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
import { editorHost } from '@volter/editor-sdk/host';
|
|
32
|
-
import type { XStateBehaviorInspection } from '@volter/threejs-runtime/behavior/xstate-inspection';
|
|
33
|
-
import { getUserData } from '@volter/threejs-runtime/ecs/user-data';
|
|
34
|
-
|
|
35
|
-
/** This package's `xstate-behavior.document.tsx`, by the id the host derives
|
|
36
|
-
* from its filename (the workspace tab is `tool:` + this). */
|
|
37
|
-
export const BEHAVIOR_DOCUMENT_ID = 'xstate-behavior.document';
|
|
38
|
-
|
|
39
|
-
/** The node's explicitly registered live behavior, through the door. */
|
|
40
|
-
export function liveBehaviorFor(nodeId: string): XStateBehaviorInspection | undefined {
|
|
41
|
-
const object = editorHost().hierarchy.object(nodeId);
|
|
42
|
-
return object ? getUserData(object, '_xstateBehavior') : undefined;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface LiveBehaviorEntry {
|
|
46
|
-
readonly nodeId: string;
|
|
47
|
-
readonly machineId: string;
|
|
48
|
-
readonly label: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Every explicitly registered live behavior actor, one row per actor. */
|
|
52
|
-
export function listLiveBehaviors(): LiveBehaviorEntry[] {
|
|
53
|
-
const seen = new Set<XStateBehaviorInspection>();
|
|
54
|
-
const out: LiveBehaviorEntry[] = [];
|
|
55
|
-
for (const [nodeId, object] of editorHost().hierarchy.objects()) {
|
|
56
|
-
const behavior = getUserData(object, '_xstateBehavior');
|
|
57
|
-
if (!behavior || seen.has(behavior)) continue;
|
|
58
|
-
seen.add(behavior);
|
|
59
|
-
out.push({ nodeId, machineId: behavior.machine.id, label: object.name || nodeId });
|
|
60
|
-
}
|
|
61
|
-
return out;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// --- The document's subject -------------------------------------------------
|
|
65
|
-
|
|
66
|
-
let subjectNodeId: string | null = null;
|
|
67
|
-
let subjectVersion = 0;
|
|
68
|
-
const subjectListeners = new Set<() => void>();
|
|
69
|
-
|
|
70
|
-
export function subscribeBehaviorSubject(listener: () => void): () => void {
|
|
71
|
-
subjectListeners.add(listener);
|
|
72
|
-
return () => {
|
|
73
|
-
subjectListeners.delete(listener);
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function behaviorSubjectVersion(): number {
|
|
78
|
-
return subjectVersion;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** The node whose actor the Behavior document draws, or null before any open. */
|
|
82
|
-
export function behaviorSubject(): string | null {
|
|
83
|
-
return subjectNodeId;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Open the Behavior document on one live actor. False — and nothing opens —
|
|
88
|
-
* when that node carries no live behavior, or when the document contribution
|
|
89
|
-
* is not registered (a build without this package).
|
|
90
|
-
*/
|
|
91
|
-
export function openBehaviorDocument(nodeId: string): boolean {
|
|
92
|
-
if (!liveBehaviorFor(nodeId)) return false;
|
|
93
|
-
if (subjectNodeId !== nodeId) {
|
|
94
|
-
subjectNodeId = nodeId;
|
|
95
|
-
subjectVersion++;
|
|
96
|
-
for (const listener of subjectListeners) listener();
|
|
97
|
-
}
|
|
98
|
-
return editorHost().workspace.openContributedDocument(BEHAVIOR_DOCUMENT_ID);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Open the first truthful live behavior, if one exists — what the Debug menu
|
|
102
|
-
* item and the standing palette action do. */
|
|
103
|
-
export function openFirstBehaviorDocument(): boolean {
|
|
104
|
-
const first = listLiveBehaviors()[0];
|
|
105
|
-
return first ? openBehaviorDocument(first.nodeId) : false;
|
|
106
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E3 ("Current state and active transition are highlighted during
|
|
3
|
-
* preview/play") — subscribes to a live XState actor and returns the full
|
|
4
|
-
* set of currently-active state ids, updating on every transition.
|
|
5
|
-
*
|
|
6
|
-
* Returns `null` (not `[]`) when no actor is provided, or the actor hasn't
|
|
7
|
-
* emitted a snapshot yet — callers should treat `null` as "fall back to the
|
|
8
|
-
* static `currentStateId` prop", and `[]` as "the actor genuinely reports no
|
|
9
|
-
* active animation-relevant states" (shouldn't normally happen for a running
|
|
10
|
-
* actor, but is a real distinct value from "no live actor at all").
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { InspectableXStateActor } from '@volter/threejs-runtime/behavior/xstate-inspection';
|
|
14
|
-
import { useEffect, useState } from 'react';
|
|
15
|
-
import type { AnyStateMachine, StateValue } from 'xstate';
|
|
16
|
-
import { activeStateIdsOf } from './xstate-graph';
|
|
17
|
-
|
|
18
|
-
// The actor is the engine binding's own minimal shape, not XState's
|
|
19
|
-
// `AnyActorRef` — see `XStateAnimationActor`'s note. It keeps `value` as
|
|
20
|
-
// `unknown`, so interpreting it as a `StateValue` is this inspector's job and
|
|
21
|
-
// happens in exactly the two places below.
|
|
22
|
-
export function useLiveActorState(
|
|
23
|
-
machine: AnyStateMachine,
|
|
24
|
-
actor: InspectableXStateActor | undefined,
|
|
25
|
-
): string[] | null {
|
|
26
|
-
const [activeIds, setActiveIds] = useState<string[] | null>(null);
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
if (!actor) {
|
|
30
|
-
setActiveIds(null);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
// Seed from whatever snapshot the actor already has (it may have started
|
|
34
|
-
// and transitioned before this component mounted), then follow every
|
|
35
|
-
// subsequent transition.
|
|
36
|
-
setActiveIds(activeStateIdsOf(machine, actor.getSnapshot().value as StateValue));
|
|
37
|
-
const subscription = actor.subscribe((snapshot) => {
|
|
38
|
-
setActiveIds(activeStateIdsOf(machine, snapshot.value as StateValue));
|
|
39
|
-
});
|
|
40
|
-
return () => subscription.unsubscribe();
|
|
41
|
-
}, [machine, actor]);
|
|
42
|
-
|
|
43
|
-
return activeIds;
|
|
44
|
-
}
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E3 (spec §11 E3) — pure, UI-free extraction of a renderable node/edge graph
|
|
3
|
-
* from a real XState v5 machine. No separate graph format is persisted
|
|
4
|
-
* anywhere (E3's own AC): this module reads `machine.root` (a `StateNode`) and
|
|
5
|
-
* its `.states`/`.on`/ `.always` directly, every time it's called —
|
|
6
|
-
* the "graph" is just a derived view, recomputed from the live machine
|
|
7
|
-
* configuration.
|
|
8
|
-
*
|
|
9
|
-
* ## Which xstate v5 accessors this uses, and why
|
|
10
|
-
*
|
|
11
|
-
* Confirmed against the installed `xstate@5.32.4` (`node_modules/xstate/dist/
|
|
12
|
-
* declarations/src/StateNode.d.ts`) and by instrumenting the real
|
|
13
|
-
* `characterAnimationMachine` (examples/third-person/src/components.ts) at
|
|
14
|
-
* runtime:
|
|
15
|
-
*
|
|
16
|
-
* - `StateNode.states: Record<string, StateNode>` — child state nodes,
|
|
17
|
-
* walked recursively for nested/compound machines. Absent (`{}`) on atomic
|
|
18
|
-
* leaves.
|
|
19
|
-
* - `StateNode.id` — the full dotted id (e.g. `"character-animation.idle"`),
|
|
20
|
-
* used as this module's node id throughout (stable, globally unique within
|
|
21
|
-
* one machine).
|
|
22
|
-
* - `StateNode.on: TransitionDefinitionMap` — a `Record<eventType,
|
|
23
|
-
* TransitionDefinition[]>` of this state's OWN event-triggered transitions
|
|
24
|
-
* (does NOT include ancestor/root `on` handlers with no target on this
|
|
25
|
-
* node, and does NOT include `always` — confirmed by probing the real
|
|
26
|
-
* machine: `idle.on` has only `JUMP`, not the root-level `UPDATE`).
|
|
27
|
-
* - `StateNode.always: TransitionDefinition[] | undefined` — eventless
|
|
28
|
-
* ("always"/guarded-every-microstep) transitions, kept SEPARATE from `.on`
|
|
29
|
-
* by xstate itself; each has `eventType: ''`. Rendered as edges labeled
|
|
30
|
-
* "always" rather than an event name.
|
|
31
|
-
* - `TransitionDefinition.target: readonly StateNode[] | undefined` —
|
|
32
|
-
* resolved target state node(s) (already-resolved, not a string to
|
|
33
|
-
* re-parse); `undefined` for an internal/actionless transition (no target
|
|
34
|
-
* state change) — those are skipped as edges (nothing to point an arrow at).
|
|
35
|
-
* - `TransitionDefinition.guard` — a string (named guard), a `{type, params}`
|
|
36
|
-
* object (parameterized named guard), or a plain predicate function
|
|
37
|
-
* (`(args, params) => boolean`, the common case for hand-authored
|
|
38
|
-
* machines like `characterAnimationMachine`, which uses inline
|
|
39
|
-
* `({ context }) => context.speed > 0.1` arrows). `guardLabel` below
|
|
40
|
-
* normalizes all three to a short display string.
|
|
41
|
-
* - `StateNode.type: 'atomic' | 'compound' | 'parallel' | 'final' |
|
|
42
|
-
* 'history'` — surfaced per node so the panel can show it (a compound/
|
|
43
|
-
* parallel state has children; the graph still renders it as its own node
|
|
44
|
-
* PLUS its children as separate nodes with a parent edge implied by
|
|
45
|
-
* nesting depth, not a literal drawn edge).
|
|
46
|
-
*
|
|
47
|
-
* No `getStateNodes()` call is needed here: that xstate helper resolves the
|
|
48
|
-
* set of state nodes ACTIVE for one `StateValue` (a snapshot-shaped query).
|
|
49
|
-
* This module wants the machine's full STATIC configuration (every declared
|
|
50
|
-
* state, reachable or not, active or not) — a plain recursive walk of
|
|
51
|
-
* `.states` from `machine.root` is the correct and sufficient tool for that.
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
|
-
import type { AnyStateMachine, AnyStateNode, StateValue } from 'xstate';
|
|
55
|
-
import { getStateNodes } from 'xstate';
|
|
56
|
-
|
|
57
|
-
export interface XStateGraphNode {
|
|
58
|
-
/** Full dotted state id, e.g. `"character-animation.locomotion"`. */
|
|
59
|
-
id: string;
|
|
60
|
-
/** The relative key, e.g. `"locomotion"`. */
|
|
61
|
-
key: string;
|
|
62
|
-
/** Dotted id of the immediate parent state node, or `null` for the root. */
|
|
63
|
-
parentId: string | null;
|
|
64
|
-
/** Depth from the root (root = 0), used by the layered layout. */
|
|
65
|
-
depth: number;
|
|
66
|
-
type: 'atomic' | 'compound' | 'parallel' | 'final' | 'history';
|
|
67
|
-
/** True for the machine's single configured initial leaf-path state at each level. */
|
|
68
|
-
isInitial: boolean;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface XStateGraphEdge {
|
|
72
|
-
/** Stable synthetic id for React `key` props: `${source}--${eventLabel}--${target}--${index}`. */
|
|
73
|
-
id: string;
|
|
74
|
-
source: string;
|
|
75
|
-
target: string;
|
|
76
|
-
/** The triggering event name, or `'always'` for eventless transitions. */
|
|
77
|
-
eventLabel: string;
|
|
78
|
-
/** `true` for an `always` (eventless) transition. */
|
|
79
|
-
isEventless: boolean;
|
|
80
|
-
/** Normalized guard display label, or `undefined` when the transition is unguarded. */
|
|
81
|
-
guardLabel: string | undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface XStateGraph {
|
|
85
|
-
machineId: string;
|
|
86
|
-
nodes: XStateGraphNode[];
|
|
87
|
-
edges: XStateGraphEdge[];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
91
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Normalize an xstate v5 `TransitionDefinition['guard']` to a short display
|
|
96
|
-
* label. Handles the three shapes xstate allows (confirmed above):
|
|
97
|
-
* - a string → the guard name verbatim (`"someNamedGuard"`).
|
|
98
|
-
* - a `{ type, params? }` object → `.type` (a parameterized named guard).
|
|
99
|
-
* - a function → its `.name` when xstate/JS gave it a real one (property-
|
|
100
|
-
* shorthand functions like `guard: someNamedFn` keep their declared name),
|
|
101
|
-
* otherwise (the common inline-arrow case, e.g.
|
|
102
|
-
* `({ context }) => context.speed > 0.1`) fall back to a truncated
|
|
103
|
-
* `.toString()` of the function body so the condition is still legible
|
|
104
|
-
* rather than showing a useless generic "guard" (xstate's own destructured-
|
|
105
|
-
* param naming quirk: an arrow assigned to an object property named
|
|
106
|
-
* `guard` is named `"guard"` by the JS engine itself, which conveys
|
|
107
|
-
* nothing — confirmed by probing the real `characterAnimationMachine`).
|
|
108
|
-
*/
|
|
109
|
-
export function guardLabel(guard: unknown): string | undefined {
|
|
110
|
-
if (guard == null) return undefined;
|
|
111
|
-
if (typeof guard === 'string') return guard;
|
|
112
|
-
if (typeof guard === 'function') {
|
|
113
|
-
const name = guard.name;
|
|
114
|
-
if (name && name !== 'guard' && name !== 'anonymous') return name;
|
|
115
|
-
const src = guard.toString().replace(/\s+/g, ' ').trim();
|
|
116
|
-
return src.length > 60 ? `${src.slice(0, 57)}...` : src;
|
|
117
|
-
}
|
|
118
|
-
if (isPlainObject(guard) && typeof guard['type'] === 'string') {
|
|
119
|
-
return guard['type'];
|
|
120
|
-
}
|
|
121
|
-
return '(guard)';
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Every dotted-id descendant state node of `node`, depth-first, including `node` itself. */
|
|
125
|
-
function* walkStateNodes(
|
|
126
|
-
node: AnyStateNode,
|
|
127
|
-
parentId: string | null,
|
|
128
|
-
depth: number,
|
|
129
|
-
): Generator<{
|
|
130
|
-
node: AnyStateNode;
|
|
131
|
-
parentId: string | null;
|
|
132
|
-
depth: number;
|
|
133
|
-
}> {
|
|
134
|
-
yield { node, parentId, depth };
|
|
135
|
-
for (const child of Object.values(node.states)) {
|
|
136
|
-
yield* walkStateNodes(child, node.id, depth + 1);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** True iff `node` is the configured initial child of its parent (root-relative; always true for the root itself). */
|
|
141
|
-
function isInitialChild(node: AnyStateNode): boolean {
|
|
142
|
-
const parent = node.parent;
|
|
143
|
-
if (!parent) return true;
|
|
144
|
-
if (parent.type !== 'compound') return false; // parallel/atomic parents have no single "initial" child
|
|
145
|
-
return parent.initial?.target?.some((t) => t.id === node.id) ?? false;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* One state node's outgoing edges: its own event-triggered (`on`) transitions
|
|
150
|
-
* plus its eventless (`always`) transitions — kept as a separate helper from
|
|
151
|
-
* {@link buildXStateGraph} purely to keep that function's cognitive
|
|
152
|
-
* complexity low; the logic is unchanged from a single inline loop.
|
|
153
|
-
*/
|
|
154
|
-
function edgesFromNode(node: AnyStateNode, nextEdgeId: () => number): XStateGraphEdge[] {
|
|
155
|
-
const edges: XStateGraphEdge[] = [];
|
|
156
|
-
for (const [eventType, defs] of Object.entries(node.on)) {
|
|
157
|
-
for (const def of defs) {
|
|
158
|
-
for (const target of def.target ?? []) {
|
|
159
|
-
edges.push({
|
|
160
|
-
id: `${node.id}--${eventType}--${target.id}--${nextEdgeId()}`,
|
|
161
|
-
source: node.id,
|
|
162
|
-
target: target.id,
|
|
163
|
-
eventLabel: eventType,
|
|
164
|
-
isEventless: false,
|
|
165
|
-
guardLabel: guardLabel(def.guard),
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
for (const def of node.always ?? []) {
|
|
171
|
-
for (const target of def.target ?? []) {
|
|
172
|
-
edges.push({
|
|
173
|
-
id: `${node.id}--always--${target.id}--${nextEdgeId()}`,
|
|
174
|
-
source: node.id,
|
|
175
|
-
target: target.id,
|
|
176
|
-
eventLabel: 'always',
|
|
177
|
-
isEventless: true,
|
|
178
|
-
guardLabel: guardLabel(def.guard),
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return edges;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Extract a renderable {@link XStateGraph} from a real XState machine.
|
|
187
|
-
* Accepts `AnyStateMachine` (what `setup(...).createMachine(...)` and
|
|
188
|
-
* `createMachine(...)` both return) — works for the shipped
|
|
189
|
-
* `characterAnimationMachine` and any other hand-authored machine.
|
|
190
|
-
*
|
|
191
|
-
* Animation metadata is intentionally absent: this graph represents behavior
|
|
192
|
-
* statechart structure only. Animation playback has its own native-runtime
|
|
193
|
-
* projection in the Animation workspace.
|
|
194
|
-
*/
|
|
195
|
-
export function buildXStateGraph(machine: AnyStateMachine): XStateGraph {
|
|
196
|
-
const root = machine.root;
|
|
197
|
-
|
|
198
|
-
const nodes: XStateGraphNode[] = [];
|
|
199
|
-
const edges: XStateGraphEdge[] = [];
|
|
200
|
-
let edgeSeq = 0;
|
|
201
|
-
const nextEdgeId = () => edgeSeq++;
|
|
202
|
-
|
|
203
|
-
for (const { node, parentId, depth } of walkStateNodes(root, null, 0)) {
|
|
204
|
-
nodes.push({
|
|
205
|
-
id: node.id,
|
|
206
|
-
key: node.key,
|
|
207
|
-
parentId,
|
|
208
|
-
depth,
|
|
209
|
-
type: node.type,
|
|
210
|
-
isInitial: isInitialChild(node),
|
|
211
|
-
});
|
|
212
|
-
edges.push(...edgesFromNode(node, nextEdgeId));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return { machineId: root.id, nodes, edges };
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* The full state ids XState reports as ACTIVE for a given snapshot value —
|
|
220
|
-
* every ancestor plus the leaf (e.g. a snapshot in `"locomotion"` reports
|
|
221
|
-
* both `"character-animation"` and `"character-animation.locomotion"`).
|
|
222
|
-
*
|
|
223
|
-
* Uses xstate's own `getStateNodes(rootNode, stateValue)` (`xstate` package
|
|
224
|
-
* root export, confirmed present in the installed v5.32.4 and exercised
|
|
225
|
-
* directly against a real machine) rather than `snapshot.getMeta()` —
|
|
226
|
-
* `getMeta()` only returns states that declare a `meta` object at all
|
|
227
|
-
* (confirmed by probing a real machine: a meta-less compound ancestor is
|
|
228
|
-
* silently absent from `getMeta()`'s keys even while active), which would
|
|
229
|
-
* under-highlight the graph for any machine with meta-less parent/compound
|
|
230
|
-
* states. `getStateNodes` returns the complete active set regardless of
|
|
231
|
-
* whether a node has `meta`.
|
|
232
|
-
*/
|
|
233
|
-
export function activeStateIdsOf(machine: AnyStateMachine, stateValue: StateValue): string[] {
|
|
234
|
-
return getStateNodes(machine.root, stateValue).map((n) => n.id);
|
|
235
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E3 — a deterministic, dependency-free layered layout for an
|
|
3
|
-
* {@link XStateGraph}. No graph-layout library exists in this repo (checked:
|
|
4
|
-
* no reactflow/dagre/elkjs/d3 in `packages/editor/package.json` or the root
|
|
5
|
-
* manifest) and the spec explicitly says a fancy graph engine is not
|
|
6
|
-
* required ("a simple deterministic layout is fine — grid/columns by depth").
|
|
7
|
-
*
|
|
8
|
-
* Layout rule: one COLUMN per state-tree depth (root=0, its children=1,
|
|
9
|
-
* grandchildren=2, …) — this both (a) reads naturally for the shallow
|
|
10
|
-
* behavior machines (§5.6's examples are 1-2 levels
|
|
11
|
-
* deep) and (b) guarantees a parent is always positioned before/left-of its
|
|
12
|
-
* children, which keeps nesting legible even before any edges are drawn.
|
|
13
|
-
* Within a column, nodes are stacked top-to-bottom in the machine's own
|
|
14
|
-
* declaration order (`StateNode.order` is xstate's own deterministic
|
|
15
|
-
* document-order counter) — so the layout is 100% a pure function of the
|
|
16
|
-
* machine, not randomized/force-simulated, and is stable across renders
|
|
17
|
-
* (required for a sane React key/position diff and for tests to assert
|
|
18
|
-
* exact pixel positions).
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import type { XStateGraph, XStateGraphNode } from './xstate-graph';
|
|
22
|
-
|
|
23
|
-
export interface LayoutNode {
|
|
24
|
-
node: XStateGraphNode;
|
|
25
|
-
x: number;
|
|
26
|
-
y: number;
|
|
27
|
-
width: number;
|
|
28
|
-
height: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface XStateLayout {
|
|
32
|
-
nodes: LayoutNode[];
|
|
33
|
-
positionById: Map<string, LayoutNode>;
|
|
34
|
-
width: number;
|
|
35
|
-
height: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const COLUMN_WIDTH = 220;
|
|
39
|
-
const COLUMN_GAP = 100;
|
|
40
|
-
const ROW_HEIGHT = 64;
|
|
41
|
-
const ROW_GAP = 24;
|
|
42
|
-
const NODE_WIDTH = 180;
|
|
43
|
-
const MARGIN = 24;
|
|
44
|
-
|
|
45
|
-
/** Lay out a graph's nodes into depth-based columns, declaration order within each column. */
|
|
46
|
-
export function layoutXStateGraph(graph: XStateGraph): XStateLayout {
|
|
47
|
-
const byDepth = new Map<number, XStateGraphNode[]>();
|
|
48
|
-
for (const node of graph.nodes) {
|
|
49
|
-
const bucket = byDepth.get(node.depth);
|
|
50
|
-
if (bucket) bucket.push(node);
|
|
51
|
-
else byDepth.set(node.depth, [node]);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const nodes: LayoutNode[] = [];
|
|
55
|
-
const positionById = new Map<string, LayoutNode>();
|
|
56
|
-
let maxDepth = 0;
|
|
57
|
-
let maxRows = 0;
|
|
58
|
-
|
|
59
|
-
for (const [depth, column] of byDepth) {
|
|
60
|
-
maxDepth = Math.max(maxDepth, depth);
|
|
61
|
-
maxRows = Math.max(maxRows, column.length);
|
|
62
|
-
const x = MARGIN + depth * (COLUMN_WIDTH + COLUMN_GAP);
|
|
63
|
-
column.forEach((node, row) => {
|
|
64
|
-
const y = MARGIN + row * (ROW_HEIGHT + ROW_GAP);
|
|
65
|
-
const laidOut: LayoutNode = { node, x, y, width: NODE_WIDTH, height: ROW_HEIGHT };
|
|
66
|
-
nodes.push(laidOut);
|
|
67
|
-
positionById.set(node.id, laidOut);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const width = MARGIN * 2 + (maxDepth + 1) * COLUMN_WIDTH + maxDepth * COLUMN_GAP;
|
|
72
|
-
const height =
|
|
73
|
-
MARGIN * 2 + Math.max(1, maxRows) * ROW_HEIGHT + Math.max(0, maxRows - 1) * ROW_GAP;
|
|
74
|
-
|
|
75
|
-
return { nodes, positionById, width, height };
|
|
76
|
-
}
|