@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,373 @@
1
+ /**
2
+ * THE `SystemAdapters` TWO-STATE RULE FOR A NATIVE MOUNT — the pure half of
3
+ * `@volter/editor-game/coverage/native-system-coverage.ts`.
4
+ *
5
+ * ## The defect this closes
6
+ *
7
+ * `@volter/editor-game/coverage/root-coverage.ts` made coverage provenance-neutral for the
8
+ * `editor.*` family — every mounted root, native or ingested, gets a row per
9
+ * `AuthoringAdapter` provider. It deliberately keeps ONLY that family, on the
10
+ * grounds that the remaining facts are "the ingested lane's own questions".
11
+ * That is true of `contract.*` (a native game declares no `window.vgaiGame`
12
+ * and never will) and false of `system.*`: a `SystemAdapters` slot is read off
13
+ * the editor's OWN live registry (`authoring/active-systems.ts`), which a
14
+ * native game fills exactly as an ingested one does. So the slots went
15
+ * unreported for every first-party game, and the measured case is a
16
+ * first-party Colyseus project whose Network panel is dark in precisely the
17
+ * same way a single-player project's is — with nothing standing anywhere to
18
+ * say which of the two it is.
19
+ *
20
+ * ## Why an absence here can be a positive answer at all
21
+ *
22
+ * A foreign game's silence is ambiguous by construction: an omitted key means
23
+ * "unsupported", which is the same shape as "nobody looked" — which is exactly
24
+ * why the contract needs an explicit `{ present: false, evidence }` record.
25
+ * A native game has no such ambiguity for the ENGINE-OWNED slots, because the
26
+ * engine BUILT the bag (`runtime/game.ts`'s `computeSystemAdapters` merges what
27
+ * each mounted root's `mounted.systems` carries). An unbound engine-owned slot
28
+ * on a live native mount is therefore the engine's own statement that no
29
+ * mounted root built that subsystem — not an unasked question.
30
+ *
31
+ * `networking` is the exception, and it is the whole point. Nothing engine-side
32
+ * builds it: a game registers a `NetworkingAdapter` itself. So its absence is
33
+ * ruled out only by the PROJECT'S OWN FILE — no `server` block in
34
+ * `vgai.project.json` means there is no room to describe. A game that DOES
35
+ * declare a room and binds nothing is `unanswered`, i.e. a standing warning.
36
+ *
37
+ * ## Provenance neutrality
38
+ *
39
+ * Nothing here consults a route, a game id, or a provenance flag. Both
40
+ * measurers answer the same question — "is this slot at a terminal state, and
41
+ * what is the evidence?" — over the facts their own lane actually has, and each
42
+ * carries its own `fix` text so the shared row builder in
43
+ * `coverage/capability-coverage.ts` never has to ask who produced the measurement.
44
+ *
45
+ * Pure over its inputs, so the whole table is unit-testable with no browser and
46
+ * no live session.
47
+ */
48
+
49
+ import type { DeclaredSystemAbsence } from '@volter/game-runtime/runtime/game';
50
+ import type { SystemAdapters } from '@volter/editor-project/adapter/system-adapter';
51
+ import {
52
+ SYSTEM_ADAPTER_SLOTS,
53
+ type SystemAdapterMeasurement,
54
+ type SystemAdapterSlot,
55
+ } from './capability-coverage';
56
+ import { inspectSystemAdapterSeam } from '@volter/editor-core/coverage/system-seam-evidence';
57
+
58
+ /** What the native derivation needs to know that the live registry cannot tell
59
+ * it. Both facts are read off things that already exist; neither is a guess. */
60
+ export interface NativeProjectFacts {
61
+ /**
62
+ * `vgai.project.json` declares a `server` block (a Colyseus room) — read from
63
+ * the active project view (`project-manager.ts`'s `ActiveProject.server`).
64
+ * The decisive project-level declaration for whether this game is networked.
65
+ */
66
+ readonly declaresServer: boolean;
67
+ /**
68
+ * A game is actually MOUNTED right now. False means the registry is empty
69
+ * because nothing is running, not because no root built a subsystem — and an
70
+ * empty registry read as a fistful of positive absences would be the exact
71
+ * wrong answer, so nothing is terminal in that case.
72
+ */
73
+ readonly mounted: boolean;
74
+ /**
75
+ * What the mounted roots' own `systems` tables said with `absent(reason)` —
76
+ * `Game.declaredSystemAbsences`, harvested through
77
+ * `binding.observation.entrySystems` at mount.
78
+ *
79
+ * This is the DETECT-OR-DECLARE half, and it outranks every generic excuse
80
+ * below: the table's evidence is the GAME's own words about its own source,
81
+ * where {@link ENGINE_OWNED_EVIDENCE} can only say what the engine would have
82
+ * had to build. A game that declares nothing loses nothing — it falls through
83
+ * to exactly the derivation it had before.
84
+ */
85
+ readonly declaredAbsences: readonly DeclaredSystemAbsence[];
86
+ /**
87
+ * The absences above were RETAINED from this session's last play mount
88
+ * rather than read off a live Game. The declaration is a static fact about
89
+ * the entry's `systems` table, so it remains the game's own answer while
90
+ * stopped — but the row says which reading it is, because "as of the last
91
+ * mount" and "live right now" are different claims.
92
+ */
93
+ readonly absencesFromLastMount?: boolean;
94
+ /**
95
+ * The project's own dependency names — the DETECT half of detect-or-declare.
96
+ *
97
+ * `null` means the question was not answered (an unlanded fetch, an
98
+ * unreadable package.json) and MUST NOT be read as "no libraries":
99
+ * an unasked question that reassures is exactly the failure this family
100
+ * exists to prevent, so a `null` detector simply stands down and every row
101
+ * reports what it would have without it.
102
+ */
103
+ readonly dependencies: readonly string[] | null;
104
+ }
105
+
106
+ /**
107
+ * Libraries whose presence CONTRADICTS an empty slot — a project shipping one
108
+ * of these is carrying the subsystem, whoever failed to bind it.
109
+ *
110
+ * Deliberately narrow, because a false positive here turns a correct terminal
111
+ * row into a permanent false work order. `@colyseus/schema` is the worked
112
+ * exclusion: it is shared state TYPES, which a single-player game legitimately
113
+ * depends on to compile against a room definition it never joins — only the
114
+ * CLIENT (`colyseus.js`) is evidence that this game talks to a server.
115
+ * `renderDebug`, `camera` and `debug` have no library tell at all (the host
116
+ * derives them from its own wiring), so they are absent from this table and
117
+ * nothing about them is ever inferred from a dependency list.
118
+ */
119
+ const SLOT_LIBRARIES: Partial<Record<SystemAdapterSlot, readonly string[]>> = {
120
+ physics: ['@react-three/rapier', '@dimforge/rapier3d-compat', '@dimforge/rapier2d-compat'],
121
+ networking: ['colyseus.js'],
122
+ navigation: ['recast-navigation', '@recast-navigation/core'],
123
+ audio: ['tone', 'howler'],
124
+ };
125
+
126
+ /** The libraries this project ships for `slot`, or `[]`. */
127
+ function shippedLibraries(slot: SystemAdapterSlot, facts: NativeProjectFacts): readonly string[] {
128
+ if (facts.dependencies === null) return [];
129
+ const tells = SLOT_LIBRARIES[slot];
130
+ if (!tells) return [];
131
+ return tells.filter((tell) => facts.dependencies?.includes(tell));
132
+ }
133
+
134
+ /**
135
+ * The fix for a slot whose library ships but whose adapter is unbound — and it
136
+ * names BOTH spellings of a terminal answer, because either is legitimate and
137
+ * the author is the only one who knows which is true.
138
+ */
139
+ function contradictionFix(slot: SystemAdapterSlot, libraries: readonly string[]): string {
140
+ return (
141
+ `this project depends on ${libraries.join(' + ')}, so "nothing built a ${slot} world" is not ` +
142
+ `a settled answer. Reach a terminal state either way: BIND it — declare a ${slot} adapter in ` +
143
+ `the root entry's \`systems\` table (the engine ships \`rapierPhysicsSystem()\` for the R3F ` +
144
+ `Rapier bridge; other slots take the game's own adapter) — or DECLARE the absence with ` +
145
+ `\`absent(reason)\` in that same table. The reason must NAME the dependency and say why it ` +
146
+ `is there while the subsystem is not (a transitive pull, a leftover, a build-only use); a ` +
147
+ `declared absence blind to the shipped dependency grades malformed, not terminal.`
148
+ );
149
+ }
150
+
151
+ /**
152
+ * Whether a declared-absence reason ENGAGES with a contradicting shipped
153
+ * library — mechanically: it names the package, or a DISTINCTIVE word of the
154
+ * package's own name (`@react-three/rapier` is acknowledged by a reason that
155
+ * says "rapier"). A reason that names the evidence is the game answering the
156
+ * contradiction; one that does not never saw it.
157
+ *
158
+ * The candidates are word-boundary matches on the package TAIL's tokens with
159
+ * ecosystem/generic words dropped — measured adversarially before this
160
+ * shape: bare-substring segment matching let "pure three.js visual scene"
161
+ * acknowledge `@react-three/rapier` (via `three`), "backwards compat"
162
+ * acknowledge `rapier3d-compat` (via `compat`), and "milestone" contain
163
+ * `tone`. When the tail is ENTIRELY generic (`@recast-navigation/core`),
164
+ * the scope's own words become the candidates instead — the package still
165
+ * has a name, and the reason must say it.
166
+ */
167
+ function reasonAcknowledges(reason: string, library: string): boolean {
168
+ // The full name counts — but a PLAIN name needs word boundaries, or
169
+ // "milestone" would contain `tone`. A scoped/punctuated name is already
170
+ // too distinctive to appear by accident.
171
+ const escaped = library.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
172
+ if (/^[a-z0-9]+$/i.test(library)) {
173
+ if (new RegExp(`\\b${escaped}\\b`, 'i').test(reason)) return true;
174
+ } else if (reason.toLowerCase().includes(library.toLowerCase())) {
175
+ return true;
176
+ }
177
+ const GENERIC = new Set([
178
+ 'compat',
179
+ 'core',
180
+ 'client',
181
+ 'server',
182
+ 'node',
183
+ 'react',
184
+ 'three',
185
+ 'libs',
186
+ 'api',
187
+ 'sdk',
188
+ 'util',
189
+ 'utils',
190
+ 'plugin',
191
+ 'plugins',
192
+ 'tools',
193
+ 'types',
194
+ 'browser',
195
+ ]);
196
+ const tokensOf = (part: string): string[] =>
197
+ part
198
+ .toLowerCase()
199
+ .split(/[.\-_/]+/)
200
+ .filter((token) => token.length >= 4 && !GENERIC.has(token));
201
+ const [scopePart, tailPart] = library.toLowerCase().startsWith('@')
202
+ ? [library.slice(1, library.indexOf('/')), library.slice(library.indexOf('/') + 1)]
203
+ : ['', library];
204
+ let candidates = tokensOf(tailPart);
205
+ if (candidates.length === 0) candidates = tokensOf(scopePart);
206
+ // A digit-suffixed token also answers to its stem: `rapier3d` ⇒ `rapier`.
207
+ const stems = candidates
208
+ .map((token) => token.replace(/\d+d?$/, ''))
209
+ .filter((stem) => stem.length >= 4);
210
+ return [...new Set([...candidates, ...stems])].some((token) =>
211
+ new RegExp(`\\b${token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(reason),
212
+ );
213
+ }
214
+
215
+ /** The file fact that rules `networking` out, in the file's own terms. */
216
+ const NO_SERVER_EVIDENCE =
217
+ "this project's vgai.project.json declares no `server` block, so there is no room for a " +
218
+ 'NetworkingAdapter to describe';
219
+
220
+ /**
221
+ * Why an unbound ENGINE-OWNED slot is terminal on a live native mount: what the
222
+ * engine would have had to build for the slot to be filled, so a reader can
223
+ * check the claim against the root's own source rather than take it on faith.
224
+ *
225
+ * Keyed by slot rather than listed, so a slot with no entry falls through to
226
+ * `unanswered` (a work order) instead of silently acquiring an excuse. `debug`
227
+ * is deliberately absent: the game-scoped registry binds it unconditionally
228
+ * (`runtime/game.ts`'s `computeSystemAdapters` seeds it before walking the
229
+ * roots), so an unbound `debug` on a live mount means something is genuinely
230
+ * wrong and must stay loud.
231
+ */
232
+ const ENGINE_OWNED_EVIDENCE: Partial<Record<SystemAdapterSlot, string>> = {
233
+ physics:
234
+ 'no mounted root built a physics world — the engine seeds this slot from the root that ' +
235
+ 'creates one (a `setup` root, or an R3F tree using the Rapier bridge), so nothing in this ' +
236
+ 'game simulates bodies',
237
+ navigation:
238
+ "no mounted root registered a navigation adapter — the engine seeds this slot from a world's " +
239
+ 'own NavMeshManager registration, so this game has no navmesh',
240
+ audio:
241
+ 'no mounted root built an audio context — the engine seeds this slot from the root that ' +
242
+ 'creates one, so there is no audio graph for the host to mute, meter or graph',
243
+ camera:
244
+ 'no mounted root registered a camera-ownership system — the engine ships ' +
245
+ '`createCameraOwnershipSystem` for a game that takes camera control (a cutscene rig), so ' +
246
+ "this game's camera is gameplay-owned throughout",
247
+ renderDebug:
248
+ 'no mounted root captured a real WebGL2 render context to derive one from — the engine seeds ' +
249
+ 'this slot only where the root owns the capture wiring ' +
250
+ '(`adapter/setup-three-root-adapter.ts`), so the Frame debugger and the render-memory view ' +
251
+ 'have nothing to show for this game',
252
+ };
253
+
254
+ /** The fix a native game's author can actually act on, per slot. Never the
255
+ * ingested lane's `window.vgaiGame` text, which would be false here. */
256
+ function nativeSlotFix(slot: SystemAdapterSlot, facts: NativeProjectFacts): string {
257
+ if (!facts.mounted) {
258
+ return 'nothing is mounted, so the live system registry has not been asked yet — enter Play, or open a project whose roots mount, and the slot reports its real state';
259
+ }
260
+ if (slot === 'networking') {
261
+ return (
262
+ "register a NetworkingAdapter from the game's own code " +
263
+ "(`ctx.registerSystemAdapter('networking', …)`) — this project declares a `server` block, " +
264
+ "so the editor's Network panel is dark for a game that IS networked"
265
+ );
266
+ }
267
+ return `register a ${slot} adapter from the game's own code (\`ctx.registerSystemAdapter('${slot}', …)\`), or build the subsystem the engine seeds it from`;
268
+ }
269
+
270
+ /**
271
+ * The terminal state of every `SystemAdapters` slot for a live NATIVE mount.
272
+ *
273
+ * `bound` is read off the editor's own live registry — the very object its
274
+ * panels call, so a slot that "registered" but never reached the registry
275
+ * reports honestly. Everything else is decided by the facts above, and a bound
276
+ * adapter always wins because the registry is the stronger measurement.
277
+ */
278
+ export function measureNativeSystemAdapters(
279
+ active: SystemAdapters,
280
+ facts: NativeProjectFacts,
281
+ ): readonly SystemAdapterMeasurement[] {
282
+ const declared = new Map(facts.declaredAbsences.map((a) => [a.slot, a]));
283
+ return SYSTEM_ADAPTER_SLOTS.map((slot): SystemAdapterMeasurement => {
284
+ const absence = declared.get(slot);
285
+ if (active[slot] != null) {
286
+ // A DECLARED absence contradicted by a live adapter. Both statements are
287
+ // about the same slot and they cannot both be true, so neither terminal
288
+ // state may be printed as if it were settled: `malformed` is the existing
289
+ // loud state for a declaration that could not be honoured, and it is the
290
+ // honest verdict here. Which one is wrong is the author's to find — the
291
+ // row names both halves so they can.
292
+ if (absence) {
293
+ return {
294
+ slot,
295
+ state: 'malformed',
296
+ evidence:
297
+ `root "${absence.rootId}" declares this slot absent — "${absence.reason}" — but a ` +
298
+ `${slot} adapter is bound on the live mount. One of the two is wrong: either the ` +
299
+ 'declaration is stale (delete the `absent()` marker) or something registers an ' +
300
+ 'adapter the game does not mean to have.',
301
+ };
302
+ }
303
+ return {
304
+ slot,
305
+ state: 'bound',
306
+ proof: inspectSystemAdapterSeam({
307
+ slot,
308
+ adapter: active[slot] as NonNullable<SystemAdapters[typeof slot]>,
309
+ subject: 'game',
310
+ }),
311
+ };
312
+ }
313
+ if (!facts.mounted) return { slot, state: 'unanswered', fix: nativeSlotFix(slot, facts) };
314
+ // The game's own answer outranks every derived excuse below — it is a
315
+ // statement about this game's source, where the fallbacks can only describe
316
+ // what the engine would have had to build.
317
+ const libraries = shippedLibraries(slot, facts);
318
+ if (absence) {
319
+ // DECLARED absent while the project ships the library. The declaration
320
+ // may still be right (a transitive pull, a leftover, a build-only use) —
321
+ // but only if it ENGAGES with the evidence: a reason that names the
322
+ // dependency is the game saying why it is there without the subsystem,
323
+ // and is honored as the game's answer. A reason blind to a shipped
324
+ // library is loud, naming both halves, exactly like the bound-adapter
325
+ // contradiction above — a terminal row there would stop anyone ever
326
+ // checking.
327
+ const unacknowledged = libraries.filter((lib) => !reasonAcknowledges(absence.reason, lib));
328
+ if (unacknowledged.length > 0) {
329
+ return {
330
+ slot,
331
+ state: 'malformed',
332
+ evidence:
333
+ `root "${absence.rootId}" declares this slot absent — "${absence.reason}" — but this ` +
334
+ `project depends on ${unacknowledged.join(' + ')} and the reason does not mention ` +
335
+ 'it. Either the absence is stale, or it holds and the reason should say why that ' +
336
+ 'dependency is present without the subsystem (naming it).',
337
+ };
338
+ }
339
+ const lastMountNote = facts.absencesFromLastMount
340
+ ? ' — the declaration read at this session’s last play mount; play to re-read it live'
341
+ : '';
342
+ return {
343
+ slot,
344
+ state: 'empty',
345
+ attestedBy: 'game',
346
+ evidence:
347
+ libraries.length > 0
348
+ ? `declared absent by root "${absence.rootId}": ${absence.reason} ` +
349
+ `(the project does depend on ${libraries.join(' + ')}; the reason engages with that)${lastMountNote}`
350
+ : `declared absent by root "${absence.rootId}": ${absence.reason}${lastMountNote}`,
351
+ };
352
+ }
353
+ // Nothing declared, and the library IS here: the host's inference below
354
+ // would print a confident absence over real evidence to the contrary. That
355
+ // is an UNANSWERED question — the only state that is a work order — not a
356
+ // terminal one.
357
+ if (libraries.length > 0) {
358
+ return { slot, state: 'unanswered', fix: contradictionFix(slot, libraries) };
359
+ }
360
+ // Everything below is the HOST reasoning from an unfilled slot, not the
361
+ // game speaking. Tagging it says so in the row, and names the mechanism
362
+ // that would let the game answer for itself.
363
+ if (slot === 'networking') {
364
+ return facts.declaresServer
365
+ ? { slot, state: 'unanswered', fix: nativeSlotFix(slot, facts) }
366
+ : { slot, state: 'empty', attestedBy: 'host', evidence: NO_SERVER_EVIDENCE };
367
+ }
368
+ const evidence = ENGINE_OWNED_EVIDENCE[slot];
369
+ return evidence
370
+ ? { slot, state: 'empty', attestedBy: 'host', evidence }
371
+ : { slot, state: 'unanswered', fix: nativeSlotFix(slot, facts) };
372
+ });
373
+ }
@@ -0,0 +1,104 @@
1
+ import { EditorBadge } from '@volter/editor-sdk/widgets';
2
+ import type { CSSProperties, ReactNode } from 'react';
3
+
4
+ export function DesignSystemPage({
5
+ eyebrow,
6
+ title,
7
+ summary,
8
+ children,
9
+ testId,
10
+ }: {
11
+ eyebrow: string;
12
+ title: string;
13
+ summary: string;
14
+ children: ReactNode;
15
+ testId?: string;
16
+ }) {
17
+ return (
18
+ <main className="ds-page" data-testid={testId}>
19
+ <header className="ds-page-header">
20
+ <span className="ds-eyebrow">{eyebrow}</span>
21
+ <h1 className="ds-page-title">{title}</h1>
22
+ <p className="ds-page-summary">{summary}</p>
23
+ </header>
24
+ {children}
25
+ </main>
26
+ );
27
+ }
28
+
29
+ export function StorySection({
30
+ title,
31
+ description,
32
+ children,
33
+ }: {
34
+ title: string;
35
+ description?: string;
36
+ children: ReactNode;
37
+ }) {
38
+ return (
39
+ <section className="ds-section">
40
+ <header className="ds-section-header">
41
+ <h2 className="ds-section-title">{title}</h2>
42
+ {description && <p className="ds-section-description">{description}</p>}
43
+ </header>
44
+ {children}
45
+ </section>
46
+ );
47
+ }
48
+
49
+ export function Specimen({
50
+ title,
51
+ description,
52
+ children,
53
+ }: {
54
+ title: string;
55
+ description?: string;
56
+ children: ReactNode;
57
+ }) {
58
+ return (
59
+ <article className="ds-specimen">
60
+ <header className="ds-specimen-header">
61
+ <h3 className="ds-specimen-title">{title}</h3>
62
+ {description && <p className="ds-specimen-description">{description}</p>}
63
+ </header>
64
+ {children}
65
+ </article>
66
+ );
67
+ }
68
+
69
+ export function TokenSwatch({ name, value }: { name: string; value: string }) {
70
+ return (
71
+ <div className="ds-token">
72
+ <div className="ds-token-swatch" style={{ background: value }} />
73
+ <div className="ds-token-meta">
74
+ <span className="ds-token-name">{name}</span>
75
+ <span className="ds-token-value">{value}</span>
76
+ </div>
77
+ </div>
78
+ );
79
+ }
80
+
81
+ export function Measure({ name, value, width }: { name: string; value: string; width: number }) {
82
+ return (
83
+ <div className="ds-measure-row">
84
+ <span className="ds-token-name">{name}</span>
85
+ <span className="ds-measure" style={{ width: `${width}px` }} />
86
+ <span className="ds-token-value">{value}</span>
87
+ </div>
88
+ );
89
+ }
90
+
91
+ export function StateRow({ label, children }: { label: string; children: ReactNode }) {
92
+ return (
93
+ <>
94
+ <span className="ds-state-label">{label}</span>
95
+ <div className="ds-demo-row">{children}</div>
96
+ </>
97
+ );
98
+ }
99
+
100
+ export function CoverageStatus({ layer }: { layer: string }) {
101
+ return <EditorBadge>{layer}</EditorBadge>;
102
+ }
103
+
104
+ export const boundedControl: CSSProperties = { width: 220 };