@volter/editor-core 0.5.57

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 (719) hide show
  1. package/BUNDLED_NOTICES +1527 -0
  2. package/LICENSE +686 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/NOTICE +18 -0
  5. package/README.md +24 -0
  6. package/dist/build/chunk-JS4D4XZ3.js +175 -0
  7. package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
  8. package/dist/build/vite-plugin-product-contributions.js +100 -0
  9. package/dist/build/vite-plugin-product-contributions.js.map +7 -0
  10. package/dist/build/vite-plugin-shared-react.js +25 -0
  11. package/dist/build/vite-plugin-shared-react.js.map +7 -0
  12. package/dist/build/vite-plugin-shared-three.js +86 -0
  13. package/dist/build/vite-plugin-shared-three.js.map +7 -0
  14. package/dist/server/chunk-KMJC7AGO.js +98 -0
  15. package/dist/server/chunk-KMJC7AGO.js.map +7 -0
  16. package/dist/server/chunk-PSILUMGS.js +46 -0
  17. package/dist/server/chunk-PSILUMGS.js.map +7 -0
  18. package/dist/server/frame-proxy.js +255 -0
  19. package/dist/server/frame-proxy.js.map +7 -0
  20. package/dist/server/open-browser.js +53 -0
  21. package/dist/server/open-browser.js.map +7 -0
  22. package/dist/server/process-shutdown.js +127 -0
  23. package/dist/server/process-shutdown.js.map +7 -0
  24. package/dist/server/session-registry.js +141 -0
  25. package/dist/server/session-registry.js.map +7 -0
  26. package/dist/server/spawn-opener.js +11 -0
  27. package/dist/server/spawn-opener.js.map +7 -0
  28. package/dist/server/worktree-identity.js +11 -0
  29. package/dist/server/worktree-identity.js.map +7 -0
  30. package/dist-server/packaged.mjs +88816 -0
  31. package/package.json +118 -0
  32. package/scripts/build-plugins.mjs +14 -0
  33. package/scripts/build-server.mjs +26 -0
  34. package/scripts/build-session.mjs +9 -0
  35. package/server/account-credentials.ts +60 -0
  36. package/server/account-service.ts +1434 -0
  37. package/server/adapter-region-includes.ts +47 -0
  38. package/server/asset-catalog-v2.ts +175 -0
  39. package/server/asset-history-snapshots.ts +196 -0
  40. package/server/asset-ledger-store.ts +195 -0
  41. package/server/asset-library-routes.ts +1595 -0
  42. package/server/blender-wasm-artifact.ts +430 -0
  43. package/server/boot-timings.ts +106 -0
  44. package/server/canonical-path.ts +50 -0
  45. package/server/checkout-workspace-preflight.ts +349 -0
  46. package/server/cloud-asset-catalog.ts +158 -0
  47. package/server/coding-inference-launch.ts +237 -0
  48. package/server/collaboration-account-client.ts +202 -0
  49. package/server/collaboration-attribution.ts +122 -0
  50. package/server/collaboration-session.ts +934 -0
  51. package/server/comfyui-bridge.ts +248 -0
  52. package/server/console-ledger.ts +499 -0
  53. package/server/creation-site-transform.ts +304 -0
  54. package/server/creation-site-write.ts +648 -0
  55. package/server/data-file-serialize.ts +158 -0
  56. package/server/editor-brand-html.ts +72 -0
  57. package/server/editor-control-socket.ts +366 -0
  58. package/server/editor-server-options.ts +190 -0
  59. package/server/editor-server.ts +1483 -0
  60. package/server/editor-sse.ts +352 -0
  61. package/server/engine-provenance.ts +169 -0
  62. package/server/engine-source-restart.ts +117 -0
  63. package/server/frame-bridge.ts +115 -0
  64. package/server/frame-proxy.ts +490 -0
  65. package/server/frame-workbench.ts +259 -0
  66. package/server/frontend-handoff.ts +229 -0
  67. package/server/game-globals-shadow.ts +113 -0
  68. package/server/gameplay-session-retention.ts +149 -0
  69. package/server/gameplay-sessions.ts +123 -0
  70. package/server/generation-jobs.ts +201 -0
  71. package/server/generation-reconciler.ts +137 -0
  72. package/server/generative-execution-context.ts +59 -0
  73. package/server/git-workflow.ts +612 -0
  74. package/server/harness-chat-caller.ts +36 -0
  75. package/server/harness-chat-service.ts +1681 -0
  76. package/server/idle-shutdown.ts +170 -0
  77. package/server/js-profiling-policy.ts +52 -0
  78. package/server/launcher-settings.ts +84 -0
  79. package/server/local-asset-catalog.ts +410 -0
  80. package/server/managed-account-defaults.ts +22 -0
  81. package/server/mock-control-plane.ts +540 -0
  82. package/server/model-import-conversion.ts +345 -0
  83. package/server/native-credential-store.ts +162 -0
  84. package/server/open-browser.ts +118 -0
  85. package/server/packaged.ts +1192 -0
  86. package/server/play-stall.ts +126 -0
  87. package/server/process-shutdown.ts +218 -0
  88. package/server/product-presets.ts +55 -0
  89. package/server/project-build-artifact.ts +95 -0
  90. package/server/project-components.ts +100 -0
  91. package/server/project-dependency-invalidation.ts +114 -0
  92. package/server/project-file-scan.ts +235 -0
  93. package/server/project-hmr-files.ts +215 -0
  94. package/server/project-install-roots.ts +210 -0
  95. package/server/project-kinds.ts +71 -0
  96. package/server/project-mcp-servers.ts +82 -0
  97. package/server/project-module-freshness.ts +109 -0
  98. package/server/project-module-instance.ts +375 -0
  99. package/server/project-optimize-deps-entries.ts +827 -0
  100. package/server/project-output-writer.ts +679 -0
  101. package/server/project-package-origin.ts +63 -0
  102. package/server/project-root-surface.ts +831 -0
  103. package/server/project-scratch-path.ts +47 -0
  104. package/server/project-script-hmr.ts +619 -0
  105. package/server/project-serving-plugins.ts +236 -0
  106. package/server/project-tools.ts +653 -0
  107. package/server/project-validation.ts +250 -0
  108. package/server/project-verbs.ts +127 -0
  109. package/server/project-view.ts +52 -0
  110. package/server/project-watch.ts +1134 -0
  111. package/server/project-work-coordinator.ts +288 -0
  112. package/server/provider-credentials.ts +331 -0
  113. package/server/recent-projects-store.ts +98 -0
  114. package/server/redact-secrets.ts +71 -0
  115. package/server/repository-presence.ts +267 -0
  116. package/server/routes/account.ts +357 -0
  117. package/server/routes/agents.ts +60 -0
  118. package/server/routes/assets.ts +154 -0
  119. package/server/routes/build.ts +13 -0
  120. package/server/routes/collaboration.ts +595 -0
  121. package/server/routes/configurations.ts +419 -0
  122. package/server/routes/context.ts +264 -0
  123. package/server/routes/control-plane.ts +1811 -0
  124. package/server/routes/logs.ts +272 -0
  125. package/server/routes/project-identity.ts +302 -0
  126. package/server/routes/project-open.ts +429 -0
  127. package/server/routes/project-source.ts +686 -0
  128. package/server/routes/project-state.ts +383 -0
  129. package/server/routes/relay.ts +1029 -0
  130. package/server/routes/route-helpers.ts +29 -0
  131. package/server/routes/served-modules.ts +138 -0
  132. package/server/routes/session-tabs.ts +196 -0
  133. package/server/routes/settings.ts +121 -0
  134. package/server/routes/share-control.ts +486 -0
  135. package/server/routes/themes.ts +116 -0
  136. package/server/routes/tools.ts +230 -0
  137. package/server/routes/worktrees.ts +595 -0
  138. package/server/scoped-game-css.ts +461 -0
  139. package/server/server-utils.ts +1194 -0
  140. package/server/session-product.ts +112 -0
  141. package/server/session-registry.ts +230 -0
  142. package/server/share-claims.ts +138 -0
  143. package/server/share-host.ts +526 -0
  144. package/server/share-session-gateway.ts +1362 -0
  145. package/server/share-tunnel.ts +123 -0
  146. package/server/spawn-opener.ts +74 -0
  147. package/server/support/play/log-format.ts +76 -0
  148. package/server/support/play/session-record.ts +199 -0
  149. package/server/support/project/build-discipline.ts +718 -0
  150. package/server/support/project/inspection-node.ts +119 -0
  151. package/server/support/project/log-naming.ts +54 -0
  152. package/server/support/project/provenance.ts +290 -0
  153. package/server/support/project/run-name.ts +18 -0
  154. package/server/support/project/session-journal.ts +830 -0
  155. package/server/support/project/shared.ts +327 -0
  156. package/server/tab-bootstrap.ts +62 -0
  157. package/server/tab-heartbeat.ts +477 -0
  158. package/server/tab-lifecycle.ts +550 -0
  159. package/server/tab-presence.ts +1508 -0
  160. package/server/team-agent-mirror.ts +95 -0
  161. package/server/twin-auth.ts +138 -0
  162. package/server/vendored-lock-recorder.ts +968 -0
  163. package/server/worktree-identity.ts +121 -0
  164. package/server/worktree-management.ts +398 -0
  165. package/server/worktree-retention.d.mts +13 -0
  166. package/server/worktree-retention.mjs +205 -0
  167. package/src/EditorContext.tsx +169 -0
  168. package/src/account.ts +333 -0
  169. package/src/action-registry.ts +532 -0
  170. package/src/active-product.ts +74 -0
  171. package/src/active-project.ts +155 -0
  172. package/src/adapter-editor-config.ts +25 -0
  173. package/src/adapter-observation.ts +49 -0
  174. package/src/animation/stage-transport.ts +379 -0
  175. package/src/animation/three-clips-subject.ts +172 -0
  176. package/src/api/asset-library-wire.ts +45 -0
  177. package/src/api/assets.ts +365 -0
  178. package/src/api/base.ts +10 -0
  179. package/src/api/build.ts +99 -0
  180. package/src/api/git-wire.ts +56 -0
  181. package/src/api/logs.ts +92 -0
  182. package/src/api/project-identity.ts +74 -0
  183. package/src/api/project-open.ts +355 -0
  184. package/src/api/project-source.ts +162 -0
  185. package/src/api/project-state.ts +107 -0
  186. package/src/api/relay.ts +268 -0
  187. package/src/api/settings.ts +36 -0
  188. package/src/api/themes.ts +45 -0
  189. package/src/api/worktrees.ts +257 -0
  190. package/src/asset-compare-core.ts +171 -0
  191. package/src/asset-compare.ts +294 -0
  192. package/src/asset-editor-context.tsx +100 -0
  193. package/src/asset-events.ts +97 -0
  194. package/src/asset-inspector-actions.ts +87 -0
  195. package/src/asset-preview-framing.ts +357 -0
  196. package/src/asset-preview.ts +2802 -0
  197. package/src/asset-selection-viewer-registry.ts +113 -0
  198. package/src/asset-selection.ts +133 -0
  199. package/src/asset-workflow/asset-capabilities.ts +344 -0
  200. package/src/asset-workflow/asset-import-jobs.ts +106 -0
  201. package/src/asset-workflow/asset-ledger-backend.ts +126 -0
  202. package/src/asset-workflow/asset-ledger.ts +156 -0
  203. package/src/asset-workflow/asset-materialization-report.ts +140 -0
  204. package/src/asset-workflow/asset-pack-manifest.ts +320 -0
  205. package/src/asset-workflow/asset-types.ts +142 -0
  206. package/src/asset-workflow/asset-workflow-quality.ts +61 -0
  207. package/src/asset-workflow/audio-preview-player.ts +193 -0
  208. package/src/asset-workflow/audio-waveform.ts +22 -0
  209. package/src/asset-workflow/cloud-asset-client.ts +263 -0
  210. package/src/asset-workflow/folder-preview.ts +345 -0
  211. package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
  212. package/src/asset-workflow/image-view-scale.ts +32 -0
  213. package/src/asset-workflow/import-contract.ts +124 -0
  214. package/src/asset-workflow/ledger-write-lock.ts +244 -0
  215. package/src/asset-workflow/model-inspection.ts +818 -0
  216. package/src/asset-workflow/pixi-spritesheet.ts +197 -0
  217. package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
  218. package/src/asset-workflow/project-asset-commands.ts +23 -0
  219. package/src/asset-workflow/project-asset-health.ts +139 -0
  220. package/src/asset-workflow/project-asset-operations.ts +154 -0
  221. package/src/asset-workflow/project-asset-roots.ts +68 -0
  222. package/src/asset-workflow/project-content.ts +288 -0
  223. package/src/asset-workflow/project-source-index.ts +550 -0
  224. package/src/asset-workflow/thumbnail-system.ts +256 -0
  225. package/src/authoring/active-adapter.ts +199 -0
  226. package/src/authoring/active-systems.ts +422 -0
  227. package/src/authoring/adapter-key.ts +18 -0
  228. package/src/authoring/authoring-asset-url.ts +27 -0
  229. package/src/authoring/bootstrap-state.ts +49 -0
  230. package/src/authoring/boundary-authoring-adapter.ts +184 -0
  231. package/src/authoring/breakpoint-state.ts +43 -0
  232. package/src/authoring/canvas-scene-guides.ts +76 -0
  233. package/src/authoring/component-instance-root.ts +171 -0
  234. package/src/authoring/composite-authoring-adapter.ts +2110 -0
  235. package/src/authoring/consumer-actions.ts +520 -0
  236. package/src/authoring/css-numeric-style.ts +97 -0
  237. package/src/authoring/design-time-layers.ts +849 -0
  238. package/src/authoring/design-time-mount-registry.ts +235 -0
  239. package/src/authoring/design-time-settle.ts +343 -0
  240. package/src/authoring/edit-mode-authoring.ts +619 -0
  241. package/src/authoring/eyedropper-session.ts +60 -0
  242. package/src/authoring/instance-source-menu-register.ts +13 -0
  243. package/src/authoring/instance-source-menu.ts +135 -0
  244. package/src/authoring/layered-pick.ts +183 -0
  245. package/src/authoring/live-gesture-lock.ts +50 -0
  246. package/src/authoring/live-object-transform.ts +62 -0
  247. package/src/authoring/mount-failure-report.ts +154 -0
  248. package/src/authoring/mounted-root-subjects.ts +144 -0
  249. package/src/authoring/no-authoring-adapter.ts +55 -0
  250. package/src/authoring/null-inspection-subjects.tsx +75 -0
  251. package/src/authoring/object3d-document-persistence.ts +106 -0
  252. package/src/authoring/object3d-document-session-registry.ts +120 -0
  253. package/src/authoring/object3d-document-session.ts +1326 -0
  254. package/src/authoring/object3d-gesture-controller.ts +113 -0
  255. package/src/authoring/panel-authoring.ts +121 -0
  256. package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
  257. package/src/authoring/prefab-instance-section-model.ts +55 -0
  258. package/src/authoring/project-authoring-session.ts +105 -0
  259. package/src/authoring/provenance.ts +99 -0
  260. package/src/authoring/quarks-particle-systems.ts +19 -0
  261. package/src/authoring/react-canvas-navigation.ts +255 -0
  262. package/src/authoring/react-design-canvas-style.ts +20 -0
  263. package/src/authoring/react-story-board.ts +937 -0
  264. package/src/authoring/selection-scope.ts +195 -0
  265. package/src/authoring/shell-document-ops.ts +169 -0
  266. package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
  267. package/src/authoring/shell-viewport-policy.ts +48 -0
  268. package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
  269. package/src/authoring/stories-scope.ts +35 -0
  270. package/src/authoring/story-board-chrome-fit.ts +107 -0
  271. package/src/authoring/story-board-presentation.ts +111 -0
  272. package/src/authoring/three-projection-core.ts +226 -0
  273. package/src/authoring/viewport-pick-context.ts +40 -0
  274. package/src/authoring/viewport-raycast.ts +240 -0
  275. package/src/authoring/viewport-tool-context.ts +73 -0
  276. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  277. package/src/authoring/world-hidden-viewport.ts +152 -0
  278. package/src/authoring/world-pan-state.ts +198 -0
  279. package/src/authoring/world-session-state.ts +101 -0
  280. package/src/authoring/write-pipe.ts +173 -0
  281. package/src/availability-tick.ts +66 -0
  282. package/src/bitmap-label.ts +120 -0
  283. package/src/blender-tab-metrics.ts +161 -0
  284. package/src/board-open-actions.ts +20 -0
  285. package/src/boot-routing.ts +391 -0
  286. package/src/build-identity.ts +15 -0
  287. package/src/bytes-codec.ts +62 -0
  288. package/src/camera-authoring.ts +175 -0
  289. package/src/cancellation-reason.ts +58 -0
  290. package/src/canvas-preview-frames.ts +482 -0
  291. package/src/capture-camera-pose.ts +77 -0
  292. package/src/chrome-registry.ts +159 -0
  293. package/src/chrome-slot-registry.ts +91 -0
  294. package/src/collaboration-client.ts +264 -0
  295. package/src/collaboration-presence.ts +41 -0
  296. package/src/command-dispatch.ts +19 -0
  297. package/src/command-listener.ts +2743 -0
  298. package/src/command-registry.ts +70 -0
  299. package/src/component-board-registry.ts +205 -0
  300. package/src/component-states-registry.ts +200 -0
  301. package/src/components/AgentPresentationNotice.tsx +75 -0
  302. package/src/components/AlignToolbar.tsx +204 -0
  303. package/src/components/AppRoot.tsx +394 -0
  304. package/src/components/ApplicationMenus.tsx +415 -0
  305. package/src/components/AssetBrowser.tsx +2843 -0
  306. package/src/components/AssetEditorShell.tsx +216 -0
  307. package/src/components/AssetInspectorToolSection.tsx +124 -0
  308. package/src/components/BoardRulers.tsx +354 -0
  309. package/src/components/CameraInfo.tsx +81 -0
  310. package/src/components/CanvasSceneViewport.tsx +556 -0
  311. package/src/components/CapabilityCoverageSection.tsx +185 -0
  312. package/src/components/CenterDocuments.tsx +22 -0
  313. package/src/components/ChromeSlot.tsx +20 -0
  314. package/src/components/CodeView.tsx +470 -0
  315. package/src/components/CollaborationHeaderControl.css +74 -0
  316. package/src/components/CompactInspectorCard.tsx +190 -0
  317. package/src/components/CompactInspectorShell.tsx +39 -0
  318. package/src/components/ConsolePanel.css +55 -0
  319. package/src/components/ConsolePanel.tsx +270 -0
  320. package/src/components/DefaultEditorLayout.tsx +106 -0
  321. package/src/components/DocumentHeaderStrip.tsx +96 -0
  322. package/src/components/DocumentShelfRail.tsx +45 -0
  323. package/src/components/DocumentThumbnail.tsx +345 -0
  324. package/src/components/EditorLeaseGuard.tsx +372 -0
  325. package/src/components/ErrorBanner.tsx +123 -0
  326. package/src/components/FolderPreviewTile.tsx +159 -0
  327. package/src/components/GameHierarchy.tsx +3842 -0
  328. package/src/components/GameplaySessionTimeline.tsx +295 -0
  329. package/src/components/InspectionProjection.tsx +968 -0
  330. package/src/components/Inspector.tsx +233 -0
  331. package/src/components/InspectorCanvasPreview.tsx +35 -0
  332. package/src/components/InspectorFieldsSection.tsx +290 -0
  333. package/src/components/InspectorObjectPreview.tsx +57 -0
  334. package/src/components/InspectorStoriesSection.tsx +92 -0
  335. package/src/components/InspectorToolSection.tsx +96 -0
  336. package/src/components/InspectorTransformSection.tsx +245 -0
  337. package/src/components/LightExplorerPanel.tsx +433 -0
  338. package/src/components/MediaProperties.tsx +145 -0
  339. package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
  340. package/src/components/Object3DDocumentToolbar.css +100 -0
  341. package/src/components/Object3DDocumentToolbar.tsx +454 -0
  342. package/src/components/Object3DDocumentViewport.tsx +58 -0
  343. package/src/components/ProjectHeader.css +617 -0
  344. package/src/components/ProjectHeader.tsx +328 -0
  345. package/src/components/ProjectLayout.tsx +249 -0
  346. package/src/components/ReactCanvasControls.tsx +284 -0
  347. package/src/components/RootSelectionOverlay.tsx +3162 -0
  348. package/src/components/RootTextEditor.tsx +79 -0
  349. package/src/components/SaveStatus.tsx +70 -0
  350. package/src/components/StageHost.tsx +1954 -0
  351. package/src/components/StageOverlays.tsx +21 -0
  352. package/src/components/StartupErrorScreen.tsx +76 -0
  353. package/src/components/StartupLoadingScreen.tsx +65 -0
  354. package/src/components/StatsOverlay.tsx +78 -0
  355. package/src/components/SurfaceStateOverlay.tsx +24 -0
  356. package/src/components/ToolContributionSurfaces.tsx +65 -0
  357. package/src/components/ToolHost.tsx +370 -0
  358. package/src/components/ToolObject3DPreview.tsx +39 -0
  359. package/src/components/Toolbar.tsx +595 -0
  360. package/src/components/TransientHint.tsx +44 -0
  361. package/src/components/TransportStrip.tsx +174 -0
  362. package/src/components/VersionControlSection.tsx +470 -0
  363. package/src/components/VgaiLogo.css +83 -0
  364. package/src/components/VgaiLogo.tsx +35 -0
  365. package/src/components/ViewportControlsHint.tsx +60 -0
  366. package/src/components/ViewportFurniture.tsx +401 -0
  367. package/src/components/ViewportOverlay.tsx +145 -0
  368. package/src/components/ViewportOverlaysMenu.tsx +153 -0
  369. package/src/components/ViewportShadingMenu.tsx +300 -0
  370. package/src/components/ViewportViewMenu.tsx +101 -0
  371. package/src/components/WorkspaceDocumentSurface.tsx +204 -0
  372. package/src/components/WorkspaceUtilitySurface.tsx +22 -0
  373. package/src/components/WorktreeSwitcher.css +239 -0
  374. package/src/components/WorktreeSwitcher.tsx +890 -0
  375. package/src/components/account-documents.tsx +1162 -0
  376. package/src/components/asset-documents.tsx +812 -0
  377. package/src/components/asset-editor-persistence.ts +216 -0
  378. package/src/components/asset-selection-section.tsx +535 -0
  379. package/src/components/asset-thumbnails.tsx +312 -0
  380. package/src/components/asset-viewers/AudioViewer.tsx +201 -0
  381. package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
  382. package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
  383. package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
  384. package/src/components/asset-viewers/ImageViewer.tsx +300 -0
  385. package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
  386. package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
  387. package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
  388. package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
  389. package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
  390. package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
  391. package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
  392. package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
  393. package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
  394. package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
  395. package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
  396. package/src/components/asset-viewers/VideoViewer.tsx +101 -0
  397. package/src/components/asset-viewers/shader-source.ts +144 -0
  398. package/src/components/asset-workflow.css +731 -0
  399. package/src/components/board-guides.ts +150 -0
  400. package/src/components/compact-inspector.css +539 -0
  401. package/src/components/core-utilities.tsx +90 -0
  402. package/src/components/editor-notifications.css +40 -0
  403. package/src/components/engine-workspace.css +274 -0
  404. package/src/components/inspector-preview-section.tsx +238 -0
  405. package/src/components/inspector-property-grouping.ts +64 -0
  406. package/src/components/inspector-revert-label.ts +20 -0
  407. package/src/components/inspector-selection.ts +42 -0
  408. package/src/components/inspector-stories-gating.ts +171 -0
  409. package/src/components/inspector-transform-subject.ts +11 -0
  410. package/src/components/inspector-transform.ts +75 -0
  411. package/src/components/kind-documents.tsx +500 -0
  412. package/src/components/palette-action-publisher.tsx +140 -0
  413. package/src/components/primitives/DraftColorInput.tsx +74 -0
  414. package/src/components/product-shell.css +1829 -0
  415. package/src/components/project-tool-documents.tsx +410 -0
  416. package/src/components/scene-documents.tsx +223 -0
  417. package/src/components/stage-keyboard.tsx +37 -0
  418. package/src/components/stage-overlay-set.tsx +107 -0
  419. package/src/components/stage-presence-markers.ts +478 -0
  420. package/src/components/standard-viewport-dressing.ts +417 -0
  421. package/src/components/status-contributions.tsx +403 -0
  422. package/src/components/tool-documents.tsx +309 -0
  423. package/src/components/tool-schema-form.tsx +262 -0
  424. package/src/components/use-after-paint.ts +41 -0
  425. package/src/components/use-project-image-assets.ts +86 -0
  426. package/src/components/viewport-header-controls.css +90 -0
  427. package/src/components/viewport-surface-status.tsx +55 -0
  428. package/src/components/workspace-history.ts +32 -0
  429. package/src/components/workspace-static-panel-registry.tsx +152 -0
  430. package/src/components/workspace-surfaces.css +621 -0
  431. package/src/components/world-documents.tsx +586 -0
  432. package/src/components/world-overlay-gestures.ts +1694 -0
  433. package/src/composite-screenshot.ts +1641 -0
  434. package/src/console-sync.ts +131 -0
  435. package/src/constraint-helper.ts +338 -0
  436. package/src/content-entry-source-registry.ts +184 -0
  437. package/src/coverage/authoring-seam-evidence.ts +300 -0
  438. package/src/coverage/canvas-reveal.ts +192 -0
  439. package/src/coverage/design-time-surfaces.ts +101 -0
  440. package/src/coverage/live-seam-evidence.ts +11 -0
  441. package/src/coverage/ontology-invariants.ts +466 -0
  442. package/src/coverage/session-vitals.ts +501 -0
  443. package/src/coverage/system-seam-evidence.ts +72 -0
  444. package/src/crash-null-boundary.ts +36 -0
  445. package/src/creation-site-edit.ts +1479 -0
  446. package/src/creation-site-registry.ts +160 -0
  447. package/src/delegate-harness-registry.ts +143 -0
  448. package/src/document-context-registry.ts +177 -0
  449. package/src/document-open-registry.ts +200 -0
  450. package/src/document-preview-source.ts +20 -0
  451. package/src/document-renderer-session.ts +138 -0
  452. package/src/editor-api.ts +46 -0
  453. package/src/editor-chrome-capture.ts +139 -0
  454. package/src/editor-commands.ts +164 -0
  455. package/src/editor-console.ts +563 -0
  456. package/src/editor-current-view.ts +82 -0
  457. package/src/editor-document-probe.ts +881 -0
  458. package/src/editor-git-client.ts +115 -0
  459. package/src/editor-host-door.ts +462 -0
  460. package/src/editor-hotkeys.ts +842 -0
  461. package/src/editor-lease-view.ts +39 -0
  462. package/src/editor-lease.ts +415 -0
  463. package/src/editor-mode.ts +19 -0
  464. package/src/editor-notifications.ts +140 -0
  465. package/src/editor-presence.ts +563 -0
  466. package/src/editor-presentation-activity.ts +50 -0
  467. package/src/editor-presentation-notice.ts +42 -0
  468. package/src/editor-runtime.tsx +145 -0
  469. package/src/editor-server-response.ts +86 -0
  470. package/src/editor-session-attribution.ts +75 -0
  471. package/src/editor-session-mode.ts +54 -0
  472. package/src/editor-shell-store.ts +1304 -0
  473. package/src/editor-state-facets.ts +74 -0
  474. package/src/editor-styles.css +33 -0
  475. package/src/editor-view-presentation.ts +810 -0
  476. package/src/editor-viewport.ts +5302 -0
  477. package/src/entity-lod.ts +31 -0
  478. package/src/entity-object.ts +91 -0
  479. package/src/files/file-provider.ts +62 -0
  480. package/src/files/project-files.ts +264 -0
  481. package/src/finders/index.ts +136 -0
  482. package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
  483. package/src/frame/bridge.tsx +1408 -0
  484. package/src/frame/product.ts +77 -0
  485. package/src/gameplay-dom-recording.ts +319 -0
  486. package/src/gameplay-export-state.ts +14 -0
  487. package/src/gameplay-replay.ts +417 -0
  488. package/src/gameplay-session-time.ts +9 -0
  489. package/src/gameplay-sessions.ts +204 -0
  490. package/src/harness-chat-types.ts +253 -0
  491. package/src/hierarchy-component-marks.ts +298 -0
  492. package/src/hierarchy-drop.ts +91 -0
  493. package/src/hierarchy-expansion-state.ts +80 -0
  494. package/src/hierarchy-header-slot.ts +52 -0
  495. package/src/hierarchy-internals.ts +197 -0
  496. package/src/hierarchy-kind-icon.ts +217 -0
  497. package/src/hierarchy-mark-reader.ts +73 -0
  498. package/src/hierarchy-menu-registry.ts +67 -0
  499. package/src/hierarchy-node-rows.ts +307 -0
  500. package/src/hierarchy-panel-view.ts +280 -0
  501. package/src/hierarchy-projection.ts +76 -0
  502. package/src/hierarchy-row-cache.ts +243 -0
  503. package/src/hierarchy-row-model.ts +308 -0
  504. package/src/hierarchy-rows.ts +11 -0
  505. package/src/hierarchy-walk.ts +86 -0
  506. package/src/history/editor-session.ts +25 -0
  507. package/src/history/history-commands.ts +147 -0
  508. package/src/history/history-delegate.ts +163 -0
  509. package/src/history/history-limit-notices.ts +43 -0
  510. package/src/history/history-service.ts +1173 -0
  511. package/src/history/persistence-coordinator.ts +35 -0
  512. package/src/history/project-file-history.ts +386 -0
  513. package/src/history/project-root-history-backends.ts +139 -0
  514. package/src/history/resource-registry.ts +209 -0
  515. package/src/history/snapshot-store.ts +103 -0
  516. package/src/history/source-history-backend.ts +546 -0
  517. package/src/history/types.ts +124 -0
  518. package/src/hmr-registration-group.ts +67 -0
  519. package/src/hmr-stable-react-context.ts +23 -0
  520. package/src/hotkeys.ts +188 -0
  521. package/src/inference-diagnostics.ts +69 -0
  522. package/src/initial-project.ts +80 -0
  523. package/src/inspection/active-subject.ts +578 -0
  524. package/src/inspection/active-surface.ts +142 -0
  525. package/src/inspection/compose.ts +1064 -0
  526. package/src/inspection/display.ts +170 -0
  527. package/src/inspection/document-subject.ts +109 -0
  528. package/src/inspection/game-subject.ts +85 -0
  529. package/src/inspection/model.ts +542 -0
  530. package/src/inspection/null-subject.ts +115 -0
  531. package/src/inspection/serialize.ts +357 -0
  532. package/src/inspection/use-active-inspection.ts +180 -0
  533. package/src/inspector-presentation.ts +201 -0
  534. package/src/inspector-section-registry.ts +221 -0
  535. package/src/instance-source-actions.ts +163 -0
  536. package/src/instanced-presentation.ts +164 -0
  537. package/src/js-heap.ts +71 -0
  538. package/src/key-actions.ts +91 -0
  539. package/src/keymap-presets.ts +401 -0
  540. package/src/layout-policy.ts +31 -0
  541. package/src/learn-links.ts +73 -0
  542. package/src/light-explorer-model.ts +134 -0
  543. package/src/live-canvas-frame.ts +55 -0
  544. package/src/live-document.ts +279 -0
  545. package/src/live-module-source.ts +230 -0
  546. package/src/live-session-registry.ts +220 -0
  547. package/src/live-transition.ts +633 -0
  548. package/src/manifest-project.ts +107 -0
  549. package/src/model-thumbnail.ts +523 -0
  550. package/src/native-selection-style.ts +202 -0
  551. package/src/object3d-document-write-policy.ts +137 -0
  552. package/src/packaged-runtime.ts +108 -0
  553. package/src/palettes/maya.palette.json +57 -0
  554. package/src/palettes/substance.palette.json +57 -0
  555. package/src/pasteboard-module.ts +81 -0
  556. package/src/performance-profiler.ts +367 -0
  557. package/src/performance-sources.ts +69 -0
  558. package/src/play-boot-phase.ts +145 -0
  559. package/src/presentation-surface.ts +248 -0
  560. package/src/project-adapter.ts +1143 -0
  561. package/src/project-asset-refresh.ts +26 -0
  562. package/src/project-declaration-refresh.ts +31 -0
  563. package/src/project-local-state.ts +118 -0
  564. package/src/project-manager.ts +243 -0
  565. package/src/project-module-changes.ts +163 -0
  566. package/src/project-module-split.ts +266 -0
  567. package/src/project-provenance.ts +115 -0
  568. package/src/project-ready.ts +42 -0
  569. package/src/project-session-reset.ts +67 -0
  570. package/src/project-shape.ts +68 -0
  571. package/src/project-tool-discovery.ts +66 -0
  572. package/src/project-tools.ts +107 -0
  573. package/src/project-work-types.ts +149 -0
  574. package/src/projection/three.ts +898 -0
  575. package/src/projection/types.ts +44 -0
  576. package/src/readiness.ts +113 -0
  577. package/src/reflection-probe-helper.ts +142 -0
  578. package/src/reported-play-state.ts +90 -0
  579. package/src/resolve-relative-specifier.ts +33 -0
  580. package/src/scene-document-plan.ts +315 -0
  581. package/src/scene-framing.ts +315 -0
  582. package/src/scene-live-open.ts +210 -0
  583. package/src/scene-view-fog.ts +89 -0
  584. package/src/scoped-game-css.ts +152 -0
  585. package/src/session-orphan-record.ts +193 -0
  586. package/src/session-tombstone.ts +126 -0
  587. package/src/settings/settings-provider.ts +82 -0
  588. package/src/settings-store.ts +345 -0
  589. package/src/shared-view-restore.ts +42 -0
  590. package/src/shell-store-door.ts +45 -0
  591. package/src/source-conflict.ts +122 -0
  592. package/src/spatial-handle-visuals.ts +332 -0
  593. package/src/stage-context.ts +341 -0
  594. package/src/stage-store-registry.ts +63 -0
  595. package/src/stale-chunk-recovery.ts +34 -0
  596. package/src/state-report-deferral.ts +73 -0
  597. package/src/storage/host-files-storage.ts +97 -0
  598. package/src/storage/http-storage.ts +174 -0
  599. package/src/storage/index.ts +75 -0
  600. package/src/storage/mem-storage.ts +149 -0
  601. package/src/storage/path-lock.ts +44 -0
  602. package/src/storage/paths.ts +26 -0
  603. package/src/storage/types.ts +116 -0
  604. package/src/stories/StoryComponentThumbnail.tsx +184 -0
  605. package/src/stories/StoryPreviewMount.tsx +306 -0
  606. package/src/stories/component-content-source.tsx +106 -0
  607. package/src/stories/component-states-source.ts +78 -0
  608. package/src/stories/compose-project-stories.ts +255 -0
  609. package/src/stories/pixi-story-model.ts +30 -0
  610. package/src/stories/prefabs-finder.ts +54 -0
  611. package/src/stories/prefabs-from-stories.ts +182 -0
  612. package/src/stories/project-story-discovery.ts +78 -0
  613. package/src/stories/project-story-regions.ts +24 -0
  614. package/src/stories/story-actions.ts +54 -0
  615. package/src/stories/story-capture-command.ts +124 -0
  616. package/src/stories/story-capture.ts +584 -0
  617. package/src/stories/story-declared-medium.ts +126 -0
  618. package/src/stories/story-discovery.ts +176 -0
  619. package/src/stories/story-document-openers.ts +36 -0
  620. package/src/stories/story-dom-runtime.ts +78 -0
  621. package/src/stories/story-grouping.ts +111 -0
  622. package/src/stories/story-lane.ts +88 -0
  623. package/src/stories/story-mount-turn.ts +27 -0
  624. package/src/stories/story-opener.ts +260 -0
  625. package/src/stories/story-pixi-preview.ts +408 -0
  626. package/src/stories/story-presentation.ts +215 -0
  627. package/src/stories/story-registry.ts +517 -0
  628. package/src/stories/story-three-preview.ts +807 -0
  629. package/src/stories/three-story-model.ts +96 -0
  630. package/src/story-three-preview-runtime.ts +56 -0
  631. package/src/surface-keyboard.ts +112 -0
  632. package/src/surface-state.ts +124 -0
  633. package/src/tab-bootstrap.js +628 -0
  634. package/src/tab-census.ts +198 -0
  635. package/src/tab-lifecycle-client.ts +196 -0
  636. package/src/theme-library.ts +844 -0
  637. package/src/theme-preference.ts +378 -0
  638. package/src/theme.css +3437 -0
  639. package/src/three-viewport/bone-selection-highlight.ts +119 -0
  640. package/src/three-viewport/camera-fit.ts +41 -0
  641. package/src/three-viewport/interactive-renderer.ts +132 -0
  642. package/src/three-viewport/selection-brackets.ts +310 -0
  643. package/src/three-viewport/selection-outline.ts +183 -0
  644. package/src/three-viewport/skeleton-helper.ts +61 -0
  645. package/src/three-viewport/source-color.ts +167 -0
  646. package/src/three-viewport/studio-environment.ts +96 -0
  647. package/src/three-viewport-presentation.ts +23 -0
  648. package/src/tool-contribution-play.ts +74 -0
  649. package/src/tool-loader.ts +1798 -0
  650. package/src/transform-mode-request.ts +57 -0
  651. package/src/transient-hint.ts +78 -0
  652. package/src/trigger-volume-helper.ts +116 -0
  653. package/src/ui-source/adapter-region-includes.ts +241 -0
  654. package/src/ui-source/ensure-import.ts +132 -0
  655. package/src/ui-source/file-region-resolver.ts +302 -0
  656. package/src/ui-source/inspect.ts +775 -0
  657. package/src/ui-source/oid-transform.ts +1845 -0
  658. package/src/ui-source/plan-csf-story.ts +196 -0
  659. package/src/ui-source/plan-extract-component.ts +421 -0
  660. package/src/ui-source/plan-fork-component.ts +621 -0
  661. package/src/ui-source/plan-named-style.ts +95 -0
  662. package/src/ui-source/plan-source-edit.ts +353 -0
  663. package/src/ui-source/r3f-contract-resolver.ts +327 -0
  664. package/src/ui-source/r3f-diagnostic-index.ts +165 -0
  665. package/src/ui-source/r3f-environment-binding.ts +133 -0
  666. package/src/ui-source/r3f-joint-binding.ts +251 -0
  667. package/src/ui-source/r3f-lod-binding.ts +91 -0
  668. package/src/ui-source/r3f-particle-binding.ts +235 -0
  669. package/src/ui-source/r3f-physics-binding.ts +483 -0
  670. package/src/ui-source/r3f-project-contracts.ts +444 -0
  671. package/src/ui-source/relative-import-specifier.ts +34 -0
  672. package/src/ui-source/reparent-guard.ts +544 -0
  673. package/src/ui-source/source-edit-request.ts +97 -0
  674. package/src/ui-source/source-write-backend.ts +618 -0
  675. package/src/ui-source/syntactic-prop-specs.ts +296 -0
  676. package/src/ui-source/tier-source-write-backend.ts +279 -0
  677. package/src/ui-source/ts-ast.ts +171 -0
  678. package/src/ui-source/utility-class-support.ts +169 -0
  679. package/src/ui-source/write-component-default.ts +133 -0
  680. package/src/ui-source/writer.ts +1989 -0
  681. package/src/viewport-activation-timings.ts +840 -0
  682. package/src/viewport-authoring-policy.ts +186 -0
  683. package/src/viewport-controls-hint.ts +57 -0
  684. package/src/viewport-door.ts +237 -0
  685. package/src/viewport-shading-boundary.ts +12 -0
  686. package/src/vite-error-surface.ts +141 -0
  687. package/src/wait-until.ts +37 -0
  688. package/src/workspace-areas.ts +156 -0
  689. package/src/workspace-aux-commands.ts +11 -0
  690. package/src/workspace-available-documents.ts +127 -0
  691. package/src/workspace-core-utilities.ts +31 -0
  692. package/src/workspace-document-ids.ts +59 -0
  693. package/src/workspace-document-registry.ts +612 -0
  694. package/src/workspace-document-restore.ts +145 -0
  695. package/src/workspace-host-commands.ts +141 -0
  696. package/src/workspace-persistence-gate.ts +40 -0
  697. package/src/workspace-play-utilities.ts +44 -0
  698. package/src/workspace-presets.ts +425 -0
  699. package/src/workspace-regions.ts +294 -0
  700. package/src/workspace-state-persistence.ts +536 -0
  701. package/src/workspace-static-panels.ts +73 -0
  702. package/src/workspace-status-registry.ts +121 -0
  703. package/src/workspace-style.ts +239 -0
  704. package/src/workspace-utility-commands.ts +74 -0
  705. package/src/workspace-utility-registry.ts +263 -0
  706. package/src/workspace-viewport-rect.ts +97 -0
  707. package/src/world-adoption.ts +115 -0
  708. package/src/world-document-routing.ts +104 -0
  709. package/vite-plugin-creation-site-write.ts +184 -0
  710. package/vite-plugin-creation-site.ts +80 -0
  711. package/vite-plugin-game-static.ts +303 -0
  712. package/vite-plugin-module-doorways.ts +336 -0
  713. package/vite-plugin-product-contributions.ts +197 -0
  714. package/vite-plugin-project-game-static.ts +125 -0
  715. package/vite-plugin-project-jsx-js.ts +65 -0
  716. package/vite-plugin-project-root-absolute-assets.ts +359 -0
  717. package/vite-plugin-shared-react.ts +420 -0
  718. package/vite-plugin-shared-three.ts +203 -0
  719. package/vite-plugin-ui-oid.ts +2106 -0
