@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.
Files changed (281) hide show
  1. package/contributions/audio-unlock.service.ts +2 -2
  2. package/contributions/autoplay.service.ts +1 -1
  3. package/contributions/bridge.command.ts +3 -3
  4. package/contributions/canvas/component-board.service.ts +16 -0
  5. package/contributions/canvas/design-time-mount.service.ts +45 -0
  6. package/contributions/canvas-story-capture.service.ts +12 -0
  7. package/contributions/edit-mode-audio.service.ts +2 -2
  8. package/contributions/edit-mode-networking.service.ts +1 -1
  9. package/contributions/gameplay.command.ts +4 -4
  10. package/contributions/generation.service.ts +3 -3
  11. package/contributions/generations.status.tsx +1 -1
  12. package/contributions/godot.palette.json +99 -0
  13. package/contributions/godot.style.ts +82 -0
  14. package/contributions/godot.view.ts +75 -0
  15. package/contributions/ingest.service.ts +4 -4
  16. package/contributions/instances.command.ts +1 -1
  17. package/contributions/navmesh.menu.ts +1 -1
  18. package/contributions/network-observer.service.ts +14 -0
  19. package/contributions/play.command.ts +11 -5
  20. package/contributions/react/component-board.service.ts +1 -1
  21. package/contributions/react/design-time-mount.service.ts +1 -1
  22. package/contributions/react/react-inspector.service.ts +2 -2
  23. package/contributions/scene-document.service.ts +5 -5
  24. package/contributions/state-watch.menu.ts +1 -1
  25. package/contributions/state-watch.utility.tsx +1 -1
  26. package/contributions/team-playtest.service.ts +4 -4
  27. package/contributions/three/component-board.service.ts +1 -1
  28. package/contributions/three/component-verbs.command.ts +8 -8
  29. package/contributions/three/three-authoring.service.ts +8 -5
  30. package/contributions/three-story-capture.service.ts +12 -0
  31. package/contributions/unity.palette.json +102 -0
  32. package/contributions/unity.style.ts +72 -0
  33. package/contributions/unity.view.ts +82 -0
  34. package/contributions/unreal.palette.json +105 -0
  35. package/contributions/unreal.style.ts +66 -0
  36. package/contributions/unreal.view.ts +93 -0
  37. package/dist-node/serving.mjs +16 -0
  38. package/package.json +33 -16
  39. package/serving/index.ts +27 -0
  40. package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
  41. package/src/asset-budget/asset-budget-model.ts +4 -4
  42. package/src/asset-budget/optimize-apply.ts +7 -7
  43. package/src/audio/AudioDebuggerPanel.tsx +1 -1
  44. package/src/bridge/dispatch.ts +16 -16
  45. package/src/bridge/live-frames.ts +1 -1
  46. package/src/bridge/screenshot.ts +6 -6
  47. package/src/build/BuildProfilesPanel.tsx +3 -3
  48. package/src/canvas/canvas-board/CanvasBoardDocument.tsx +676 -0
  49. package/src/canvas/canvas-board/canvas-board-model.ts +407 -0
  50. package/src/canvas/canvas-board/canvas-component-board.ts +56 -0
  51. package/src/canvas/canvas-design-mount.ts +524 -0
  52. package/src/canvas/design-time-canvas-mount.ts +79 -0
  53. package/src/coverage/live-authoring-surface.ts +6 -6
  54. package/src/coverage/live-project-verbs.ts +2 -2
  55. package/src/coverage/native-system-coverage.ts +6 -6
  56. package/src/coverage/root-coverage.ts +1 -1
  57. package/src/coverage/session-coverage.ts +5 -5
  58. package/src/design-system-stories/ApplicationChrome.stories.tsx +5 -5
  59. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +13 -53
  60. package/src/edit-mode/edit-mode-audio.ts +4 -4
  61. package/src/edit-mode/edit-mode-networking.ts +2 -2
  62. package/src/game-document/GameCaptureFrameButton.tsx +2 -2
  63. package/src/game-document/GameDocument.tsx +2 -2
  64. package/src/game-document/GamePanel.tsx +8 -8
  65. package/src/game-document/InstanceInspectorPicker.tsx +2 -2
  66. package/src/game-document/crowd-debug.ts +1 -1
  67. package/src/game-document/device-preview.ts +10 -11
  68. package/src/game-document/physics-debug.ts +1 -1
  69. package/src/generation/GenerationActivity.tsx +6 -8
  70. package/src/generation/generation-documents.tsx +8 -8
  71. package/src/generation/generation-jobs.ts +2 -2
  72. package/src/generation/generation-presentation.ts +1 -1
  73. package/src/host/adapter-reach.ts +1 -1
  74. package/src/host/adapter-runtime-bindings.ts +97 -6
  75. package/src/host/api/configurations.ts +2 -2
  76. package/src/host/authoring/babylon-authoring-adapter.ts +8 -8
  77. package/src/host/authoring/contract-hierarchy-authoring.ts +1 -1
  78. package/src/host/authoring/contract-scenes-stories.ts +1 -1
  79. package/src/host/authoring/creation-site-related.ts +2 -2
  80. package/src/host/authoring/ephemeral-persistence.ts +1 -1
  81. package/src/host/authoring/gesture-persist.ts +2 -2
  82. package/src/host/authoring/ingest-data-writer.ts +1 -1
  83. package/src/host/authoring/ingest-source-persistence.ts +8 -8
  84. package/src/host/authoring/mount-isolated-pixi-screen.ts +1 -1
  85. package/src/host/authoring/mounted-authoring.ts +4 -4
  86. package/src/host/authoring/phaser-live-authoring-adapter.ts +4 -4
  87. package/src/host/authoring/pixi-authoring-adapter.ts +42 -17
  88. package/src/host/authoring/pixi-creation-site-write-target.ts +2 -2
  89. package/src/host/authoring/pixi-live-write-target.ts +7 -7
  90. package/src/host/authoring/pixi-source-identity.ts +3 -3
  91. package/src/host/authoring/pixi-source-write-target.ts +1614 -0
  92. package/src/host/authoring/pixi-still-presentation.ts +2 -2
  93. package/src/host/authoring/pixi-structure-history.ts +3 -3
  94. package/src/host/authoring/pixi-transform-channels.ts +16 -14
  95. package/src/host/authoring/source-persistence-backend.ts +6 -6
  96. package/src/host/authoring/struct-write-pipe.ts +3 -3
  97. package/src/host/binding-resolver.ts +8 -9
  98. package/src/host/browser-transpile.ts +2 -2
  99. package/src/host/canvas-entry-runtime.ts +59 -48
  100. package/src/host/canvas-preview-frames.ts +482 -0
  101. package/src/host/components/CameraAuthoringOverlay.tsx +1 -1
  102. package/src/host/components/HeaderTelemetry.tsx +9 -16
  103. package/src/host/components/PixiIsolationSceneContent.tsx +17 -16
  104. package/src/host/components/ThreeIsolationSceneContent.tsx +5 -5
  105. package/src/host/components/frame-debugger-model.ts +2 -2
  106. package/src/host/components/header-telemetry-model.ts +2 -2
  107. package/src/host/components/scene-document.tsx +29 -61
  108. package/src/host/components/utility-view-state.ts +1 -1
  109. package/src/host/components/world-root-stage-binding.tsx +17 -26
  110. package/src/host/components/world-root-stage.ts +130 -63
  111. package/src/host/coverage/capability-coverage.ts +13 -1
  112. package/src/host/coverage/game-contract-seam-evidence.ts +1 -1
  113. package/src/host/coverage/project-verb-coverage.ts +2 -17
  114. package/src/host/coverage/system-adapter-coverage.ts +4 -5
  115. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +14 -8
  116. package/src/host/document-preview-three.ts +2 -2
  117. package/src/host/entry-adjudication.ts +6 -6
  118. package/src/host/game-css-scope-transform.ts +4 -0
  119. package/src/host/game-module-access.ts +1 -1
  120. package/src/host/game-realm-page.ts +16 -8
  121. package/src/host/game-realm-reclaim.ts +1 -1
  122. package/src/host/gameplay-export.ts +25 -14
  123. package/src/host/gameplay-recording.ts +5 -5
  124. package/src/host/gated-globals.ts +6 -6
  125. package/src/host/history/json-history-resource.ts +3 -3
  126. package/src/host/instance-extract-actions.ts +1 -1
  127. package/src/host/instance-fork-actions.ts +1 -1
  128. package/src/host/projection/dom.ts +2 -2
  129. package/src/host/projection/pixi.ts +25 -3
  130. package/src/host/r3f-entry-runtime.ts +65 -34
  131. package/src/host/react-mount-runtime.ts +8 -49
  132. package/src/host/realm-services.ts +2 -2
  133. package/src/host/roots/canvas-root.tsx +361 -0
  134. package/src/host/roots/module-root.ts +1 -1
  135. package/src/host/roots/r3f-root.tsx +473 -0
  136. package/src/host/roots/react-root.ts +9 -43
  137. package/src/host/scene-view-drawability.ts +2 -2
  138. package/src/host/served-bundle-runtime-modules.ts +1 -19
  139. package/src/host/server-log-bridge.ts +3 -3
  140. package/src/host/stories/mounted-story-viewport-source.ts +1 -1
  141. package/src/host/stories/pixi-story-model.ts +30 -0
  142. package/src/host/stories/story-media-captures.ts +46 -0
  143. package/src/host/stories/story-media-presence.ts +3 -3
  144. package/src/host/stories/story-pixi-preview.ts +408 -0
  145. package/src/host/stories/story-three-preview.ts +806 -0
  146. package/src/host/stories/three-story-captures.ts +35 -0
  147. package/src/host/story-three-preview-runtime.ts +56 -0
  148. package/src/host/three-ingest-runtime.ts +1 -1
  149. package/src/host/use-active-performance-source.ts +3 -3
  150. package/src/host/viewport-pose-memory.ts +1 -1
  151. package/src/host/viewport-root-presentation.ts +6 -5
  152. package/src/ingest/active-ingest.ts +2 -2
  153. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
  154. package/src/ingest/authoring/ingest-root-adapter.ts +13 -13
  155. package/src/ingest/capture-wait-report.ts +1 -1
  156. package/src/ingest/deferred-ingest-play.ts +11 -10
  157. package/src/ingest/discovery-public-ingest.ts +2 -2
  158. package/src/ingest/ingest-boot-viewport.ts +6 -8
  159. package/src/ingest/ingest-canvas-scene-document.tsx +17 -16
  160. package/src/ingest/ingest-canvas-scene.ts +5 -8
  161. package/src/ingest/ingest-evidence-hook.ts +1 -1
  162. package/src/ingest/ingest-frame-snapshot.ts +1 -1
  163. package/src/ingest/ingest-play-control.ts +4 -4
  164. package/src/ingest/ingest-render-debug.ts +11 -11
  165. package/src/ingest/ingest-siblings.ts +16 -28
  166. package/src/ingest/module-mode.ts +18 -19
  167. package/src/ingest/mount-canvas-ingest-root.ts +27 -27
  168. package/src/ingest/mount-coverage.ts +4 -4
  169. package/src/ingest/mount-dom-ingest-root.ts +16 -16
  170. package/src/ingest/mount-ingest-root.ts +14 -14
  171. package/src/ingest/mount-three-ingest-root.ts +12 -12
  172. package/src/ingest/resolve-canvas.ts +1 -1
  173. package/src/ingest/served-html-boot.ts +1 -1
  174. package/src/ingest/surface-canvas.ts +1 -1
  175. package/src/ingest/unmount-ingest-root.ts +7 -7
  176. package/src/navmesh/navmesh-handler.ts +24 -16
  177. package/src/network/NetworkInspectorPanel.tsx +449 -14
  178. package/src/network/network-inspector-model.ts +14 -2
  179. package/src/play/play-log-events.ts +1 -1
  180. package/src/play/play-mode.ts +99 -142
  181. package/src/play/play-recording.ts +2 -2
  182. package/src/play/react-play-live-authoring.ts +3 -3
  183. package/src/play/run-selection.ts +93 -0
  184. package/src/play-bar/PlayBar.tsx +20 -39
  185. package/src/profiler/FrameDebuggerPanel.tsx +1 -1
  186. package/src/profiler/PerformancePanel.tsx +4 -4
  187. package/src/react/design-time-react-mount.ts +37 -85
  188. package/src/react/dom-authoring-adapter.ts +11 -11
  189. package/src/react/react-inspector-section.tsx +13 -13
  190. package/src/react/react-world-authoring-adapter.ts +31 -143
  191. package/src/react/story-documents/story-documents.tsx +18 -22
  192. package/src/react/ui-board-document.tsx +10 -11
  193. package/src/react/ui-component-board.ts +5 -5
  194. package/src/runtime/adapter/audio-meter.ts +21 -0
  195. package/src/runtime/adapter/first-party-audio-system.ts +230 -0
  196. package/src/runtime/adapter/ingest/contract-debug-adapter.ts +114 -0
  197. package/src/runtime/adapter/ingest/contract-system-adapters.ts +256 -0
  198. package/src/runtime/adapter/ingest/merge-debug-adapters.ts +197 -0
  199. package/src/runtime/adapter/ingest/observation-debug-adapter.ts +162 -0
  200. package/src/runtime/adapter/ingest/upstream-pin.ts +51 -0
  201. package/src/runtime/adapter/native-debug-module.ts +498 -0
  202. package/src/runtime/audio/bus-mixer.ts +161 -0
  203. package/src/runtime/audio/pose-guard.ts +80 -0
  204. package/src/runtime/core/frame-pacing.ts +126 -0
  205. package/src/runtime/core/game-loop.ts +225 -0
  206. package/src/runtime/core/game-scoped-slot.ts +28 -0
  207. package/src/runtime/core/seeded-random.ts +162 -0
  208. package/src/runtime/core/sim-clock.ts +391 -0
  209. package/src/runtime/core/system-runner.ts +269 -0
  210. package/src/runtime/core/types.ts +104 -0
  211. package/src/runtime/create-runtime.ts +1128 -0
  212. package/src/runtime/debug-bridge.ts +570 -0
  213. package/src/runtime/debug-registry.ts +899 -0
  214. package/src/runtime/dev/chrome-trace.ts +153 -0
  215. package/src/runtime/dev/instruments.ts +403 -0
  216. package/src/runtime/dev/logger.ts +119 -0
  217. package/src/runtime/dev/performance-profiler.ts +367 -0
  218. package/src/runtime/dev/register-render-vitals.ts +276 -0
  219. package/src/runtime/dev/render-census.ts +354 -0
  220. package/src/runtime/dev/render-debug-adapter.ts +218 -0
  221. package/src/runtime/dev/render-memory.ts +226 -0
  222. package/src/runtime/dev/render-vitals.ts +338 -0
  223. package/src/runtime/dev/static-batch-advisor.ts +188 -0
  224. package/src/runtime/dev/webgl-frame-capture.ts +366 -0
  225. package/src/runtime/dev/webgl-gpu-timer.ts +53 -0
  226. package/src/runtime/dev-build.ts +47 -0
  227. package/src/runtime/game.ts +1636 -0
  228. package/src/runtime/gameplay-rng-trap.ts +135 -0
  229. package/src/runtime/host-context.ts +64 -0
  230. package/src/runtime/input-router.ts +169 -0
  231. package/src/runtime/mount-manifest.ts +480 -0
  232. package/src/runtime/pixi/authoring.ts +706 -0
  233. package/src/runtime/pixi/ingest.ts +116 -0
  234. package/src/runtime/pixi/physics-registry.ts +49 -0
  235. package/src/runtime/pixi/render-pass-bracket.ts +117 -0
  236. package/src/runtime/pixi/scene-capture.ts +179 -0
  237. package/src/runtime/pixi/system-adapters.ts +69 -0
  238. package/src/runtime/playtest.ts +22 -0
  239. package/src/runtime/presentation.ts +141 -0
  240. package/src/runtime/render-control.ts +642 -0
  241. package/src/runtime/render-seed.ts +77 -0
  242. package/src/runtime/run-ticks-settled.ts +73 -0
  243. package/src/runtime/setup/setup-audio.ts +72 -0
  244. package/src/services/audio-pose-guard.ts +2 -2
  245. package/src/services/game-audio.ts +152 -0
  246. package/src/services/game-network.ts +657 -0
  247. package/src/services/game-physics.ts +334 -0
  248. package/src/state-watch/StateWatchPanel.tsx +1 -1
  249. package/src/three/authoring/camera-runtime-inspector-section.tsx +4 -3
  250. package/src/three/authoring/constraint-inspector-section.tsx +8 -7
  251. package/src/three/authoring/model-asset-inspector-section.tsx +14 -17
  252. package/src/three/authoring/oid-source-persistence.ts +13 -13
  253. package/src/three/authoring/r3f-design-session.ts +79 -61
  254. package/src/three/authoring/r3f-source-authoring-adapter.ts +112 -108
  255. package/src/three/authoring/reflection-probe-inspector-section.tsx +5 -4
  256. package/src/three/authoring/spatial-joint-handles.ts +1 -1
  257. package/src/three/authoring/spatial-lod-handles.ts +1 -1
  258. package/src/three/authoring/spatial-particle-handles.ts +1 -1
  259. package/src/three/authoring/three-authoring-adapter.ts +33 -33
  260. package/src/three/component-verbs/extract-menu.ts +8 -7
  261. package/src/three/component-verbs/fork-menu.ts +8 -7
  262. package/src/three/component-verbs/internals-menu.ts +3 -3
  263. package/src/three/drei-asset-caches.ts +13 -0
  264. package/src/three/story-documents/three-story-documents.tsx +24 -34
  265. package/src/three/three-board/ThreeBoardDocument.tsx +25 -25
  266. package/src/three/three-board/board-scene.ts +7 -7
  267. package/src/three/three-board/three-component-board.ts +5 -5
  268. package/contributions/react/pasteboard.action.ts +0 -41
  269. package/contributions/xstate-behavior.action.ts +0 -42
  270. package/contributions/xstate-behavior.document.tsx +0 -73
  271. package/contributions/xstate-behavior.inspector.tsx +0 -28
  272. package/contributions/xstate-behavior.menu.ts +0 -23
  273. package/src/react/pasteboard-materialize.ts +0 -154
  274. package/src/services/game-audio-unlock.ts +0 -48
  275. package/src/xstate/XStateBehaviorSection.tsx +0 -130
  276. package/src/xstate/XStateMachineInspector.tsx +0 -566
  277. package/src/xstate/character-animation-machine.fixture.ts +0 -74
  278. package/src/xstate/live-behaviors.ts +0 -106
  279. package/src/xstate/use-live-actor-state.ts +0 -44
  280. package/src/xstate/xstate-graph.ts +0 -235
  281. 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
- }