@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,372 @@
1
+ /**
2
+ * The client half of the SUPERVISED LEASE (policy: ../editor-lease.ts).
3
+ *
4
+ * A watchdog that polls the editor server's identity every few seconds, reads
5
+ * this page's control-channel liveness alongside it, and acts on the pair:
6
+ *
7
+ * - the session is provably GONE (failed polls AND a dropped control socket)
8
+ * or TAKEN OVER → tear the running game down (which drops the Colyseus room,
9
+ * so no ghost player is left behind) and raise the "Editor disconnected"
10
+ * overlay;
11
+ * - the polls fail but the control socket is still up → the server is BUSY,
12
+ * not gone. Say so in a passive banner that changes nothing and blocks
13
+ * nothing, and clear it the moment a poll answers.
14
+ *
15
+ * That second branch is the fix for the defect this file caused: a blocking,
16
+ * non-dismissable overlay eats every pointer event while `page.evaluate` keeps
17
+ * working, so a false void is invisible to every JS-level check and turns a
18
+ * 12-second hiccup into a lost session. Measured 2026-08-09: `kill -STOP` on a
19
+ * healthy dev server raised the scrim in ~19s with the control socket still
20
+ * OPEN. Nothing here may block the page on evidence that weak — and the
21
+ * overlay that DOES appear is dismissable ("Continue anyway") and clears
22
+ * itself when the same server answers again.
23
+ *
24
+ * This is the UNGRACEFUL counterpart to the graceful `tab-close` path
25
+ * (tab-lifecycle-client.ts): a clean `vgai close` pushes an event the tab acts
26
+ * on, but a killed/crashed server sends nothing.
27
+ *
28
+ * ## Why this watchdog is not (only) a timer
29
+ *
30
+ * An interval is not a heartbeat the page controls. Measured twice on live
31
+ * sessions, 2026-08-19: after its server was SIGKILLed, the tab made zero
32
+ * network attempts of any kind for the rest of the run — no poll, no socket
33
+ * reconnect, no worker beat — and a relaunched server on the same port went
34
+ * unnoticed too. Once from a hidden tab over four and a half minutes, once
35
+ * from a tab that was visible at the kill, over forty-five seconds. Why the
36
+ * page stops is not established (see ../editor-lease.ts); that it may stop is.
37
+ * A watchdog that can only fire from `setInterval` is therefore asleep for
38
+ * exactly the stretch the user is away, and still owes three polls when they
39
+ * come back and start clicking.
40
+ *
41
+ * So the poll is driven by the MOMENTS THAT MATTER as well as the clock: the
42
+ * tab becoming visible again, a `pageshow` restore, and either edge of the
43
+ * control socket. The clock moved into the DECISION instead — a void needs the
44
+ * darkness to have lasted `LEASE_MIN_DARK_WINDOW_MS`, so polling eagerly can
45
+ * never turn a server that is merely between processes into a dead one.
46
+ *
47
+ * And because the page may be closed or discarded while the overlay is up, the
48
+ * void also leaves a durable note (`session-orphan-record.ts`) that the NEXT
49
+ * session on this address reads and reports. The overlay is what the user in
50
+ * front of the tab sees; the note is what everyone else gets.
51
+ *
52
+ * Editor-only by construction: it lives behind the `/__editor` server, so a
53
+ * standalone `npm run game` page (which has no such server) never runs it —
54
+ * no game-side flag is involved.
55
+ */
56
+
57
+ import { useEffect, useRef, useState } from 'react';
58
+ import { pollEditorLeaseIdentity } from '../editor-api';
59
+ import {
60
+ DEFAULT_LEASE_FAILURE_THRESHOLD,
61
+ decideLeaseRecovery,
62
+ decideLeaseState,
63
+ forgetLeaseFailures,
64
+ initialLeaseWatchState,
65
+ LEASE_MIN_DARK_WINDOW_MS,
66
+ LEASE_POLL_INTERVAL_MS,
67
+ type LeaseChannelState,
68
+ type LeasePollResult,
69
+ type LeaseVoidReason,
70
+ type LeaseWatchState,
71
+ reduceLeasePoll,
72
+ } from '../editor-lease';
73
+ import { publishEditorLeaseView } from '../editor-lease-view';
74
+ import { dismissNotification, notify } from '../editor-notifications';
75
+ import { connectEvents, readControlChannelState } from '../editor-presence';
76
+ import { clearSessionOrphanRecord, writeSessionOrphanRecord } from '../session-orphan-record';
77
+ import { clearSessionEnded, markSessionEnded } from '../session-tombstone';
78
+
79
+ /** The last path segment of a project root, for user-facing copy. */
80
+ function projectName(path: string | null): string | null {
81
+ if (!path) return null;
82
+ const segments = path.split(/[\\/]/).filter(Boolean);
83
+ return segments.at(-1) ?? null;
84
+ }
85
+
86
+ /**
87
+ * The one-shot teardown a void lease triggers: drop the Colyseus room (via the
88
+ * game's React unmount) and stop the sim. `exitPlayMode` is imported lazily so
89
+ * the guard — and therefore AppRoot's module graph — never eagerly pulls in the
90
+ * engine renderer play-mode drags along; it is only needed on the rare void,
91
+ * not on the poll cadence. A teardown throw is swallowed: the overlay must
92
+ * still tell the user the tab is dead.
93
+ */
94
+ async function tearDownVoidLease(): Promise<void> {
95
+ try {
96
+ const { stopAllLiveSessions } = await import('../live-session-registry');
97
+ stopAllLiveSessions();
98
+ } catch {
99
+ /* the overlay is raised regardless — see above */
100
+ }
101
+ }
102
+
103
+ /**
104
+ * What the guard is currently showing. `void` carries whether a fresh boot is
105
+ * on offer, so the overlay can hand back a way out instead of being a dead end.
106
+ */
107
+ type GuardView =
108
+ | { kind: 'quiet' }
109
+ /** `busy` — polls failing over a live channel. `stale` — a real void the
110
+ * user chose to keep reading; the page is a snapshot and says so. */
111
+ | { kind: 'degraded'; subject: 'busy' | 'stale' }
112
+ | { kind: 'void'; reason: LeaseVoidReason; reloadOffered: boolean };
113
+
114
+ /**
115
+ * Mounts the watchdog and renders its notice. Place it at the editor root
116
+ * (AppRoot) so the overlay covers the whole editor.
117
+ */
118
+ export function EditorLeaseGuard() {
119
+ const [view, setView] = useState<GuardView>({ kind: 'quiet' });
120
+ // The rolling watch state and the one-shot latches live in refs: the interval
121
+ // closure reads them without re-subscribing, and the teardown must run
122
+ // exactly once per void.
123
+ const stateRef = useRef<LeaseWatchState>(initialLeaseWatchState(Date.now()));
124
+ const voidedRef = useRef<LeaseVoidReason | null>(null);
125
+ // Sticky for the page: once the user says "continue anyway", this guard never
126
+ // blocks them again. A second scrim after an explicit dismissal is the same
127
+ // trap wearing a later timestamp.
128
+ const dismissedRef = useRef(false);
129
+
130
+ useEffect(() => {
131
+ let cancelled = false;
132
+ let timer: ReturnType<typeof setInterval> | null = null;
133
+
134
+ const stop = () => {
135
+ if (timer !== null) {
136
+ clearInterval(timer);
137
+ timer = null;
138
+ }
139
+ };
140
+
141
+ // Say it, THEN clean up. The timer keeps running for `server-gone` so the
142
+ // tombstone can recover; `taken-over` stops, because that tab yielded to a
143
+ // live window and must never come back to fight it.
144
+ const enterVoidState = async (reason: LeaseVoidReason) => {
145
+ voidedRef.current = reason;
146
+ // The page-wide latch: from here the command listener refuses relayed
147
+ // traffic and reports itself detached, so the server's tab table stops
148
+ // treating this page as a live editor (`session-tombstone.ts`). The
149
+ // overlay below is the human half of the same fact.
150
+ markSessionEnded(reason);
151
+ if (reason === 'taken-over') stop();
152
+ // ORDER IS THE POINT. Everything that tells somebody — the overlay on
153
+ // screen and the note for the next session — happens before the
154
+ // teardown, and neither touches the network. The teardown is a dynamic
155
+ // import; today it resolves from the module cache, but the server whose
156
+ // module graph it belongs to is by definition dead at this instant, and
157
+ // a page that announces its own death only after a fetch to the corpse
158
+ // has the failure mode this whole file exists to remove.
159
+ if (reason === 'server-gone') {
160
+ writeSessionOrphanRecord({
161
+ at: Date.now(),
162
+ reason: 'server-gone',
163
+ project: stateRef.current.bootIdentity?.project ?? null,
164
+ });
165
+ }
166
+ setView(
167
+ dismissedRef.current
168
+ ? { kind: 'degraded', subject: 'stale' }
169
+ : { kind: 'void', reason, reloadOffered: false },
170
+ );
171
+ await tearDownVoidLease();
172
+ };
173
+
174
+ // The tombstone watch: this page has already declared itself dead and is
175
+ // now only asking whether anything came back. `taken-over` never reaches
176
+ // here — its timer stops, because that tab yielded to a live window.
177
+ const tickTombstone = (result: LeasePollResult, channel: LeaseChannelState, now: number) => {
178
+ switch (decideLeaseRecovery(stateRef.current.bootIdentity, result)) {
179
+ case 'resume': {
180
+ // The SAME process answered: it was never gone, so nothing here is
181
+ // stale and the session is kept. This is the branch that turns a
182
+ // false void back into a working editor.
183
+ voidedRef.current = null;
184
+ // …and the page is live again, so lift the tombstone: control
185
+ // traffic must stop being refused the moment the evidence says the
186
+ // process never died.
187
+ clearSessionEnded();
188
+ // Retract the note too. It said this window was orphaned; the
189
+ // process answering proves it was not, and a record nobody can
190
+ // trust is worse than none.
191
+ clearSessionOrphanRecord();
192
+ stateRef.current = reduceLeasePoll(stateRef.current, result, channel, now);
193
+ setView({ kind: 'quiet' });
194
+ return;
195
+ }
196
+ case 'reload':
197
+ // A new process serves our project: the page IS stale, so a full
198
+ // boot is the honest recovery (../editor-lease.ts revival contract).
199
+ // Unless the user asked to keep this window — "continue anyway"
200
+ // means exactly "do not take it away from me", and overriding that
201
+ // is the same class of defect as the scrim it dismissed.
202
+ if (dismissedRef.current) return;
203
+ window.location.reload();
204
+ return;
205
+ case 'offer-reload':
206
+ setView((current) =>
207
+ current.kind === 'void' && !current.reloadOffered
208
+ ? { ...current, reloadOffered: true }
209
+ : current,
210
+ );
211
+ return;
212
+ default:
213
+ return;
214
+ }
215
+ };
216
+
217
+ const tick = async () => {
218
+ const result = await pollEditorLeaseIdentity();
219
+ const channel = readControlChannelState();
220
+ const now = Date.now();
221
+ if (cancelled) return;
222
+
223
+ if (voidedRef.current !== null) {
224
+ tickTombstone(result, channel, now);
225
+ return;
226
+ }
227
+
228
+ stateRef.current = reduceLeasePoll(stateRef.current, result, channel, now);
229
+ const next = decideLeaseState({
230
+ consecutiveFailures: stateRef.current.consecutiveFailures,
231
+ unbackedFailures: stateRef.current.unbackedFailures,
232
+ failureThreshold: DEFAULT_LEASE_FAILURE_THRESHOLD,
233
+ darkForMs: now - stateRef.current.darkSince,
234
+ minDarkWindowMs: LEASE_MIN_DARK_WINDOW_MS,
235
+ bootIdentity: stateRef.current.bootIdentity,
236
+ lastIdentity: stateRef.current.lastIdentity,
237
+ });
238
+ if (next.status === 'void' && next.reason !== null) {
239
+ await enterVoidState(next.reason);
240
+ return;
241
+ }
242
+ setView(
243
+ next.status === 'degraded' ? { kind: 'degraded', subject: 'busy' } : { kind: 'quiet' },
244
+ );
245
+ };
246
+
247
+ // One poll at a time. The interval and the event triggers below all land
248
+ // in here, and overlapping polls would fold two readings of the same
249
+ // moment into the counter as if they were two moments.
250
+ let polling = false;
251
+ const poll = () => {
252
+ if (polling || cancelled) return;
253
+ polling = true;
254
+ void tick().finally(() => {
255
+ polling = false;
256
+ });
257
+ };
258
+
259
+ // The page was NOT RUNNING and now is. Measured 2026-08-19: a hidden tab
260
+ // whose server had been killed made no network attempt of any kind for
261
+ // four and a half minutes — the browser had suspended the page and its
262
+ // heartbeat worker with it. Until the tab is shown again there is no
263
+ // watchdog, only a timer the browser has stopped calling; the ONLY moment
264
+ // that matters is this one, and waiting a further interval to look would
265
+ // spend the user's first clicks hanging on a server the page could have
266
+ // asked about immediately.
267
+ //
268
+ // The failure run is dropped first: those counters are testimony about
269
+ // elapsed time and this page slept through it (see `forgetLeaseFailures`).
270
+ const wake = () => {
271
+ if (cancelled) return;
272
+ if (voidedRef.current === null) {
273
+ stateRef.current = forgetLeaseFailures(stateRef.current, Date.now());
274
+ }
275
+ poll();
276
+ };
277
+ const onVisibility = () => {
278
+ if (document.visibilityState === 'visible') wake();
279
+ };
280
+ document.addEventListener('visibilitychange', onVisibility);
281
+ window.addEventListener('pageshow', wake);
282
+
283
+ // The control socket is the fast half of the evidence: a killed process
284
+ // drops it in milliseconds, while the poll cadence is seconds. Looking the
285
+ // instant it moves — in EITHER direction — is what makes the notice track
286
+ // the server rather than the clock. No reset here: unlike a wake, these
287
+ // fire on a page that has been awake and watching, so its readings stand,
288
+ // and resetting on the reconnect attempts a dead server produces would
289
+ // stop the run from ever accumulating.
290
+ let channelEvents: { close(): void } | null = null;
291
+ try {
292
+ const events = connectEvents();
293
+ events.addEventListener('error', poll);
294
+ events.addEventListener('open', poll);
295
+ channelEvents = events;
296
+ } catch {
297
+ // No control channel available on this page (no `EventSource`, a
298
+ // transport the bootstrap never built). The interval below still runs;
299
+ // the guard is simply back to its slower reading.
300
+ }
301
+
302
+ timer = setInterval(poll, LEASE_POLL_INTERVAL_MS);
303
+ return () => {
304
+ cancelled = true;
305
+ stop();
306
+ document.removeEventListener('visibilitychange', onVisibility);
307
+ window.removeEventListener('pageshow', wake);
308
+ channelEvents?.close();
309
+ };
310
+ }, []);
311
+
312
+ // STATE goes to the status bar (`editor-lease-view.ts`); the EVENT goes to
313
+ // the notification stack. Neither blocks the page: the modal this guard
314
+ // used to raise ate every click on a window that was often perfectly
315
+ // readable, and the owner named it (2026-09-04) as the wrong shape.
316
+ useEffect(() => {
317
+ publishEditorLeaseView(view);
318
+ const name = projectName(stateRef.current.bootIdentity?.project ?? null);
319
+ const dismiss = () => {
320
+ dismissedRef.current = true;
321
+ setView({ kind: 'degraded', subject: 'stale' });
322
+ };
323
+ if (view.kind === 'quiet') {
324
+ dismissNotification(LEASE_NOTIFICATION_ID);
325
+ return;
326
+ }
327
+ if (view.kind === 'degraded') {
328
+ notify({
329
+ id: LEASE_NOTIFICATION_ID,
330
+ tone: 'warning',
331
+ sticky: true,
332
+ ...(view.subject === 'busy'
333
+ ? {
334
+ title: 'Editor server is slow to answer.',
335
+ detail:
336
+ 'Still connected — this window keeps working, and this clears itself when the server catches up.',
337
+ }
338
+ : {
339
+ title: "This window's editor server has stopped.",
340
+ detail:
341
+ 'You chose to keep reading it — nothing here saves, and it will not reconnect on its own. Reload once the editor is served here again.',
342
+ actions: [{ label: 'Reload', primary: true, run: () => window.location.reload() }],
343
+ }),
344
+ });
345
+ return;
346
+ }
347
+ notify({
348
+ id: LEASE_NOTIFICATION_ID,
349
+ tone: 'error',
350
+ sticky: true,
351
+ title: 'Editor disconnected.',
352
+ detail:
353
+ view.reason === 'taken-over'
354
+ ? `${name ? `${name}'s` : "This project's"} editor is now running in another window. This one has yielded.`
355
+ : `This window's editor server has stopped — nothing here is saved, and a running game has left its room. Reopen with \`volter-editor edit ${name ?? '<project>'}\`${name ? `, or leave this window open: it reloads by itself when ${name}'s editor is served here again.` : '.'}`,
356
+ onDismiss: () => {
357
+ if (!dismissedRef.current) dismiss();
358
+ },
359
+ actions: [
360
+ ...(view.reloadOffered
361
+ ? [{ label: 'Reload', primary: true, run: () => window.location.reload() }]
362
+ : []),
363
+ { label: 'Close window', run: () => window.close() },
364
+ { label: 'Continue anyway', run: dismiss },
365
+ ],
366
+ });
367
+ }, [view]);
368
+
369
+ return null;
370
+ }
371
+
372
+ const LEASE_NOTIFICATION_ID = 'editor-lease';
@@ -0,0 +1,123 @@
1
+ /**
2
+ * The one error card the pre-editor surfaces render.
3
+ *
4
+ * Two moods, one component:
5
+ * - `inline` — an action failure (create/open/browse) above a surface the user
6
+ * is still using.
7
+ * - `startup` — the BOOT failure, where the card is the page: the title is
8
+ * promoted to the page's only `<h1>` and the recovery command is labelled
9
+ * (`StartupErrorScreen`).
10
+ *
11
+ * It lives in its own module so the boot-failure surface does not have to
12
+ * import the launcher to reuse the card — the whole point of that surface is
13
+ * that the launcher is NOT on screen (PD-6/7 residual B: an unreadable
14
+ * manifest rendered "What do you want to do?" with a banner over it, so a
15
+ * loud server-side throw still looked like a fresh launcher in a screenshot).
16
+ */
17
+
18
+ import { faCheck, faCopy, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
19
+ import type { StartupRecovery } from '@volter/editor-sdk/session/editor-compatibility';
20
+ import { Button, EditorIcon } from '@volter/editor-sdk/widgets';
21
+ import { useEffect, useState } from 'react';
22
+
23
+ export interface ScreenError {
24
+ message: string;
25
+ recovery?: StartupRecovery;
26
+ }
27
+
28
+ export function ErrorBanner({
29
+ error,
30
+ onDismiss,
31
+ onRetry,
32
+ startup = false,
33
+ }: {
34
+ error: ScreenError;
35
+ onDismiss?: (() => void) | undefined;
36
+ onRetry?: (() => void) | undefined;
37
+ startup?: boolean;
38
+ }) {
39
+ const command = error.recovery && 'command' in error.recovery ? error.recovery.command : null;
40
+ const [copied, setCopied] = useState(false);
41
+
42
+ useEffect(() => {
43
+ if (!copied) return;
44
+ const timer = window.setTimeout(() => setCopied(false), 1600);
45
+ return () => window.clearTimeout(timer);
46
+ }, [copied]);
47
+
48
+ return (
49
+ <div role="alert" className="vgai-error-card vgai-shell-rise">
50
+ <div className="vgai-error-card-icon" aria-hidden="true">
51
+ <EditorIcon icon={faTriangleExclamation} />
52
+ </div>
53
+ <div className="vgai-error-card-body">
54
+ {startup ? (
55
+ <h1 className="vgai-error-card-title">
56
+ {error.recovery?.title ?? 'Couldn’t open this project'}
57
+ </h1>
58
+ ) : (
59
+ error.recovery && <div className="vgai-error-card-title">{error.recovery.title}</div>
60
+ )}
61
+ {error.recovery && <p className="vgai-error-card-guidance">{error.recovery.guidance}</p>}
62
+ <div
63
+ className="vgai-error-card-message"
64
+ data-testid={startup ? 'project-startup-error-details' : undefined}
65
+ >
66
+ {error.message}
67
+ </div>
68
+ {command && (
69
+ <div>
70
+ {startup && (
71
+ <div className="vgai-error-card-recovery-label">RUN FROM THE PROJECT FOLDER</div>
72
+ )}
73
+ <div className="vgai-error-card-command">
74
+ <code
75
+ data-testid={
76
+ startup ? 'project-startup-recovery-command' : 'project-screen-recovery-command'
77
+ }
78
+ >
79
+ {command}
80
+ </code>
81
+ <Button
82
+ type="button"
83
+ variant="secondary"
84
+ size="compact"
85
+ onClick={() => {
86
+ void navigator.clipboard?.writeText(command);
87
+ setCopied(true);
88
+ }}
89
+ >
90
+ <EditorIcon icon={copied ? faCheck : faCopy} aria-hidden="true" />
91
+ {copied ? 'Copied' : 'Copy'}
92
+ </Button>
93
+ </div>
94
+ </div>
95
+ )}
96
+ <div className="vgai-error-card-actions">
97
+ {onRetry && (
98
+ <Button
99
+ type="button"
100
+ variant="primary"
101
+ className="vgai-shell-cta"
102
+ data-testid="retry-project-detection"
103
+ onClick={onRetry}
104
+ >
105
+ Retry
106
+ </Button>
107
+ )}
108
+ {onDismiss && (
109
+ <Button
110
+ type="button"
111
+ variant="ghost"
112
+ className="vgai-shell-cta"
113
+ aria-label="Dismiss error"
114
+ onClick={onDismiss}
115
+ >
116
+ Dismiss
117
+ </Button>
118
+ )}
119
+ </div>
120
+ </div>
121
+ </div>
122
+ );
123
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Composite folder thumbnail for the asset-browser grid. Resolves the
3
+ * folder's representative contents through the memoized `getFolderPreview`
4
+ * scan and composites up to four cells inside the standard tile preview box:
5
+ * 1 → full bleed, 2 → side-by-side halves, 3 → hero left + two stacked right,
6
+ * 4 → 2×2 grid. Cells reuse the exact asset-tile pipelines — `<img>` for
7
+ * images, manifest-then-queued offscreen render for models (at LOWER queue
8
+ * priority than direct asset tiles, so folder cells never delay them), and
9
+ * the synthetic glyphs for the remaining kinds. The flat folder icon shows
10
+ * until the summary resolves (then the composite fades in, no layout shift);
11
+ * an empty folder renders a distinct dimmed open-folder state.
12
+ *
13
+ * The whole composite is decorative (`aria-hidden`): the folder card itself
14
+ * is the `role="option"` whose accessible name is the folder name, and any
15
+ * label inside the preview would concatenate into that name — breaking
16
+ * exact-name selectors and double-announcing to screen readers.
17
+ */
18
+
19
+ import { faFolder, faFolderOpen } from '@fortawesome/free-solid-svg-icons';
20
+ import { EditorIcon } from '@volter/editor-sdk/widgets';
21
+ import { useEffect, useState } from 'react';
22
+ import { assetCapabilities } from '../asset-workflow/asset-capabilities';
23
+ import {
24
+ type FolderPreviewItem,
25
+ type FolderPreviewSummary,
26
+ getFolderPreview,
27
+ } from '../asset-workflow/folder-preview';
28
+ import { type AssetRootId, assetRootServingUrl } from '../asset-workflow/project-asset-roots';
29
+ import {
30
+ AssetIcon,
31
+ FOLDER_CELL_THUMBNAIL_PRIORITY,
32
+ TypedAssetThumbnail,
33
+ useModelThumbnailSource,
34
+ } from './asset-thumbnails';
35
+
36
+ /** Root-relative preview item path → the URL its root serves it at. */
37
+ function folderItemUrl(root: AssetRootId, item: FolderPreviewItem): string {
38
+ return assetRootServingUrl(root, item.path);
39
+ }
40
+
41
+ function FolderPreviewImageCell({ root, item }: { root: AssetRootId; item: FolderPreviewItem }) {
42
+ const [failed, setFailed] = useState(false);
43
+ if (failed) return <AssetIcon kind="image" size={14} />;
44
+ return (
45
+ <img
46
+ src={folderItemUrl(root, item)}
47
+ alt={item.name}
48
+ loading="lazy"
49
+ onError={() => setFailed(true)}
50
+ />
51
+ );
52
+ }
53
+
54
+ function FolderPreviewModelCell({ root, item }: { root: AssetRootId; item: FolderPreviewItem }) {
55
+ const thumbnail = useModelThumbnailSource(
56
+ folderItemUrl(root, item),
57
+ FOLDER_CELL_THUMBNAIL_PRIORITY,
58
+ );
59
+ if (thumbnail.failed) return <AssetIcon kind="model" size={14} />;
60
+ // Quiet while resolving: the inset cell background reads as pending —
61
+ // folder cells never show per-cell spinners.
62
+ if (!thumbnail.src) return null;
63
+ return (
64
+ <img
65
+ src={thumbnail.src}
66
+ alt={item.name}
67
+ data-fit="contain"
68
+ onError={thumbnail.fromManifest ? thumbnail.rejectManifest : undefined}
69
+ />
70
+ );
71
+ }
72
+
73
+ function FolderPreviewCell({ root, item }: { root: AssetRootId; item: FolderPreviewItem }) {
74
+ if (assetCapabilities(item.name).editor === 'environment') {
75
+ return <TypedAssetThumbnail kind="image" name={item.name} variant="cell" />;
76
+ }
77
+ if (item.kind === 'image') return <FolderPreviewImageCell root={root} item={item} />;
78
+ if (item.kind === 'model') return <FolderPreviewModelCell root={root} item={item} />;
79
+ return <TypedAssetThumbnail kind={item.kind} name={item.name} variant="cell" />;
80
+ }
81
+
82
+ export interface FolderPreviewTileProps {
83
+ /** Which asset root this folder lives in — passed through to
84
+ * `getFolderPreview` AND used to build each cell's serving URL. */
85
+ root: AssetRootId;
86
+ /** Folder path relative to the asset root. */
87
+ folderPath: string;
88
+ /** Bump after `invalidateFolderPreviews` to re-resolve without remounting. */
89
+ revision: number;
90
+ }
91
+
92
+ export function FolderPreviewTile({ root, folderPath, revision }: FolderPreviewTileProps) {
93
+ const [summary, setSummary] = useState<FolderPreviewSummary | 'failed' | null>(null);
94
+
95
+ useEffect(() => {
96
+ setSummary(null);
97
+ }, [root, folderPath]);
98
+
99
+ useEffect(() => {
100
+ let cancelled = false;
101
+ getFolderPreview(root, folderPath).then(
102
+ (result) => {
103
+ if (!cancelled) setSummary(result);
104
+ },
105
+ () => {
106
+ if (!cancelled) setSummary('failed');
107
+ },
108
+ );
109
+ return () => {
110
+ cancelled = true;
111
+ };
112
+ }, [root, folderPath, revision]);
113
+
114
+ // Loading (and scan failure): the classic flat folder icon, no spinner —
115
+ // the composite swaps in with a fade once the summary resolves.
116
+ if (summary === null || summary === 'failed') return <AssetIcon kind="folder" />;
117
+
118
+ if (summary.totalAssets === 0) {
119
+ return (
120
+ <div className="vgai-folder-preview" data-testid="folder-preview-empty" aria-hidden="true">
121
+ <div className="vgai-folder-preview__empty">
122
+ <EditorIcon icon={faFolderOpen} className="vgai-folder-preview__empty-glyph" />
123
+ <span className="vgai-folder-preview__empty-caption">Empty</span>
124
+ </div>
125
+ </div>
126
+ );
127
+ }
128
+
129
+ const count = summary.scannedAll ? `${summary.totalAssets}` : `${summary.totalAssets}+`;
130
+
131
+ // Assets exist but none are previewable — keep the flat folder identity,
132
+ // still faded in and counted so the tile reflects the resolved summary.
133
+ if (summary.items.length === 0) {
134
+ return (
135
+ <div className="vgai-folder-preview" data-testid="folder-preview-plain" aria-hidden="true">
136
+ <div className="vgai-folder-preview__fallback">
137
+ <AssetIcon kind="folder" />
138
+ </div>
139
+ <span className="vgai-folder-preview__count">{count}</span>
140
+ </div>
141
+ );
142
+ }
143
+
144
+ return (
145
+ <div className="vgai-folder-preview" data-testid="folder-preview" aria-hidden="true">
146
+ <div className="vgai-folder-preview__grid" data-count={summary.items.length}>
147
+ {summary.items.map((item) => (
148
+ <div key={item.path} className="vgai-folder-preview__cell">
149
+ <FolderPreviewCell root={root} item={item} />
150
+ </div>
151
+ ))}
152
+ </div>
153
+ <span className="vgai-folder-preview__chip">
154
+ <EditorIcon icon={faFolder} />
155
+ </span>
156
+ <span className="vgai-folder-preview__count">{count}</span>
157
+ </div>
158
+ );
159
+ }