@volter/editor-game 0.5.65

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 (302) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +23 -0
  3. package/contributions/asset-budget-asset.menu.ts +26 -0
  4. package/contributions/asset-budget.action.ts +18 -0
  5. package/contributions/asset-budget.document.tsx +21 -0
  6. package/contributions/asset-budget.menu.ts +22 -0
  7. package/contributions/audio-unlock.service.ts +17 -0
  8. package/contributions/audio.utility.tsx +15 -0
  9. package/contributions/autoplay.service.ts +48 -0
  10. package/contributions/bridge.command.ts +172 -0
  11. package/contributions/build-profiles.document.tsx +21 -0
  12. package/contributions/build-progress.status.tsx +45 -0
  13. package/contributions/build.action.ts +26 -0
  14. package/contributions/build.command.ts +27 -0
  15. package/contributions/build.header.tsx +37 -0
  16. package/contributions/build.menu.ts +31 -0
  17. package/contributions/build.service.ts +31 -0
  18. package/contributions/connection.status.tsx +43 -0
  19. package/contributions/coverage.service.ts +118 -0
  20. package/contributions/edit-mode-audio.service.ts +23 -0
  21. package/contributions/edit-mode-networking.service.ts +30 -0
  22. package/contributions/game-document.service.ts +25 -0
  23. package/contributions/game-eval.command.ts +210 -0
  24. package/contributions/game.layout.ts +19 -0
  25. package/contributions/gameplay.command.ts +255 -0
  26. package/contributions/generation.service.ts +96 -0
  27. package/contributions/generations.status.tsx +53 -0
  28. package/contributions/ingest.service.ts +80 -0
  29. package/contributions/instances.command.ts +75 -0
  30. package/contributions/navmesh.menu.ts +39 -0
  31. package/contributions/navmesh.service.ts +19 -0
  32. package/contributions/network.utility.tsx +17 -0
  33. package/contributions/play.command.ts +355 -0
  34. package/contributions/profiler.action.ts +16 -0
  35. package/contributions/profiler.menu.ts +22 -0
  36. package/contributions/profiler.utility.tsx +17 -0
  37. package/contributions/react/component-board.service.ts +21 -0
  38. package/contributions/react/design-time-mount.service.ts +60 -0
  39. package/contributions/react/pasteboard.action.ts +41 -0
  40. package/contributions/react/react-inspector.service.ts +85 -0
  41. package/contributions/react/story-documents.service.ts +44 -0
  42. package/contributions/scene-document.service.ts +32 -0
  43. package/contributions/state-watch.action.ts +17 -0
  44. package/contributions/state-watch.menu.ts +23 -0
  45. package/contributions/state-watch.utility.tsx +20 -0
  46. package/contributions/team-playtest.service.ts +124 -0
  47. package/contributions/three/camera-runtime.inspector.tsx +34 -0
  48. package/contributions/three/component-board.service.ts +24 -0
  49. package/contributions/three/component-verbs.command.ts +110 -0
  50. package/contributions/three/component-verbs.service.ts +92 -0
  51. package/contributions/three/constraints.inspector.tsx +34 -0
  52. package/contributions/three/model-asset-sections.service.ts +26 -0
  53. package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
  54. package/contributions/three/story-documents.service.ts +31 -0
  55. package/contributions/three/three-authoring.service.ts +66 -0
  56. package/contributions/transport.header.tsx +19 -0
  57. package/contributions/xstate-behavior.action.ts +42 -0
  58. package/contributions/xstate-behavior.document.tsx +73 -0
  59. package/contributions/xstate-behavior.inspector.tsx +28 -0
  60. package/contributions/xstate-behavior.menu.ts +23 -0
  61. package/package.json +144 -0
  62. package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
  63. package/src/asset-budget/asset-budget-model.ts +799 -0
  64. package/src/asset-budget/basis-encoder.ts +165 -0
  65. package/src/asset-budget/gltf-io.ts +154 -0
  66. package/src/asset-budget/gltf-optimize.ts +384 -0
  67. package/src/asset-budget/image-dims.ts +78 -0
  68. package/src/asset-budget/optimize-apply.ts +221 -0
  69. package/src/audio/AudioDebuggerPanel.tsx +457 -0
  70. package/src/audio/audio-debugger-model.ts +87 -0
  71. package/src/bridge/call.ts +60 -0
  72. package/src/bridge/dispatch.ts +459 -0
  73. package/src/bridge/live-frames.ts +25 -0
  74. package/src/bridge/screenshot.ts +316 -0
  75. package/src/build/BuildProfilesPanel.tsx +476 -0
  76. package/src/build/build-session.ts +247 -0
  77. package/src/build/format-bytes.ts +14 -0
  78. package/src/command-results.ts +36 -0
  79. package/src/coverage/live-authoring-surface.ts +30 -0
  80. package/src/coverage/live-project-verbs.ts +162 -0
  81. package/src/coverage/native-system-coverage.ts +143 -0
  82. package/src/coverage/root-coverage.ts +79 -0
  83. package/src/coverage/session-coverage.ts +193 -0
  84. package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
  85. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
  86. package/src/edit-mode/edit-mode-audio.ts +56 -0
  87. package/src/edit-mode/edit-mode-networking.ts +112 -0
  88. package/src/game-document/DevicePresetPicker.tsx +84 -0
  89. package/src/game-document/GameCaptureFrameButton.tsx +45 -0
  90. package/src/game-document/GameDocument.tsx +215 -0
  91. package/src/game-document/GamePanel.tsx +662 -0
  92. package/src/game-document/InstanceInspectorPicker.tsx +183 -0
  93. package/src/game-document/crowd-debug.ts +183 -0
  94. package/src/game-document/device-preview.ts +336 -0
  95. package/src/game-document/game-view-store.ts +107 -0
  96. package/src/game-document/physics-debug.ts +187 -0
  97. package/src/generation/GenerationActivity.tsx +421 -0
  98. package/src/generation/GenerationGallery.css +231 -0
  99. package/src/generation/generation-documents.tsx +338 -0
  100. package/src/generation/generation-jobs.ts +128 -0
  101. package/src/generation/generation-presentation.ts +257 -0
  102. package/src/host/adapter-reach.ts +445 -0
  103. package/src/host/adapter-runtime-bindings.ts +303 -0
  104. package/src/host/after-paint.ts +112 -0
  105. package/src/host/api/configurations.ts +66 -0
  106. package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
  107. package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
  108. package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
  109. package/src/host/authoring/contract-scenes-stories.ts +204 -0
  110. package/src/host/authoring/creation-site-related.ts +55 -0
  111. package/src/host/authoring/ephemeral-persistence.ts +29 -0
  112. package/src/host/authoring/gesture-persist.ts +84 -0
  113. package/src/host/authoring/ingest-data-writer.ts +232 -0
  114. package/src/host/authoring/ingest-source-persistence.ts +826 -0
  115. package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
  116. package/src/host/authoring/mounted-authoring.ts +41 -0
  117. package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
  118. package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
  119. package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
  120. package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
  121. package/src/host/authoring/pixi-isolation-assets.ts +25 -0
  122. package/src/host/authoring/pixi-live-write-target.ts +979 -0
  123. package/src/host/authoring/pixi-source-identity.ts +141 -0
  124. package/src/host/authoring/pixi-still-presentation.ts +71 -0
  125. package/src/host/authoring/pixi-structure-history.ts +237 -0
  126. package/src/host/authoring/pixi-transform-channels.ts +205 -0
  127. package/src/host/authoring/selection-remount-handoff.ts +23 -0
  128. package/src/host/authoring/source-persistence-backend.ts +373 -0
  129. package/src/host/authoring/source-refresh-revisions.ts +81 -0
  130. package/src/host/authoring/struct-write-pipe.ts +143 -0
  131. package/src/host/auto-frame-window.ts +89 -0
  132. package/src/host/binding-resolver.ts +393 -0
  133. package/src/host/browser-transpile.ts +631 -0
  134. package/src/host/canvas-entry-runtime.ts +95 -0
  135. package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
  136. package/src/host/components/HeaderTelemetry.tsx +341 -0
  137. package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
  138. package/src/host/components/ResolutionPicker.tsx +89 -0
  139. package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
  140. package/src/host/components/frame-debugger-model.ts +579 -0
  141. package/src/host/components/header-telemetry-model.ts +74 -0
  142. package/src/host/components/scene-document.tsx +447 -0
  143. package/src/host/components/utility-view-state.ts +87 -0
  144. package/src/host/components/world-root-stage-binding.tsx +133 -0
  145. package/src/host/components/world-root-stage.ts +1046 -0
  146. package/src/host/coverage/authoring-read-probe.ts +583 -0
  147. package/src/host/coverage/capability-coverage.ts +1587 -0
  148. package/src/host/coverage/coverage-accounting.ts +261 -0
  149. package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
  150. package/src/host/coverage/project-verb-coverage.ts +148 -0
  151. package/src/host/coverage/system-adapter-coverage.ts +373 -0
  152. package/src/host/design-system-stories/StoryLayout.tsx +104 -0
  153. package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
  154. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
  155. package/src/host/document-preview-three.ts +110 -0
  156. package/src/host/entry-adjudication.ts +89 -0
  157. package/src/host/game-location-guard.ts +150 -0
  158. package/src/host/game-module-access.ts +196 -0
  159. package/src/host/game-realm-page.ts +358 -0
  160. package/src/host/game-realm-reclaim.ts +55 -0
  161. package/src/host/game-realm-storage.ts +104 -0
  162. package/src/host/gameplay-export.ts +288 -0
  163. package/src/host/gameplay-recording.ts +717 -0
  164. package/src/host/gated-globals.ts +1511 -0
  165. package/src/host/history/json-history-resource.ts +254 -0
  166. package/src/host/ingest/registry.ts +261 -0
  167. package/src/host/instance-extract-actions.ts +120 -0
  168. package/src/host/instance-fork-actions.ts +120 -0
  169. package/src/host/play-control-hook.ts +26 -0
  170. package/src/host/playwright-shim.ts +479 -0
  171. package/src/host/projection/dom.ts +295 -0
  172. package/src/host/projection/pixi.ts +288 -0
  173. package/src/host/r3f-entry-runtime.ts +77 -0
  174. package/src/host/react-mount-runtime.ts +162 -0
  175. package/src/host/realm-services.ts +148 -0
  176. package/src/host/recording-preview.ts +106 -0
  177. package/src/host/roots/module-root.ts +203 -0
  178. package/src/host/roots/react-root.ts +181 -0
  179. package/src/host/same-realm-loop-gate.ts +544 -0
  180. package/src/host/scene-view-drawability.ts +69 -0
  181. package/src/host/sdk/tools.ts +31 -0
  182. package/src/host/served-bundle-runtime-modules.ts +331 -0
  183. package/src/host/server-log-bridge.ts +60 -0
  184. package/src/host/staged-projects.ts +24 -0
  185. package/src/host/stories/mounted-story-viewport-source.ts +64 -0
  186. package/src/host/stories/story-arg-descriptors.ts +50 -0
  187. package/src/host/stories/story-media-presence.ts +152 -0
  188. package/src/host/surface-content.ts +87 -0
  189. package/src/host/take-named-export.ts +23 -0
  190. package/src/host/three-ingest-runtime.ts +76 -0
  191. package/src/host/types-fastnoise-lite.d.ts +7 -0
  192. package/src/host/types-mikktspace.d.ts +20 -0
  193. package/src/host/types-troika-three-text.d.ts +7 -0
  194. package/src/host/use-active-performance-source.ts +53 -0
  195. package/src/host/viewport-pose-memory.ts +48 -0
  196. package/src/host/viewport-root-presentation.ts +40 -0
  197. package/src/ingest/active-ingest.ts +251 -0
  198. package/src/ingest/active-scene-navigation.ts +43 -0
  199. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
  200. package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
  201. package/src/ingest/capture-wait-report.ts +122 -0
  202. package/src/ingest/deferred-ingest-play.ts +216 -0
  203. package/src/ingest/deferred-ingest-session.ts +23 -0
  204. package/src/ingest/discovery-public-ingest.ts +242 -0
  205. package/src/ingest/dom-stub-mark.ts +7 -0
  206. package/src/ingest/entry-load.ts +56 -0
  207. package/src/ingest/game-contract-realm.ts +34 -0
  208. package/src/ingest/game-pointer-lock.ts +89 -0
  209. package/src/ingest/held-scene-repaint.ts +73 -0
  210. package/src/ingest/host-surface-box.ts +174 -0
  211. package/src/ingest/ingest-boot-viewport.ts +54 -0
  212. package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
  213. package/src/ingest/ingest-canvas-scene.ts +50 -0
  214. package/src/ingest/ingest-evidence-hook.ts +131 -0
  215. package/src/ingest/ingest-frame-snapshot.ts +183 -0
  216. package/src/ingest/ingest-play-commands.ts +134 -0
  217. package/src/ingest/ingest-play-control.ts +299 -0
  218. package/src/ingest/ingest-render-debug.ts +320 -0
  219. package/src/ingest/ingest-siblings.ts +487 -0
  220. package/src/ingest/ingest-status.ts +62 -0
  221. package/src/ingest/live-ingest-facet.ts +46 -0
  222. package/src/ingest/module-mode.ts +229 -0
  223. package/src/ingest/mount-canvas-ingest-root.ts +899 -0
  224. package/src/ingest/mount-coverage.ts +296 -0
  225. package/src/ingest/mount-dom-ingest-root.ts +282 -0
  226. package/src/ingest/mount-ingest-root.ts +744 -0
  227. package/src/ingest/mount-three-ingest-root.ts +366 -0
  228. package/src/ingest/resolve-canvas.ts +47 -0
  229. package/src/ingest/resolve-three.ts +123 -0
  230. package/src/ingest/served-bundle.ts +109 -0
  231. package/src/ingest/served-html-boot.ts +292 -0
  232. package/src/ingest/surface-canvas.ts +84 -0
  233. package/src/ingest/surface-dom.ts +84 -0
  234. package/src/ingest/surface-three.ts +128 -0
  235. package/src/ingest/types.ts +76 -0
  236. package/src/ingest/unmount-ingest-root.ts +224 -0
  237. package/src/navmesh/navmesh-actions.ts +27 -0
  238. package/src/navmesh/navmesh-handler.ts +237 -0
  239. package/src/navmesh/navmesh-workflow-store.ts +78 -0
  240. package/src/network/NetworkInspectorPanel.tsx +644 -0
  241. package/src/network/network-inspector-model.ts +225 -0
  242. package/src/play/play-boot-stall.ts +118 -0
  243. package/src/play/play-log-events.ts +26 -0
  244. package/src/play/play-mode.ts +2553 -0
  245. package/src/play/play-recording.ts +335 -0
  246. package/src/play/react-play-live-authoring.ts +165 -0
  247. package/src/play-bar/PlayBar.tsx +488 -0
  248. package/src/play-bar/PlayerCountPicker.tsx +100 -0
  249. package/src/profiler/FrameDebuggerPanel.tsx +458 -0
  250. package/src/profiler/PerformancePanel.tsx +1068 -0
  251. package/src/profiler/ProfilerPanel.tsx +53 -0
  252. package/src/profiler/frame-debugger-store.ts +97 -0
  253. package/src/profiler/main-thread-busy.ts +90 -0
  254. package/src/react/design-time-react-mount.ts +782 -0
  255. package/src/react/dom-authoring-adapter.ts +764 -0
  256. package/src/react/pasteboard-materialize.ts +154 -0
  257. package/src/react/react-inspector-section.tsx +2496 -0
  258. package/src/react/react-world-authoring-adapter.ts +3795 -0
  259. package/src/react/story-documents/story-args-section.ts +19 -0
  260. package/src/react/story-documents/story-documents.tsx +1380 -0
  261. package/src/react/story-paint-bounds.ts +94 -0
  262. package/src/react/ui-board-document.tsx +101 -0
  263. package/src/react/ui-board-title.ts +9 -0
  264. package/src/react/ui-component-board.ts +72 -0
  265. package/src/services/audio-pose-guard.ts +81 -0
  266. package/src/services/game-audio-unlock.ts +48 -0
  267. package/src/state-watch/StateWatchPanel.tsx +535 -0
  268. package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
  269. package/src/three/authoring/constraint-inspector-section.tsx +189 -0
  270. package/src/three/authoring/design-time-renderer.ts +189 -0
  271. package/src/three/authoring/model-asset-inspector-section.css +41 -0
  272. package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
  273. package/src/three/authoring/oid-source-persistence.ts +557 -0
  274. package/src/three/authoring/r3f-design-session.ts +1174 -0
  275. package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
  276. package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
  277. package/src/three/authoring/spatial-audio-handles.ts +215 -0
  278. package/src/three/authoring/spatial-collider-handles.ts +229 -0
  279. package/src/three/authoring/spatial-joint-handles.ts +114 -0
  280. package/src/three/authoring/spatial-light-handles.ts +178 -0
  281. package/src/three/authoring/spatial-lod-handles.ts +93 -0
  282. package/src/three/authoring/spatial-particle-handles.ts +368 -0
  283. package/src/three/authoring/three-authoring-adapter.ts +1953 -0
  284. package/src/three/authoring/three-scene-identity.ts +19 -0
  285. package/src/three/authoring/three-spatial-handles.ts +161 -0
  286. package/src/three/authoring/typed-three-inspector.ts +528 -0
  287. package/src/three/component-verbs/extract-menu.ts +71 -0
  288. package/src/three/component-verbs/fork-menu.ts +78 -0
  289. package/src/three/component-verbs/internals-menu.ts +91 -0
  290. package/src/three/story-documents/three-story-documents.tsx +607 -0
  291. package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
  292. package/src/three/three-board/board-framing.ts +412 -0
  293. package/src/three/three-board/board-layout.ts +401 -0
  294. package/src/three/three-board/board-scene.ts +901 -0
  295. package/src/three/three-board/three-component-board.ts +62 -0
  296. package/src/xstate/XStateBehaviorSection.tsx +130 -0
  297. package/src/xstate/XStateMachineInspector.tsx +566 -0
  298. package/src/xstate/character-animation-machine.fixture.ts +74 -0
  299. package/src/xstate/live-behaviors.ts +106 -0
  300. package/src/xstate/use-live-actor-state.ts +44 -0
  301. package/src/xstate/xstate-graph.ts +235 -0
  302. package/src/xstate/xstate-layout.ts +76 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * THE SCENE DOCUMENT (`@volter/editor-sdk/services`, a `workspace.service`
3
+ * contribution). The kit routes every three root's edit document to
4
+ * `workspace:scene` and no longer synthesizes it: what that document shows is
5
+ * the project's world, mounted by the game runtime this package carries, so
6
+ * the document is this package's (`src/host/components/scene-document.tsx`).
7
+ *
8
+ * The session store arrives after the contribution pass, and a project switch
9
+ * brings a new one, so the document binds on each store's ARRIVAL
10
+ * (`@volter/editor-core/shell-store-door`'s `onShellStore`, as Play's
11
+ * autoplay entry does) and unbinds the previous store's binding first.
12
+ */
13
+ import { onShellStore } from '@volter/editor-core/shell-store-door';
14
+ import { unregisterAvailableWorkspaceDocument } from '@volter/editor-core/workspace-available-documents';
15
+ import { SCENE_DOCUMENT_ID } from '@volter/editor-core/workspace-document-ids';
16
+ import { bindSceneDocument } from '../src/host/components/scene-document';
17
+
18
+ export const point = 'workspace.service';
19
+
20
+ export function start(): () => void {
21
+ let unbind: (() => void) | null = null;
22
+ const stopStore = onShellStore((store) => {
23
+ unbind?.();
24
+ unbind = bindSceneDocument(store);
25
+ });
26
+ return () => {
27
+ stopStore();
28
+ unbind?.();
29
+ unbind = null;
30
+ unregisterAvailableWorkspaceDocument(SCENE_DOCUMENT_ID);
31
+ };
32
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `Open State Watch` in the command palette — the same reveal as Debug ▸
3
+ * State Watch. The host lists a contributed action unconditionally, so the
4
+ * reveal is a no-op while nothing registers the tab (`available` gates it).
5
+ */
6
+ import type { ActionContribution } from '@volter/editor-sdk/chrome';
7
+ import { editorHost } from '@volter/editor-sdk/host';
8
+
9
+ export const point = 'workspace.action';
10
+
11
+ export const actions: ActionContribution['actions'] = [
12
+ {
13
+ id: 'state-watch',
14
+ label: 'Open State Watch',
15
+ execute: () => editorHost().workspace.showUtility('tool:state-watch.utility'),
16
+ },
17
+ ];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Debug ▸ State Watch — disabled while no game exposes a `DebugAdapter`, the
3
+ * same predicate the drawer tab's own `available` gate reads. `disabled` is
4
+ * asked at render, so it follows the session.
5
+ */
6
+ import { getActiveDebug } from '@volter/editor-core/authoring/active-systems';
7
+ import type { MenuContribution } from '@volter/editor-sdk/chrome';
8
+ import { editorHost } from '@volter/editor-sdk/host';
9
+
10
+ export const point = 'workspace.menu';
11
+
12
+ export const menu: MenuContribution = {
13
+ menu: 'debug',
14
+ items: [
15
+ {
16
+ id: 'state-watch',
17
+ label: 'State Watch',
18
+ testId: 'menu-state-watch',
19
+ disabled: () => getActiveDebug() == null,
20
+ execute: () => editorHost().workspace.showUtility('tool:state-watch.utility'),
21
+ },
22
+ ],
23
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * State Watch as a drawer tab: a read-only tree and per-project pins over the
3
+ * focused game's native `DebugAdapter` providers — the same adapter
4
+ * `@volter/editor-live` reads. On offer only while that seam exists; a tab over no
5
+ * providers would be a measurement nobody made.
6
+ *
7
+ * `available` reads the PRIMARY authoring mount's debug adapter, exactly what
8
+ * the panel itself reads, so the gate and the content can never disagree.
9
+ */
10
+ import { getActiveDebug } from '@volter/editor-core/authoring/active-systems';
11
+ import { StateWatchPanel } from '../src/state-watch/StateWatchPanel';
12
+
13
+ export const point = 'workspace.utility';
14
+ export const title = 'State Watch';
15
+ export const order = 19;
16
+ export const available = (): boolean => getActiveDebug() != null;
17
+
18
+ export default function StateWatchUtility() {
19
+ return <StateWatchPanel />;
20
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * THE TEAM PLAYTEST — a collaborator with the `test` capability starts a
3
+ * playtest, and this editor joins it (`@volter/editor-sdk/services`, a
4
+ * `workspace.service` contribution): the snapshot's `activePlaytest` decides
5
+ * whether this tab enters play with the team's seed and room key, and the
6
+ * playtest's presence row reports the play and networking states back.
7
+ *
8
+ * It left `components/AppRoot.tsx` when Play left the host (WORK.md §The
9
+ * workbench, P3b): the shell must not name Play to run one. Collaboration,
10
+ * presence and the active networking adapter stay the host's and are reached
11
+ * through the `@editor/*` alias.
12
+ */
13
+
14
+ import { getActiveNetworking, subscribeActiveNetworking } from '@volter/editor-core/authoring/active-systems';
15
+ import { connectCollaboration } from '@volter/editor-core/collaboration-client';
16
+ import { reportCollaborationPresence } from '@volter/editor-core/collaboration-presence';
17
+ import { EDITOR_PARTICIPANT_ID } from '@volter/editor-core/editor-presence';
18
+ import { activePlaytest, enterPlayMode, exitPlayMode } from '../src/play/play-mode';
19
+
20
+ export const point = 'workspace.service';
21
+
22
+ export function start(): () => void {
23
+ let requestedPlaytestId: string | null = null;
24
+ const failedPlaytests = new Set<string>();
25
+ let unsubscribeNetworking: (() => void) | null = null;
26
+ let unsubscribeNetworkingReplacement: (() => void) | null = null;
27
+ let subscribedNetworking = getActiveNetworking();
28
+ const stopNetworking = () => {
29
+ unsubscribeNetworking?.();
30
+ unsubscribeNetworking = null;
31
+ unsubscribeNetworkingReplacement?.();
32
+ unsubscribeNetworkingReplacement = null;
33
+ subscribedNetworking = null;
34
+ };
35
+ const disconnect = connectCollaboration((snapshot) => {
36
+ const canTest =
37
+ snapshot.participants
38
+ .find((participant) => participant.participantId === EDITOR_PARTICIPANT_ID)
39
+ ?.capabilities.includes('test') ?? false;
40
+ const team = snapshot.activePlaytest;
41
+ const active = activePlaytest();
42
+ if (!canTest || !team) {
43
+ requestedPlaytestId = null;
44
+ stopNetworking();
45
+ reportCollaborationPresence({ playtest: null });
46
+ if (active?.mode === 'team') exitPlayMode();
47
+ if (!team) failedPlaytests.clear();
48
+ return;
49
+ }
50
+ if (active?.mode === 'team' && active.id === team.id) return;
51
+ if (requestedPlaytestId === team.id) return;
52
+ if (failedPlaytests.has(team.id)) return;
53
+ requestedPlaytestId = team.id;
54
+ reportCollaborationPresence({
55
+ playtest: {
56
+ id: team.id,
57
+ play: 'starting',
58
+ networking: 'connecting',
59
+ roomName: null,
60
+ problem: null,
61
+ },
62
+ });
63
+ void enterPlayMode(team.seed, {
64
+ mode: 'team',
65
+ id: team.id,
66
+ roomKey: team.roomKey,
67
+ revision: team.revision,
68
+ participantId: EDITOR_PARTICIPANT_ID,
69
+ })
70
+ .then(() => {
71
+ if (requestedPlaytestId !== team.id) return;
72
+ stopNetworking();
73
+ const report = () => {
74
+ const networking = getActiveNetworking();
75
+ const state = networking?.getConnectionState?.();
76
+ const room = networking?.getRoomInfo?.();
77
+ reportCollaborationPresence({
78
+ playtest: {
79
+ id: team.id,
80
+ play: 'ready',
81
+ networking: !networking
82
+ ? 'not-declared'
83
+ : state === 'connected' && room
84
+ ? 'ready'
85
+ : state === 'error'
86
+ ? 'error'
87
+ : 'connecting',
88
+ roomName: room?.roomName ?? null,
89
+ problem: state === 'error' ? 'The game networking adapter reported an error.' : null,
90
+ },
91
+ });
92
+ };
93
+ const bindNetworking = () => {
94
+ const networking = getActiveNetworking();
95
+ if (networking !== subscribedNetworking) {
96
+ unsubscribeNetworking?.();
97
+ subscribedNetworking = networking;
98
+ unsubscribeNetworking = networking?.subscribe(report) ?? null;
99
+ }
100
+ report();
101
+ };
102
+ unsubscribeNetworkingReplacement = subscribeActiveNetworking(bindNetworking);
103
+ bindNetworking();
104
+ })
105
+ .catch((error) => {
106
+ if (requestedPlaytestId !== team.id) return;
107
+ requestedPlaytestId = null;
108
+ failedPlaytests.add(team.id);
109
+ reportCollaborationPresence({
110
+ playtest: {
111
+ id: team.id,
112
+ play: 'error',
113
+ networking: 'error',
114
+ roomName: null,
115
+ problem: error instanceof Error ? error.message : String(error),
116
+ },
117
+ });
118
+ });
119
+ });
120
+ return () => {
121
+ stopNetworking();
122
+ disconnect();
123
+ };
124
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The Runtime Camera Inspector section (`selection.inspector`): what the
3
+ * engine's camera director is actually doing with a selected camera — the
4
+ * active one, its priority, and a blend in flight.
5
+ *
6
+ * `match` asks the adapter for the node's `Object3D` and answers only for a
7
+ * real camera the director knows, so the section cannot render for a subject
8
+ * the runtime has never seen.
9
+ *
10
+ * On the contributed band, see `constraints.inspector.tsx`'s note; the number
11
+ * below is this section's relative order among its siblings.
12
+ */
13
+ import type { AuthoringAdapter } from '@volter/editor-project/adapter';
14
+ import type {
15
+ ToolContributionNode,
16
+ ToolInspectorContributionProps,
17
+ } from '@volter/editor-sdk/contributions';
18
+ import { CameraRuntimeSection, matches } from '../../src/three/authoring/camera-runtime-inspector-section';
19
+
20
+ export const point = 'selection.inspector';
21
+ export const title = 'Runtime Camera';
22
+ export const icon = 'camera';
23
+ export const order = 350;
24
+
25
+ export function match(node: ToolContributionNode | null, adapter: unknown): boolean {
26
+ return matches(node as never, adapter as AuthoringAdapter);
27
+ }
28
+
29
+ export default function CameraRuntimeInspector({
30
+ adapter,
31
+ nodeId,
32
+ }: ToolInspectorContributionProps) {
33
+ return <CameraRuntimeSection adapter={adapter as AuthoringAdapter} nodeId={nodeId} />;
34
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * THE THREE MEDIUM'S COMPONENT BOARD (`@volter/editor-sdk/services`, a
3
+ * `workspace.service` contribution): `@volter/editor-game` tells the host's component
4
+ * board registry that this project's `three` stories have a `3D` board.
5
+ *
6
+ * TRANSCRIBED from `@volter/editor-game/contributions/react/component-board.service.ts`,
7
+ * landed in the same unit.
8
+ *
9
+ * WHY A PACKAGE AND NOT THE KIT'S CSF LANE. The board builds a THREE.Scene of
10
+ * the project's three-medium stories on the host's Object3D stage — it is the
11
+ * sibling of this package's `three-story-documents.tsx` (the turntable), and
12
+ * the substrate rule decides it. Portable CSF is medium-neutral and pulls in
13
+ * no three; a THREE.Scene builder inside it would make "do you want stories?"
14
+ * the same question as "do you want three?".
15
+ */
16
+
17
+ import { registerComponentBoard } from '@volter/editor-core/component-board-registry';
18
+ import { threeComponentBoard } from '../../src/three/three-board/three-component-board';
19
+
20
+ export const point = 'workspace.service';
21
+
22
+ export function start(): () => void {
23
+ return registerComponentBoard(threeComponentBoard);
24
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * THE COMPONENT-VERB commands of the session wire
3
+ * (`@volter/editor-sdk/commands`, a `workspace.command` contribution):
4
+ * `extract-component` and `fork-component`.
5
+ *
6
+ * These are the same two operations the hierarchy row's menu items perform
7
+ * (`component-verbs.service.ts`), over the control API, because an agent
8
+ * authoring through the editor has no pointer to right-click with. They left
9
+ * `command-listener.ts`'s switch — and their rows left `command-table.ts` —
10
+ * with the menus, the same way the Blender verbs did when Blender's editor
11
+ * half left the host (`blender.command.ts`'s header, WORK.md §The workbench,
12
+ * item D).
13
+ *
14
+ * EACH ROW CARRIES ITS OWN RELAY BUDGET, transcribed from the host table rows
15
+ * they left (`command-table.ts`, `alwaysRefresh(30_000)` on both): the write
16
+ * goes through the dev server's source-plan route, and `derivedRefresh` is
17
+ * `'always'` because both rewrite the project's own TSX.
18
+ *
19
+ * `id` defaults to the current selection — the menu's own subject — so the
20
+ * command reads the same way the click does. The answer is the action's
21
+ * sentence (created, naming the new files, or the plan's own named refusal),
22
+ * and `ok` mirrors whether the write landed: the ok/error split is decided by
23
+ * `isExtractedHint`/`isForkedHint`, owned beside the wording they match, so
24
+ * the two cannot drift.
25
+ */
26
+
27
+ import { getActiveAuthoring } from '@volter/editor-core/authoring/active-adapter';
28
+ import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
29
+ import { canExtractNode, isExtractedHint } from '../../src/host/instance-extract-actions';
30
+ import { canForkInstance, isForkedHint } from '../../src/host/instance-fork-actions';
31
+ import { shellStoreForHost } from '@volter/editor-core/shell-store-door';
32
+ import type { CommandContribution } from '@volter/editor-sdk/commands';
33
+ import { instanceExtractSourceFor } from '../../src/three/component-verbs/extract-menu';
34
+ import { instanceForkSourceFor } from '../../src/three/component-verbs/fork-menu';
35
+
36
+ /** The shell store, or the refusal that says the page has not installed one —
37
+ * the same shape `bridge.command.ts` takes it in, made explicit because both
38
+ * verbs need the SELECTION as well as the adapter. */
39
+ const NO_STORE = { ok: false as const, error: 'the editor shell has not started yet.' };
40
+
41
+ /** The row's id, or the one selected row — the menu's subject. */
42
+ function subjectId(cmd: Record<string, unknown>, store: EditorShellStore): string | undefined {
43
+ const given = cmd['id'];
44
+ if (typeof given === 'string' && given.trim() !== '') return given;
45
+ return [...store.selectedEntityIds][0];
46
+ }
47
+
48
+ export const commands: CommandContribution['commands'] = {
49
+ 'extract-component': {
50
+ timeoutMs: 30_000,
51
+ derivedRefresh: 'always',
52
+ handle: async (cmd) => {
53
+ try {
54
+ const store = shellStoreForHost();
55
+ if (!store) return NO_STORE;
56
+ const id = subjectId(cmd, store);
57
+ if (!id) return { ok: false, error: 'extract-component needs an id or a selected row.' };
58
+ const adapter = getActiveAuthoring(store);
59
+ const source = instanceExtractSourceFor(adapter, id);
60
+ if (!canExtractNode(adapter.hierarchy.node(id), source, id)) {
61
+ return {
62
+ ok: false,
63
+ error:
64
+ `"${id}" is not extractable: only a native source element row (not a component ` +
65
+ 'instance, group row, or document) with a writable source backend can be extracted.',
66
+ };
67
+ }
68
+ const name = cmd['name'];
69
+ const hint = await source?.extractComponent?.(
70
+ id,
71
+ typeof name === 'string' && name.trim() !== '' ? name : undefined,
72
+ );
73
+ if (hint === undefined) return { ok: false, error: `"${id}" lost its extract seam.` };
74
+ return isExtractedHint(hint) ? { ok: true, data: { hint } } : { ok: false, error: hint };
75
+ } catch (error) {
76
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
77
+ }
78
+ },
79
+ },
80
+ 'fork-component': {
81
+ timeoutMs: 30_000,
82
+ derivedRefresh: 'always',
83
+ handle: async (cmd) => {
84
+ try {
85
+ const store = shellStoreForHost();
86
+ if (!store) return NO_STORE;
87
+ const id = subjectId(cmd, store);
88
+ if (!id) return { ok: false, error: 'fork-component needs an id or a selected row.' };
89
+ const adapter = getActiveAuthoring(store);
90
+ const source = instanceForkSourceFor(adapter, id);
91
+ if (!canForkInstance(adapter.hierarchy.node(id), source, id)) {
92
+ return {
93
+ ok: false,
94
+ error:
95
+ `"${id}" is not forkable: only a component-instance row whose callsite AND ` +
96
+ 'definition both resolve, on an adapter with a writable source backend, can be ' +
97
+ 'forked.',
98
+ };
99
+ }
100
+ const hint = await source?.forkComponent?.(id);
101
+ if (hint === undefined) return { ok: false, error: `"${id}" lost its fork seam.` };
102
+ return isForkedHint(hint) ? { ok: true, data: { hint } } : { ok: false, error: hint };
103
+ } catch (error) {
104
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
105
+ }
106
+ },
107
+ },
108
+ };
109
+
110
+ export const point = 'workspace.command';
@@ -0,0 +1,92 @@
1
+ /**
2
+ * THE COMPONENT-INSTANCE VERBS of an R3F world's hierarchy row
3
+ * (`@volter/editor-sdk/services`, a `workspace.service` contribution):
4
+ * "Extract Component…", "Fork Component…" and "Reveal / Hide Internals".
5
+ *
6
+ * ## Why this is a package and not the host's
7
+ *
8
+ * They were three host modules registered at boot by three bare side-effect
9
+ * imports in `main.tsx` (`./authoring/instance-extract-menu-register` and its
10
+ * two siblings). Their subject is a project's TSX COMPONENT ESTATE — a row
11
+ * that came from `<HeroBox/>` in `src/prefabs/hero.tsx`, whose subtree becomes
12
+ * a new prefab with a portable CSF story, whose module gets copied and
13
+ * retargeted, whose internal nodes a component rendered itself. A MODEL
14
+ * DOCUMENT HAS NONE OF THOSE: the open-source `models` build authors geometry
15
+ * and has no component instances at all, so it paid eight files of the host's
16
+ * eager closure for three menu items it can never show.
17
+ *
18
+ * ## Why `@volter/editor-game` and not a package named for the substrate
19
+ *
20
+ * The substrate rule (WORK.md §The open-source launch, unit 9) sends a module
21
+ * to a package named for its substrate only when a SECOND sibling package
22
+ * imports it. Measured 2026-09-18: nothing outside the editor imports these
23
+ * three, and the only implementer of `extractComponent`/`forkComponent`
24
+ * anywhere in the estate is the host's own `r3f-source-authoring-adapter.ts`.
25
+ * With no second sibling, the rule's default stands — the game lane's, because
26
+ * a prefab estate with CSF stories is what a game's scene is made of.
27
+ *
28
+ * ## The seam, which is not a new one
29
+ *
30
+ * `@editor/hierarchy-menu-registry` is the host's own registry and has always
31
+ * been the "escape hatch, registered elsewhere by the adapter modules"
32
+ * (`components/GameHierarchy.tsx:26`, `hierarchy-menu-registry.ts:50`); the
33
+ * package REGISTERS and the panel names no lane. The service point is
34
+ * transcribed from `@volter/editor-game`'s `react-inspector.service.ts`, which installs
35
+ * a contributed Inspector section the same way and for the same reason: a menu
36
+ * item is not a document, so it never opens and never persists, and what a
37
+ * contribution pass owns is when its registration exists. Host internals are
38
+ * reached through the `@editor/*` alias the editor's Vite serves to every
39
+ * contribution — the precedent `@volter/editor-game`'s own `bridge.command.ts` and
40
+ * `instances.command.ts` already set.
41
+ *
42
+ * ## What did NOT come, and why
43
+ *
44
+ * `instance-extract-actions.ts` and `instance-fork-actions.ts` — the pure
45
+ * applicability rules and every sentence these verbs can say — stay HOST
46
+ * FILES, because the host's `authoring/r3f-source-authoring-adapter.ts` mints
47
+ * those sentences when it performs the write. They leave the EAGER closure
48
+ * with this cut (nothing in the boot path reaches them any more), which is the
49
+ * shape units 5 and 11 landed; they become this package's own when the R3F
50
+ * source adapter moves.
51
+ *
52
+ * The SOURCE verbs ("Go to Callsite" / "Open Component Source") did not come
53
+ * either, and the measurement says why: four host callers, three of them in
54
+ * `keep` buckets and one of them the launch's own feature —
55
+ * `components/GameHierarchy.tsx:115` (the panel's double-click),
56
+ * `three-board/ThreeBoardDocument.tsx:71` and
57
+ * `authoring/creation-site-related.ts:25`. (A fourth was
58
+ * `authoring/model-drill.ts`, deleted 2026-09-19 with the TypeScript model
59
+ * module it drilled into.) Moving them needs a row-activation
60
+ * seam and a locator extraction; that is its own unit, not a rider on this one.
61
+ */
62
+
63
+ import {
64
+ ensureInstanceExtractMenuRegistered,
65
+ unregisterInstanceExtractMenu,
66
+ } from '../../src/three/component-verbs/extract-menu';
67
+ import {
68
+ ensureInstanceForkMenuRegistered,
69
+ unregisterInstanceForkMenu,
70
+ } from '../../src/three/component-verbs/fork-menu';
71
+ import {
72
+ ensureInstanceInternalsMenuRegistered,
73
+ unregisterInstanceInternalsMenu,
74
+ } from '../../src/three/component-verbs/internals-menu';
75
+
76
+ export const point = 'workspace.service';
77
+
78
+ export function start(): () => void {
79
+ ensureInstanceExtractMenuRegistered();
80
+ ensureInstanceForkMenuRegistered();
81
+ ensureInstanceInternalsMenuRegistered();
82
+ // Unlike the inspector section this point is transcribed from, the stop is
83
+ // REAL: `registerHierarchyMenuItems` hands back an unregister and the
84
+ // registry is a plain list the panel reads on every open, so a pass that
85
+ // ends without calling these would leave three matchers answering for a
86
+ // lane that is no longer loaded.
87
+ return () => {
88
+ unregisterInstanceExtractMenu();
89
+ unregisterInstanceForkMenu();
90
+ unregisterInstanceInternalsMenu();
91
+ };
92
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The Constraints Inspector section (`selection.inspector`): the IK and
3
+ * look-at constraints the engine's `constraintsOf` marks on a selected
4
+ * `Object3D`, with each one's resolve status.
5
+ *
6
+ * `match` reads the marks off the adapter's own `hierarchy.object3D`, so it
7
+ * matches only a node that actually carries one — the section cannot render
8
+ * for a subject with no constraints.
9
+ *
10
+ * IT SITS IN THE CONTRIBUTED BAND NOW, after the kit's own blocks rather than
11
+ * between the group and stories blocks. That is what a contribution IS: the
12
+ * built-in band is the kit's and `contributedSectionOrder` adds
13
+ * `CONTRIBUTED_SECTION_ORDER` to whatever a module declares. The number below
14
+ * is the relative order this section kept among its three siblings.
15
+ */
16
+ import type { AuthoringAdapter } from '@volter/editor-project/adapter';
17
+ import type {
18
+ ToolContributionNode,
19
+ ToolInspectorContributionProps,
20
+ } from '@volter/editor-sdk/contributions';
21
+ import { ConstraintStackSection, matches } from '../../src/three/authoring/constraint-inspector-section';
22
+
23
+ export const point = 'selection.inspector';
24
+ export const title = 'Constraints';
25
+ export const icon = 'link';
26
+ export const order = 450;
27
+
28
+ export function match(node: ToolContributionNode | null, adapter: unknown): boolean {
29
+ return matches(node as never, adapter as AuthoringAdapter);
30
+ }
31
+
32
+ export default function ConstraintsInspector({ adapter, nodeId }: ToolInspectorContributionProps) {
33
+ return <ConstraintStackSection adapter={adapter as AuthoringAdapter} nodeId={nodeId} />;
34
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * THE OBJECT3D MODEL DOCUMENT'S INSPECTOR SECTIONS
3
+ * (`@volter/editor-sdk/services`, a `workspace.service` contribution): the
4
+ * Source / Geometry / Materials / Animation rail a loaded three model shows,
5
+ * and the per-node sections inside it.
6
+ *
7
+ * A SERVICE and not a `selection.inspector`, unlike the three sections beside
8
+ * it, and the reason is measured rather than stylistic: these are SECTION
9
+ * PRODUCERS (`InspectorSectionsProducer` — `sections(node, adapter)` returning
10
+ * a list), because one subject earns a whole rail of identified sections, and
11
+ * the `selection.inspector` point carries exactly ONE component. Registering
12
+ * through `registerInspectorSections` from a service reaches the same registry
13
+ * the point reaches, with the shape this subject actually has.
14
+ *
15
+ * The stop is a no-op: the registrations live in an HMR group the module owns,
16
+ * and the model document outlives any one contribution pass.
17
+ */
18
+
19
+ import { ensureModelAssetSectionsRegistered } from '../../src/three/authoring/model-asset-inspector-section';
20
+
21
+ export const point = 'workspace.service';
22
+
23
+ export function start(): () => void {
24
+ ensureModelAssetSectionsRegistered();
25
+ return () => {};
26
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The reflection probe's Capture Inspector section (`selection.inspector`):
3
+ * the probe's own capture controls for a selected probe node.
4
+ *
5
+ * On the contributed band, see `constraints.inspector.tsx`'s note; the number
6
+ * below is this section's relative order among its siblings.
7
+ */
8
+ import type { AuthoringAdapter } from '@volter/editor-project/adapter';
9
+ import type {
10
+ ToolContributionNode,
11
+ ToolInspectorContributionProps,
12
+ } from '@volter/editor-sdk/contributions';
13
+ import {
14
+ matches,
15
+ ReflectionProbeCaptureSection,
16
+ } from '../../src/three/authoring/reflection-probe-inspector-section';
17
+
18
+ export const point = 'selection.inspector';
19
+ export const title = 'Capture';
20
+ export const icon = 'camera-rotate';
21
+ export const order = 500;
22
+
23
+ export function match(node: ToolContributionNode | null, adapter: unknown): boolean {
24
+ return matches(node as never, adapter as AuthoringAdapter);
25
+ }
26
+
27
+ export default function ReflectionProbeCaptureInspector({
28
+ adapter,
29
+ nodeId,
30
+ }: ToolInspectorContributionProps) {
31
+ return <ReflectionProbeCaptureSection adapter={adapter as AuthoringAdapter} nodeId={nodeId} />;
32
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * THE THREE STORY TURNTABLE (`@volter/editor-sdk/services`, a
3
+ * `workspace.service` contribution): the center document a portable CSF story
4
+ * whose medium is `three` opens as — the story's R3F world mounted on the
5
+ * kit's Object3D stage, with its `Story Args` Inspector section.
6
+ *
7
+ * The DOM/canvas isolated story document is `@volter/editor-game`'s contribution of the
8
+ * same name. They were one service while both documents lived in
9
+ * `@volter/editor-game`; splitting them is what lets a product compose one medium
10
+ * without the other, and neither registers anything the other needs.
11
+ *
12
+ * A SERVICE and not a `workspace.document`, for the same reason as its
13
+ * sibling: this is a kind with an unbounded set of documents, one per
14
+ * (module, story). The document opens through its address
15
+ * (`@editor/document-open-registry`) and restores through its own restorer
16
+ * (`@editor/workspace-document-restore`).
17
+ */
18
+
19
+ import { ensureThreeStoryContributionsRegistered } from '../../src/three/story-documents/three-story-documents';
20
+
21
+ export const point = 'workspace.service';
22
+
23
+ export function start(): () => void {
24
+ // The module registers its opener and restorer at module scope; this call
25
+ // installs the story-scoped Inspector section. It is idempotent and returns
26
+ // nothing; the registration group it owns is HMR-safe by construction.
27
+ ensureThreeStoryContributionsRegistered();
28
+ // The stop is a no-op: the registration group is HMR-owned and an open story
29
+ // document outlives any one contribution pass.
30
+ return () => {};
31
+ }