@@ -0,0 +1,315 @@
1
+ /**
2
+ * THE EDIT TAB ROW, as pure logic — what the adapter's scene table means for
3
+ * the workspace's open documents.
4
+ *
5
+ * ARCHITECTURE-CORE §Editor: "the scene table (entries, default, authorable
6
+ * flags, reach) is ONE adapter declaration with two clients — the Edit tab row
7
+ * projects it as isolation documents through the substrate family's piece
8
+ * mounts, and Play's `open()` walks it as live navigation." This module is the
9
+ * decision half of the FIRST client; `components/scene-documents.tsx` is the
10
+ * half that talks to the workspace registry, and `command-listener.ts`'s
11
+ * `open` verb resolves ONE entry through the very same table below — so the
12
+ * tab row and the verb can never disagree about what an entry is.
13
+ *
14
+ * ZERO INFERENCE. Every answer here is read off a DECLARED field of the
15
+ * entry — `authorable`, `reach`, `source`, `region`. Nothing is derived from a
16
+ * folder, a filename, a root, or the shape of the project: an entry the
17
+ * adapter did not declare simply is not here, and an entry it declared
18
+ * unreachable stays unreachable with the game's own reason attached.
19
+ */
20
+
21
+ import type { DocumentEntry, SceneSource } from '@volter/editor-project/adapter/adapter-module';
22
+ import type { ResolvedDocumentTable } from './project-adapter';
23
+
24
+ /** Document-id namespace for a per-scene isolation document. Distinct from
25
+ * `three-story:` ids (the story turntable, `@vgai/game`'s): a scene is
26
+ * reached through the entrypoint's own selection table, not through a story,
27
+ * and the two can legitimately name the same composition. */
28
+ export const SCENE_DOCUMENT_PREFIX = 'scene:';
29
+
30
+ /** The workspace document id for one entrypoint-selection scene entry. */
31
+ export function sceneDocumentId(entryId: string): string {
32
+ return `${SCENE_DOCUMENT_PREFIX}${entryId}`;
33
+ }
34
+
35
+ /** A standing isolation tab — `scene:TitleScreen`, not `workspace:scene`. */
36
+ export function isIsolationSceneDocumentId(id: string): boolean {
37
+ return id.startsWith(SCENE_DOCUMENT_PREFIX);
38
+ }
39
+
40
+ /**
41
+ * What the Edit workspace does with ONE scene-table entry.
42
+ *
43
+ * `no-document` is not a failure — a traversal entry and a declared-unreachable
44
+ * one are both honest table rows that simply own no Edit document. Its `reason`
45
+ * is the game's OWN sentence whenever the game supplied one (a `none` reach
46
+ * carries its `reason` verbatim), so a refusal quotes the adapter rather than
47
+ * paraphrasing it.
48
+ */
49
+ export type SceneDocumentPlan =
50
+ /** Mounting the region IS opening this scene — it is the root's own standing
51
+ * document, which this entry merely NAMES. No second document. */
52
+ | { readonly kind: 'root-document'; readonly regionId: string; readonly title: string }
53
+ /** A viewport document of its own, mounting `source` in isolation. */
54
+ | {
55
+ readonly kind: 'isolation-document';
56
+ readonly documentId: string;
57
+ readonly title: string;
58
+ /** Owning root, carried into the document so its declared region — not
59
+ * its filename or export shape — decides the mounting surface. */
60
+ readonly regionId: string | null;
61
+ readonly source: SceneSource;
62
+ readonly isolationSetup?: SceneSource;
63
+ /** Present when this piece is also a running-game screen. Play's `open`
64
+ * still navigates live; Edit mounts the source in isolation and never
65
+ * holds the live host. */
66
+ readonly contractSceneId?: string;
67
+ }
68
+ /** A portable story mounts it — the existing story-document opener owns it. */
69
+ | { readonly kind: 'story-document'; readonly title: string; readonly storyId: string }
70
+ /**
71
+ * No Edit document, with the reason stated.
72
+ *
73
+ * `liveReach` separates "no document, and nothing else either" from "no
74
+ * document BECAUSE this scene is REACHED live" — the Edit workspace treats
75
+ * both the same (neither is a tab), while the `open` verb must route the
76
+ * second to the running game instead of refusing it, and must tell a caller
77
+ * with no game running to start one rather than that the scene is a dead end.
78
+ *
79
+ * Present means the live half REACHES this entry — it opens it and answers
80
+ * ok. Absent means it does not, which is two different things and
81
+ * deliberately one field: the live half may have no business with the entry
82
+ * at all (it answers `null` for `story`/`none`). A session that cannot
83
+ * remount still refuses `entrypoint-selection` coded; that is a session
84
+ * fact, not a reach fact — the intended path is remount-at-key and ends
85
+ * `ok: true`. {@link liveReachOf} is where that line is drawn.
86
+ *
87
+ * It is derived from REACH ALONE ({@link liveReachOf}), never from
88
+ * `authorable`, because the live half is the authority on this half of the
89
+ * question and it reads reach only (`scene-live-open.ts` switches on
90
+ * `entry.reach.kind`). A planner that also asked `authorable` here would call
91
+ * a screen the running game navigates to a dead end — the exact collapse this
92
+ * field exists to prevent.
93
+ */
94
+ | {
95
+ readonly kind: 'no-document';
96
+ readonly reason: string;
97
+ readonly liveReach?: 'game-contract' | 'root-mount' | 'entrypoint-selection';
98
+ };
99
+
100
+ /**
101
+ * THE ONE DERIVATION of `liveReach`, from the entry's DECLARED reach and
102
+ * nothing else — never from `authorable`, so the two clients cannot disagree.
103
+ *
104
+ * It answers for exactly the reaches `openLiveSceneEntry` REACHES: it
105
+ * navigates a `game-contract` scene through the game's own contract, it
106
+ * shows the document a `root-mount` scene draws into, and it remounts play
107
+ * at an `entrypoint-selection` key. All three end `ok: true` when the
108
+ * session can honour them, so for all three the honest Edit-half refusal
109
+ * with nothing running is `SCENE_NAVIGATION_NOT_RUNNING` — start the game
110
+ * and ask again.
111
+ *
112
+ * A session that cannot remount still refuses `entrypoint-selection` with
113
+ * `SCENE_NOT_OPENABLE_LIVE` — that is the same shape as a `game-contract`
114
+ * scene whose running game publishes no contract (`SCENE_CONTRACT_UNAVAILABLE`).
115
+ * The field names the intended live path, not every session's ability to
116
+ * walk it.
117
+ *
118
+ * A spread rather than a value so `exactOptionalPropertyTypes` gets an ABSENT
119
+ * key for "no live reach", never an explicit `undefined`. Every `no-document`
120
+ * return below spreads it unconditionally — the two where it is currently
121
+ * provably empty included — so this function stays the SOLE decider and a
122
+ * later widening cannot leave a branch silently behind.
123
+ */
124
+ function liveReachOf(entry: DocumentEntry): {
125
+ readonly liveReach?: 'game-contract' | 'root-mount' | 'entrypoint-selection';
126
+ } {
127
+ const { kind } = entry.reach;
128
+ return kind === 'game-contract' || kind === 'root-mount' || kind === 'entrypoint-selection'
129
+ ? { liveReach: kind }
130
+ : {};
131
+ }
132
+
133
+ /**
134
+ * Plan ONE entry.
135
+ *
136
+ * Order matters: `authorable` is asked FIRST because a traversal entry
137
+ * (boot/loading choreography) earns no column whatever its reach — the flag is
138
+ * about what the entry IS, while reach is about how the running game gets
139
+ * there.
140
+ *
141
+ * That order decides the DOCUMENT, and only the document. `liveReach` is
142
+ * carried through it untouched: whether a running game can be sent to a screen
143
+ * is the game's contract's business, not the host's opinion of whether the
144
+ * screen is worth authoring, and the live half honours that by reading reach
145
+ * alone.
146
+ */
147
+ export function planSceneDocument(entry: DocumentEntry): SceneDocumentPlan {
148
+ if (!entry.authorable) {
149
+ return {
150
+ kind: 'no-document',
151
+ ...liveReachOf(entry),
152
+ reason:
153
+ `"${entry.id}" is declared TRAVERSAL (boot/loading choreography), not an authorable ` +
154
+ 'composition, so it has no Edit document.',
155
+ };
156
+ }
157
+ switch (entry.reach.kind) {
158
+ case 'none':
159
+ // VERBATIM. The adapter wrote this sentence about its own game; a host
160
+ // paraphrase would be the host claiming to know why.
161
+ return { kind: 'no-document', ...liveReachOf(entry), reason: entry.reach.reason };
162
+ case 'root-mount':
163
+ return entry.region === null
164
+ ? {
165
+ // Still LIVE-REACHED: `openLiveSceneEntry` shows the document the
166
+ // running game draws into and never looks at `region`, so the
167
+ // missing region costs this entry its Edit document and nothing
168
+ // else.
169
+ kind: 'no-document',
170
+ ...liveReachOf(entry),
171
+ reason:
172
+ `"${entry.id}" is reached by mounting a region, but the entry names no region, ` +
173
+ 'so there is no standing document it could be.',
174
+ }
175
+ : { kind: 'root-document', regionId: entry.region, title: entry.label };
176
+ case 'entrypoint-selection':
177
+ // ACTIVE means the entrypoint indexes its selection table with THIS key,
178
+ // so the region's standing document already mounts this composition —
179
+ // opening a second document for it would show the same thing twice under
180
+ // two titles. The entry names the standing document instead, exactly as
181
+ // `root-mount` does. A region-less entry keeps the isolation document:
182
+ // there is no honest mapping from "active" to a document without one, and
183
+ // inventing a region is what zero-inference forbids.
184
+ if (entry.reach.active === true && entry.region !== null) {
185
+ return { kind: 'root-document', regionId: entry.region, title: entry.label };
186
+ }
187
+ return entry.source
188
+ ? {
189
+ kind: 'isolation-document',
190
+ documentId: sceneDocumentId(entry.id),
191
+ title: entry.label,
192
+ regionId: entry.region,
193
+ source: entry.source,
194
+ ...(entry.isolationSetup ? { isolationSetup: entry.isolationSetup } : {}),
195
+ }
196
+ : {
197
+ kind: 'no-document',
198
+ ...liveReachOf(entry),
199
+ reason:
200
+ `"${entry.id}" is selected at the entrypoint's \`${entry.reach.selection}\` slot, ` +
201
+ 'but the finder could not resolve the module it names, so there is nothing to ' +
202
+ 'mount in isolation.',
203
+ };
204
+ case 'game-contract':
205
+ // Authorable + a source module is an Edit piece: every authorable scene
206
+ // gets a tab (ARCHITECTURE-CORE §Editor). Play still navigates live
207
+ // through `liveReach`. Without a source there is nothing to isolate, so
208
+ // Edit stays silent and `open` asks the running game.
209
+ return entry.source
210
+ ? {
211
+ kind: 'isolation-document',
212
+ documentId: sceneDocumentId(entry.id),
213
+ title: entry.label,
214
+ regionId: entry.region,
215
+ source: entry.source,
216
+ ...(entry.isolationSetup ? { isolationSetup: entry.isolationSetup } : {}),
217
+ contractSceneId: entry.reach.sceneId,
218
+ }
219
+ : {
220
+ kind: 'no-document',
221
+ ...liveReachOf(entry),
222
+ reason:
223
+ `"${entry.id}" is reached only through the running game's own navigation ` +
224
+ `(its scenes contract calls it "${entry.reach.sceneId}"), and the entry names no ` +
225
+ 'source module, so there is no Edit document — open it while the game is running.',
226
+ };
227
+ case 'story':
228
+ return { kind: 'story-document', title: entry.label, storyId: entry.reach.storyId };
229
+ }
230
+ }
231
+
232
+ /** One row of the Edit tab row. */
233
+ export interface SceneTabRow {
234
+ readonly entry: DocumentEntry;
235
+ /** Never `no-document` — a row that owns no document is not a tab. */
236
+ readonly plan: Exclude<SceneDocumentPlan, { kind: 'no-document' }>;
237
+ /** The table's declared default entry. At most one row carries it. */
238
+ readonly isDefault: boolean;
239
+ }
240
+
241
+ /**
242
+ * The whole Edit tab row: every AUTHORABLE SCENE the table declares that owns
243
+ * a document, in the table's own order.
244
+ *
245
+ * Prefabs are excluded deliberately, and they are not second-class for it: a
246
+ * prefab already surfaces through the boards and Content, and it opens as a
247
+ * sibling document on demand through the same verb (`open`). What a standing
248
+ * TAB means is "this is one of the game's scenes", which is exactly the
249
+ * `kind: 'scene'` declaration.
250
+ */
251
+ export function sceneTabRow(table: ResolvedDocumentTable): readonly SceneTabRow[] {
252
+ const rows: SceneTabRow[] = [];
253
+ for (const entry of table.entries) {
254
+ if (entry.kind !== 'scene') continue;
255
+ const plan = planSceneDocument(entry);
256
+ if (plan.kind === 'no-document') continue;
257
+ rows.push({ entry, plan, isDefault: table.default === entry.id });
258
+ }
259
+ return rows;
260
+ }
261
+
262
+ /** What Edit and Content actually present from one resolved scene table. */
263
+ export function authoringSurfaceFromTable(
264
+ table: ResolvedDocumentTable,
265
+ openDocumentIds: readonly string[],
266
+ availableDocumentIds: readonly string[],
267
+ ): {
268
+ readonly sceneDocuments: number;
269
+ readonly isolationDocuments: number;
270
+ readonly openIsolationDocuments: number;
271
+ readonly availableIsolationDocuments: number;
272
+ readonly pieces: number;
273
+ /** Entries of kind `scene` — the surface prefabs are placed on; a table
274
+ * with none (a models or website project) has no prefabs by design. */
275
+ readonly sceneEntries: number;
276
+ } {
277
+ const tabs = sceneTabRow(table);
278
+ const isolationIds = tabs.flatMap((row) =>
279
+ row.plan.kind === 'isolation-document' ? [row.plan.documentId] : [],
280
+ );
281
+ const open = new Set(openDocumentIds);
282
+ const available = new Set(availableDocumentIds);
283
+ // A finder's OWN document kind (a `model` from `@volter/editor-blender`'s finder, a
284
+ // `page`, a `shot`) opens through that kind's document, never through the
285
+ // scene plan above — which answers `no-document` for it because it names
286
+ // no region. Those are documents Edit presents all the same; measured
287
+ // 2026-09-17, a models project reported "no authorable scene with a
288
+ // document to open" beside its open model document.
289
+ const kindDocuments = table.entries.filter(
290
+ (entry) => entry.authorable && entry.kind !== 'scene' && entry.kind !== 'prefab',
291
+ ).length;
292
+ return {
293
+ sceneDocuments: tabs.length + kindDocuments,
294
+ isolationDocuments: isolationIds.length,
295
+ openIsolationDocuments: isolationIds.filter((id) => open.has(id)).length,
296
+ availableIsolationDocuments: isolationIds.filter((id) => available.has(id)).length,
297
+ pieces: table.entries.filter((entry) => entry.kind === 'prefab').length,
298
+ sceneEntries: table.entries.filter((entry) => entry.kind === 'scene').length,
299
+ };
300
+ }
301
+
302
+ /**
303
+ * Isolation tabs ARE the Edit scene row. A generic Scene tab
304
+ * (`workspace:scene` / `workspace:canvas-scene`) would be a second surface
305
+ * for the same game — the live host, or an empty world — and only belongs
306
+ * when this table has no isolation documents (or also names a root document
307
+ * that Scene already is).
308
+ */
309
+ export function isolationTabsReplaceGenericScene(table: ResolvedDocumentTable): boolean {
310
+ const rows = sceneTabRow(table);
311
+ return (
312
+ rows.some((row) => row.plan.kind === 'isolation-document') &&
313
+ !rows.some((row) => row.plan.kind === 'root-document')
314
+ );
315
+ }
@@ -0,0 +1,315 @@
1
+ /**
2
+ * What the editor camera should look at when it adopts a world nobody authored
3
+ * here — the two answers behind `EditorViewport.focusOnScene`.
4
+ *
5
+ * ## The measured defect
6
+ *
7
+ * Auto-frame framed the game's ENTIRE content AABB. Measured on a Cuberun
8
+ * mount: a 2,000-radius BackSide skybox sphere, a sun mesh at `z = -2000` and a
9
+ * hyperspace prop at `z ≈ -5365` stretched that AABB to ~8,400 units deep, so
10
+ * the fit distance landed the camera 12,870 units away from a ~1,000-unit
11
+ * gameplay corridor. 93% of the Scene viewport was black, and the one visible
12
+ * thing was the OUTSIDE of the skybox. The same world framed at working
13
+ * distance is vivid and correct. (Descent measured the same looseness at a
14
+ * smaller scale: a camera at ~2,580 for an ~800-unit world.)
15
+ *
16
+ * The union is the wrong instrument because a backdrop is not content the
17
+ * reader is looking FOR — it is content that exists to be far away. One prop
18
+ * 5,000 units out costs the whole framing, and no amount of care in the fit
19
+ * math can recover it.
20
+ *
21
+ * ## The two answers, in order
22
+ *
23
+ * 1. {@link viewFromGameCamera} — a game's author already decided the
24
+ * interesting view, so seeding the orbit camera from the world matrix of a
25
+ * camera the game itself placed opens the Scene tab where the game looks.
26
+ * {@link pickGameCamera} decides which camera that is.
27
+ * 2. {@link frameableContentBounds} — the fallback for a game whose camera we
28
+ * cannot name. It trims outliers off the union before framing.
29
+ *
30
+ * ## Why gap-trimming and not a percentile
31
+ *
32
+ * A blanket percentile (frame the inner 90% of content) would tighten the
33
+ * framing of EVERY world, including the uniform ones that are framed correctly
34
+ * today. This trims only across a spatial GAP: a face of the bounding box moves
35
+ * inward only when a small share of boxes sits beyond a void spanning a large
36
+ * fraction of the current diagonal. A uniformly-filled world has no such gap,
37
+ * so it comes back byte-identical to the plain union — that identity is the
38
+ * first property the tests pin.
39
+ */
40
+
41
+ import { isEditorOwnedObject } from '@volter/editor-threejs/viewport/editor-layers';
42
+ import * as THREE from 'three';
43
+
44
+ /** Tuning for {@link frameableContentBounds}. Defaults are the shipped values. */
45
+ export interface FrameableBoundsOptions {
46
+ /** Largest share of the boxes all trimming together may discard. */
47
+ maxOutlierFraction?: number;
48
+ /** A gap must span this share of the current content diagonal to be a gap. */
49
+ minGapFraction?: number;
50
+ /** Below this many boxes there is no distribution to judge; nothing trims. */
51
+ minSampleSize?: number;
52
+ /** Cap on trim passes, so cost stays bounded on a huge world. */
53
+ maxPasses?: number;
54
+ }
55
+
56
+ const DEFAULTS: Required<FrameableBoundsOptions> = {
57
+ maxOutlierFraction: 0.1,
58
+ minGapFraction: 0.2,
59
+ minSampleSize: 8,
60
+ maxPasses: 8,
61
+ };
62
+
63
+ /** Orbit pivot distance when there is no content to measure one from. */
64
+ const EMPTY_CONTENT_PIVOT_DISTANCE = 10;
65
+
66
+ /** How far down the view axis the content must start for the pivot to mean
67
+ * anything — as a share of the content's own radius. */
68
+ const MIN_PIVOT_RADIUS_FRACTION = 0.01;
69
+
70
+ type Axis = 'x' | 'y' | 'z';
71
+ const AXES: readonly Axis[] = ['x', 'y', 'z'];
72
+
73
+ function unionOf(boxes: readonly THREE.Box3[], indices: Iterable<number>): THREE.Box3 {
74
+ const out = new THREE.Box3();
75
+ for (const i of indices) out.union(boxes[i] as THREE.Box3);
76
+ return out;
77
+ }
78
+
79
+ /** One candidate trim: the boxes beyond the widest gap on one box face. */
80
+ interface GapCut {
81
+ gap: number;
82
+ discard: number[];
83
+ }
84
+
85
+ /**
86
+ * The widest gap found scanning inward from any of the six faces, across at
87
+ * most `budget` boxes.
88
+ *
89
+ * Scanning from a FACE (rather than clustering centers) is what catches a
90
+ * skybox: its centre sits with the content, and only its extent is an outlier.
91
+ */
92
+ function widestOuterGap(
93
+ boxes: readonly THREE.Box3[],
94
+ kept: ReadonlySet<number>,
95
+ budget: number,
96
+ ): GapCut | null {
97
+ let best: GapCut | null = null;
98
+ for (const axis of AXES) {
99
+ for (const side of ['min', 'max'] as const) {
100
+ const values: Array<{ i: number; v: number }> = [];
101
+ for (const i of kept) values.push({ i, v: (boxes[i] as THREE.Box3)[side][axis] });
102
+ // Outliers are the smallest values on a min face and the largest on a max
103
+ // face; sort so index 0 is always the outermost.
104
+ values.sort((a, b) => (side === 'min' ? a.v - b.v : b.v - a.v));
105
+ const reach = Math.min(budget, values.length - 1);
106
+ for (let k = 0; k < reach; k++) {
107
+ const gap = Math.abs((values[k + 1] as { v: number }).v - (values[k] as { v: number }).v);
108
+ if (best && gap <= best.gap) continue;
109
+ best = { gap, discard: values.slice(0, k + 1).map((e) => e.i) };
110
+ }
111
+ }
112
+ }
113
+ return best;
114
+ }
115
+
116
+ /**
117
+ * The box worth FRAMING, given one world-space box per content node.
118
+ *
119
+ * Returns the plain union whenever there is nothing defensible to trim: too few
120
+ * boxes to judge, no gap wide enough, or a budget of zero. Never returns an
121
+ * empty box for non-empty input.
122
+ */
123
+ export function frameableContentBounds(
124
+ boxes: readonly THREE.Box3[],
125
+ options: FrameableBoundsOptions = {},
126
+ ): THREE.Box3 {
127
+ const opts = { ...DEFAULTS, ...options };
128
+ const content = boxes.filter((b) => !b.isEmpty());
129
+ const all = content.map((_, i) => i);
130
+ const full = unionOf(content, all);
131
+ if (content.length < opts.minSampleSize) return full;
132
+
133
+ let budget = Math.floor(content.length * opts.maxOutlierFraction);
134
+ if (budget < 1) return full;
135
+
136
+ const kept = new Set(all);
137
+ let bounds = full;
138
+ const size = new THREE.Vector3();
139
+ for (let pass = 0; pass < opts.maxPasses && budget > 0; pass++) {
140
+ const diagonal = bounds.getSize(size).length();
141
+ if (!(diagonal > 0)) break;
142
+ const cut = widestOuterGap(content, kept, budget);
143
+ if (!cut || cut.gap <= opts.minGapFraction * diagonal) break;
144
+ for (const i of cut.discard) kept.delete(i);
145
+ budget -= cut.discard.length;
146
+ bounds = unionOf(content, kept);
147
+ }
148
+ return bounds.isEmpty() ? full : bounds;
149
+ }
150
+
151
+ /** An orbit-camera pose: where the eye sits and what it orbits around. */
152
+ export interface SeededView {
153
+ position: THREE.Vector3;
154
+ target: THREE.Vector3;
155
+ }
156
+
157
+ /**
158
+ * The editor orbit pose that reproduces `camera`'s own view.
159
+ *
160
+ * The eye is the game camera's world position and the view direction is its
161
+ * own; the only thing invented is the orbit PIVOT, which is placed at the depth
162
+ * of the content along that view direction — the point the reader is already
163
+ * looking at, so the first orbit drag turns around the world rather than
164
+ * around a point behind their head.
165
+ *
166
+ * Returns `null` when that pivot cannot be placed: the world sits BEHIND the
167
+ * camera (a view showing none of the game is not worth adopting), or it sits
168
+ * ON it, which leaves an orbit pivot at the eye — measured as a target
169
+ * 4.6e-14 units out, an orbit control that can only spin in place. Both fall
170
+ * back to {@link frameableContentBounds}.
171
+ */
172
+ export function viewFromGameCamera(camera: THREE.Camera, content: THREE.Box3): SeededView | null {
173
+ const forward = camera.getWorldDirection(new THREE.Vector3());
174
+ const position = new THREE.Vector3().setFromMatrixPosition(camera.matrixWorld);
175
+ if (!Number.isFinite(position.lengthSq()) || forward.lengthSq() === 0) return null;
176
+ if (content.isEmpty()) {
177
+ return {
178
+ position,
179
+ target: position.clone().addScaledVector(forward, EMPTY_CONTENT_PIVOT_DISTANCE),
180
+ };
181
+ }
182
+ const sphere = content.getBoundingSphere(new THREE.Sphere());
183
+ const along = sphere.center.clone().sub(position).dot(forward);
184
+ if (!(along > sphere.radius * MIN_PIVOT_RADIUS_FRACTION)) return null;
185
+ return { position, target: position.clone().addScaledVector(forward, along) };
186
+ }
187
+
188
+ /**
189
+ * The NDC points a seeded view is sampled at: the centre plus a ring at 60% of
190
+ * the way to each edge/corner. Nine rays — enough that a view whose middle is
191
+ * blocked by one prop still reads as open, cheap enough to run on every seed
192
+ * attempt over a whole game graph.
193
+ */
194
+ const OCCLUSION_SAMPLES: ReadonlyArray<readonly [number, number]> = [
195
+ [0, 0],
196
+ [-0.6, 0.6],
197
+ [0.6, 0.6],
198
+ [-0.6, -0.6],
199
+ [0.6, -0.6],
200
+ [0, 0.6],
201
+ [0, -0.6],
202
+ [-0.6, 0],
203
+ [0.6, 0],
204
+ ];
205
+
206
+ /**
207
+ * How near a hit has to be, as a share of the view's own pivot distance, to
208
+ * count as "pressed against the lens" rather than "part of the world". At 2%,
209
+ * a camera looking 100 units into a world is blocked only by something inside
210
+ * 2 units of it.
211
+ */
212
+ const NEAR_FIELD_FRACTION = 0.02;
213
+
214
+ const _raycaster = new THREE.Raycaster();
215
+ const _ndc = new THREE.Vector2();
216
+
217
+ /** Three's raycast reports hits on invisible objects; a hidden collider proxy
218
+ * in front of the lens blocks nothing a reader would see. */
219
+ function isVisibleInWorld(object: THREE.Object3D): boolean {
220
+ for (let node: THREE.Object3D | null = object; node; node = node.parent) {
221
+ if (!node.visible) return false;
222
+ }
223
+ return true;
224
+ }
225
+
226
+ /**
227
+ * Does a view posed HERE show the world, or a surface pressed against the lens?
228
+ *
229
+ * The measured defect: a game's own camera is placed for the game's own moment
230
+ * — inside a cockpit, behind a wall it is about to drive through, under
231
+ * terrain that has not settled — and adopting it as the editor's opening view
232
+ * can hand the reader a full-bleed rectangle of rock. The view is a perfectly
233
+ * good gameplay camera and a useless first look, and nothing in the pose
234
+ * itself says which.
235
+ *
236
+ * The instrument is geometric, not pixels: nine rays through the frustum, and
237
+ * a view SHOWS THE WORLD when at least one of them either escapes (sky,
238
+ * backdrop, open space) or reaches something farther than
239
+ * {@link NEAR_FIELD_FRACTION} of what this view claims to be looking at. The
240
+ * threshold is relative to the view's own pivot distance, so it needs no
241
+ * world-scale constant and reads the same on a 10-unit room and a 10,000-unit
242
+ * canyon.
243
+ *
244
+ * Deliberately conservative: EVERY sample must be blocked in the near field
245
+ * before the view is refused, so an ordinary view with a prop in the middle or
246
+ * ground filling its lower half still passes. A refusal here costs the reader
247
+ * only the game's own camera — the caller falls back to
248
+ * {@link frameableContentBounds}, which always shows something.
249
+ *
250
+ * Three's raycast honours `material.side` exactly as the renderer does, and
251
+ * that alignment is the reason this instrument is honest rather than merely
252
+ * cheap: a camera sealed inside a front-faced solid is invisible to BOTH — the
253
+ * reader sees through the shell, the rays escape through it, and the view is
254
+ * correctly not called blocked. The failure this catches is the one that is
255
+ * actually on screen: front faces, filling the frame, right at the lens.
256
+ *
257
+ * (The screenshot lane measures the same failure from the other side, on
258
+ * PIXELS, in `composite-screenshot.ts`. That instrument needs a drawn frame
259
+ * and a GPU readback, and it cannot tell a legitimately flat picture from a
260
+ * blocked one — which is why the decision to adopt a pose is made here, before
261
+ * anything is drawn.)
262
+ */
263
+ export function seededViewShowsWorld(
264
+ camera: THREE.Camera,
265
+ content: readonly THREE.Object3D[],
266
+ pivotDistance: number,
267
+ ): boolean {
268
+ if (!(pivotDistance > 0) || content.length === 0) return true;
269
+ const nearField = pivotDistance * NEAR_FIELD_FRACTION;
270
+ for (const [x, y] of OCCLUSION_SAMPLES) {
271
+ _ndc.set(x, y);
272
+ _raycaster.setFromCamera(_ndc, camera);
273
+ const hits = _raycaster.intersectObjects(content as THREE.Object3D[], true);
274
+ const blocker = hits.find((hit) => isVisibleInWorld(hit.object));
275
+ if (!blocker || blocker.distance > nearField) return true;
276
+ }
277
+ return false;
278
+ }
279
+
280
+ /** Every camera in the game's own scene, in scene order, editor furniture and
281
+ * its subtrees excluded. */
282
+ function gameCameras(node: THREE.Object3D, out: THREE.Camera[] = []): THREE.Camera[] {
283
+ if (isEditorOwnedObject(node)) return out;
284
+ if ((node as THREE.Camera).isCamera) out.push(node as THREE.Camera);
285
+ for (const child of node.children) gameCameras(child, out);
286
+ return out;
287
+ }
288
+
289
+ /**
290
+ * The camera whose view an ingest mount should adopt — one the game itself
291
+ * CREATED AND PLACED, which means one that is in the game's own scene graph.
292
+ *
293
+ * The capture's own camera (the one the first `render(scene, camera)` used) is
294
+ * preferred WHEN it is one of those, and is otherwise ignored, because on the
295
+ * first rendered frame it can be a reconciler's placeholder rather than the
296
+ * author's camera. Measured on Cuberun: the captured camera came back with an
297
+ * identity world matrix at the origin while the game's own
298
+ * `<PerspectiveCamera position={[0, 10, -10]}>` was sitting in the scene at
299
+ * (0, 8, 3.5) looking down the corridor — seeding from the capture opened on a
300
+ * flat wall of ground plane, seeding from the scene's camera opened on the
301
+ * game.
302
+ *
303
+ * Editor furniture is never adopted: the capture trap sits on the shared
304
+ * `WebGLRenderer.prototype`, so the editor's own cameras are the one thing
305
+ * that must never come back from here.
306
+ */
307
+ export function pickGameCamera(
308
+ captured: THREE.Camera | null | undefined,
309
+ scene: THREE.Object3D,
310
+ ): THREE.Camera | null {
311
+ const cameras = gameCameras(scene);
312
+ if (cameras.length === 0) return null;
313
+ if (captured && cameras.includes(captured)) return captured;
314
+ return cameras[0] as THREE.Camera;
315
+ }