@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,255 @@
1
+ /**
2
+ * The DEBUG-SEAM verbs of the session wire (`@volter/editor-sdk/commands`, a
3
+ * `workspace.command` contribution): reading a running game's declared state
4
+ * and commands, and actuating its input, time scale, seed and tick loop.
5
+ *
6
+ * All eight read/actuate the LIVE play session's game-scoped debug registry
7
+ * (`SystemAdapters.debug`, installed by `setActiveSystems` on play enter) or
8
+ * its InputManager/GameLoop (via `getPlayRuntimeAccess`). A stopped session
9
+ * answers with a structured failure, never a fabricated success; a running
10
+ * game that exposes NO debug adapter (a non-first-party mount) degrades to
11
+ * the honest empty/null shape.
12
+ *
13
+ * Play mode and the active-systems registry are the game skew's own modules,
14
+ * still housed in the editor until Play leaves the host (WORK.md §The
15
+ * workbench, G); they are reached through the `@editor/*` alias the editor's
16
+ * Vite serves to every contribution, and become this package's own imports
17
+ * when they move. Nothing here is host API.
18
+ */
19
+
20
+ import { getActiveSystems } from '@volter/editor-core/authoring/active-systems';
21
+ import type { CommandContribution } from '@volter/editor-sdk/commands';
22
+ import { notPlayingResult, structuredErrorResult } from '../src/command-results';
23
+ import { getPlayRuntimeAccess, isPlayModeActive } from '../src/play/play-mode';
24
+
25
+ export const point = 'workspace.command';
26
+
27
+ export const commands: CommandContribution['commands'] = {
28
+ 'list-gameplay-state': {
29
+ derivedRefresh: 'if-content-changed',
30
+ handle: () => {
31
+ const debug = getActiveSystems().debug;
32
+ if (!debug) {
33
+ return isPlayModeActive() ? { ok: true, data: { providers: [] } } : notPlayingResult();
34
+ }
35
+ return { ok: true, data: { providers: debug.providers() } };
36
+ },
37
+ },
38
+ 'inspect-gameplay-state': {
39
+ derivedRefresh: 'if-content-changed',
40
+ handle: (cmd) => {
41
+ const debug = getActiveSystems().debug;
42
+ if (!debug) {
43
+ return isPlayModeActive() ? { ok: true, data: { state: null } } : notPlayingResult();
44
+ }
45
+ const keys = cmd['keys'] as string[] | undefined;
46
+ try {
47
+ const state: Record<string, unknown> = {};
48
+ if (keys && keys.length > 0) {
49
+ for (const key of keys) state[key] = debug.state(key);
50
+ } else {
51
+ Object.assign(state, debug.stateAll());
52
+ }
53
+ return { ok: true, data: { state } };
54
+ } catch (err) {
55
+ return structuredErrorResult(err);
56
+ }
57
+ },
58
+ },
59
+ 'list-debug-commands': {
60
+ derivedRefresh: 'if-content-changed',
61
+ handle: () => {
62
+ const debug = getActiveSystems().debug;
63
+ if (!debug) {
64
+ return isPlayModeActive() ? { ok: true, data: { commands: [] } } : notPlayingResult();
65
+ }
66
+ return { ok: true, data: { commands: debug.commands() } };
67
+ },
68
+ },
69
+ 'invoke-debug-command': {
70
+ derivedRefresh: 'always',
71
+ handle: async (cmd) => {
72
+ const debug = getActiveSystems().debug;
73
+ if (!debug) {
74
+ if (!isPlayModeActive()) return notPlayingResult();
75
+ // Honest not-registered shape: no adapter means NOTHING is registered.
76
+ return {
77
+ ok: false,
78
+ error: `debug: no command registered under "${cmd['name']}" — this game exposes no debug adapter`,
79
+ data: { code: 'DEBUG_COMMAND_NOT_REGISTERED', registered: [] },
80
+ };
81
+ }
82
+ try {
83
+ const result = await debug.invoke(cmd['name'] as string, (cmd['args'] as unknown[]) ?? []);
84
+ return { ok: true, data: { result } };
85
+ } catch (err) {
86
+ return structuredErrorResult(err);
87
+ }
88
+ },
89
+ },
90
+ 'inject-input': {
91
+ derivedRefresh: 'none',
92
+ handle: (cmd) => {
93
+ const runtime = getPlayRuntimeAccess();
94
+ if (!runtime) {
95
+ return { ok: false, error: 'not in play mode — start play before injecting input' };
96
+ }
97
+ // D15/T-D15.5 (review objection 2's fix) — resolved through the SAME
98
+ // per-world `DebugRegistry.getVirtualInputTarget(worldId?)` the debug
99
+ // bridge's `window.__vgai.input.*` calls, instead of the old
100
+ // `Game.input` (always the DEFAULT world only) — an explicit
101
+ // `cmd['worldId']` reaches that world specifically; omitted resolves
102
+ // to the same default both doors now share. Throws
103
+ // `DEBUG_INPUT_WORLD_NOT_FOUND` (caught below) for an explicit,
104
+ // unregistered world id.
105
+ let input: ReturnType<typeof runtime.getInputTarget>;
106
+ try {
107
+ input = runtime.getInputTarget(cmd['worldId'] as string | undefined);
108
+ } catch (err) {
109
+ return structuredErrorResult(err);
110
+ }
111
+ if (!input) {
112
+ return {
113
+ ok: false,
114
+ error: 'this play session has no first-party InputManager to inject into',
115
+ };
116
+ }
117
+ try {
118
+ // Action-level is primary (frozen decision, spec §3.2): the honest
119
+ // gated path — a gated actuation surfaces {delivered:false, reason},
120
+ // never a false ok. The four legacy kinds map to the persistent
121
+ // test-source injectors (read back only through declared test_*
122
+ // bindings, so `delivered` here means "the source was written").
123
+ if (cmd['kind'] === 'action') {
124
+ const atTick = cmd['atTick'];
125
+ // D15/T-D15.5 — `atTick` defers to InputManager.scheduleActionAtTick
126
+ // instead of an immediate setVirtualAction. scheduleActionAtTick
127
+ // throws InputActionError/a valueType mismatch (same as
128
+ // setVirtualAction) or InputTickError ('TICK_ALREADY_PASSED',
129
+ // data.currentTick) — all caught by this handler's outer try/catch
130
+ // below via structuredErrorResult.
131
+ if (typeof atTick === 'number') {
132
+ input.scheduleActionAtTick(
133
+ atTick,
134
+ cmd['action'] as string,
135
+ cmd['value'] as boolean | number | { x: number; y: number },
136
+ );
137
+ return { ok: true, data: { scheduled: true, tick: atTick } };
138
+ }
139
+ const outcome = input.setVirtualAction(
140
+ cmd['action'] as string,
141
+ cmd['value'] as boolean | number | { x: number; y: number },
142
+ );
143
+ return { ok: true, data: outcome };
144
+ }
145
+ const sourceId = cmd['sourceId'] as string;
146
+ switch (cmd['kind']) {
147
+ case 'axis':
148
+ input.injectAxis(sourceId, cmd['value'] as number);
149
+ break;
150
+ case 'vector2':
151
+ input.injectVector2(sourceId, cmd['value'] as { x: number; y: number });
152
+ break;
153
+ case 'pointerDelta':
154
+ input.injectPointerDelta(sourceId, cmd['value'] as { x: number; y: number });
155
+ break;
156
+ case 'pointerPosition':
157
+ input.injectPointerPosition(sourceId, cmd['value'] as { x: number; y: number });
158
+ break;
159
+ default:
160
+ return { ok: false, error: `unknown injection kind "${cmd['kind']}"` };
161
+ }
162
+ return { ok: true, data: { delivered: true } };
163
+ } catch (err) {
164
+ return structuredErrorResult(err);
165
+ }
166
+ },
167
+ },
168
+ 'set-time-scale': {
169
+ derivedRefresh: 'none',
170
+ handle: (cmd) => {
171
+ const runtime = getPlayRuntimeAccess();
172
+ if (!runtime) {
173
+ return { ok: false, error: 'not in play mode — start play before setting time-scale' };
174
+ }
175
+ runtime.loop.timeScale = cmd['timeScale'] as number;
176
+ // Read back after the loop's own [0,8] clamp so the caller sees the
177
+ // value actually applied.
178
+ return { ok: true, data: { timeScale: runtime.loop.timeScale } };
179
+ },
180
+ },
181
+ // D15/T-D15.6 — the `play.seed.set` relay verb: reaches the live
182
+ // session's `ctx.random.reseed(seed)` through `getPlayRuntimeAccess()`'s
183
+ // `random` accessor. Gated on `determinismDeclared` (the running
184
+ // project's manifest `determinism.seededRandom`, per T4.1: reseeding a
185
+ // project with no documented determinism contract would silently imply
186
+ // one exists) — the structured `DETERMINISM_NOT_DECLARED` marker the SDK
187
+ // maps to its own declared error code.
188
+ 'set-seed': {
189
+ derivedRefresh: 'none',
190
+ handle: (cmd) => {
191
+ const runtime = getPlayRuntimeAccess();
192
+ if (!runtime) {
193
+ return { ok: false, error: 'not in play mode — start play before setting the seed' };
194
+ }
195
+ if (!runtime.determinismDeclared) {
196
+ return {
197
+ ok: false,
198
+ error:
199
+ "this project's manifest does not declare determinism.seededRandom — " +
200
+ 'play.seed.set has no documented contract to reseed against (D15)',
201
+ data: { code: 'DETERMINISM_NOT_DECLARED' },
202
+ };
203
+ }
204
+ if (!runtime.random) {
205
+ return {
206
+ ok: false,
207
+ error: 'this play session has no first-party ctx.random to reseed',
208
+ };
209
+ }
210
+ runtime.random.reseed(cmd['seed'] as number);
211
+ // Read back the live, post-reseed value (mirrors set-time-scale's own
212
+ // read-after-apply convention immediately above).
213
+ return { ok: true, data: { seed: runtime.random.seed } };
214
+ },
215
+ },
216
+ // D15/T-D15.4 — the `play.runTicks` relay verb: reaches the live
217
+ // session's `GameInternal.runTicks` through `getPlayRuntimeAccess()`'s
218
+ // `runTicks` accessor (itself backed by the SAME
219
+ // `DebugRegistry.getRunTicksTarget()` the bridge's
220
+ // `window.__vgai.runTicks` calls — one implementation, byte-identical
221
+ // behavior across doors, D17). `runTicks` itself throws a structured
222
+ // `DebugError` (`RUN_TICKS_PAUSED`) while paused — never a silent no-op —
223
+ // which `structuredErrorResult` below carries through as `data.code`.
224
+ 'run-ticks': {
225
+ derivedRefresh: 'none',
226
+ handle: async (cmd) => {
227
+ const runtime = getPlayRuntimeAccess();
228
+ if (!runtime) {
229
+ return { ok: false, error: 'not in play mode — start play before running ticks' };
230
+ }
231
+ if (!runtime.runTicks) {
232
+ return {
233
+ ok: false,
234
+ error: 'this play session has no first-party Game to run ticks on',
235
+ data: { code: 'RUN_TICKS_UNAVAILABLE' },
236
+ };
237
+ }
238
+ try {
239
+ const n = cmd['n'] as number;
240
+ const render = cmd['render'] as 'last' | 'all' | 'none' | undefined;
241
+ // The settled-aware driver (`runtime/run-ticks-settled.ts`): a tick never races a
242
+ // scene remount's async commit — byte-identical with the bridge's `runTicksSettled`
243
+ // door (D17). With no world-settled probe registered it is `runTicks` exactly.
244
+ if (runtime.runTicksSettled) {
245
+ await runtime.runTicksSettled(n, render ? { render } : undefined);
246
+ } else {
247
+ runtime.runTicks(n, render ? { render } : undefined);
248
+ }
249
+ return { ok: true };
250
+ } catch (err) {
251
+ return structuredErrorResult(err);
252
+ }
253
+ },
254
+ },
255
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * THE GENERATION ESTATE (`@volter/editor-sdk/services`, a `workspace.service`
3
+ * contribution): the paid-provider lane's editor surface — the Generations
4
+ * drawer gallery over the durable job ledger, the Create-generation document
5
+ * and the per-job result document.
6
+ *
7
+ * They were host modules (`generation-jobs.ts`, `components/GenerationActivity
8
+ * .tsx`, `components/generation-documents.tsx`, `components/generation-
9
+ * presentation.ts`), which put a metered service's ledger, its provider
10
+ * vocabulary and its billing strings inside a base library for making IDEs
11
+ * (WORK.md §The open-source launch, phase 1 unit 6). Nothing here is generic
12
+ * over document kinds or surfaces; every line is about one lane.
13
+ *
14
+ * TWO DOORS, both already the host's:
15
+ * - the drawer TAB is `registerWorkspaceUtility` — a `.utility` contribution
16
+ * would be the shorter spelling, but that point carries no attention BADGE
17
+ * and the tab's whole job is to say how many results are unseen, so the
18
+ * registry call is made here directly (the story documents' service does
19
+ * the same, for the same reason);
20
+ * - the two DOCUMENTS open through `document-open-registry.ts` under the
21
+ * `generation` VIEW ADDRESS — the kind `@volter/editor-sdk`'s `EditorView`
22
+ * already names — so `editor-view-presentation.ts` addresses a generation
23
+ * without importing one, and a build without this package answers `null`
24
+ * to the address instead of pretending.
25
+ *
26
+ * The SERVER half (`packages/editor/server/generation-jobs.ts`, the
27
+ * reconciler, `routes/generations.ts`) is still the editor's: there is no
28
+ * server-side service point yet (WORKBENCH.md §Contribution points, `service`
29
+ * — "the SERVER side … is still the editor's"). This module talks to those
30
+ * routes over HTTP exactly as it did when it lived in the host.
31
+ */
32
+
33
+ import { registerDocumentOpener } from '@volter/editor-core/document-open-registry';
34
+ import { CONTRIBUTED_WORKSPACE_UTILITIES } from '@volter/editor-core/workspace-core-utilities';
35
+ import { registerWorkspaceUtility } from '@volter/editor-core/workspace-utility-registry';
36
+ import { GenerationActivity } from '../src/generation/GenerationActivity';
37
+ import {
38
+ openGenerationCreateDocument,
39
+ openGenerationDocument,
40
+ } from '../src/generation/generation-documents';
41
+ import {
42
+ generationJobsSnapshot,
43
+ generationJobsVersion,
44
+ generationUnreadCount,
45
+ refreshGenerationJobs,
46
+ startGenerationJobActivity,
47
+ subscribeGenerationJobs,
48
+ } from '../src/generation/generation-jobs';
49
+
50
+ export const point = 'workspace.service';
51
+
52
+ /** The `generation` view address (`EditorView['document']`, `@volter/editor-sdk`).
53
+ * `id` is a job id, or `create` for the submit document. */
54
+ const GENERATION_VIEW_ADDRESS = 'generation';
55
+
56
+ function generationsBadge() {
57
+ const count = generationUnreadCount(generationJobsSnapshot().jobs);
58
+ return count > 0 ? ({ count, severity: 'info' } as const) : null;
59
+ }
60
+
61
+ export function start(): () => void {
62
+ const stops = [
63
+ // The ledger POLL, and its cadence, are this lane's own: it was
64
+ // `EditorContext.tsx`'s `useEffect`, so the host held a 2-second timer for
65
+ // a metered service. Deliberately NOT `session.onSample` — that door is the
66
+ // five-second VITALS tick for derivations of the session, and hanging a
67
+ // remote-ledger fetch on it would slow a visible result by 3s for no
68
+ // measured reason. The service owns the interval and ends it on stop.
69
+ startGenerationJobActivity(),
70
+ registerWorkspaceUtility({
71
+ ...CONTRIBUTED_WORKSPACE_UTILITIES.generations,
72
+ order: 15,
73
+ Content: GenerationActivity,
74
+ badge: generationsBadge,
75
+ subscribeBadge: subscribeGenerationJobs,
76
+ badgeSnapshot: generationJobsVersion,
77
+ visibleByDefault: false,
78
+ closeable: true,
79
+ }),
80
+ registerDocumentOpener<{ readonly id: string }>({
81
+ id: GENERATION_VIEW_ADDRESS,
82
+ owner: '@volter/editor-game/generation',
83
+ open: (_store, request) => {
84
+ if (request.id === 'create') return openGenerationCreateDocument();
85
+ const job = generationJobsSnapshot().jobs.find((entry) => entry.id === request.id);
86
+ return job ? openGenerationDocument(job) : null;
87
+ },
88
+ // A job the page has not fetched yet is not a missing job: refresh the
89
+ // ledger once and the address resolves.
90
+ settle: refreshGenerationJobs,
91
+ }),
92
+ ];
93
+ return () => {
94
+ for (const stop of stops) stop();
95
+ };
96
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * GENERATIONS in the status bar (`workspace.status`) — permanent access to the
3
+ * paid-provider lane's results, including when there are none yet.
4
+ *
5
+ * It was `status-contributions.tsx`'s `GenerationsStatus`, a host module, so
6
+ * the base host named the generation ledger to paint a pill. The pill is the
7
+ * same; what changed is who owns it (WORK.md §The open-source launch, phase 1
8
+ * unit 6 — the generation estate is a service lane, not a base library's
9
+ * concern).
10
+ */
11
+
12
+ import { CONTRIBUTED_WORKSPACE_UTILITIES } from '@volter/editor-core/workspace-core-utilities';
13
+ import { editorHost } from '@volter/editor-sdk/host';
14
+ import { Button } from '@volter/editor-sdk/widgets';
15
+ import { useSyncExternalStore } from 'react';
16
+ import {
17
+ generationJobIsActive,
18
+ generationJobsSnapshot,
19
+ generationUnreadCount,
20
+ subscribeGenerationJobs,
21
+ } from '../src/generation/generation-jobs';
22
+
23
+ export const point = 'workspace.status';
24
+ export const title = 'Generations';
25
+ export const align = 'left';
26
+ export const order = 24;
27
+
28
+ export default function GenerationsStatus() {
29
+ const { jobs } = useSyncExternalStore(
30
+ subscribeGenerationJobs,
31
+ generationJobsSnapshot,
32
+ generationJobsSnapshot,
33
+ );
34
+ const unseen = generationUnreadCount(jobs);
35
+ const running = jobs.filter(generationJobIsActive).length;
36
+ return (
37
+ <Button
38
+ type="button"
39
+ variant="ghost"
40
+ size="compact"
41
+ className="vgai-status-action"
42
+ data-testid="status-generations"
43
+ aria-label={`Generations, ${unseen} unseen, ${running} running`}
44
+ title={`${unseen} unseen generation results, ${running} running — open Generations`}
45
+ onClick={() =>
46
+ editorHost().workspace.showUtility(CONTRIBUTED_WORKSPACE_UTILITIES.generations.id)
47
+ }
48
+ >
49
+ Generations · {unseen}
50
+ {running > 0 ? ` · ${running} running` : ''}
51
+ </Button>
52
+ );
53
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * THE INGEST LANE, started (`@volter/editor-sdk/services`, a
3
+ * `workspace.service` contribution).
4
+ *
5
+ * Three jobs, and the order matters:
6
+ *
7
+ * 1. IMPORTING the lane's modules is what runs their registrations. A package
8
+ * module runs only when something imports it, and the lane's live-session
9
+ * registration, its `ingest`/`ingestCaptureWait` state facets and its
10
+ * `onShellStore` binding are module-load side effects of
11
+ * `mount-ingest-root.ts`. The static import below is therefore load-bearing
12
+ * — `vgai status` carries `ingest: null` / `ingestCaptureWait: null` before
13
+ * contribution pass. A product that composes no `@volter/editor-game` reports
14
+ * neither key, which is the honest state: no lane claims to mount
15
+ * unmodified games.
16
+ *
17
+ * 2. AUTO-LAUNCH on `project.onReady`. The two boot chains (`the world root's stage`,
18
+ * `NonThreeAuthoringBootstrap`) used to call `autoLaunchIngest(store)` by
19
+ * name; they now only run the project-ready hook, and this is its first
20
+ * registrant (WORK.md §P4). That hook is LATCHED per project, because a
21
+ * contribution pass can finish on either side of the boot chain;
22
+ * `autoLaunchIngest`'s own dedupe makes the second arrival a no-op. And
23
+ * `hosted-example.ts`'s auto-play decision still runs AFTER the hook, so
24
+ * `isIngestActive()` is answered by a mount this hook already made.
25
+ *
26
+ * 3. REGISTERING THIS LANE'S ADAPTER DECLARATIONS. A repo-vendored game does
27
+ * not ship its own `vgai.adapter.ts` — by the REALM rule its host-realm
28
+ * declaration lives in the in-tree registry (`@editor/ingest/registry`),
29
+ * which the host used to import directly and therefore carried in every
30
+ * editor boot, a `models` build that mounts no unmodified game included.
31
+ * The host now owns only the PRECEDENCE and asks a registered source
32
+ * (`@editor/project-adapter`'s `registerAdapterDefinitionSource`); this lane owns the table,
33
+ * because these are ITS declarations for content IT mounts. The host WAITS
34
+ * for this registration when a project's manifest declares an ingest root
35
+ * — a contract fact it reads before any contribution loads — so the
36
+ * registration arriving on the contribution pass is on time by
37
+ * construction, never a re-mount after a wrong one.
38
+ *
39
+ * The store comes from `@editor/shell-store-door`, the same arrival
40
+ * `mount-ingest-root.ts` binds itself on — read at launch time rather than
41
+ * captured, so neither ordering can leave the launch holding `null`.
42
+ */
43
+ import { editorConsole } from '@volter/editor-core/editor-console';
44
+ import { importIngestAdapterModule, ingestAdapterModulePath } from '../src/host/ingest/registry';
45
+ import { registerAdapterDefinitionSource } from '@volter/editor-core/project-adapter';
46
+ import { shellStoreForHost } from '@volter/editor-core/shell-store-door';
47
+ import { editorHost } from '@volter/editor-sdk/host';
48
+ import { autoLaunchIngest } from '../src/ingest/mount-ingest-root';
49
+
50
+ export const point = 'workspace.service';
51
+
52
+ export function start(): () => void {
53
+ const stopSource = registerAdapterDefinitionSource({
54
+ id: 'in-tree-ingest-registry',
55
+ owner: '@volter/editor-game/contributions/ingest.service.ts',
56
+ modulePathFor: (rootId) => ingestAdapterModulePath(rootId),
57
+ // A module that exists and throws is left to throw: `defineAdapter`
58
+ // validates at evaluation time, and the host names that failure by path
59
+ // rather than swallowing it into the native default.
60
+ importModule: async (rootId) => await importIngestAdapterModule(rootId),
61
+ });
62
+ const stopReady = editorHost().project.onReady(async () => {
63
+ const store = shellStoreForHost();
64
+ if (!store) {
65
+ // The store arrives with `AppRoot`, long before any boot chain reaches
66
+ // ready, so this cannot happen quietly — say so rather than skipping an
67
+ // auto-launch the manifest asked for.
68
+ editorConsole.error(
69
+ 'Ingest auto-launch skipped: the editor shell store had not arrived at project ready.',
70
+ 'ingest',
71
+ );
72
+ return;
73
+ }
74
+ await autoLaunchIngest(store);
75
+ });
76
+ return () => {
77
+ stopReady();
78
+ stopSource();
79
+ };
80
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The INSTANCE verbs of the session wire (`@volter/editor-sdk/commands`, a
3
+ * `workspace.command` contribution): how many instances of the game play
4
+ * split-screen, and which exist.
5
+ *
6
+ * Play mode and the live instance registry are the game skew's own modules,
7
+ * still housed in the editor until Play leaves the host (WORK.md §The
8
+ * workbench, G); they are reached through the `@editor/*` alias the editor's
9
+ * Vite serves to every contribution, and become this package's own imports
10
+ * when they move. Nothing here is host API.
11
+ */
12
+
13
+ import { liveInstanceIds } from '@volter/editor-core/authoring/active-systems';
14
+ import type { CommandContribution } from '@volter/editor-sdk/commands';
15
+ import {
16
+ instanceEntries,
17
+ isPlayModeActive,
18
+ setDesiredExtraInstances,
19
+ setDesiredInstanceNames,
20
+ } from '../src/play/play-mode';
21
+
22
+ export const point = 'workspace.command';
23
+
24
+ const notPlaying = () => ({
25
+ ok: false,
26
+ error: 'not in play mode — start play before using the debug seam',
27
+ });
28
+
29
+ export const commands: CommandContribution['commands'] = {
30
+ // Multiplayer authoring: how many instances of this game to show
31
+ // split-screen (total, including the primary). The GamePanel reconciles by
32
+ // mounting/unmounting extra instances to match. Requires a live play
33
+ // session — there is no primary to sit beside otherwise — and reads as a
34
+ // no-op-that-lies if silently ignored, so it fails loudly when stopped.
35
+ 'set-instance-count': {
36
+ derivedRefresh: 'always',
37
+ handle: (cmd) => {
38
+ if (!isPlayModeActive()) return notPlaying();
39
+ // `names` (optional) labels the instances (index 0 = primary); its length
40
+ // is then the total count. Otherwise `count` is the total, with default
41
+ // "Player N" labels.
42
+ const names = cmd['names'];
43
+ if (Array.isArray(names) && names.every((n) => typeof n === 'string')) {
44
+ if (names.length < 1) {
45
+ return { ok: false, error: 'set-instance-count "names" must have at least one entry.' };
46
+ }
47
+ setDesiredInstanceNames(names as string[]);
48
+ return { ok: true };
49
+ }
50
+ const count = cmd['count'];
51
+ if (typeof count !== 'number' || !Number.isFinite(count) || count < 1) {
52
+ return {
53
+ ok: false,
54
+ error: `set-instance-count requires a whole "count" >= 1 (or a "names" array), got ${String(count)}.`,
55
+ };
56
+ }
57
+ setDesiredExtraInstances(Math.floor(count) - 1);
58
+ return { ok: true };
59
+ },
60
+ },
61
+ // The instance-enumeration op — the ONE session-wire question that must
62
+ // NOT route through the bridge: it answers "which instances exist", and the
63
+ // bridge RESOLVES an instance (and refuses when several are live), so
64
+ // asking it "which exist" is the exact ambiguity it throws on. Returns the
65
+ // live mount ids so `@volter/editor-live`'s `game.instances()` can hand back one
66
+ // addressed `GameClient` per id. No play gate: `liveInstanceIds()` is `[]`
67
+ // when nothing is mounted, which is the honest answer, not a precondition
68
+ // failure. `named` adds the friendly labels a HUD/driver shows ("Player
69
+ // 2"), from the play-mode instances (ids without a play-mode name — e.g. an
70
+ // ingest mount — simply do not appear in `named`).
71
+ 'list-instances': {
72
+ derivedRefresh: 'if-content-changed',
73
+ handle: () => ({ ok: true, data: { instances: liveInstanceIds(), named: instanceEntries() } }),
74
+ },
75
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Debug ▸ Bake/Clear NavMesh — the navmesh session verbs as menu items.
3
+ *
4
+ * Both dispatch the window events `../src/navmesh/navmesh-handler.ts` listens
5
+ * on (started by `navmesh.service.ts`), through `../src/navmesh/navmesh-actions.ts`:
6
+ * the handler and the trigger import the SAME names, so a rename cannot
7
+ * silently unwire the control. The whole cluster — handler, event names,
8
+ * workflow store — is this package's now (P6b); no host module reads it.
9
+ *
10
+ * `disabled` and the Bake label are asked at render, and the host samples them
11
+ * on its availability tick while a menu is open, so they follow the mounted
12
+ * game's `NavigationAdapter` and a bake in flight.
13
+ */
14
+ import { getActiveSystems } from '@volter/editor-core/authoring/active-systems';
15
+ import type { MenuContribution } from '@volter/editor-sdk/chrome';
16
+ import { dispatchNavMeshBake, dispatchNavMeshClear } from '../src/navmesh/navmesh-actions';
17
+ import { navBakeBusy, navMeshBaked } from '../src/navmesh/navmesh-workflow-store';
18
+
19
+ export const point = 'workspace.menu';
20
+
21
+ export const menu: MenuContribution = {
22
+ menu: 'debug',
23
+ items: [
24
+ {
25
+ id: 'navmesh-bake',
26
+ label: () => (navMeshBaked() ? 'Rebake NavMesh' : 'Bake NavMesh'),
27
+ testId: 'navmesh-bake',
28
+ disabled: () => navBakeBusy() || !getActiveSystems().navigation?.bake,
29
+ execute: dispatchNavMeshBake,
30
+ },
31
+ {
32
+ id: 'navmesh-clear',
33
+ label: 'Clear NavMesh',
34
+ testId: 'navmesh-clear',
35
+ disabled: () => navBakeBusy() || !navMeshBaked() || !getActiveSystems().navigation?.clear,
36
+ execute: dispatchNavMeshClear,
37
+ },
38
+ ],
39
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * THE NAVMESH SESSION HANDLER (`@volter/editor-sdk/services`, a
3
+ * `workspace.service` contribution): the listener behind Debug ▸ Bake /
4
+ * Clear NavMesh (`navmesh.menu.ts`), which bakes the authoring scene's
5
+ * walkable-tagged meshes through the mounted game's `NavigationAdapter` and
6
+ * shows the result as the viewport's `navmesh` helper.
7
+ *
8
+ * It was a mount effect in the host's one-and-only scene panel, so a
9
+ * build with no `@volter/editor-game` still carried it and that panel owned its
10
+ * lifetime. A service instead: the contribution pass starts it and stops it
11
+ * before the next one, and no stage names a lane.
12
+ */
13
+ import { setupNavMeshHandlers } from '../src/navmesh/navmesh-handler';
14
+
15
+ export const point = 'workspace.service';
16
+
17
+ export function start(): () => void {
18
+ return setupNavMeshHandlers();
19
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The Network inspector as a drawer tab (W3b): replicated-state tree,
3
+ * message log, rate sparklines and the conditioner over the inspected
4
+ * session's `NetworkingAdapter`. On offer only while such an adapter is
5
+ * registered — a tab over nothing would be a measurement nobody made.
6
+ */
7
+ import { editorHost } from '@volter/editor-sdk/host';
8
+ import { NetworkInspectorPanel } from '../src/network/NetworkInspectorPanel';
9
+
10
+ export const point = 'workspace.utility';
11
+ export const title = 'Network';
12
+ export const order = 25;
13
+ export const available = (): boolean => editorHost().systems.inspectedNetworking() !== null;
14
+
15
+ export default function NetworkUtility() {
16
+ return <NetworkInspectorPanel />;
17
+ }