@fps-games/editor 0.2.1-beta.3 → 0.2.1-beta.4

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 (514) hide show
  1. package/dist/build-info.json +44 -44
  2. package/dist/editor-commands/capabilities/assets.d.ts +8 -0
  3. package/dist/editor-commands/capabilities/assets.d.ts.map +1 -0
  4. package/dist/{agent-bridge → editor-commands}/capabilities/assets.js +41 -41
  5. package/dist/editor-commands/capabilities/assets.js.map +1 -0
  6. package/dist/editor-commands/capabilities/editor-state.d.ts +5 -0
  7. package/dist/editor-commands/capabilities/editor-state.d.ts.map +1 -0
  8. package/dist/{agent-bridge → editor-commands}/capabilities/editor-state.js +22 -22
  9. package/dist/editor-commands/capabilities/editor-state.js.map +1 -0
  10. package/dist/editor-commands/capabilities/hierarchy.d.ts +4 -0
  11. package/dist/editor-commands/capabilities/hierarchy.d.ts.map +1 -0
  12. package/dist/{agent-bridge → editor-commands}/capabilities/hierarchy.js +5 -5
  13. package/dist/editor-commands/capabilities/hierarchy.js.map +1 -0
  14. package/dist/editor-commands/capabilities/host-lifecycle.d.ts +11 -0
  15. package/dist/editor-commands/capabilities/host-lifecycle.d.ts.map +1 -0
  16. package/dist/editor-commands/capabilities/host-lifecycle.js +149 -0
  17. package/dist/editor-commands/capabilities/host-lifecycle.js.map +1 -0
  18. package/dist/editor-commands/capabilities/prefabs.d.ts +5 -0
  19. package/dist/editor-commands/capabilities/prefabs.d.ts.map +1 -0
  20. package/dist/{agent-bridge → editor-commands}/capabilities/prefabs.js +93 -93
  21. package/dist/editor-commands/capabilities/prefabs.js.map +1 -0
  22. package/dist/editor-commands/capabilities/scene-objects.d.ts +8 -0
  23. package/dist/editor-commands/capabilities/scene-objects.d.ts.map +1 -0
  24. package/dist/editor-commands/capabilities/scene-objects.js +480 -0
  25. package/dist/editor-commands/capabilities/scene-objects.js.map +1 -0
  26. package/dist/editor-commands/capabilities/selection.d.ts +5 -0
  27. package/dist/editor-commands/capabilities/selection.d.ts.map +1 -0
  28. package/dist/editor-commands/capabilities/selection.js +60 -0
  29. package/dist/editor-commands/capabilities/selection.js.map +1 -0
  30. package/dist/editor-commands/capabilities/shadows.d.ts +5 -0
  31. package/dist/editor-commands/capabilities/shadows.d.ts.map +1 -0
  32. package/dist/{agent-bridge → editor-commands}/capabilities/shadows.js +74 -74
  33. package/dist/editor-commands/capabilities/shadows.js.map +1 -0
  34. package/dist/editor-commands/capabilities/transform.d.ts +5 -0
  35. package/dist/editor-commands/capabilities/transform.d.ts.map +1 -0
  36. package/dist/editor-commands/capabilities/transform.js +164 -0
  37. package/dist/editor-commands/capabilities/transform.js.map +1 -0
  38. package/dist/editor-commands/capabilities/ui-regions.d.ts +4 -0
  39. package/dist/editor-commands/capabilities/ui-regions.d.ts.map +1 -0
  40. package/dist/{agent-bridge → editor-commands}/capabilities/ui-regions.js +55 -57
  41. package/dist/editor-commands/capabilities/ui-regions.js.map +1 -0
  42. package/dist/editor-commands/capabilities/viewport.d.ts +5 -0
  43. package/dist/editor-commands/capabilities/viewport.d.ts.map +1 -0
  44. package/dist/{agent-bridge → editor-commands}/capabilities/viewport.js +100 -100
  45. package/dist/editor-commands/capabilities/viewport.js.map +1 -0
  46. package/dist/editor-commands/command-sources.d.ts +7 -0
  47. package/dist/editor-commands/command-sources.d.ts.map +1 -0
  48. package/dist/editor-commands/command-sources.js +34 -0
  49. package/dist/editor-commands/command-sources.js.map +1 -0
  50. package/dist/editor-commands/diagnostics.d.ts +12 -0
  51. package/dist/editor-commands/diagnostics.d.ts.map +1 -0
  52. package/dist/{agent-bridge → editor-commands}/diagnostics.js +4 -4
  53. package/dist/editor-commands/diagnostics.js.map +1 -0
  54. package/dist/{agent-bridge → editor-commands}/index.d.ts +1 -1
  55. package/dist/editor-commands/index.d.ts.map +1 -0
  56. package/dist/{agent-bridge → editor-commands}/index.js +1 -1
  57. package/dist/editor-commands/index.js.map +1 -0
  58. package/dist/editor-commands/register.d.ts +4 -0
  59. package/dist/editor-commands/register.d.ts.map +1 -0
  60. package/dist/{agent-bridge → editor-commands}/register.js +323 -286
  61. package/dist/editor-commands/register.js.map +1 -0
  62. package/dist/{agent-bridge → editor-commands}/shared.d.ts +80 -80
  63. package/dist/editor-commands/shared.d.ts.map +1 -0
  64. package/dist/{agent-bridge → editor-commands}/shared.js +29 -29
  65. package/dist/editor-commands/shared.js.map +1 -0
  66. package/dist/editor-commands/source-types.d.ts +110 -0
  67. package/dist/editor-commands/source-types.d.ts.map +1 -0
  68. package/dist/editor-commands/source-types.js.map +1 -0
  69. package/dist/editor-plugin-commands.d.ts +4 -0
  70. package/dist/editor-plugin-commands.d.ts.map +1 -0
  71. package/dist/editor-plugin-commands.js +18 -0
  72. package/dist/editor-plugin-commands.js.map +1 -0
  73. package/dist/index.d.ts +2 -2
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +1 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/local-editor-commands.d.ts +2 -0
  78. package/dist/local-editor-commands.d.ts.map +1 -0
  79. package/dist/local-editor-commands.js +2 -0
  80. package/dist/local-editor-commands.js.map +1 -0
  81. package/dist/local-editor-harness.d.ts +35 -21
  82. package/dist/local-editor-harness.d.ts.map +1 -1
  83. package/dist/local-editor-harness.js +1721 -367
  84. package/dist/local-editor-harness.js.map +1 -1
  85. package/dist/local-editor-prefab-stage-transaction.d.ts +2 -1
  86. package/dist/local-editor-prefab-stage-transaction.d.ts.map +1 -1
  87. package/dist/local-editor-prefab-stage-transaction.js +6 -5
  88. package/dist/local-editor-prefab-stage-transaction.js.map +1 -1
  89. package/dist/local-editor-scene-view-interaction-runtime.d.ts.map +1 -1
  90. package/dist/local-editor-scene-view-interaction-runtime.js +2 -5
  91. package/dist/local-editor-scene-view-interaction-runtime.js.map +1 -1
  92. package/dist/playable-local-editor-entry.d.ts +10 -0
  93. package/dist/playable-local-editor-entry.d.ts.map +1 -1
  94. package/dist/playable-local-editor-entry.js +10 -2
  95. package/dist/playable-local-editor-entry.js.map +1 -1
  96. package/dist/playable-local-editor-host.d.ts +3 -32
  97. package/dist/playable-local-editor-host.d.ts.map +1 -1
  98. package/dist/playable-local-editor-host.js +6 -70
  99. package/dist/playable-local-editor-host.js.map +1 -1
  100. package/dist/playable-product-local-editor.d.ts +1 -2
  101. package/dist/playable-product-local-editor.d.ts.map +1 -1
  102. package/dist/playable-product-local-editor.js +0 -1
  103. package/dist/playable-product-local-editor.js.map +1 -1
  104. package/dist/playable-product-projection-preview.d.ts.map +1 -1
  105. package/dist/playable-product-projection-preview.js +1 -1
  106. package/dist/playable-product-projection-preview.js.map +1 -1
  107. package/dist/playable-product-scene-capabilities.d.ts +0 -2
  108. package/dist/playable-product-scene-capabilities.d.ts.map +1 -1
  109. package/dist/playable-product-scene-capabilities.js +33 -6
  110. package/dist/playable-product-scene-capabilities.js.map +1 -1
  111. package/dist/playable-runtime-babylon.d.ts +4 -0
  112. package/dist/playable-runtime-babylon.d.ts.map +1 -1
  113. package/dist/playable-runtime-babylon.js +4 -0
  114. package/dist/playable-runtime-babylon.js.map +1 -1
  115. package/dist/playable-sdk.d.ts +1 -1
  116. package/dist/playable-sdk.d.ts.map +1 -1
  117. package/dist/playable-sdk.js.map +1 -1
  118. package/dist/playable-standard-project.d.ts +0 -2
  119. package/dist/playable-standard-project.d.ts.map +1 -1
  120. package/dist/playable-standard-project.js +1 -2
  121. package/dist/playable-standard-project.js.map +1 -1
  122. package/dist/project-service-projection.d.ts +16 -0
  123. package/dist/project-service-projection.d.ts.map +1 -0
  124. package/dist/project-service-projection.js +67 -0
  125. package/dist/project-service-projection.js.map +1 -0
  126. package/node_modules/@fps-games/babylon-renderer/dist/post-process/babylon-default-pipeline.d.ts +18 -0
  127. package/node_modules/@fps-games/babylon-renderer/dist/post-process/babylon-default-pipeline.d.ts.map +1 -1
  128. package/node_modules/@fps-games/babylon-renderer/dist/post-process/babylon-default-pipeline.js +54 -18
  129. package/node_modules/@fps-games/babylon-renderer/dist/post-process/babylon-default-pipeline.js.map +1 -1
  130. package/node_modules/@fps-games/babylon-renderer/package.json +9 -9
  131. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +1 -0
  132. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts.map +1 -1
  133. package/node_modules/@fps-games/editor-babylon/dist/projection.js +1 -1
  134. package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
  135. package/node_modules/@fps-games/editor-babylon/dist/selection-presentation-controller.d.ts.map +1 -1
  136. package/node_modules/@fps-games/editor-babylon/dist/selection-presentation-controller.js +127 -13
  137. package/node_modules/@fps-games/editor-babylon/dist/selection-presentation-controller.js.map +1 -1
  138. package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts.map +1 -1
  139. package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js +66 -1
  140. package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js.map +1 -1
  141. package/node_modules/@fps-games/editor-babylon/package.json +6 -6
  142. package/node_modules/@fps-games/editor-browser/dist/editor-command-service-client.d.ts +32 -0
  143. package/node_modules/@fps-games/editor-browser/dist/editor-command-service-client.d.ts.map +1 -0
  144. package/node_modules/@fps-games/editor-browser/dist/editor-command-service-client.js +254 -0
  145. package/node_modules/@fps-games/editor-browser/dist/editor-command-service-client.js.map +1 -0
  146. package/node_modules/@fps-games/editor-browser/dist/index.d.ts +3 -1
  147. package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
  148. package/node_modules/@fps-games/editor-browser/dist/index.js +2 -0
  149. package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
  150. package/node_modules/@fps-games/editor-browser/dist/local-editor-command-center.d.ts +68 -0
  151. package/node_modules/@fps-games/editor-browser/dist/local-editor-command-center.d.ts.map +1 -0
  152. package/node_modules/@fps-games/editor-browser/dist/local-editor-command-center.js +650 -0
  153. package/node_modules/@fps-games/editor-browser/dist/local-editor-command-center.js.map +1 -0
  154. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-formal-surface-visibility.d.ts +1 -0
  155. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-formal-surface-visibility.d.ts.map +1 -1
  156. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-formal-surface-visibility.js +22 -0
  157. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-formal-surface-visibility.js.map +1 -1
  158. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +8 -0
  159. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -1
  160. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +117 -6
  161. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -1
  162. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts +14 -1
  163. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts.map +1 -1
  164. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js +100 -1
  165. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js.map +1 -1
  166. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts +8 -1
  167. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
  168. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +75 -2
  169. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
  170. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +37 -12
  171. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
  172. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.js.map +1 -1
  173. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +25 -2
  174. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
  175. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +332 -107
  176. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
  177. package/node_modules/@fps-games/editor-browser/package.json +7 -7
  178. package/node_modules/@fps-games/editor-core/dist/editor-authoring-host.d.ts +107 -0
  179. package/node_modules/@fps-games/editor-core/dist/editor-authoring-host.d.ts.map +1 -0
  180. package/node_modules/@fps-games/editor-core/dist/editor-authoring-host.js +1211 -0
  181. package/node_modules/@fps-games/editor-core/dist/editor-authoring-host.js.map +1 -0
  182. package/node_modules/@fps-games/editor-core/dist/editor-command-capability-adapter.d.ts +34 -0
  183. package/node_modules/@fps-games/editor-core/dist/editor-command-capability-adapter.d.ts.map +1 -0
  184. package/node_modules/@fps-games/editor-core/dist/editor-command-capability-adapter.js +260 -0
  185. package/node_modules/@fps-games/editor-core/dist/editor-command-capability-adapter.js.map +1 -0
  186. package/node_modules/@fps-games/editor-core/dist/editor-command-composed-authority.d.ts +9 -0
  187. package/node_modules/@fps-games/editor-core/dist/editor-command-composed-authority.d.ts.map +1 -0
  188. package/node_modules/@fps-games/editor-core/dist/editor-command-composed-authority.js +14 -0
  189. package/node_modules/@fps-games/editor-core/dist/editor-command-composed-authority.js.map +1 -0
  190. package/node_modules/@fps-games/editor-core/dist/editor-command-observability.d.ts +18 -0
  191. package/node_modules/@fps-games/editor-core/dist/editor-command-observability.d.ts.map +1 -0
  192. package/node_modules/@fps-games/editor-core/dist/editor-command-observability.js +113 -0
  193. package/node_modules/@fps-games/editor-core/dist/editor-command-observability.js.map +1 -0
  194. package/node_modules/@fps-games/editor-core/dist/editor-command-system.d.ts +82 -0
  195. package/node_modules/@fps-games/editor-core/dist/editor-command-system.d.ts.map +1 -0
  196. package/node_modules/@fps-games/editor-core/dist/editor-command-system.js +1098 -0
  197. package/node_modules/@fps-games/editor-core/dist/editor-command-system.js.map +1 -0
  198. package/node_modules/@fps-games/editor-core/dist/editor-plugin-host.d.ts +6 -7
  199. package/node_modules/@fps-games/editor-core/dist/editor-plugin-host.d.ts.map +1 -1
  200. package/node_modules/@fps-games/editor-core/dist/editor-plugin-host.js +62 -103
  201. package/node_modules/@fps-games/editor-core/dist/editor-plugin-host.js.map +1 -1
  202. package/node_modules/@fps-games/editor-core/dist/editor-service-gateway.d.ts +30 -0
  203. package/node_modules/@fps-games/editor-core/dist/editor-service-gateway.d.ts.map +1 -0
  204. package/node_modules/@fps-games/editor-core/dist/editor-service-gateway.js +540 -0
  205. package/node_modules/@fps-games/editor-core/dist/editor-service-gateway.js.map +1 -0
  206. package/node_modules/@fps-games/editor-core/dist/editor-session-authority.d.ts +9 -0
  207. package/node_modules/@fps-games/editor-core/dist/editor-session-authority.d.ts.map +1 -0
  208. package/node_modules/@fps-games/editor-core/dist/editor-session-authority.js +61 -0
  209. package/node_modules/@fps-games/editor-core/dist/editor-session-authority.js.map +1 -0
  210. package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts +55 -0
  211. package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts.map +1 -1
  212. package/node_modules/@fps-games/editor-core/dist/editor-session.js +247 -6
  213. package/node_modules/@fps-games/editor-core/dist/editor-session.js.map +1 -1
  214. package/node_modules/@fps-games/editor-core/dist/index.d.ts +6 -2
  215. package/node_modules/@fps-games/editor-core/dist/index.d.ts.map +1 -1
  216. package/node_modules/@fps-games/editor-core/dist/index.js +6 -2
  217. package/node_modules/@fps-games/editor-core/dist/index.js.map +1 -1
  218. package/node_modules/@fps-games/editor-core/package.json +6 -6
  219. package/node_modules/@fps-games/editor-forge-play/dist/index.d.ts +0 -1
  220. package/node_modules/@fps-games/editor-forge-play/dist/index.d.ts.map +1 -1
  221. package/node_modules/@fps-games/editor-forge-play/dist/index.js +0 -1
  222. package/node_modules/@fps-games/editor-forge-play/dist/index.js.map +1 -1
  223. package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
  224. package/node_modules/@fps-games/editor-playable-sdk/dist/bridge-command/protocol.d.ts +2 -0
  225. package/node_modules/@fps-games/editor-playable-sdk/dist/bridge-command/protocol.d.ts.map +1 -1
  226. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.d.ts.map +1 -1
  227. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js +41 -1
  228. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js.map +1 -1
  229. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play-types.d.ts +27 -1
  230. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play-types.d.ts.map +1 -1
  231. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play-types.js.map +1 -1
  232. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play.d.ts +2 -0
  233. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play.d.ts.map +1 -1
  234. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play.js +214 -7
  235. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/forge-play.js.map +1 -1
  236. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/host-controller.d.ts +1 -0
  237. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/host-controller.d.ts.map +1 -1
  238. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/host-controller.js +11 -3
  239. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/host-controller.js.map +1 -1
  240. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/index.d.ts +2 -4
  241. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/index.d.ts.map +1 -1
  242. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/index.js +0 -1
  243. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/index.js.map +1 -1
  244. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.d.ts.map +1 -1
  245. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js +77 -31
  246. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js.map +1 -1
  247. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/types.d.ts +8 -1
  248. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/types.d.ts.map +1 -1
  249. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/types.js.map +1 -1
  250. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/asset-fields.d.ts +21 -1
  251. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/asset-fields.d.ts.map +1 -1
  252. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/asset-fields.js +90 -4
  253. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/asset-fields.js.map +1 -1
  254. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.d.ts +27 -1
  255. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.d.ts.map +1 -1
  256. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js +161 -1
  257. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js.map +1 -1
  258. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/inspector.d.ts +2 -2
  259. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/inspector.d.ts.map +1 -1
  260. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/inspector.js +5 -92
  261. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/inspector.js.map +1 -1
  262. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/scene-inspector.d.ts +7 -0
  263. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/scene-inspector.d.ts.map +1 -1
  264. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/scene-inspector.js +26 -9
  265. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/scene-inspector.js.map +1 -1
  266. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/slot-inspector.d.ts +9 -0
  267. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/slot-inspector.d.ts.map +1 -1
  268. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/slot-inspector.js +19 -8
  269. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/slot-inspector.js.map +1 -1
  270. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/material-override-inspector.d.ts +5 -0
  271. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/material-override-inspector.d.ts.map +1 -1
  272. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/material-override-inspector.js +16 -4
  273. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/material-override-inspector.js.map +1 -1
  274. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/stage-inspector.d.ts +9 -0
  275. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/stage-inspector.d.ts.map +1 -1
  276. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/stage-inspector.js +12 -2
  277. package/node_modules/@fps-games/editor-playable-sdk/dist/prefab-stage/stage-inspector.js.map +1 -1
  278. package/node_modules/@fps-games/editor-playable-sdk/dist/product/ground-decal.d.ts +2 -1
  279. package/node_modules/@fps-games/editor-playable-sdk/dist/product/ground-decal.d.ts.map +1 -1
  280. package/node_modules/@fps-games/editor-playable-sdk/dist/product/ground-decal.js +10 -5
  281. package/node_modules/@fps-games/editor-playable-sdk/dist/product/ground-decal.js.map +1 -1
  282. package/node_modules/@fps-games/editor-playable-sdk/dist/product/index.d.ts +1 -0
  283. package/node_modules/@fps-games/editor-playable-sdk/dist/product/index.d.ts.map +1 -1
  284. package/node_modules/@fps-games/editor-playable-sdk/dist/product/index.js +1 -0
  285. package/node_modules/@fps-games/editor-playable-sdk/dist/product/index.js.map +1 -1
  286. package/node_modules/@fps-games/editor-playable-sdk/dist/product/project-host-services.d.ts +17 -82
  287. package/node_modules/@fps-games/editor-playable-sdk/dist/product/project-host-services.d.ts.map +1 -1
  288. package/node_modules/@fps-games/editor-playable-sdk/dist/product/project-host-services.js +53 -57
  289. package/node_modules/@fps-games/editor-playable-sdk/dist/product/project-host-services.js.map +1 -1
  290. package/node_modules/@fps-games/editor-playable-sdk/dist/product/scene-session.d.ts.map +1 -1
  291. package/node_modules/@fps-games/editor-playable-sdk/dist/product/scene-session.js +3 -2
  292. package/node_modules/@fps-games/editor-playable-sdk/dist/product/scene-session.js.map +1 -1
  293. package/node_modules/@fps-games/editor-playable-sdk/dist/product/standard-authoring-commands.d.ts +10 -0
  294. package/node_modules/@fps-games/editor-playable-sdk/dist/product/standard-authoring-commands.d.ts.map +1 -0
  295. package/node_modules/@fps-games/editor-playable-sdk/dist/product/standard-authoring-commands.js +256 -0
  296. package/node_modules/@fps-games/editor-playable-sdk/dist/product/standard-authoring-commands.js.map +1 -0
  297. package/node_modules/@fps-games/editor-playable-sdk/dist/product/standard-scene-assembly.d.ts.map +1 -1
  298. package/node_modules/@fps-games/editor-playable-sdk/dist/product/standard-scene-assembly.js +7 -5
  299. package/node_modules/@fps-games/editor-playable-sdk/dist/product/standard-scene-assembly.js.map +1 -1
  300. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/client.d.ts.map +1 -1
  301. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/client.js +8 -0
  302. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/client.js.map +1 -1
  303. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/types.d.ts +1 -0
  304. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/types.d.ts.map +1 -1
  305. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/types.js.map +1 -1
  306. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/inspectors.js +134 -12
  307. package/node_modules/@fps-games/editor-playable-sdk/dist/upgrade-doctor/inspectors.js.map +1 -1
  308. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/editor-scene-workflow.d.ts.map +1 -1
  309. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/editor-scene-workflow.js +47 -0
  310. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/editor-scene-workflow.js.map +1 -1
  311. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/editor-service-vite.d.ts +27 -0
  312. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/editor-service-vite.d.ts.map +1 -0
  313. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/editor-service-vite.js +592 -0
  314. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/editor-service-vite.js.map +1 -0
  315. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts +3 -1
  316. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts.map +1 -1
  317. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js +2 -1
  318. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js.map +1 -1
  319. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/plugin.d.ts.map +1 -1
  320. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/plugin.js +61 -6
  321. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/plugin.js.map +1 -1
  322. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-adapter.d.ts +0 -2
  323. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-adapter.d.ts.map +1 -1
  324. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-adapter.js +1 -10
  325. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-adapter.js.map +1 -1
  326. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-authoring-api.d.ts.map +1 -1
  327. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-authoring-api.js +21 -0
  328. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-authoring-api.js.map +1 -1
  329. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-authoring-backend.d.ts +13 -0
  330. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-authoring-backend.d.ts.map +1 -0
  331. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-authoring-backend.js +18 -0
  332. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-authoring-backend.js.map +1 -0
  333. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-editor-service.d.ts +28 -0
  334. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-editor-service.d.ts.map +1 -0
  335. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-editor-service.js +863 -0
  336. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-editor-service.js.map +1 -0
  337. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/types.d.ts +2 -0
  338. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/types.d.ts.map +1 -1
  339. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/types.js.map +1 -1
  340. package/node_modules/@fps-games/editor-playable-sdk/package.json +6 -6
  341. package/node_modules/@fps-games/editor-protocol/dist/editor-command-capabilities.d.ts +1626 -0
  342. package/node_modules/@fps-games/editor-protocol/dist/editor-command-capabilities.d.ts.map +1 -0
  343. package/node_modules/@fps-games/editor-protocol/dist/{agent-bridge.js → editor-command-capabilities.js} +766 -620
  344. package/node_modules/@fps-games/editor-protocol/dist/editor-command-capabilities.js.map +1 -0
  345. package/node_modules/@fps-games/editor-protocol/dist/editor-command-trace.d.ts +69 -0
  346. package/node_modules/@fps-games/editor-protocol/dist/editor-command-trace.d.ts.map +1 -0
  347. package/node_modules/@fps-games/editor-protocol/dist/editor-command-trace.js +22 -0
  348. package/node_modules/@fps-games/editor-protocol/dist/editor-command-trace.js.map +1 -0
  349. package/node_modules/@fps-games/editor-protocol/dist/editor-command.d.ts +136 -0
  350. package/node_modules/@fps-games/editor-protocol/dist/editor-command.d.ts.map +1 -0
  351. package/node_modules/@fps-games/editor-protocol/dist/editor-command.js +2 -0
  352. package/node_modules/@fps-games/editor-protocol/dist/editor-command.js.map +1 -0
  353. package/node_modules/@fps-games/editor-protocol/dist/editor-service.d.ts +225 -0
  354. package/node_modules/@fps-games/editor-protocol/dist/editor-service.d.ts.map +1 -0
  355. package/node_modules/@fps-games/editor-protocol/dist/editor-service.js +4 -0
  356. package/node_modules/@fps-games/editor-protocol/dist/editor-service.js.map +1 -0
  357. package/node_modules/@fps-games/editor-protocol/dist/index.d.ts +6 -3
  358. package/node_modules/@fps-games/editor-protocol/dist/index.d.ts.map +1 -1
  359. package/node_modules/@fps-games/editor-protocol/dist/index.js +6 -3
  360. package/node_modules/@fps-games/editor-protocol/dist/index.js.map +1 -1
  361. package/node_modules/@fps-games/editor-protocol/package.json +2 -2
  362. package/node_modules/@fps-games/editor-tooling/package.json +3 -3
  363. package/node_modules/@fps-games/editor-ui/package.json +1 -1
  364. package/node_modules/@fps-games/playable-assets/package.json +1 -1
  365. package/node_modules/@fps-games/playable-contracts/package.json +1 -1
  366. package/node_modules/@fps-games/playable-rendering/package.json +2 -2
  367. package/node_modules/@fps-games/playable-runtime/package.json +12 -12
  368. package/node_modules/@fps-games/playable-runtime-data/package.json +4 -4
  369. package/node_modules/@fps-games/playable-scene/package.json +2 -2
  370. package/node_modules/@fps-games/plugin-api/package.json +1 -1
  371. package/node_modules/@fps-games/plugin-assets/dist/version.d.ts +1 -1
  372. package/node_modules/@fps-games/plugin-assets/dist/version.js +1 -1
  373. package/node_modules/@fps-games/plugin-assets/dist/version.js.map +1 -1
  374. package/node_modules/@fps-games/plugin-assets/package.json +6 -6
  375. package/node_modules/@fps-games/plugin-hierarchy/dist/kernel.d.ts +1 -0
  376. package/node_modules/@fps-games/plugin-hierarchy/dist/kernel.d.ts.map +1 -1
  377. package/node_modules/@fps-games/plugin-hierarchy/dist/kernel.js +78 -4
  378. package/node_modules/@fps-games/plugin-hierarchy/dist/kernel.js.map +1 -1
  379. package/node_modules/@fps-games/plugin-hierarchy/dist/version.d.ts +1 -1
  380. package/node_modules/@fps-games/plugin-hierarchy/dist/version.js +1 -1
  381. package/node_modules/@fps-games/plugin-hierarchy/dist/version.js.map +1 -1
  382. package/node_modules/@fps-games/plugin-hierarchy/package.json +5 -5
  383. package/node_modules/@fps-games/plugin-host/package.json +2 -2
  384. package/node_modules/@fps-games/plugin-markers/dist/version.d.ts +1 -1
  385. package/node_modules/@fps-games/plugin-markers/dist/version.js +1 -1
  386. package/node_modules/@fps-games/plugin-markers/dist/version.js.map +1 -1
  387. package/node_modules/@fps-games/plugin-markers/package.json +5 -5
  388. package/node_modules/@fps-games/plugin-materials/dist/version.d.ts +1 -1
  389. package/node_modules/@fps-games/plugin-materials/dist/version.js +1 -1
  390. package/node_modules/@fps-games/plugin-materials/dist/version.js.map +1 -1
  391. package/node_modules/@fps-games/plugin-materials/package.json +5 -5
  392. package/node_modules/@fps-games/plugin-rendering/dist/version.d.ts +1 -1
  393. package/node_modules/@fps-games/plugin-rendering/dist/version.js +1 -1
  394. package/node_modules/@fps-games/plugin-rendering/dist/version.js.map +1 -1
  395. package/node_modules/@fps-games/plugin-rendering/package.json +5 -5
  396. package/node_modules/@fps-games/plugin-scene/dist/version.d.ts +1 -1
  397. package/node_modules/@fps-games/plugin-scene/dist/version.js +1 -1
  398. package/node_modules/@fps-games/plugin-scene/dist/version.js.map +1 -1
  399. package/node_modules/@fps-games/plugin-scene/package.json +5 -5
  400. package/node_modules/@fps-games/plugin-shadow-map-experiment/dist/version.d.ts +1 -1
  401. package/node_modules/@fps-games/plugin-shadow-map-experiment/dist/version.js +1 -1
  402. package/node_modules/@fps-games/plugin-shadow-map-experiment/dist/version.js.map +1 -1
  403. package/node_modules/@fps-games/plugin-shadow-map-experiment/package.json +5 -5
  404. package/node_modules/@fps-games/plugin-shadows/dist/version.d.ts +1 -1
  405. package/node_modules/@fps-games/plugin-shadows/dist/version.js +1 -1
  406. package/node_modules/@fps-games/plugin-shadows/dist/version.js.map +1 -1
  407. package/node_modules/@fps-games/plugin-shadows/package.json +7 -7
  408. package/package.json +23 -23
  409. package/dist/agent-bridge/capabilities/assets.d.ts +0 -8
  410. package/dist/agent-bridge/capabilities/assets.d.ts.map +0 -1
  411. package/dist/agent-bridge/capabilities/assets.js.map +0 -1
  412. package/dist/agent-bridge/capabilities/editor-state.d.ts +0 -5
  413. package/dist/agent-bridge/capabilities/editor-state.d.ts.map +0 -1
  414. package/dist/agent-bridge/capabilities/editor-state.js.map +0 -1
  415. package/dist/agent-bridge/capabilities/hierarchy.d.ts +0 -4
  416. package/dist/agent-bridge/capabilities/hierarchy.d.ts.map +0 -1
  417. package/dist/agent-bridge/capabilities/hierarchy.js.map +0 -1
  418. package/dist/agent-bridge/capabilities/host-lifecycle.d.ts +0 -11
  419. package/dist/agent-bridge/capabilities/host-lifecycle.d.ts.map +0 -1
  420. package/dist/agent-bridge/capabilities/host-lifecycle.js +0 -149
  421. package/dist/agent-bridge/capabilities/host-lifecycle.js.map +0 -1
  422. package/dist/agent-bridge/capabilities/prefabs.d.ts +0 -5
  423. package/dist/agent-bridge/capabilities/prefabs.d.ts.map +0 -1
  424. package/dist/agent-bridge/capabilities/prefabs.js.map +0 -1
  425. package/dist/agent-bridge/capabilities/scene-objects.d.ts +0 -8
  426. package/dist/agent-bridge/capabilities/scene-objects.d.ts.map +0 -1
  427. package/dist/agent-bridge/capabilities/scene-objects.js +0 -480
  428. package/dist/agent-bridge/capabilities/scene-objects.js.map +0 -1
  429. package/dist/agent-bridge/capabilities/selection.d.ts +0 -5
  430. package/dist/agent-bridge/capabilities/selection.d.ts.map +0 -1
  431. package/dist/agent-bridge/capabilities/selection.js +0 -60
  432. package/dist/agent-bridge/capabilities/selection.js.map +0 -1
  433. package/dist/agent-bridge/capabilities/shadows.d.ts +0 -5
  434. package/dist/agent-bridge/capabilities/shadows.d.ts.map +0 -1
  435. package/dist/agent-bridge/capabilities/shadows.js.map +0 -1
  436. package/dist/agent-bridge/capabilities/transform.d.ts +0 -5
  437. package/dist/agent-bridge/capabilities/transform.d.ts.map +0 -1
  438. package/dist/agent-bridge/capabilities/transform.js +0 -164
  439. package/dist/agent-bridge/capabilities/transform.js.map +0 -1
  440. package/dist/agent-bridge/capabilities/ui-regions.d.ts +0 -4
  441. package/dist/agent-bridge/capabilities/ui-regions.d.ts.map +0 -1
  442. package/dist/agent-bridge/capabilities/ui-regions.js.map +0 -1
  443. package/dist/agent-bridge/capabilities/viewport.d.ts +0 -5
  444. package/dist/agent-bridge/capabilities/viewport.d.ts.map +0 -1
  445. package/dist/agent-bridge/capabilities/viewport.js.map +0 -1
  446. package/dist/agent-bridge/capability-sources.d.ts +0 -7
  447. package/dist/agent-bridge/capability-sources.d.ts.map +0 -1
  448. package/dist/agent-bridge/capability-sources.js +0 -34
  449. package/dist/agent-bridge/capability-sources.js.map +0 -1
  450. package/dist/agent-bridge/diagnostics.d.ts +0 -12
  451. package/dist/agent-bridge/diagnostics.d.ts.map +0 -1
  452. package/dist/agent-bridge/diagnostics.js.map +0 -1
  453. package/dist/agent-bridge/index.d.ts.map +0 -1
  454. package/dist/agent-bridge/index.js.map +0 -1
  455. package/dist/agent-bridge/register.d.ts +0 -4
  456. package/dist/agent-bridge/register.d.ts.map +0 -1
  457. package/dist/agent-bridge/register.js.map +0 -1
  458. package/dist/agent-bridge/shared.d.ts.map +0 -1
  459. package/dist/agent-bridge/shared.js.map +0 -1
  460. package/dist/agent-bridge/source-types.d.ts +0 -110
  461. package/dist/agent-bridge/source-types.d.ts.map +0 -1
  462. package/dist/agent-bridge/source-types.js.map +0 -1
  463. package/dist/editor-plugin-agent-capabilities.d.ts +0 -4
  464. package/dist/editor-plugin-agent-capabilities.d.ts.map +0 -1
  465. package/dist/editor-plugin-agent-capabilities.js +0 -64
  466. package/dist/editor-plugin-agent-capabilities.js.map +0 -1
  467. package/dist/local-editor-agent-actions.d.ts +0 -43
  468. package/dist/local-editor-agent-actions.d.ts.map +0 -1
  469. package/dist/local-editor-agent-actions.js +0 -321
  470. package/dist/local-editor-agent-actions.js.map +0 -1
  471. package/dist/local-editor-agent-bridge-transport.d.ts +0 -129
  472. package/dist/local-editor-agent-bridge-transport.d.ts.map +0 -1
  473. package/dist/local-editor-agent-bridge-transport.js +0 -403
  474. package/dist/local-editor-agent-bridge-transport.js.map +0 -1
  475. package/dist/local-editor-agent-bridge.d.ts +0 -2
  476. package/dist/local-editor-agent-bridge.d.ts.map +0 -1
  477. package/dist/local-editor-agent-bridge.js +0 -2
  478. package/dist/local-editor-agent-bridge.js.map +0 -1
  479. package/dist/local-editor-agent-context.d.ts +0 -68
  480. package/dist/local-editor-agent-context.d.ts.map +0 -1
  481. package/dist/local-editor-agent-context.js +0 -716
  482. package/dist/local-editor-agent-context.js.map +0 -1
  483. package/node_modules/@fps-games/editor-core/dist/agent-bridge.d.ts +0 -22
  484. package/node_modules/@fps-games/editor-core/dist/agent-bridge.d.ts.map +0 -1
  485. package/node_modules/@fps-games/editor-core/dist/agent-bridge.js +0 -106
  486. package/node_modules/@fps-games/editor-core/dist/agent-bridge.js.map +0 -1
  487. package/node_modules/@fps-games/editor-core/dist/agent-interaction.d.ts +0 -17
  488. package/node_modules/@fps-games/editor-core/dist/agent-interaction.d.ts.map +0 -1
  489. package/node_modules/@fps-games/editor-core/dist/agent-interaction.js +0 -14
  490. package/node_modules/@fps-games/editor-core/dist/agent-interaction.js.map +0 -1
  491. package/node_modules/@fps-games/editor-forge-play/dist/agent-transport.d.ts +0 -23
  492. package/node_modules/@fps-games/editor-forge-play/dist/agent-transport.d.ts.map +0 -1
  493. package/node_modules/@fps-games/editor-forge-play/dist/agent-transport.js +0 -47
  494. package/node_modules/@fps-games/editor-forge-play/dist/agent-transport.js.map +0 -1
  495. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/agent-descriptions.d.ts +0 -112
  496. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/agent-descriptions.d.ts.map +0 -1
  497. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/agent-descriptions.js +0 -159
  498. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/agent-descriptions.js.map +0 -1
  499. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/session-metadata.d.ts +0 -15
  500. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/session-metadata.d.ts.map +0 -1
  501. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/session-metadata.js +0 -76
  502. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/session-metadata.js.map +0 -1
  503. package/node_modules/@fps-games/editor-protocol/dist/agent-bridge.d.ts +0 -1622
  504. package/node_modules/@fps-games/editor-protocol/dist/agent-bridge.d.ts.map +0 -1
  505. package/node_modules/@fps-games/editor-protocol/dist/agent-bridge.js.map +0 -1
  506. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.d.ts +0 -68
  507. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.d.ts.map +0 -1
  508. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.js +0 -23
  509. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.js.map +0 -1
  510. package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts +0 -187
  511. package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts.map +0 -1
  512. package/node_modules/@fps-games/editor-protocol/dist/agent.js +0 -5
  513. package/node_modules/@fps-games/editor-protocol/dist/agent.js.map +0 -1
  514. /package/dist/{agent-bridge → editor-commands}/source-types.js +0 -0
@@ -1,8 +1,9 @@
1
- import { createEditorSession, FPS_EDITOR_ACTIVE_TOOL_CHANGED, FPS_EDITOR_DOCUMENT_CHANGED, FPS_EDITOR_HISTORY_CHANGED, FPS_EDITOR_SELECTION_CHANGED, createEditorAgentBridge, createInspectorRegistry, DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, DEFAULT_EDITOR_VIEWPORT_TOOL_STATE, createEmptyEditorViewportSpatialOverlayState, cloneEditorTransformSnapshot as cloneCoreEditorTransformSnapshot, composeEditorTransformMatrix, editorTransformMatricesAlmostEqual, cloneEditorViewportToolState, cloneEditorViewportSpatialOverlayState, aggregateInspectorObjectsToMultiObject, createInspectorEditPayload, compareInspectorSections, computeEditorTransformActionTargets, mergeInspectorSections, isEditorTransformTrsSnapshot, normalizeEditorTransformConstraint, readEditorTransformPosition, resolveEditorSelectionCommand, validateSceneGraphDelete, validateSceneGraphDrop, validateSceneGraphGroupSelection, validateSceneGraphMove, validateSceneGraphRename, } from '@fps-games/editor-core';
1
+ import { createEditorAuthoringHost, createEditorSession, FPS_EDITOR_ACTIVE_TOOL_CHANGED, FPS_EDITOR_DOCUMENT_CHANGED, FPS_EDITOR_HISTORY_CHANGED, FPS_EDITOR_SELECTION_CHANGED, createEditorCommandCapabilityAdapter, createEditorCommandSystem, createInspectorRegistry, DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, DEFAULT_EDITOR_VIEWPORT_TOOL_STATE, createEmptyEditorViewportSpatialOverlayState, callWithEditorAuthoringSerializedAccess, createEditorAuthoringSerializedSessionView, cloneEditorTransformSnapshot as cloneCoreEditorTransformSnapshot, composeEditorTransformMatrix, editorTransformMatricesAlmostEqual, cloneEditorViewportToolState, cloneEditorViewportSpatialOverlayState, aggregateInspectorObjectsToMultiObject, createInspectorEditPayload, compareInspectorSections, computeEditorTransformActionTargets, mergeInspectorSections, isEditorTransformTrsSnapshot, normalizeEditorTransformConstraint, readEditorTransformPosition, resolveEditorSelectionCommand, validateSceneGraphDelete, validateSceneGraphDrop, validateSceneGraphGroupSelection, validateSceneGraphMove, validateSceneGraphRename, } from '@fps-games/editor-core';
2
2
  import { createEditorToolManager, createEditorToolRegistry, resolveEditingContextProvider, } from '@fps-games/editor-core/tools';
3
3
  import { createEditorDiagnosticStore, normalizeEditorDiagnostic, normalizeEditorDiagnosticDetails, } from '@fps-games/editor-core/diagnostics';
4
- import {} from '@fps-games/editor-protocol';
5
- import { createLocalEditorBrowserUi, resolveLocalEditorBrowserAssetKind, resolveLocalEditorStatusDiagnosticLocator, toTransformOperationPolicyStatusLabel, } from '@fps-games/editor-browser';
4
+ import { EDITOR_COMMAND_ID, } from '@fps-games/editor-protocol';
5
+ import { createLocalEditorBrowserUi, createLocalEditorCommandServiceClient, resolveLocalEditorBrowserAssetKind, resolveLocalEditorStatusDiagnosticLocator, toTransformOperationPolicyStatusLabel, } from '@fps-games/editor-browser';
6
+ import { classifyProjectServiceSnapshot, createProjectServiceProjectionSessionAuthority, shouldReplacePendingProjectServiceSnapshot, waitForProjectServiceOperation, } from './project-service-projection.js';
6
7
  import { mountLocalEditorProjectModelPreview, resolveLocalEditorProjectModelThumbnail, } from './internal/project-model-preview.js';
7
8
  import { mountLocalEditorMaterialPreview } from './internal/material-preview.js';
8
9
  import { bindFpsEditorTools, } from '@fps-games/editor-core/plugin-host';
@@ -17,14 +18,12 @@ import { createPlayableBabylonOutlineInspectorSection, createPlayableBabylonOutl
17
18
  import { createLocalEditorViewportRenderCoordinator, } from './local-editor-viewport-render-coordinator.js';
18
19
  import { createLocalEditorSceneViewInteractionRuntime, } from './local-editor-scene-view-interaction-runtime.js';
19
20
  import { cloneLocalEditorPerformanceStats, countLocalEditorAuthoredObjects, createLocalEditorPerformanceStats, LOCAL_EDITOR_PERFORMANCE_MONITOR_REASON, } from './local-editor-performance-stats.js';
20
- import { registerLocalEditorAgentBridgeCapabilities } from './local-editor-agent-bridge.js';
21
- import { registerEditorPluginAgentCapabilities } from './editor-plugin-agent-capabilities.js';
21
+ import { registerLocalEditorCommandCapabilities } from './local-editor-commands.js';
22
+ import { registerEditorPluginCommands } from './editor-plugin-commands.js';
22
23
  import { createLocalEditorStandardToolRegistrations, LOCAL_EDITOR_SELECTION_CAPABILITY_ID, LOCAL_EDITOR_STANDARD_TOOL_IDS, LOCAL_EDITOR_TRANSFORM_CAPABILITY_ID, resolveLocalEditorTransformTool, } from './local-editor-tools.js';
23
24
  import { createHierarchyScopeSnapshot, createHierarchyTreeSnapshot, resolveHierarchyScopeProviders, resolveHierarchySurfaceContribution, } from '@fps-games/plugin-hierarchy';
24
- import { createLocalEditorAgentBridgeCapabilitySources, } from './agent-bridge/capability-sources.js';
25
- import { createDisabledLocalEditorAgentBridgeTransportStatus, createLocalEditorAgentBridgeWebSocketTransportFromQuery, } from './local-editor-agent-bridge-transport.js';
25
+ import { createLocalEditorCommandCapabilitySources, } from './editor-commands/command-sources.js';
26
26
  import { PLAYABLE_LOCAL_EDITOR_APPLICATION_SCOPE_ID, PLAYABLE_LOCAL_EDITOR_MAIN_DOCUMENT_SCOPE_ID, PLAYABLE_LOCAL_EDITOR_MAIN_PREVIEW_SCOPE_ID, PLAYABLE_LOCAL_EDITOR_MAIN_SESSION_SCOPE_ID, PLAYABLE_LOCAL_EDITOR_PROJECT_SCOPE_ID, } from '@fps-games/editor-playable-sdk/internal/local-harness';
27
- import { FPS_EDITOR_LIFECYCLE_TRANSITION_TYPES } from '@fps-games/editor-core/lifecycle';
28
27
  import { createMarkerGraphHierarchyItems, createMarkerGraphInspectorObject, createMarkerGraphRootInspectorObject, createMarkerPanelState, createMarkerRelationCommand, createMarkerRelationCreateManyCommand, createMarkerRelationDeleteConnectedCommand, createMarkerRelationReverseCommand, createMarkerGraphPatchFromInspectorEdit, findSpatialMarkerById, getSpatialMarkerDirectRelations, isMarkerGraphHierarchyId, isMarkerGraphHierarchyRootId, isMarkerGraphHierarchyRootRowId, isSpatialMarkerBackedByHierarchyRow, isSpatialMarkerHierarchyVisible, readMarkerGraphHierarchyMarkerIdForGraph, readSpatialMarkerIdFromHierarchySelection, } from './local-editor-marker-graph.js';
29
28
  const BATCH_EDIT_REQUIRES_MULTI_PATCH_REASON = 'Batch edit requires multi-property patch support.';
30
29
  const LARGE_SELECTION_THRESHOLD = 200;
@@ -32,6 +31,7 @@ const LARGE_SHADOW_METADATA_BATCH_THRESHOLD = 200;
32
31
  const EDITOR_PROJECTION_VALIDATION_TIMEOUT_MS = 10000;
33
32
  const EDITOR_WORLD_DISPOSE_BOUNDARY_DELAY_MS = 500;
34
33
  const RENDERING_ACTION_PROGRESS_MIN_VISIBLE_MS = 1000;
34
+ const SERIALIZED_HIERARCHY_INVOCATION = Symbol('fps.editor.serialized-hierarchy-invocation');
35
35
  const PREFAB_STAGE_CONTEXT_ACTION_IDS = Object.freeze({
36
36
  save: 'fps.editor.context.prefab.save',
37
37
  discard: 'fps.editor.context.prefab.discard',
@@ -264,22 +264,36 @@ export function createLocalEditorHarness(options) {
264
264
  }
265
265
  function createLocalEditorHarnessImplementation(options, construction) {
266
266
  const root = options.root ?? document.body;
267
+ const projectCommandService = createLocalEditorCommandServiceClient();
268
+ const browserCommandAttachmentId = `browser-command-host-${createLocalEditorAuthoringServiceInstanceId()}`;
269
+ let disposeBrowserCommandAttachment = null;
270
+ let projectedServiceInstanceId = null;
271
+ let projectedServiceRevision = null;
272
+ let projectedBrowserDocumentFingerprint = null;
273
+ let pendingProjectServiceSnapshot = null;
274
+ let stopProjectServiceWatch = null;
267
275
  let lifecycleController = null;
268
276
  let releaseApplicationAuthorityScopeResource = null;
269
277
  let releaseLifecycleRenderScopeResource = null;
270
278
  let unsubscribeLifecycleRender = null;
271
- let unregisterAgentBridgeLifecycleParticipant = null;
272
- const agentBridgeLifecycleTransports = new Map();
279
+ let suppressedDocumentMarkDirtyTransitionId = null;
280
+ let lifecyclePresentationOnlyRenderDepth = 0;
273
281
  let releaseMainSessionScopeResource = null;
274
282
  let releaseMainPreviewScopeResource = null;
275
283
  let pendingScheduledLifecycleExit = null;
276
284
  let harnessDisposed = false;
285
+ const commandServiceCommandSystem = createEditorCommandSystem();
286
+ const projectionSessionAuthority = createProjectServiceProjectionSessionAuthority();
287
+ const replaceProjectionSessionAuthority = (session) => projectionSessionAuthority.replace(session);
288
+ const runProjectionSessionMutation = (session, mutation) => projectionSessionAuthority.run(session, mutation);
277
289
  const state = {
278
290
  get mode() {
291
+ if (state.session)
292
+ return 'editor';
279
293
  const lifecycleMode = lifecycleController?.lifecycle.getSnapshot().mode;
280
294
  if (lifecycleMode)
281
295
  return lifecycleMode === 'edit' ? 'editor' : 'game';
282
- return state.world ? 'editor' : 'game';
296
+ return 'game';
283
297
  },
284
298
  get lifecycle() {
285
299
  return lifecycleController?.lifecycle ?? null;
@@ -294,6 +308,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
294
308
  hierarchyItemsCacheSession: null,
295
309
  hierarchyItemsCacheRevision: -1,
296
310
  hierarchyItemsCache: Object.freeze([]),
311
+ hierarchyPresentationFallbackRevision: 0,
297
312
  diagnosticStore: createEditorDiagnosticStore(),
298
313
  diagnosticPhase: 'edit',
299
314
  reuseDiagnosticSnapshotOnNextRender: false,
@@ -303,6 +318,17 @@ function createLocalEditorHarnessImplementation(options, construction) {
303
318
  busy: false,
304
319
  documentOperationActive: false,
305
320
  session: null,
321
+ authoringHost: null,
322
+ projectAuthoringSnapshot: null,
323
+ replaceProjectionSessionAuthority,
324
+ runBrowserAuthoringMutation: async () => {
325
+ throw new Error('Browser authoring authority is unavailable.');
326
+ },
327
+ tryRunBrowserAuthoringMutationImmediately: () => ({
328
+ ok: false,
329
+ reason: 'Browser authoring authority is unavailable.',
330
+ }),
331
+ reconcilePendingProjectServiceSnapshot: async () => { },
306
332
  createSessionChangeObserver: () => () => { },
307
333
  handleSessionObserverError: () => { },
308
334
  source: null,
@@ -345,8 +371,13 @@ function createLocalEditorHarnessImplementation(options, construction) {
345
371
  sceneViewSpatialOverlay: null,
346
372
  selectionController: null,
347
373
  selectionInspectorRefreshGeneration: 0,
348
- selectionInspectorRefreshFrame: null,
349
374
  selectionInspectorRefreshTimeout: null,
375
+ suppressDocumentMarkDirtyInspectorRefresh: false,
376
+ suppressedProjectionInspectorRefreshBatchIds: new Set(),
377
+ suppressedProjectionInspectorRefreshNodeBatches: new Map(),
378
+ inspectorPresentationGeneration: 0,
379
+ inspectorPresentationFrame: null,
380
+ inspectorPresentationPatches: null,
350
381
  sceneRenderScheduler: null,
351
382
  viewportRenderCoordinator: null,
352
383
  sceneFrameStats: null,
@@ -383,10 +414,387 @@ function createLocalEditorHarnessImplementation(options, construction) {
383
414
  statusDetails: '',
384
415
  renderingAction: null,
385
416
  summary: '',
386
- agentBridge: createEditorAgentBridge(),
387
- unregisterAgentBridgeCapabilities: null,
388
- agentBridgeTransport: null,
389
- agentBridgeStatus: createDisabledLocalEditorAgentBridgeTransportStatus(),
417
+ commandService: createEditorCommandCapabilityAdapter({
418
+ commandSystem: commandServiceCommandSystem,
419
+ resolveAuthoringRevision: () => projectCommandService
420
+ ? projectedServiceRevision
421
+ : state.authoringHost?.getSnapshot().documentRevision ?? null,
422
+ }),
423
+ unregisterCommandCapabilities: null,
424
+ };
425
+ let browserAuthoringMutationSequence = 0;
426
+ let projectServiceSyncTail = Promise.resolve();
427
+ let projectServiceSyncPending = 0;
428
+ const acquireProjectServiceSync = async () => {
429
+ projectServiceSyncPending += 1;
430
+ const previous = projectServiceSyncTail;
431
+ let releaseCurrent;
432
+ projectServiceSyncTail = new Promise(resolve => { releaseCurrent = resolve; });
433
+ await previous;
434
+ let active = true;
435
+ return () => {
436
+ if (!active)
437
+ return;
438
+ active = false;
439
+ projectServiceSyncPending -= 1;
440
+ releaseCurrent();
441
+ };
442
+ };
443
+ const tryAcquireProjectServiceSync = () => {
444
+ if (projectServiceSyncPending > 0)
445
+ return null;
446
+ projectServiceSyncPending = 1;
447
+ let releaseCurrent;
448
+ projectServiceSyncTail = new Promise(resolve => { releaseCurrent = resolve; });
449
+ let active = true;
450
+ return () => {
451
+ if (!active)
452
+ return;
453
+ active = false;
454
+ projectServiceSyncPending = 0;
455
+ releaseCurrent();
456
+ };
457
+ };
458
+ const readProjectServiceSnapshot = async () => {
459
+ if (!projectCommandService)
460
+ return null;
461
+ const response = await projectCommandService.request({ kind: 'authoring.snapshot' });
462
+ if (!response.ok || response.kind !== 'authoring.snapshot'
463
+ || response.result.kind !== 'snapshot-required')
464
+ return null;
465
+ return response.result.snapshot;
466
+ };
467
+ const commitProjectAuthoringProjection = (snapshot, documentFingerprint = createLocalEditorDocumentFingerprint(snapshot.document)) => {
468
+ state.projectAuthoringSnapshot = {
469
+ serviceInstanceId: snapshot.serviceInstanceId,
470
+ documentRevision: snapshot.documentRevision,
471
+ dirty: snapshot.dirty,
472
+ canUndo: snapshot.canUndo,
473
+ canRedo: snapshot.canRedo,
474
+ history: snapshot.history
475
+ ? { entries: snapshot.history.entries.map(entry => ({ ...entry })) }
476
+ : { entries: [] },
477
+ };
478
+ projectedServiceRevision = snapshot.documentRevision;
479
+ projectedServiceInstanceId = snapshot.serviceInstanceId;
480
+ projectedBrowserDocumentFingerprint = documentFingerprint;
481
+ if (pendingProjectServiceSnapshot?.serviceInstanceId === snapshot.serviceInstanceId
482
+ && pendingProjectServiceSnapshot.documentRevision === snapshot.documentRevision) {
483
+ pendingProjectServiceSnapshot = null;
484
+ }
485
+ };
486
+ const invokeProjectServiceCommand = async (commandId, input = {}, expectedDocumentRevision) => {
487
+ if (!projectCommandService)
488
+ throw new Error('Project Editor Service is unavailable.');
489
+ if (projectedServiceRevision === null || projectedServiceInstanceId === null) {
490
+ const snapshot = await readProjectServiceSnapshot();
491
+ if (snapshot)
492
+ await applyProjectServiceSnapshot(snapshot, true);
493
+ }
494
+ const invocationExpectedRevision = expectedDocumentRevision ?? projectedServiceRevision;
495
+ if (invocationExpectedRevision === null || projectedServiceInstanceId === null) {
496
+ throw new Error('Project Editor Service revision is not ready.');
497
+ }
498
+ const requestId = `browser-project-${++browserAuthoringMutationSequence}`;
499
+ const response = await projectCommandService.request({
500
+ kind: 'command.invoke',
501
+ invocation: {
502
+ commandId,
503
+ input: input,
504
+ requestId,
505
+ idempotencyKey: requestId,
506
+ expectedRevision: {
507
+ serviceInstanceId: projectedServiceInstanceId,
508
+ document: invocationExpectedRevision,
509
+ },
510
+ principal: { type: 'human' },
511
+ source: 'browser-ui',
512
+ clientId: browserCommandAttachmentId,
513
+ },
514
+ timeoutMs: 10000,
515
+ });
516
+ const operation = await waitForProjectServiceOperation(projectCommandService.request, response);
517
+ if (operation?.status !== 'completed') {
518
+ throw new Error(operation?.diagnostics?.map(diagnostic => diagnostic.message).join('; ')
519
+ || `Project Editor Service rejected ${commandId}.`);
520
+ }
521
+ return operation.output && typeof operation.output === 'object' && !Array.isArray(operation.output)
522
+ ? operation.output
523
+ : {};
524
+ };
525
+ const prepareProjectServiceDocumentExport = async () => {
526
+ if (!projectCommandService)
527
+ return null;
528
+ const prepared = await invokeProjectServiceCommand('document.export');
529
+ const transactionId = typeof prepared.transactionId === 'string'
530
+ ? prepared.transactionId
531
+ : null;
532
+ if (!transactionId) {
533
+ throw new Error('Project Editor Service did not return an export transaction.');
534
+ }
535
+ const response = await projectCommandService.request({
536
+ kind: 'document.export.get',
537
+ transactionId,
538
+ });
539
+ if (!response.ok || response.kind !== 'document.export' || !response.artifact) {
540
+ throw new Error('Project Editor Service did not return the prepared export artifact.');
541
+ }
542
+ return response.artifact;
543
+ };
544
+ const applyProjectServiceSnapshot = async (snapshot, syncLeaseHeld = false) => {
545
+ if (projectCommandService && !syncLeaseHeld) {
546
+ const release = await acquireProjectServiceSync();
547
+ try {
548
+ return await applyProjectServiceSnapshot(snapshot, true);
549
+ }
550
+ finally {
551
+ release();
552
+ }
553
+ }
554
+ const classification = classifyProjectServiceSnapshot(projectedServiceInstanceId, projectedServiceRevision, snapshot);
555
+ if (classification === 'stale')
556
+ return;
557
+ const serviceChanged = classification === 'service-changed';
558
+ if (shouldReplacePendingProjectServiceSnapshot(pendingProjectServiceSnapshot, snapshot)) {
559
+ pendingProjectServiceSnapshot = snapshot;
560
+ }
561
+ const session = state.session;
562
+ if (!session || state.prefabStage)
563
+ return;
564
+ const stateSnapshot = session.getState({ cloneDocuments: false });
565
+ const localDocumentFingerprint = createLocalEditorDocumentFingerprint(stateSnapshot.workingDocument);
566
+ const snapshotDocumentFingerprint = createLocalEditorDocumentFingerprint(snapshot.document);
567
+ if (!serviceChanged
568
+ && projectedServiceRevision !== null
569
+ && projectedBrowserDocumentFingerprint !== null
570
+ && localDocumentFingerprint !== projectedBrowserDocumentFingerprint) {
571
+ throw new Error('The project snapshot cannot replace Browser working state because a newer local authoring transaction is not projected.');
572
+ }
573
+ if (localDocumentFingerprint === snapshotDocumentFingerprint
574
+ && createLocalEditorDocumentFingerprint(stateSnapshot.persistedDocument)
575
+ === createLocalEditorDocumentFingerprint(snapshot.savedDocument)) {
576
+ commitProjectAuthoringProjection(snapshot, snapshotDocumentFingerprint);
577
+ return;
578
+ }
579
+ const rebaseProjection = (activeSession) => {
580
+ if (state.session !== session || state.prefabStage) {
581
+ throw new Error('Browser authoring scope changed before applying the project snapshot.');
582
+ }
583
+ runProjectionSessionMutation(activeSession, () => {
584
+ activeSession.rebaseExternalDocuments({
585
+ persistedDocument: snapshot.savedDocument,
586
+ workingDocument: snapshot.document,
587
+ });
588
+ });
589
+ };
590
+ const host = state.authoringHost;
591
+ if (host) {
592
+ const sequence = ++browserAuthoringMutationSequence;
593
+ await host.runExternalCommandMutation({
594
+ commandId: 'authoring.snapshot.project',
595
+ requestId: `project-snapshot-${sequence}`,
596
+ idempotencyKey: `project-snapshot-${sequence}`,
597
+ expectedDocumentRevision: stateSnapshot.documentRevision,
598
+ fingerprint: JSON.stringify({ serviceInstanceId: snapshot.serviceInstanceId, documentRevision: snapshot.documentRevision }),
599
+ }, rebaseProjection);
600
+ }
601
+ else {
602
+ rebaseProjection(session);
603
+ }
604
+ commitProjectAuthoringProjection(snapshot, snapshotDocumentFingerprint);
605
+ harness.render();
606
+ };
607
+ const pushBrowserDocumentToProjectService = async (document, commandId, inputFingerprint, expectedServiceInstanceId, expectedServiceRevision, baseDocumentFingerprint, rollbackSession) => {
608
+ if (!projectCommandService)
609
+ return;
610
+ if (!disposeBrowserCommandAttachment || await disposeBrowserCommandAttachment === null) {
611
+ throw new Error('Browser command attachment is unavailable.');
612
+ }
613
+ const requestId = `browser-project-${++browserAuthoringMutationSequence}`;
614
+ const response = await projectCommandService.request({
615
+ kind: 'command.invoke',
616
+ invocation: {
617
+ commandId: 'authoring.proposal.commit',
618
+ intentCommandId: commandId,
619
+ input: { commandId, inputFingerprint, document },
620
+ requestId,
621
+ idempotencyKey: requestId,
622
+ expectedRevision: {
623
+ serviceInstanceId: expectedServiceInstanceId,
624
+ document: expectedServiceRevision,
625
+ },
626
+ principal: { type: 'human' },
627
+ source: 'browser-ui',
628
+ clientId: browserCommandAttachmentId,
629
+ },
630
+ timeoutMs: 10000,
631
+ });
632
+ const operation = await waitForProjectServiceOperation(projectCommandService.request, response);
633
+ if (operation?.status === 'completed') {
634
+ return;
635
+ }
636
+ const snapshot = await readProjectServiceSnapshot();
637
+ if (snapshot) {
638
+ if (shouldReplacePendingProjectServiceSnapshot(pendingProjectServiceSnapshot, snapshot)) {
639
+ pendingProjectServiceSnapshot = snapshot;
640
+ }
641
+ }
642
+ if (snapshot && rollbackSession) {
643
+ runProjectionSessionMutation(rollbackSession, () => {
644
+ rollbackSession.rebaseExternalDocuments({
645
+ persistedDocument: snapshot.savedDocument,
646
+ workingDocument: snapshot.document,
647
+ });
648
+ });
649
+ commitProjectAuthoringProjection(snapshot);
650
+ }
651
+ const remoteFingerprint = snapshot
652
+ ? createLocalEditorDocumentFingerprint(snapshot.document)
653
+ : null;
654
+ throw new Error(remoteFingerprint !== baseDocumentFingerprint
655
+ ? 'Browser authoring was rejected because the project document changed in another client.'
656
+ : operation?.diagnostics?.map(diagnostic => diagnostic.message).join('; ')
657
+ || 'Project Editor Service rejected the Browser authoring transaction.');
658
+ };
659
+ state.reconcilePendingProjectServiceSnapshot = async () => {
660
+ if (pendingProjectServiceSnapshot) {
661
+ await applyProjectServiceSnapshot(pendingProjectServiceSnapshot);
662
+ }
663
+ };
664
+ state.runBrowserAuthoringMutation = async (commandId, fingerprint, handler) => {
665
+ const releaseProjectSync = projectCommandService ? await acquireProjectServiceSync() : () => { };
666
+ try {
667
+ const session = state.session;
668
+ if (!session)
669
+ throw new Error('Browser authoring projection is unavailable.');
670
+ if (projectCommandService) {
671
+ if (projectedServiceRevision === null) {
672
+ const remote = await readProjectServiceSnapshot();
673
+ if (remote)
674
+ await applyProjectServiceSnapshot(remote, true);
675
+ }
676
+ const expectedServiceInstanceId = projectedServiceInstanceId;
677
+ const expectedServiceRevision = projectedServiceRevision;
678
+ if (expectedServiceInstanceId === null || expectedServiceRevision === null) {
679
+ throw new Error('Project Editor Service revision is not ready.');
680
+ }
681
+ const baseDocumentFingerprint = createLocalEditorDocumentFingerprint(session.getWorkingDocument());
682
+ const transaction = session.beginTransaction();
683
+ try {
684
+ const authoringState = new Proxy(state, {
685
+ get(target, property, receiver) {
686
+ if (property === 'session')
687
+ return transaction.session;
688
+ return Reflect.get(target, property, receiver);
689
+ },
690
+ set(target, property, value, receiver) {
691
+ if (property === 'session') {
692
+ throw new Error('Browser authoring proposal handlers cannot replace the projection Session.');
693
+ }
694
+ return Reflect.set(target, property, value, receiver);
695
+ },
696
+ });
697
+ const value = await handler(authoringState);
698
+ if (state.session !== session) {
699
+ throw new Error(`Browser authoring scope changed before ${commandId}.`);
700
+ }
701
+ const proposedDocument = transaction.session.getWorkingDocument();
702
+ await pushBrowserDocumentToProjectService(proposedDocument, commandId, fingerprint, expectedServiceInstanceId, expectedServiceRevision, baseDocumentFingerprint, session);
703
+ const committedSnapshot = await readProjectServiceSnapshot();
704
+ if (!committedSnapshot) {
705
+ throw new Error('Project Editor Service did not return the committed authoring snapshot.');
706
+ }
707
+ if (state.prefabStage && state.session === session) {
708
+ const projectionState = session.getState({ cloneDocuments: false });
709
+ runProjectionSessionMutation(session, () => {
710
+ session.rebaseExternalDocuments({
711
+ persistedDocument: projectionState.persistedDocument,
712
+ workingDocument: committedSnapshot.document,
713
+ });
714
+ });
715
+ commitProjectAuthoringProjection(committedSnapshot);
716
+ harness.render();
717
+ }
718
+ else {
719
+ await applyProjectServiceSnapshot(committedSnapshot, true);
720
+ }
721
+ return value;
722
+ }
723
+ finally {
724
+ transaction.discard();
725
+ }
726
+ }
727
+ const host = state.authoringHost;
728
+ if (!host)
729
+ throw new Error('Browser authoring authority is unavailable.');
730
+ const expectedDocumentRevision = session.getState({ cloneDocuments: false }).documentRevision;
731
+ const sequence = ++browserAuthoringMutationSequence;
732
+ const requestId = `browser-${sequence}`;
733
+ return await host.runExternalCommandMutation({
734
+ commandId,
735
+ requestId,
736
+ idempotencyKey: requestId,
737
+ expectedDocumentRevision,
738
+ fingerprint,
739
+ }, activeSession => {
740
+ if (state.session !== session) {
741
+ throw new Error(`Browser authoring scope changed before ${commandId}.`);
742
+ }
743
+ const authoringState = new Proxy(state, {
744
+ get(target, property, receiver) {
745
+ if (property === 'session')
746
+ return activeSession;
747
+ return Reflect.get(target, property, receiver);
748
+ },
749
+ set(target, property, value, receiver) {
750
+ if (property === 'session') {
751
+ throw new Error('Browser authoring handlers cannot replace the active Session.');
752
+ }
753
+ return Reflect.set(target, property, value, receiver);
754
+ },
755
+ });
756
+ return handler(authoringState);
757
+ });
758
+ }
759
+ finally {
760
+ releaseProjectSync();
761
+ }
762
+ };
763
+ state.tryRunBrowserAuthoringMutationImmediately = (commandId, fingerprint, handler) => {
764
+ if (projectCommandService) {
765
+ return { ok: false, reason: 'Project Editor Service authoring is asynchronous.' };
766
+ }
767
+ const host = state.authoringHost;
768
+ const session = state.session;
769
+ if (!host || !session)
770
+ return { ok: false, reason: 'Browser authoring authority is unavailable.' };
771
+ const releaseProjectSync = tryAcquireProjectServiceSync();
772
+ if (!releaseProjectSync)
773
+ return { ok: false, reason: 'An authoring transaction is still pending.' };
774
+ const expectedDocumentRevision = session.getState({ cloneDocuments: false }).documentRevision;
775
+ const sequence = ++browserAuthoringMutationSequence;
776
+ const requestId = `browser-${sequence}`;
777
+ try {
778
+ const result = host.tryRunExternalCommandMutationImmediately({
779
+ commandId,
780
+ requestId,
781
+ idempotencyKey: requestId,
782
+ expectedDocumentRevision,
783
+ fingerprint,
784
+ }, activeSession => {
785
+ if (state.session !== session) {
786
+ throw new Error(`Browser authoring scope changed before ${commandId}.`);
787
+ }
788
+ return handler(activeSession);
789
+ });
790
+ return result.ok ? { ok: true, value: result.value } : {
791
+ ok: false,
792
+ reason: 'Another authoring transaction is running.',
793
+ };
794
+ }
795
+ finally {
796
+ releaseProjectSync();
797
+ }
390
798
  };
391
799
  const getDocumentDirty = () => {
392
800
  const stageSnapshot = state.prefabStage?.transaction.snapshot();
@@ -398,6 +806,8 @@ function createLocalEditorHarnessImplementation(options, construction) {
398
806
  if (change.documentChanged)
399
807
  state.diagnosticPhase = 'edit';
400
808
  void lifecycleController?.synchronizeDocumentState(getDocumentDirty()).catch(error => {
809
+ state.suppressDocumentMarkDirtyInspectorRefresh = false;
810
+ suppressedDocumentMarkDirtyTransitionId = null;
401
811
  console.error('[LocalEditorHarness] lifecycle document synchronization failed', error);
402
812
  });
403
813
  const eventPublisher = options.pluginHost?.eventPublisher;
@@ -456,11 +866,93 @@ function createLocalEditorHarnessImplementation(options, construction) {
456
866
  state.createSessionChangeObserver = createSessionChangeObserver;
457
867
  state.handleSessionObserverError = handleEditorSessionObserverError;
458
868
  let harness;
869
+ const runProjectServiceHistoryOperation = async (operation, historyOptions = {}) => {
870
+ const origin = historyOptions.origin ?? 'user';
871
+ const waitForProjection = historyOptions.waitForProjection ?? 'none';
872
+ const releaseProjectSync = await acquireProjectServiceSync();
873
+ try {
874
+ cancelActiveOperation(state);
875
+ const before = await readProjectServiceSnapshot();
876
+ if (!before)
877
+ throw new Error('Project Editor Service history is unavailable.');
878
+ await applyProjectServiceSnapshot(before, true);
879
+ const available = operation === 'undo' ? before.canUndo : before.canRedo;
880
+ if (!available) {
881
+ return {
882
+ ok: false,
883
+ operation,
884
+ origin,
885
+ waitForProjection,
886
+ documentChanged: false,
887
+ transactionLabel: null,
888
+ workingDocument: before.document,
889
+ dirty: before.dirty,
890
+ history: { canUndo: before.canUndo, canRedo: before.canRedo },
891
+ status: operation === 'undo' ? 'Undo unavailable' : 'Redo unavailable',
892
+ projection: createHistoryProjectionResult(waitForProjection, null, state),
893
+ };
894
+ }
895
+ await invokeProjectServiceCommand(`history.${operation}`);
896
+ const after = await readProjectServiceSnapshot();
897
+ if (!after)
898
+ throw new Error('Project Editor Service did not return the updated history snapshot.');
899
+ await applyProjectServiceSnapshot(after, true);
900
+ state.summary = summarizeDocument(options, after.document, state.session?.getSource());
901
+ state.status = operation === 'undo' ? 'Undo' : 'Redo';
902
+ state.statusTone = 'default';
903
+ state.statusToneStatus = state.status;
904
+ state.statusDetails = '';
905
+ harness.render();
906
+ const idleState = waitForProjection === 'settled'
907
+ ? await state.projection?.waitForIdle() ?? null
908
+ : null;
909
+ return {
910
+ ok: true,
911
+ operation,
912
+ origin,
913
+ waitForProjection,
914
+ documentChanged: after.documentRevision !== before.documentRevision,
915
+ transactionLabel: null,
916
+ workingDocument: after.document,
917
+ dirty: after.dirty,
918
+ history: { canUndo: after.canUndo, canRedo: after.canRedo },
919
+ status: state.status,
920
+ projection: createHistoryProjectionResult(waitForProjection, idleState, state),
921
+ };
922
+ }
923
+ catch (error) {
924
+ reportBrowserAuthoringFailure(state, () => harness.render(), error);
925
+ const snapshot = state.projectAuthoringSnapshot;
926
+ return {
927
+ ok: false,
928
+ operation,
929
+ origin,
930
+ waitForProjection,
931
+ documentChanged: false,
932
+ transactionLabel: null,
933
+ workingDocument: state.session?.getWorkingDocument() ?? null,
934
+ dirty: snapshot?.dirty ?? state.session?.isDirty() ?? false,
935
+ history: {
936
+ canUndo: snapshot?.canUndo ?? false,
937
+ canRedo: snapshot?.canRedo ?? false,
938
+ },
939
+ status: state.status,
940
+ projection: createHistoryProjectionResult(waitForProjection, null, state),
941
+ };
942
+ }
943
+ finally {
944
+ releaseProjectSync();
945
+ }
946
+ };
459
947
  let renderSelectionChange = () => harness.render();
948
+ let applySceneRenamePresentation = (_input) => false;
949
+ let applyHistoryPresentation = (_impact) => false;
950
+ let applyInspectorPresentation = (_input) => false;
951
+ let applyRenderingPresentation = () => false;
460
952
  const busyOperationExecutors = new WeakMap();
461
953
  let agentLifecycleController;
462
954
  const pluginInspector = createLocalEditorHarnessPluginInspectorOptions(options.pluginHost, options.inspector);
463
- const builtInSceneHierarchyProvider = createLocalEditorHarnessHierarchyScopeProvider(state, options, () => harness.render(), createLocalEditorSceneHierarchyScopeAdapter(state, options), busyOperationExecutors, () => renderSelectionChange());
955
+ const builtInSceneHierarchyProvider = createLocalEditorHarnessHierarchyScopeProvider(state, options, () => harness.render(), createLocalEditorSceneHierarchyScopeAdapter(state, options, input => applySceneRenamePresentation(input)), busyOperationExecutors, () => renderSelectionChange());
464
956
  const builtInPrefabStageHierarchyProvider = createLocalEditorHarnessHierarchyScopeProvider(state, options, () => harness.render(), createLocalEditorPrefabHierarchyScopeAdapter(state, options), busyOperationExecutors, () => renderSelectionChange());
465
957
  const hierarchySurface = options.pluginHost
466
958
  ? resolveHierarchySurfaceContribution(options.pluginHost.contributions)
@@ -551,6 +1043,8 @@ function createLocalEditorHarnessImplementation(options, construction) {
551
1043
  theme: options.theme,
552
1044
  localTestActions: options.localTestActions,
553
1045
  gameModeEntryVisible: options.gameModeEntryVisible,
1046
+ commandService: projectCommandService,
1047
+ commandClientId: browserCommandAttachmentId,
554
1048
  input: {
555
1049
  isShortcutReserved: (event) => state.sceneViewInput?.ownsKeyboardEvent(event) ?? false,
556
1050
  },
@@ -686,8 +1180,10 @@ function createLocalEditorHarnessImplementation(options, construction) {
686
1180
  })();
687
1181
  },
688
1182
  onCreateFromAsset: (assetId) => {
689
- if (createAssetFromBrowserIntent(state, options, assetId))
690
- harness.render();
1183
+ void state.runBrowserAuthoringMutation('object.createFromAsset', JSON.stringify({ assetId }), authoringState => createAssetFromBrowserIntent(authoringState, options, assetId)).then(changed => {
1184
+ if (changed)
1185
+ harness.render();
1186
+ }).catch(error => reportBrowserAuthoringFailure(state, harness.render, error));
691
1187
  },
692
1188
  onSelectAsset: (assetId) => {
693
1189
  if (selectBrowserAsset(state, assetId))
@@ -715,7 +1211,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
715
1211
  return;
716
1212
  }
717
1213
  else if (choice === 'discard') {
718
- discardPrefabStageChanges();
1214
+ await discardPrefabStageChanges();
719
1215
  }
720
1216
  else {
721
1217
  return;
@@ -729,12 +1225,18 @@ function createLocalEditorHarnessImplementation(options, construction) {
729
1225
  void runExclusive(state, harness.render, executor => (runPrefabContextAction(actionId, executor)), busyOperationExecutors);
730
1226
  },
731
1227
  onMarkerTypeAction: (input) => {
732
- if (patchMarkerTypeCatalog(state, options, input))
733
- harness.render();
1228
+ void state.runBrowserAuthoringMutation('marker.type.apply', JSON.stringify(input), authoringState => patchMarkerTypeCatalog(authoringState, options, input)).then(changed => {
1229
+ if (changed)
1230
+ harness.render();
1231
+ }).catch(error => reportBrowserAuthoringFailure(state, harness.render, error));
734
1232
  },
735
1233
  onMarkerRelationAction: (input) => {
736
- if (patchMarkerRelation(state, options, input))
737
- harness.render();
1234
+ void state.runBrowserAuthoringMutation('marker.relation.apply', JSON.stringify(input), authoringState => {
1235
+ const changed = patchMarkerRelation(authoringState, options, input);
1236
+ if (changed)
1237
+ harness.render();
1238
+ return changed;
1239
+ }).catch(error => reportBrowserAuthoringFailure(state, harness.render, error));
738
1240
  },
739
1241
  onAssetBrowserRefresh: () => runExclusive(state, harness.render, async () => {
740
1242
  await harness.reloadAssets();
@@ -742,9 +1244,6 @@ function createLocalEditorHarnessImplementation(options, construction) {
742
1244
  onProjectFileRead: options.persistenceAdapter.loadProjectFile
743
1245
  ? path => Promise.resolve(options.persistenceAdapter.loadProjectFile(path))
744
1246
  : undefined,
745
- onProjectEntrySendToAgent: options.agentContext
746
- ? input => Promise.resolve(options.agentContext.sendProjectEntry(input)).then(() => undefined)
747
- : undefined,
748
1247
  onProjectMediaResolve: options.persistenceAdapter.resolveProjectMedia
749
1248
  ? path => Promise.resolve(options.persistenceAdapter.resolveProjectMedia(path))
750
1249
  : undefined,
@@ -756,10 +1255,46 @@ function createLocalEditorHarnessImplementation(options, construction) {
756
1255
  harness.render();
757
1256
  },
758
1257
  onPropertyInput: (input) => {
759
- const previousStatus = state.status;
760
- const patched = patchSerializedProperty(state, options, input);
761
- if (patched || state.status !== previousStatus)
762
- harness.render();
1258
+ const refreshInspector = input.refreshInspector
1259
+ ?? !(input.source === 'custom' && input.commitMode === 'live');
1260
+ const normalizedInput = refreshInspector === input.refreshInspector
1261
+ ? input
1262
+ : { ...input, refreshInspector };
1263
+ const lifecycleSnapshot = lifecycleController?.lifecycle.getSnapshot();
1264
+ state.suppressDocumentMarkDirtyInspectorRefresh = !refreshInspector
1265
+ && normalizedInput.persistence === 'document'
1266
+ && lifecycleSnapshot?.mode === 'edit'
1267
+ && lifecycleSnapshot.document !== 'dirty';
1268
+ void state.runBrowserAuthoringMutation('inspector.property.set', JSON.stringify(normalizedInput), authoringState => {
1269
+ const previousStatus = authoringState.status;
1270
+ const patched = patchSerializedProperty(authoringState, options, normalizedInput);
1271
+ return {
1272
+ patched,
1273
+ statusChanged: authoringState.status !== previousStatus,
1274
+ };
1275
+ })
1276
+ .then(({ patched, statusChanged }) => {
1277
+ const presentationApplied = patched
1278
+ ? applyInspectorPresentation(normalizedInput)
1279
+ : false;
1280
+ if (!refreshInspector) {
1281
+ if (!presentationApplied && (patched || statusChanged)) {
1282
+ ui.updatePreview?.({
1283
+ status: state.status,
1284
+ statusTone: state.statusToneStatus === state.status ? state.statusTone : 'default',
1285
+ statusDetails: state.statusToneStatus === state.status ? state.statusDetails : '',
1286
+ session: createUiSessionState(state),
1287
+ });
1288
+ }
1289
+ return;
1290
+ }
1291
+ if ((patched || statusChanged) && !presentationApplied)
1292
+ harness.render();
1293
+ })
1294
+ .catch(error => reportBrowserAuthoringFailure(state, harness.render, error))
1295
+ .finally(() => {
1296
+ state.suppressDocumentMarkDirtyInspectorRefresh = false;
1297
+ });
763
1298
  },
764
1299
  onInspectorAction: (input) => {
765
1300
  void runExclusive(state, harness.render, async () => {
@@ -769,20 +1304,31 @@ function createLocalEditorHarnessImplementation(options, construction) {
769
1304
  },
770
1305
  onRenderingPropertyChange: options.documentAdapter.onRenderingPropertyChange
771
1306
  ? (input) => {
772
- void applyRenderingPropertyChange(state, options, input)
1307
+ void state.runBrowserAuthoringMutation('rendering.property.set', JSON.stringify(input), authoringState => applyRenderingPropertyChange(authoringState, options, input))
773
1308
  .then((changed) => {
774
- if (changed)
1309
+ if (changed && !applyRenderingPresentation())
775
1310
  harness.render();
776
- });
1311
+ })
1312
+ .catch(error => reportBrowserAuthoringFailure(state, harness.render, error));
777
1313
  }
778
1314
  : undefined,
779
1315
  onRenderingAction: options.documentAdapter.onRenderingAction
780
1316
  ? (input) => {
781
- return applyRenderingAction(state, options, input, harness.render)
782
- .then((changed) => {
783
- if (changed)
1317
+ return state.runBrowserAuthoringMutation(`rendering.${input.actionId ?? 'action'}`, JSON.stringify(input), authoringState => applyRenderingAction(authoringState, options, input, harness.render))
1318
+ .then((result) => {
1319
+ if (result.rendered && !applyRenderingPresentation())
784
1320
  harness.render();
785
- return changed;
1321
+ return result;
1322
+ })
1323
+ .catch(error => {
1324
+ reportBrowserAuthoringFailure(state, harness.render, error);
1325
+ return {
1326
+ ok: false,
1327
+ changed: false,
1328
+ rendered: false,
1329
+ status: error instanceof Error ? error.message : String(error),
1330
+ statusTone: 'error',
1331
+ };
786
1332
  });
787
1333
  }
788
1334
  : undefined,
@@ -870,8 +1416,10 @@ function createLocalEditorHarnessImplementation(options, construction) {
870
1416
  });
871
1417
  },
872
1418
  onTransformAction: (action) => {
873
- if (executeTransformAction(state, options, action))
874
- harness.render();
1419
+ void state.runBrowserAuthoringMutation(`transform.${action}`, JSON.stringify({ action }), authoringState => executeTransformAction(authoringState, options, action)).then(changed => {
1420
+ if (changed)
1421
+ harness.render();
1422
+ }).catch(error => reportBrowserAuthoringFailure(state, harness.render, error));
875
1423
  },
876
1424
  onViewportViewPresetChange: (preset) => {
877
1425
  if (setViewportViewPreset(state, options, preset))
@@ -935,28 +1483,38 @@ function createLocalEditorHarnessImplementation(options, construction) {
935
1483
  }
936
1484
  publishLocalEditorSelectionPresentation(state, options, previewState => ui.updatePreview?.(previewState), 'hierarchy-selection-presentation');
937
1485
  };
938
- const discardPrefabStageChanges = () => {
1486
+ const discardPrefabStageChanges = async (serializedAccess) => {
939
1487
  const stage = state.prefabStage;
940
1488
  if (!stage || !stage.transaction.snapshot().stageDirty)
941
1489
  return false;
942
1490
  cancelActiveOperation(state);
943
- stage.transaction.discard();
1491
+ stage.transaction.assertParentUnchanged();
944
1492
  const draftState = stage.transaction.draftSession.getState({ cloneDocuments: false });
945
- rebuildProjectionFromDocument(state, options, draftState.workingDocument, EMPTY_EDITOR_SELECTION);
1493
+ if (state.authoringHost) {
1494
+ await state.authoringHost.reset(draftState.persistedDocument, draftState.source, {
1495
+ expectedDocumentRevision: draftState.documentRevision,
1496
+ ...(serializedAccess ? { serializedAccess } : {}),
1497
+ });
1498
+ }
1499
+ else {
1500
+ await state.runBrowserAuthoringMutation('prefab.stage.discard', JSON.stringify({ documentRevision: draftState.documentRevision }), authoringState => authoringState.session?.reset(draftState.persistedDocument, draftState.source));
1501
+ }
1502
+ const resetState = stage.transaction.draftSession.getState({ cloneDocuments: false });
1503
+ rebuildProjectionFromDocument(state, options, resetState.workingDocument, EMPTY_EDITOR_SELECTION);
946
1504
  state.status = `Discarded changes to Prefab ${stage.descriptor.label}`;
947
1505
  state.statusTone = 'default';
948
1506
  state.statusToneStatus = state.status;
949
1507
  state.statusDetails = '';
950
1508
  return true;
951
1509
  };
952
- runPrefabContextAction = async (actionId, executor) => {
1510
+ runPrefabContextAction = async (actionId, executor, serializedAccess) => {
953
1511
  const stage = state.prefabStage;
954
1512
  if (!stage)
955
1513
  return false;
956
1514
  if (actionId === PREFAB_STAGE_CONTEXT_ACTION_IDS.save) {
957
1515
  if (!stage.transaction.snapshot().documentDirty)
958
1516
  return false;
959
- return harness.saveScene(executor);
1517
+ return harness.saveScene(executor, undefined, serializedAccess);
960
1518
  }
961
1519
  if (actionId === PREFAB_STAGE_CONTEXT_ACTION_IDS.discard) {
962
1520
  if (!stage.transaction.snapshot().stageDirty)
@@ -968,7 +1526,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
968
1526
  confirmLabel: '废弃更改',
969
1527
  cancelLabel: '继续编辑',
970
1528
  });
971
- return confirmed ? discardPrefabStageChanges() : false;
1529
+ return confirmed ? discardPrefabStageChanges(serializedAccess) : false;
972
1530
  }
973
1531
  if (actionId !== PREFAB_STAGE_CONTEXT_ACTION_IDS.exit)
974
1532
  return false;
@@ -984,17 +1542,155 @@ function createLocalEditorHarnessImplementation(options, construction) {
984
1542
  cancelLabel: '取消',
985
1543
  });
986
1544
  if (choice === 'save') {
987
- if (!await harness.saveScene(executor))
1545
+ if (!await harness.saveScene(executor, undefined, serializedAccess))
988
1546
  return false;
989
1547
  }
990
1548
  else if (choice === 'discard') {
991
- discardPrefabStageChanges();
1549
+ await discardPrefabStageChanges(serializedAccess);
992
1550
  }
993
1551
  else {
994
1552
  return false;
995
1553
  }
996
1554
  }
997
- return exitPrefabStage(state, options, 'reject');
1555
+ return exitPrefabStage(state, options, 'reject', serializedAccess);
1556
+ };
1557
+ applySceneRenamePresentation = ({ id, name }) => {
1558
+ const sessionState = state.session?.getState({ cloneDocuments: false });
1559
+ const document = sessionState?.workingDocument ?? null;
1560
+ const authoritativeName = document
1561
+ ? resolveAuthoritativeHierarchyLabels(state, options, document, [id])[id] ?? name
1562
+ : name;
1563
+ let applied = false;
1564
+ try {
1565
+ applied = ui.updatePresentationDelta?.({
1566
+ hierarchyLabelIds: [id],
1567
+ hierarchyLabels: { [id]: authoritativeName },
1568
+ materialUsageOwnerNames: { [id]: authoritativeName },
1569
+ inspector: ['summary'],
1570
+ session: createUiSessionState(state),
1571
+ status: state.status,
1572
+ statusTone: state.statusTone,
1573
+ statusDetails: state.statusDetails,
1574
+ }) === true;
1575
+ }
1576
+ catch {
1577
+ applied = false;
1578
+ }
1579
+ if (!applied)
1580
+ state.hierarchyPresentationFallbackRevision += 1;
1581
+ return applied;
1582
+ };
1583
+ applyHistoryPresentation = impact => {
1584
+ if (!impact || impact.hierarchy === 'full')
1585
+ return false;
1586
+ const inspectorNeedsAssembly = impact.inspector === 'full'
1587
+ || (Array.isArray(impact.inspector) && impact.inspector.includes('material'));
1588
+ if (inspectorNeedsAssembly)
1589
+ cancelInspectorPresentationPreview(state);
1590
+ const sessionState = state.session?.getState({ cloneDocuments: false });
1591
+ const document = sessionState?.workingDocument ?? null;
1592
+ const labelIds = impact.hierarchy?.labelIds ?? [];
1593
+ const labels = document
1594
+ ? resolveAuthoritativeHierarchyLabels(state, options, document, labelIds)
1595
+ : {};
1596
+ const inspectorState = inspectorNeedsAssembly && document
1597
+ ? createUiSelectionInspectorPreviewState(state, options)
1598
+ : null;
1599
+ const inspectorValuePatches = Array.isArray(impact.inspector)
1600
+ && impact.inspector.includes('transform')
1601
+ && document
1602
+ ? createHistoryTransformInspectorPresentationPatches(state, document, sessionState?.selection)
1603
+ : [];
1604
+ const renderingPanel = impact.rendering && document
1605
+ ? options.documentAdapter.getRenderingPanelState?.(document, {
1606
+ scene: state.world?.scene ?? null,
1607
+ projection: state.projection,
1608
+ renderingAction: state.renderingAction,
1609
+ }) ?? null
1610
+ : undefined;
1611
+ let applied = false;
1612
+ try {
1613
+ applied = ui.updatePresentationDelta?.({
1614
+ hierarchyLabelIds: labelIds,
1615
+ hierarchyLabels: labels,
1616
+ materialUsageOwnerNames: labels,
1617
+ inspector: impact.inspector,
1618
+ ...(inspectorValuePatches.length > 0 ? { inspectorValuePatches } : {}),
1619
+ ...(inspectorState ? {
1620
+ inspectorObject: inspectorState.inspectorObject,
1621
+ inspectorMultiObject: inspectorState.inspectorMultiObject,
1622
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
1623
+ } : {}),
1624
+ rendering: impact.rendering,
1625
+ ...(renderingPanel !== undefined ? { renderingPanel } : {}),
1626
+ session: createUiSessionState(state),
1627
+ status: state.status, statusTone: state.statusTone, statusDetails: state.statusDetails,
1628
+ }) === true;
1629
+ }
1630
+ catch {
1631
+ applied = false;
1632
+ }
1633
+ if (!applied && labelIds.length > 0)
1634
+ state.hierarchyPresentationFallbackRevision += 1;
1635
+ return applied;
1636
+ };
1637
+ applyInspectorPresentation = input => {
1638
+ if (isSerializedNamePath(input.path) && typeof input.value === 'string') {
1639
+ return applySceneRenamePresentation({ id: input.targetId, name: input.value });
1640
+ }
1641
+ const impact = resolveSerializedPresentationImpact(input.path);
1642
+ if (!impact)
1643
+ return false;
1644
+ if (impact.inspector?.includes('material'))
1645
+ cancelInspectorPresentationPreview(state);
1646
+ const sessionState = state.session?.getState({ cloneDocuments: false });
1647
+ const inspectorState = impact.inspector?.includes('material')
1648
+ ? createUiSelectionInspectorPreviewState(state, options)
1649
+ : null;
1650
+ try {
1651
+ return ui.updatePresentationDelta?.({
1652
+ inspector: impact.inspector,
1653
+ ...(impact.inspector?.includes('transform') ? {
1654
+ inspectorValuePatches: [{
1655
+ targetId: input.targetId,
1656
+ propertyPath: input.path,
1657
+ value: input.value,
1658
+ generation: state.inspectorPresentationGeneration,
1659
+ }],
1660
+ } : {}),
1661
+ ...(inspectorState ? {
1662
+ inspectorObject: inspectorState.inspectorObject,
1663
+ inspectorMultiObject: inspectorState.inspectorMultiObject,
1664
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
1665
+ } : {}),
1666
+ session: createUiSessionState(state),
1667
+ status: state.status, statusTone: state.statusTone, statusDetails: state.statusDetails,
1668
+ }) === true;
1669
+ }
1670
+ catch {
1671
+ return false;
1672
+ }
1673
+ };
1674
+ applyRenderingPresentation = () => {
1675
+ const sessionState = state.session?.getState({ cloneDocuments: false });
1676
+ const document = sessionState?.workingDocument ?? null;
1677
+ if (!document)
1678
+ return false;
1679
+ try {
1680
+ return ui.updatePresentationDelta?.({
1681
+ rendering: true,
1682
+ renderingPanel: options.documentAdapter.getRenderingPanelState?.(document, {
1683
+ scene: state.world?.scene ?? null,
1684
+ projection: state.projection,
1685
+ renderingAction: state.renderingAction,
1686
+ }) ?? null,
1687
+ session: createUiSessionState(state),
1688
+ status: state.status, statusTone: state.statusTone, statusDetails: state.statusDetails,
1689
+ }) === true;
1690
+ }
1691
+ catch {
1692
+ return false;
1693
+ }
998
1694
  };
999
1695
  const unsubscribeToolManager = toolManager.subscribe(snapshot => {
1000
1696
  if (state.toolManagerRenderSuppression !== 0)
@@ -1019,11 +1715,12 @@ function createLocalEditorHarnessImplementation(options, construction) {
1019
1715
  });
1020
1716
  construction.setCleanup(() => {
1021
1717
  harnessDisposed = true;
1718
+ stopProjectServiceWatch?.();
1719
+ stopProjectServiceWatch = null;
1720
+ void disposeBrowserCommandAttachment?.then(dispose => dispose?.());
1721
+ disposeBrowserCommandAttachment = null;
1022
1722
  pendingScheduledLifecycleExit?.cancel();
1023
1723
  pendingScheduledLifecycleExit = null;
1024
- unregisterAgentBridgeLifecycleParticipant?.();
1025
- unregisterAgentBridgeLifecycleParticipant = null;
1026
- agentBridgeLifecycleTransports.clear();
1027
1724
  releaseApplicationAuthorityScopeResource?.();
1028
1725
  releaseApplicationAuthorityScopeResource = null;
1029
1726
  releaseMainSessionScopeResource?.();
@@ -1038,6 +1735,9 @@ function createLocalEditorHarnessImplementation(options, construction) {
1038
1735
  unbindPluginTools?.();
1039
1736
  for (const unregister of [...unregisterStandardTools].reverse())
1040
1737
  unregister();
1738
+ state.authoringHost?.dispose();
1739
+ state.authoringHost = null;
1740
+ state.replaceProjectionSessionAuthority(null);
1041
1741
  disposeLocalEditorHarnessResources(state, ui, hierarchyKernel);
1042
1742
  });
1043
1743
  const startGameRuntime = async (reason) => {
@@ -1053,6 +1753,10 @@ function createLocalEditorHarnessImplementation(options, construction) {
1053
1753
  return;
1054
1754
  // The lifecycle-owned Session Scope clears these before runGame. Keep the
1055
1755
  // assignment idempotent for direct harness consumers without a coordinator.
1756
+ state.authoringHost?.dispose();
1757
+ await state.authoringHost?.drain();
1758
+ state.authoringHost = null;
1759
+ state.replaceProjectionSessionAuthority(null);
1056
1760
  state.session = null;
1057
1761
  state.source = null;
1058
1762
  state.status = reason === 'save' ? 'Saved scene; starting game' : 'Discarded editor changes; starting game';
@@ -1100,6 +1804,25 @@ function createLocalEditorHarnessImplementation(options, construction) {
1100
1804
  }, executor);
1101
1805
  return runResult.accepted ? runResult.value : false;
1102
1806
  };
1807
+ const replaceAuthoringHost = async (session) => {
1808
+ state.unregisterCommandCapabilities?.();
1809
+ state.unregisterCommandCapabilities = null;
1810
+ const previousHost = state.authoringHost;
1811
+ previousHost?.dispose();
1812
+ await previousHost?.drain();
1813
+ if (projectCommandService) {
1814
+ state.authoringHost = null;
1815
+ state.replaceProjectionSessionAuthority(session);
1816
+ return;
1817
+ }
1818
+ state.replaceProjectionSessionAuthority(null);
1819
+ state.authoringHost = createEditorAuthoringHost({
1820
+ serviceInstanceId: createLocalEditorAuthoringServiceInstanceId(),
1821
+ session,
1822
+ commandSystem: commandServiceCommandSystem,
1823
+ disposeCommandSystem: false,
1824
+ });
1825
+ };
1103
1826
  const loadOptionalProjectFiles = async () => {
1104
1827
  if (!options.persistenceAdapter.loadProjectFiles) {
1105
1828
  return { projectFiles: state.projectFiles, warning: null };
@@ -1153,7 +1876,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
1153
1876
  cloneDocument: options.documentAdapter.cloneDocument,
1154
1877
  compareDocuments: options.documentAdapter.compareDocuments,
1155
1878
  reduceDocument: options.documentAdapter.reduceDocument,
1156
- isDocumentStructureChange: options.documentAdapter.isHierarchyStructureChange,
1879
+ isDocumentStructureChange: command => resolveLocalEditorDocumentStructureChange(options.documentAdapter.isHierarchyStructureChange, command),
1157
1880
  onChange: createSessionChangeObserver(() => mainSession, {
1158
1881
  document: PLAYABLE_LOCAL_EDITOR_MAIN_DOCUMENT_SCOPE_ID,
1159
1882
  session: PLAYABLE_LOCAL_EDITOR_MAIN_SESSION_SCOPE_ID,
@@ -1162,14 +1885,22 @@ function createLocalEditorHarnessImplementation(options, construction) {
1162
1885
  });
1163
1886
  state.session = mainSession;
1164
1887
  try {
1888
+ await replaceAuthoringHost(mainSession);
1165
1889
  attachLifecycleOwnedSessionAuthority();
1890
+ activateCommandAuthority();
1166
1891
  }
1167
1892
  catch (error) {
1893
+ state.authoringHost?.dispose();
1894
+ state.authoringHost = null;
1895
+ state.replaceProjectionSessionAuthority(null);
1168
1896
  state.session = null;
1169
1897
  state.source = null;
1170
1898
  throw error;
1171
1899
  }
1172
1900
  state.summary = loadedSource?.summary ?? summarizeDocument(options, preparedDocument, source);
1901
+ if (pendingProjectServiceSnapshot) {
1902
+ await applyProjectServiceSnapshot(pendingProjectServiceSnapshot);
1903
+ }
1173
1904
  };
1174
1905
  const enterEditorWorld = async () => {
1175
1906
  if (!state.session)
@@ -1220,13 +1951,33 @@ function createLocalEditorHarnessImplementation(options, construction) {
1220
1951
  if (prefabTransaction) {
1221
1952
  prefabTransaction.assertParentUnchanged();
1222
1953
  const parentState = prefabTransaction.parentSession.getState({ cloneDocuments: false });
1223
- prefabTransaction.parentSession.reset(parentState.persistedDocument, parentState.source);
1224
- prefabTransaction.draftSession.reset(parentState.persistedDocument, parentState.source);
1954
+ if (state.authoringHost) {
1955
+ await state.authoringHost.resetWithRelatedSession(parentState.persistedDocument, parentState.source, {
1956
+ expectedDocumentRevision: sessionState.documentRevision,
1957
+ relatedSession: prefabTransaction.parentSession,
1958
+ expectedRelatedDocumentRevision: parentState.documentRevision,
1959
+ mutateRelatedSession(parentDraftSession) {
1960
+ parentDraftSession.reset(parentState.persistedDocument, parentState.source);
1961
+ },
1962
+ });
1963
+ }
1964
+ else {
1965
+ await state.runBrowserAuthoringMutation('document.discard', JSON.stringify({ documentRevision: sessionState.documentRevision }), authoringState => authoringState.session?.reset(parentState.persistedDocument, parentState.source));
1966
+ prefabTransaction.parentSession.reset(parentState.persistedDocument, parentState.source);
1967
+ }
1968
+ prefabTransaction.acceptParentRevision();
1225
1969
  state.source = parentState.source ?? null;
1226
1970
  state.summary = summarizeDocument(options, parentState.persistedDocument, state.source);
1227
1971
  }
1228
1972
  else {
1229
- state.session.reset(sessionState.persistedDocument, sessionState.source);
1973
+ if (state.authoringHost) {
1974
+ await state.authoringHost.reset(sessionState.persistedDocument, sessionState.source, {
1975
+ expectedDocumentRevision: sessionState.documentRevision,
1976
+ });
1977
+ }
1978
+ else {
1979
+ await state.runBrowserAuthoringMutation('document.discard', JSON.stringify({ documentRevision: sessionState.documentRevision }), authoringState => authoringState.session?.reset(sessionState.persistedDocument, sessionState.source));
1980
+ }
1230
1981
  state.source = sessionState.source ?? null;
1231
1982
  state.summary = summarizeDocument(options, sessionState.persistedDocument, state.source);
1232
1983
  }
@@ -1235,21 +1986,76 @@ function createLocalEditorHarnessImplementation(options, construction) {
1235
1986
  state.statusToneStatus = state.status;
1236
1987
  state.statusDetails = '';
1237
1988
  };
1238
- const markDocumentCommitted = async (expectedDocumentFingerprint) => {
1989
+ const markDocumentCommitted = async (expectedDocumentFingerprint, expectedDocumentRevision, preparedAuthoringSource) => {
1239
1990
  const sessionState = state.session?.getState({ cloneDocuments: false });
1240
1991
  if (!state.session || !sessionState)
1241
1992
  throw new Error('Cannot commit an unloaded editor document.');
1242
- if (createLocalEditorDocumentFingerprint(sessionState.workingDocument) !== expectedDocumentFingerprint) {
1993
+ const hasProjectServiceTransaction = Boolean(projectCommandService
1994
+ && typeof preparedAuthoringSource?.serviceTransactionId === 'string'
1995
+ && preparedAuthoringSource.serviceTransactionId.length > 0);
1996
+ if (!hasProjectServiceTransaction
1997
+ && createLocalEditorDocumentFingerprint(sessionState.workingDocument) !== expectedDocumentFingerprint) {
1243
1998
  throw new Error('Editor document changed after export; refusing to commit a stale snapshot.');
1244
1999
  }
2000
+ const committedDocument = (preparedAuthoringSource?.document
2001
+ ?? sessionState.workingDocument);
2002
+ const committedSource = preparedAuthoringSource?.source ?? sessionState.source;
1245
2003
  const prefabTransaction = state.prefabStage?.transaction ?? null;
2004
+ if (projectCommandService) {
2005
+ const transactionId = typeof preparedAuthoringSource?.serviceTransactionId === 'string'
2006
+ ? preparedAuthoringSource.serviceTransactionId
2007
+ : null;
2008
+ if (!transactionId) {
2009
+ throw new Error('Platform save confirmation is missing the Project Editor Service transaction.');
2010
+ }
2011
+ const serviceDocumentRevision = preparedAuthoringSource?.serviceDocumentRevision;
2012
+ if (!Number.isSafeInteger(serviceDocumentRevision) || (serviceDocumentRevision ?? -1) < 0) {
2013
+ throw new Error('Platform save confirmation is missing the Project Editor Service document revision.');
2014
+ }
2015
+ await invokeProjectServiceCommand('document.commit', { transactionId }, serviceDocumentRevision);
2016
+ const snapshot = await readProjectServiceSnapshot();
2017
+ if (!snapshot)
2018
+ throw new Error('Project Editor Service did not return the committed checkpoint.');
2019
+ await applyProjectServiceSnapshot(snapshot, true);
2020
+ state.source = sessionState.source ?? null;
2021
+ state.status = 'Platform save committed';
2022
+ state.statusTone = 'success';
2023
+ state.statusToneStatus = state.status;
2024
+ state.statusDetails = '';
2025
+ return;
2026
+ }
1246
2027
  if (prefabTransaction) {
1247
- prefabTransaction.commitSavedDocument(sessionState.workingDocument, sessionState.source, `Apply Prefab ${state.prefabStage?.descriptor.label ?? 'changes'}`);
2028
+ if (!state.authoringHost)
2029
+ throw new Error('Cannot commit Prefab without an Editor Authoring Host.');
2030
+ if (expectedDocumentRevision === null) {
2031
+ throw new Error('Platform save confirmation is missing the exported document revision.');
2032
+ }
2033
+ await state.authoringHost.markSavedWithRelatedSession(committedDocument, {
2034
+ expectedDocumentRevision,
2035
+ source: committedSource,
2036
+ resetHistory: true,
2037
+ relatedSession: prefabTransaction.parentSession,
2038
+ expectedRelatedDocumentRevision: prefabTransaction.parentSession
2039
+ .getState({ cloneDocuments: false }).documentRevision,
2040
+ mutateRelatedSession(parentDraftSession) {
2041
+ prefabTransaction.stageParentSavedDocument(parentDraftSession, committedDocument, committedSource, `Apply Prefab ${state.prefabStage?.descriptor.label ?? 'changes'}`);
2042
+ },
2043
+ });
2044
+ prefabTransaction.acceptParentRevision();
1248
2045
  }
1249
2046
  else {
1250
- state.session.markSaved(sessionState.workingDocument, sessionState.source);
2047
+ if (!state.authoringHost)
2048
+ throw new Error('Cannot commit without an Editor Authoring Host.');
2049
+ if (expectedDocumentRevision === null) {
2050
+ throw new Error('Platform save confirmation is missing the exported document revision.');
2051
+ }
2052
+ await state.authoringHost.markSaved(committedDocument, {
2053
+ expectedDocumentRevision,
2054
+ source: committedSource,
2055
+ });
1251
2056
  }
1252
- state.source = sessionState.source ?? null;
2057
+ state.source = committedSource ?? null;
2058
+ state.summary = summarizeDocument(options, committedDocument, committedSource);
1253
2059
  state.status = 'Platform save committed';
1254
2060
  state.statusTone = 'success';
1255
2061
  state.statusToneStatus = state.status;
@@ -1330,10 +2136,36 @@ function createLocalEditorHarnessImplementation(options, construction) {
1330
2136
  throw error;
1331
2137
  }
1332
2138
  };
2139
+ const renderSuppressedDocumentMarkDirtyPreview = () => {
2140
+ const lifecycleSnapshot = lifecycleController?.lifecycle.getSnapshot();
2141
+ const activeTransition = lifecycleSnapshot?.activeTransition;
2142
+ if (state.suppressDocumentMarkDirtyInspectorRefresh
2143
+ && activeTransition?.type === 'document.markDirty') {
2144
+ suppressedDocumentMarkDirtyTransitionId = activeTransition.id;
2145
+ if (!ui.updatePreview)
2146
+ return false;
2147
+ ui.updatePreview({ session: createUiSessionState(state) });
2148
+ return true;
2149
+ }
2150
+ if (suppressedDocumentMarkDirtyTransitionId && lifecycleSnapshot && !lifecycleSnapshot.transitioning) {
2151
+ suppressedDocumentMarkDirtyTransitionId = null;
2152
+ state.suppressDocumentMarkDirtyInspectorRefresh = false;
2153
+ if (!ui.updatePreview)
2154
+ return false;
2155
+ ui.updatePreview({ session: createUiSessionState(state) });
2156
+ return true;
2157
+ }
2158
+ return false;
2159
+ };
1333
2160
  harness = {
1334
2161
  render() {
1335
2162
  if (harnessDisposed)
1336
2163
  return;
2164
+ if (renderSuppressedDocumentMarkDirtyPreview())
2165
+ return;
2166
+ if (lifecyclePresentationOnlyRenderDepth > 0)
2167
+ return;
2168
+ cancelInspectorPresentationPreview(state);
1337
2169
  syncViewportCameraState(state);
1338
2170
  syncViewportMeasurementState(state);
1339
2171
  syncViewportSpatialOverlay(state, options);
@@ -1352,12 +2184,13 @@ function createLocalEditorHarnessImplementation(options, construction) {
1352
2184
  getHostServices() {
1353
2185
  return options.hostServices ?? null;
1354
2186
  },
1355
- getAgentBridge() {
1356
- return state.agentBridge;
2187
+ getCommandService() {
2188
+ return state.commandService;
1357
2189
  },
1358
2190
  getWorkingDocument() {
1359
2191
  return state.session?.getState().workingDocument ?? null;
1360
2192
  },
2193
+ prepareProjectServiceDocumentExport,
1361
2194
  projectAssetDropPoint(point) {
1362
2195
  return projectBabylonViewportPointToGround({
1363
2196
  babylon: state.babylon,
@@ -1429,7 +2262,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
1429
2262
  }
1430
2263
  return runExclusiveResult(state, harness.render, transactionExecutor => executeLocalEditorDocumentOperation(busyOperationExecutors, transactionExecutor, operation), busyOperationExecutors);
1431
2264
  },
1432
- async reloadAssets() {
2265
+ async reloadAssets(serializedAccess) {
1433
2266
  try {
1434
2267
  const previousAssets = state.assets;
1435
2268
  const [assets, projectFilesResult] = await Promise.all([
@@ -1440,7 +2273,8 @@ function createLocalEditorHarnessImplementation(options, construction) {
1440
2273
  ? findLocalEditorUpdatedAssetIds(options.documentAdapter.listAssetSnapshots(previousAssets), options.documentAdapter.listAssetSnapshots(assets))
1441
2274
  : [];
1442
2275
  if (state.session && options.documentAdapter.hydrateDocumentAssets) {
1443
- const result = applyLocalEditorUpdatedAssetsRefresh(state.session, {
2276
+ const expectedDocumentRevision = state.session.getState({ cloneDocuments: false }).documentRevision;
2277
+ const applyRefresh = (session = state.session) => applyLocalEditorUpdatedAssetsRefresh(session, {
1444
2278
  assetIds: refreshedAssetIds,
1445
2279
  assets,
1446
2280
  hydrateDocumentAssets: options.documentAdapter.hydrateDocumentAssets,
@@ -1451,6 +2285,9 @@ function createLocalEditorHarnessImplementation(options, construction) {
1451
2285
  ? { compareDocuments: options.documentAdapter.compareDocuments }
1452
2286
  : {}),
1453
2287
  });
2288
+ const result = serializedAccess
2289
+ ? state.authoringHost?.runSerializedExternalMutation(serializedAccess, expectedDocumentRevision, session => applyRefresh(session)) ?? (() => { throw new Error('Asset refresh authoring authority is unavailable.'); })()
2290
+ : await state.runBrowserAuthoringMutation('asset.library.refresh', JSON.stringify({ refreshedAssetIds }), authoringState => applyRefresh(authoringState.session));
1454
2291
  syncLocalEditorUpdatedAssetProjection(state.updatedAssetProjection, result.changed, () => {
1455
2292
  rebuildProjectionFromDocument(state, options, result.workingDocument, result.selection);
1456
2293
  });
@@ -1515,7 +2352,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
1515
2352
  loadOptionalProjectFiles(),
1516
2353
  ]);
1517
2354
  assertLocalEditorUpdatedAssetSnapshotFreshness(options.documentAdapter.resolveUpdatedAssetSnapshot(assets, { assetId: input.assetId }), input);
1518
- const result = applyLocalEditorUpdatedAssetRefresh(state.session, {
2355
+ const applyRefresh = (authoringState) => applyLocalEditorUpdatedAssetRefresh(authoringState.session, {
1519
2356
  assetId: input.assetId,
1520
2357
  assets,
1521
2358
  hydrateDocumentAssets: options.documentAdapter.hydrateDocumentAssets,
@@ -1526,6 +2363,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
1526
2363
  ? { compareDocuments: options.documentAdapter.compareDocuments }
1527
2364
  : {}),
1528
2365
  });
2366
+ const result = await state.runBrowserAuthoringMutation('asset.library.refreshUpdated', JSON.stringify(input), applyRefresh);
1529
2367
  state.assets = assets;
1530
2368
  state.projectFiles = projectFilesResult.projectFiles;
1531
2369
  state.projectFilesWarning = projectFilesResult.warning;
@@ -1579,19 +2417,33 @@ function createLocalEditorHarnessImplementation(options, construction) {
1579
2417
  }
1580
2418
  },
1581
2419
  createAssetFromAssetId(assetId, createOptions) {
1582
- const result = addAssetToDocument(state, options, assetId, createOptions);
2420
+ const execute = (session = state.session ?? undefined) => addAssetToDocument(state, options, assetId, createOptions, session);
2421
+ const immediate = state.tryRunBrowserAuthoringMutationImmediately('object.createFromAsset', JSON.stringify({ assetId, createOptions }), session => execute(session));
2422
+ const result = immediate.ok ? immediate.value : {
2423
+ ok: false,
2424
+ assetId,
2425
+ changed: false,
2426
+ status: `Asset creation unavailable: ${immediate.reason}`,
2427
+ error: 'authoring_busy',
2428
+ };
1583
2429
  harness.render();
1584
2430
  return result;
1585
2431
  },
1586
2432
  async undo(historyOptions) {
1587
- const result = applyHistoryOperation(state, options, 'undo', historyOptions);
1588
- if (result.ok)
2433
+ if (projectCommandService) {
2434
+ return runProjectServiceHistoryOperation('undo', historyOptions);
2435
+ }
2436
+ const result = await state.runBrowserAuthoringMutation('history.undo', JSON.stringify(historyOptions ?? {}), authoringState => applyHistoryOperation(authoringState, options, 'undo', historyOptions));
2437
+ if (result.ok && !applyHistoryPresentation(result.presentationImpact))
1589
2438
  harness.render();
1590
2439
  return waitForHistoryOperationProjection(state, result);
1591
2440
  },
1592
2441
  async redo(historyOptions) {
1593
- const result = applyHistoryOperation(state, options, 'redo', historyOptions);
1594
- if (result.ok)
2442
+ if (projectCommandService) {
2443
+ return runProjectServiceHistoryOperation('redo', historyOptions);
2444
+ }
2445
+ const result = await state.runBrowserAuthoringMutation('history.redo', JSON.stringify(historyOptions ?? {}), authoringState => applyHistoryOperation(authoringState, options, 'redo', historyOptions));
2446
+ if (result.ok && !applyHistoryPresentation(result.presentationImpact))
1595
2447
  harness.render();
1596
2448
  return waitForHistoryOperationProjection(state, result);
1597
2449
  },
@@ -1599,13 +2451,13 @@ function createLocalEditorHarnessImplementation(options, construction) {
1599
2451
  await loadEditorDocument();
1600
2452
  await enterEditorWorld();
1601
2453
  },
1602
- async saveScene(executor, saveOptions) {
2454
+ async saveScene(executor, saveOptions, serializedAccess) {
1603
2455
  cancelActiveOperation(state);
1604
2456
  const validationPhases = [...new Set([
1605
2457
  ...(saveOptions?.validationPhases ?? []),
1606
2458
  'save',
1607
2459
  ])];
1608
- const refreshResult = await runValidatedExclusiveDocumentOperation('save', () => harness.reloadAssets(), executor, validationPhases);
2460
+ const refreshResult = await runValidatedExclusiveDocumentOperation('save', () => harness.reloadAssets(serializedAccess), executor, validationPhases);
1609
2461
  if (!refreshResult.accepted)
1610
2462
  return false;
1611
2463
  const assetRefresh = refreshResult.value;
@@ -1620,6 +2472,19 @@ function createLocalEditorHarnessImplementation(options, construction) {
1620
2472
  const prefabTransaction = state.prefabStage?.transaction ?? null;
1621
2473
  try {
1622
2474
  prefabTransaction?.assertParentUnchanged();
2475
+ if (projectCommandService) {
2476
+ await invokeProjectServiceCommand('document.save');
2477
+ const snapshot = await readProjectServiceSnapshot();
2478
+ if (!snapshot)
2479
+ throw new Error('Project Editor Service did not return the saved checkpoint.');
2480
+ await applyProjectServiceSnapshot(snapshot, true);
2481
+ state.summary = summarizeDocument(options, snapshot.document, state.source);
2482
+ state.status = prefabTransaction ? 'Prefab saved' : 'Scene saved';
2483
+ state.statusTone = 'success';
2484
+ state.statusToneStatus = state.status;
2485
+ state.statusDetails = '';
2486
+ return true;
2487
+ }
1623
2488
  let savedSource = source;
1624
2489
  let result;
1625
2490
  if (source && options.authoringHost) {
@@ -1663,10 +2528,34 @@ function createLocalEditorHarnessImplementation(options, construction) {
1663
2528
  state.source = savedSource ?? null;
1664
2529
  if (state.session) {
1665
2530
  if (prefabTransaction && state.prefabStage?.transaction === prefabTransaction) {
1666
- prefabTransaction.commitSavedDocument(preparedDocument, savedSource ?? undefined, `Apply Prefab ${state.prefabStage.descriptor.label}`);
2531
+ if (!state.authoringHost)
2532
+ throw new Error('Cannot save Prefab without an Editor Authoring Host.');
2533
+ if (expectedAuthoredVersion === null)
2534
+ throw new Error('Cannot save without an expected document revision.');
2535
+ await state.authoringHost.markSavedWithRelatedSession(preparedDocument, {
2536
+ expectedDocumentRevision: expectedAuthoredVersion,
2537
+ source: savedSource ?? undefined,
2538
+ resetHistory: true,
2539
+ relatedSession: prefabTransaction.parentSession,
2540
+ expectedRelatedDocumentRevision: prefabTransaction.parentSession
2541
+ .getState({ cloneDocuments: false }).documentRevision,
2542
+ mutateRelatedSession(parentDraftSession) {
2543
+ prefabTransaction.stageParentSavedDocument(parentDraftSession, preparedDocument, savedSource ?? undefined, `Apply Prefab ${state.prefabStage?.descriptor.label ?? 'changes'}`);
2544
+ },
2545
+ ...(serializedAccess ? { serializedAccess } : {}),
2546
+ });
2547
+ prefabTransaction.acceptParentRevision();
1667
2548
  }
1668
2549
  else {
1669
- state.session.markSaved(preparedDocument, savedSource ?? undefined);
2550
+ if (!state.authoringHost)
2551
+ throw new Error('Cannot save without an Editor Authoring Host.');
2552
+ if (expectedAuthoredVersion === null)
2553
+ throw new Error('Cannot save without an expected document revision.');
2554
+ await state.authoringHost.markSaved(preparedDocument, {
2555
+ expectedDocumentRevision: expectedAuthoredVersion,
2556
+ source: savedSource ?? undefined,
2557
+ ...(serializedAccess ? { serializedAccess } : {}),
2558
+ });
1670
2559
  }
1671
2560
  }
1672
2561
  else {
@@ -1678,7 +2567,7 @@ function createLocalEditorHarnessImplementation(options, construction) {
1678
2567
  cloneDocument: options.documentAdapter.cloneDocument,
1679
2568
  compareDocuments: options.documentAdapter.compareDocuments,
1680
2569
  reduceDocument: options.documentAdapter.reduceDocument,
1681
- isDocumentStructureChange: options.documentAdapter.isHierarchyStructureChange,
2570
+ isDocumentStructureChange: command => resolveLocalEditorDocumentStructureChange(options.documentAdapter.isHierarchyStructureChange, command),
1682
2571
  onChange: createSessionChangeObserver(() => mainSession, {
1683
2572
  document: PLAYABLE_LOCAL_EDITOR_MAIN_DOCUMENT_SCOPE_ID,
1684
2573
  session: PLAYABLE_LOCAL_EDITOR_MAIN_SESSION_SCOPE_ID,
@@ -1687,9 +2576,14 @@ function createLocalEditorHarnessImplementation(options, construction) {
1687
2576
  });
1688
2577
  state.session = mainSession;
1689
2578
  try {
2579
+ await replaceAuthoringHost(mainSession);
1690
2580
  attachLifecycleOwnedSessionAuthority();
2581
+ activateCommandAuthority();
1691
2582
  }
1692
2583
  catch (error) {
2584
+ state.authoringHost?.dispose();
2585
+ state.authoringHost = null;
2586
+ state.replaceProjectionSessionAuthority(null);
1693
2587
  state.session = null;
1694
2588
  state.source = null;
1695
2589
  throw error;
@@ -1736,20 +2630,22 @@ function createLocalEditorHarnessImplementation(options, construction) {
1736
2630
  };
1737
2631
  agentLifecycleController = {
1738
2632
  enterEditor: () => harness.enterEditor(),
1739
- saveScene: (_operation, _context, executor) => harness.saveScene(executor),
2633
+ saveScene: (_operation, context, executor) => harness.saveScene(executor, undefined, context
2634
+ ?.serializedAuthoringAccess),
1740
2635
  saveAndRunGame: (_context, executor) => harness.saveAndRunGame(executor),
1741
2636
  async discardAndRunGame(_context, executor) {
1742
2637
  await harness.discardAndRunGame(executor);
1743
2638
  return true;
1744
2639
  },
1745
- async prepareSaveAndRunGame(_context, executor) {
2640
+ async prepareSaveAndRunGame(context, executor) {
1746
2641
  return harness.runExclusiveDocumentOperation(async (transactionExecutor) => {
1747
2642
  const initialValidation = await runValidatedExclusiveDocumentOperation('run', async () => true, transactionExecutor);
1748
2643
  if (!initialValidation.accepted)
1749
2644
  return { accepted: false };
1750
2645
  const saved = await harness.saveScene(transactionExecutor, {
1751
2646
  validationPhases: ['run', 'save'],
1752
- });
2647
+ }, context
2648
+ ?.serializedAuthoringAccess);
1753
2649
  if (!saved)
1754
2650
  return { accepted: false };
1755
2651
  const finalValidation = await runValidatedExclusiveDocumentOperation('run', async (documentRevision) => documentRevision, transactionExecutor);
@@ -1797,21 +2693,26 @@ function createLocalEditorHarnessImplementation(options, construction) {
1797
2693
  }, executor);
1798
2694
  },
1799
2695
  };
1800
- const agentBridgeCapabilitySourceOperations = {
1801
- createAssetFromAssetId: (assetId, createOptions) => harness.createAssetFromAssetId(assetId, createOptions),
1802
- getPrefabStage: () => summarizeAgentBridgePrefabStage(state, options),
1803
- getPrefabStageDetails: () => summarizeAgentBridgePrefabStageDetails(state, options),
1804
- openPrefabStage: input => runExclusiveResult(state, harness.render, async (executor) => {
1805
- const previousStage = summarizeAgentBridgePrefabStage(state, options);
2696
+ const commandServiceCapabilitySourceOperations = {
2697
+ createAssetFromAssetId: (assetId, createOptions) => {
2698
+ const result = addAssetToDocument(state, options, assetId, createOptions);
2699
+ harness.render();
2700
+ return result;
2701
+ },
2702
+ getPrefabStage: () => summarizeEditorCommandPrefabStage(state, options),
2703
+ getPrefabStageDetails: () => summarizeEditorCommandPrefabStageDetails(state, options),
2704
+ openPrefabStage: (input, access) => runExclusiveResult(state, harness.render, async (executor) => {
2705
+ const serializedAccess = access;
2706
+ const previousStage = summarizeEditorCommandPrefabStage(state, options);
1806
2707
  if (previousStage
1807
2708
  && previousStage.assetId !== input.assetId
1808
2709
  && state.prefabStage?.transaction.snapshot().stageDirty) {
1809
2710
  if (input.dirtyAction === 'save') {
1810
- if (!await harness.saveScene(executor))
2711
+ if (!await harness.saveScene(executor, undefined, serializedAccess))
1811
2712
  return null;
1812
2713
  }
1813
2714
  else if (input.dirtyAction === 'discard') {
1814
- discardPrefabStageChanges();
2715
+ await discardPrefabStageChanges(serializedAccess);
1815
2716
  }
1816
2717
  else {
1817
2718
  state.status = 'Prefab has unsaved changes';
@@ -1826,32 +2727,33 @@ function createLocalEditorHarnessImplementation(options, construction) {
1826
2727
  assetId: input.assetId,
1827
2728
  browserAssetId: input.browserAssetId,
1828
2729
  assetKind: 'prefab',
1829
- });
1830
- const stage = summarizeAgentBridgePrefabStage(state, options);
2730
+ }, serializedAccess);
2731
+ const stage = summarizeEditorCommandPrefabStage(state, options);
1831
2732
  if (!opened || !stage)
1832
2733
  return null;
1833
- const alreadyOpen = isSameAgentBridgePrefabStage(previousStage, stage);
2734
+ const alreadyOpen = isSameEditorCommandPrefabStage(previousStage, stage);
1834
2735
  return {
1835
2736
  opened: !alreadyOpen,
1836
2737
  alreadyOpen,
1837
- prefab: createAgentBridgePrefabSummaryFromStage(stage),
2738
+ prefab: createEditorCommandPrefabSummaryFromStage(stage),
1838
2739
  stage,
1839
2740
  status: state.status,
1840
2741
  };
1841
2742
  }, busyOperationExecutors),
1842
- exitPrefabStage: (input = {}) => runExclusiveResult(state, harness.render, async (executor) => {
1843
- const previousStage = summarizeAgentBridgePrefabStage(state, options);
2743
+ exitPrefabStage: (input = {}, access) => runExclusiveResult(state, harness.render, async (executor) => {
2744
+ const serializedAccess = access;
2745
+ const previousStage = summarizeEditorCommandPrefabStage(state, options);
1844
2746
  if (state.prefabStage?.transaction.snapshot().stageDirty) {
1845
2747
  if (input.dirtyAction === 'save') {
1846
- if (!await harness.saveScene(executor)) {
2748
+ if (!await harness.saveScene(executor, undefined, serializedAccess)) {
1847
2749
  return { exited: false, wasOpen: true, previousStage, status: state.status };
1848
2750
  }
1849
2751
  }
1850
2752
  else if (input.dirtyAction === 'discard') {
1851
- discardPrefabStageChanges();
2753
+ await discardPrefabStageChanges(serializedAccess);
1852
2754
  }
1853
2755
  }
1854
- const exited = await exitPrefabStage(state, options, 'reject');
2756
+ const exited = await exitPrefabStage(state, options, 'reject', serializedAccess);
1855
2757
  return {
1856
2758
  exited,
1857
2759
  wasOpen: !!previousStage,
@@ -1859,8 +2761,8 @@ function createLocalEditorHarnessImplementation(options, construction) {
1859
2761
  status: exited ? state.status : previousStage ? state.status : 'No Prefab stage is open.',
1860
2762
  };
1861
2763
  }, busyOperationExecutors),
1862
- savePrefabStage: () => runExclusiveResult(state, harness.render, executor => (runPrefabContextAction(PREFAB_STAGE_CONTEXT_ACTION_IDS.save, executor)), busyOperationExecutors),
1863
- discardPrefabStage: () => discardPrefabStageChanges(),
2764
+ savePrefabStage: access => runExclusiveResult(state, harness.render, executor => (runPrefabContextAction(PREFAB_STAGE_CONTEXT_ACTION_IDS.save, executor, access)), busyOperationExecutors),
2765
+ discardPrefabStage: access => discardPrefabStageChanges(access),
1864
2766
  selectPrefabStageItem: itemId => selectPrefabStageItemWithPreviewSync(state, options, itemId),
1865
2767
  runPrefabStageAssetAction: input => patchBrowserAssetAction(state, options, input),
1866
2768
  getHierarchyReadModel,
@@ -1933,10 +2835,10 @@ function createLocalEditorHarnessImplementation(options, construction) {
1933
2835
  })),
1934
2836
  };
1935
2837
  },
1936
- executeHierarchyOperation: async (input) => {
2838
+ executeHierarchyOperation: async (input, serializedAccess) => {
1937
2839
  const model = getRequiredHierarchyViewModel();
1938
2840
  const { snapshot } = model;
1939
- const result = await hierarchyKernel.dispatch({
2841
+ const invocation = {
1940
2842
  type: 'operation.execute',
1941
2843
  operationId: input.operationId,
1942
2844
  expectedAuthorityToken: model.authorityToken,
@@ -1948,9 +2850,13 @@ function createLocalEditorHarnessImplementation(options, construction) {
1948
2850
  contextNodeId: 'contextNodeId' in input ? input.contextNodeId ?? null : snapshot.activeId,
1949
2851
  targetIds: input.targetIds ?? [...snapshot.selectedIds],
1950
2852
  activeId: 'activeId' in input ? input.activeId ?? null : snapshot.activeId,
2853
+ ...(serializedAccess !== undefined
2854
+ ? { [SERIALIZED_HIERARCHY_INVOCATION]: serializedAccess }
2855
+ : {}),
1951
2856
  },
1952
2857
  ...(input.input ? { input: input.input } : {}),
1953
- });
2858
+ };
2859
+ const result = await hierarchyKernel.dispatch(invocation);
1954
2860
  return {
1955
2861
  ok: result.ok,
1956
2862
  changed: result.changed,
@@ -2012,10 +2918,10 @@ function createLocalEditorHarnessImplementation(options, construction) {
2012
2918
  },
2013
2919
  runRenderingAction: actionId => applyRenderingAction(state, options, { actionId }, () => harness.render()),
2014
2920
  };
2015
- const registerAgentBridgeCapabilities = () => {
2016
- if (state.unregisterAgentBridgeCapabilities)
2921
+ const registerCommandCapabilities = () => {
2922
+ if (state.unregisterCommandCapabilities)
2017
2923
  return;
2018
- const unregisterStandardAgentCapabilities = registerLocalEditorAgentBridgeCapabilities(state.agentBridge, createLocalEditorAgentBridgeCapabilitySources(state, options, () => harness.render(), context => runExclusive(state, harness.render, () => agentLifecycleController.enterEditor(context), busyOperationExecutors), async (context) => {
2924
+ const unregisterStandardAgentCapabilities = registerLocalEditorCommandCapabilities(state.commandService, createLocalEditorCommandCapabilitySources(state, options, () => harness.render(), context => runExclusive(state, harness.render, () => agentLifecycleController.enterEditor(context), busyOperationExecutors), async (context) => {
2019
2925
  const prepared = await runExclusiveResult(state, harness.render, executor => agentLifecycleController.prepareSaveAndRunGame(context, executor), busyOperationExecutors);
2020
2926
  if (prepared.accepted)
2021
2927
  scheduleEditorLifecycleExit(executor => prepared.run(executor));
@@ -2025,10 +2931,12 @@ function createLocalEditorHarnessImplementation(options, construction) {
2025
2931
  if (prepared.accepted)
2026
2932
  scheduleEditorLifecycleExit(executor => prepared.run(executor));
2027
2933
  return prepared.accepted;
2028
- }, agentBridgeCapabilitySourceOperations));
2934
+ }, commandServiceCapabilitySourceOperations), state.authoringHost ?? undefined);
2029
2935
  try {
2030
- const unregisterPluginAgentCapabilities = registerEditorPluginAgentCapabilities(state.agentBridge, options.pluginHost);
2031
- state.unregisterAgentBridgeCapabilities = () => {
2936
+ const unregisterPluginAgentCapabilities = state.authoringHost
2937
+ ? registerEditorPluginCommands(state.commandService, options.pluginHost, state.authoringHost)
2938
+ : () => undefined;
2939
+ state.unregisterCommandCapabilities = () => {
2032
2940
  const errors = [];
2033
2941
  try {
2034
2942
  unregisterPluginAgentCapabilities();
@@ -2043,44 +2951,50 @@ function createLocalEditorHarnessImplementation(options, construction) {
2043
2951
  errors.push(error);
2044
2952
  }
2045
2953
  if (errors.length > 0) {
2046
- const error = new Error('Local Editor Agent capability cleanup failed.');
2954
+ const error = new Error('Local Editor command cleanup failed.');
2047
2955
  error.errors = errors;
2048
2956
  throw error;
2049
2957
  }
2050
2958
  };
2959
+ if (projectCommandService && !disposeBrowserCommandAttachment) {
2960
+ const handlers = Object.fromEntries(state.commandService.commandSystem.listManifests()
2961
+ .map(manifest => [manifest.id, async (input, request) => {
2962
+ if (harnessDisposed)
2963
+ throw new Error('Local editor harness has been disposed.');
2964
+ const result = await state.commandService.callCapability(manifest.id, input, {
2965
+ ...(request.invocation.traceId ? { traceId: request.invocation.traceId } : {}),
2966
+ ...(request.invocation.traceStepId ? { traceStepId: request.invocation.traceStepId } : {}),
2967
+ });
2968
+ if (!result.ok)
2969
+ throw new Error(result.diagnostics.map(diagnostic => diagnostic.message).join('; '));
2970
+ return result.output;
2971
+ }]));
2972
+ disposeBrowserCommandAttachment = projectCommandService.attach({
2973
+ attachmentId: browserCommandAttachmentId,
2974
+ label: 'Local Editor Command Host',
2975
+ handlers,
2976
+ }).catch(() => null);
2977
+ }
2051
2978
  }
2052
2979
  catch (error) {
2053
2980
  try {
2054
2981
  unregisterStandardAgentCapabilities();
2055
2982
  }
2056
2983
  catch (cleanupError) {
2057
- const combined = new Error('Plugin Agent capability registration failed and standard capability rollback reported errors.');
2984
+ const combined = new Error('Plugin command registration failed and standard command rollback reported errors.');
2058
2985
  combined.errors = [error, cleanupError];
2059
2986
  throw combined;
2060
2987
  }
2061
2988
  throw error;
2062
2989
  }
2063
2990
  };
2064
- const activateAgentBridgeAuthority = () => {
2065
- registerAgentBridgeCapabilities();
2066
- if (state.agentBridgeTransport)
2067
- return;
2068
- state.agentBridgeTransport = createLocalEditorAgentBridgeWebSocketTransportFromQuery(state.agentBridge, {
2069
- metadata: options.agentBridge?.sessionMetadata,
2070
- onStatusChange: agentBridgeStatus => {
2071
- state.agentBridgeStatus = agentBridgeStatus;
2072
- harness.render();
2073
- },
2074
- });
2991
+ const activateCommandAuthority = () => {
2992
+ registerCommandCapabilities();
2075
2993
  };
2076
- const disposeAgentBridgeAuthority = () => {
2077
- if (state.agentBridgeTransport) {
2078
- state.agentBridgeTransport.dispose();
2079
- state.agentBridgeTransport = null;
2080
- }
2081
- if (state.unregisterAgentBridgeCapabilities) {
2082
- state.unregisterAgentBridgeCapabilities();
2083
- state.unregisterAgentBridgeCapabilities = null;
2994
+ const disposeCommandAuthority = () => {
2995
+ if (state.unregisterCommandCapabilities) {
2996
+ state.unregisterCommandCapabilities();
2997
+ state.unregisterCommandCapabilities = null;
2084
2998
  }
2085
2999
  };
2086
3000
  const attachLifecycleOwnedApplicationAuthority = () => {
@@ -2088,46 +3002,14 @@ function createLocalEditorHarnessImplementation(options, construction) {
2088
3002
  return;
2089
3003
  if (!unsubscribeLifecycleRender)
2090
3004
  attachLifecycleRenderSubscription();
2091
- activateAgentBridgeAuthority();
2092
- attachAgentBridgeLifecycleParticipant();
3005
+ activateCommandAuthority();
2093
3006
  if (!releaseApplicationAuthorityScopeResource) {
2094
3007
  releaseApplicationAuthorityScopeResource = lifecycleController.addScopeResource(PLAYABLE_LOCAL_EDITOR_APPLICATION_SCOPE_ID, () => {
2095
- disposeAgentBridgeAuthority();
3008
+ disposeCommandAuthority();
2096
3009
  releaseApplicationAuthorityScopeResource = null;
2097
3010
  });
2098
3011
  }
2099
3012
  };
2100
- const reportAgentBridgeLifecycleTransition = (event) => {
2101
- const transitionId = event.transition.id;
2102
- const transport = agentBridgeLifecycleTransports.get(transitionId)
2103
- ?? state.agentBridgeTransport;
2104
- transport?.reportLifecycleTransition(event);
2105
- if (event.status !== 'started')
2106
- agentBridgeLifecycleTransports.delete(transitionId);
2107
- };
2108
- const attachAgentBridgeLifecycleParticipant = () => {
2109
- unregisterAgentBridgeLifecycleParticipant?.();
2110
- unregisterAgentBridgeLifecycleParticipant = null;
2111
- if (!lifecycleController)
2112
- return;
2113
- unregisterAgentBridgeLifecycleParticipant = lifecycleController.registerParticipant({
2114
- id: 'fps.editor.agent-bridge.lifecycle-trace',
2115
- transitions: FPS_EDITOR_LIFECYCLE_TRANSITION_TYPES,
2116
- prepare(context) {
2117
- const transport = state.agentBridgeTransport;
2118
- if (transport)
2119
- agentBridgeLifecycleTransports.set(context.transition.id, transport);
2120
- reportAgentBridgeLifecycleTransition({
2121
- status: 'started',
2122
- transition: context.transition,
2123
- previous: context.before,
2124
- current: context.getSnapshot(),
2125
- });
2126
- },
2127
- settled: reportAgentBridgeLifecycleTransition,
2128
- failed: reportAgentBridgeLifecycleTransition,
2129
- });
2130
- };
2131
3013
  const attachLifecycleRenderSubscription = () => {
2132
3014
  releaseLifecycleRenderScopeResource?.();
2133
3015
  releaseLifecycleRenderScopeResource = null;
@@ -2136,7 +3018,26 @@ function createLocalEditorHarnessImplementation(options, construction) {
2136
3018
  if (!lifecycleController)
2137
3019
  return;
2138
3020
  const lifecycle = lifecycleController.lifecycle;
2139
- const unsubscribe = lifecycle.subscribe(() => harness.render());
3021
+ const unsubscribe = lifecycle.subscribe(() => {
3022
+ let presentationApplied = false;
3023
+ try {
3024
+ presentationApplied = ui.updatePresentationDelta?.({
3025
+ pluginPanel: createLocalEditorPluginPanelState(options.pluginHost, state.lifecycle),
3026
+ }) === true;
3027
+ }
3028
+ catch {
3029
+ // The authoritative full render below preserves lifecycle presentation on adapter failure.
3030
+ }
3031
+ if (presentationApplied)
3032
+ lifecyclePresentationOnlyRenderDepth += 1;
3033
+ try {
3034
+ harness.render();
3035
+ }
3036
+ finally {
3037
+ if (presentationApplied)
3038
+ lifecyclePresentationOnlyRenderDepth -= 1;
3039
+ }
3040
+ });
2140
3041
  unsubscribeLifecycleRender = unsubscribe;
2141
3042
  releaseLifecycleRenderScopeResource = lifecycleController.addScopeResource(PLAYABLE_LOCAL_EDITOR_APPLICATION_SCOPE_ID, () => {
2142
3043
  unsubscribe();
@@ -2149,14 +3050,18 @@ function createLocalEditorHarnessImplementation(options, construction) {
2149
3050
  if (!lifecycleController || !state.session)
2150
3051
  return;
2151
3052
  if (!releaseMainSessionScopeResource) {
2152
- releaseMainSessionScopeResource = lifecycleController.addScopeResource(PLAYABLE_LOCAL_EDITOR_MAIN_SESSION_SCOPE_ID, () => {
3053
+ releaseMainSessionScopeResource = lifecycleController.addScopeResource(PLAYABLE_LOCAL_EDITOR_MAIN_SESSION_SCOPE_ID, async () => {
3054
+ state.authoringHost?.dispose();
3055
+ await state.authoringHost?.drain();
3056
+ state.authoringHost = null;
3057
+ state.replaceProjectionSessionAuthority(null);
2153
3058
  state.session = null;
2154
3059
  state.source = null;
2155
3060
  releaseMainSessionScopeResource = null;
2156
3061
  });
2157
3062
  }
2158
3063
  };
2159
- activateAgentBridgeAuthority();
3064
+ activateCommandAuthority();
2160
3065
  registerLocalEditorHarnessLifecycleOperations(harness, {
2161
3066
  runGame: startGameRuntime,
2162
3067
  validateRun: async () => {
@@ -2197,9 +3102,45 @@ function createLocalEditorHarnessImplementation(options, construction) {
2197
3102
  state.statusDetails = stringifyLocalEditorError(error) ?? 'Unknown Tool activation error';
2198
3103
  harness.render();
2199
3104
  });
3105
+ if (projectCommandService) {
3106
+ void projectCommandService.watchAuthoring({
3107
+ onSnapshot: snapshot => applyProjectServiceSnapshot(snapshot),
3108
+ async onEvents(events) {
3109
+ if (events.length === 0)
3110
+ return;
3111
+ const snapshot = await readProjectServiceSnapshot();
3112
+ if (snapshot)
3113
+ await applyProjectServiceSnapshot(snapshot);
3114
+ },
3115
+ onIdle: () => state.reconcilePendingProjectServiceSnapshot(),
3116
+ onError(error) {
3117
+ if (harnessDisposed)
3118
+ return;
3119
+ reportBrowserAuthoringFailure(state, () => harness.render(), error);
3120
+ },
3121
+ }).then((stop) => {
3122
+ if (harnessDisposed)
3123
+ stop();
3124
+ else
3125
+ stopProjectServiceWatch = stop;
3126
+ }).catch((error) => {
3127
+ if (harnessDisposed)
3128
+ return;
3129
+ state.status = 'Project Editor Service connection failed';
3130
+ state.statusTone = 'error';
3131
+ state.statusToneStatus = state.status;
3132
+ state.statusDetails = stringifyLocalEditorError(error) ?? 'Unknown Editor Service error';
3133
+ harness.render();
3134
+ });
3135
+ }
2200
3136
  harness.render();
2201
3137
  return harness;
2202
3138
  }
3139
+ let localEditorAuthoringServiceSequence = 0;
3140
+ function createLocalEditorAuthoringServiceInstanceId() {
3141
+ localEditorAuthoringServiceSequence += 1;
3142
+ return `local-editor-${Date.now().toString(36)}-${localEditorAuthoringServiceSequence.toString(36)}`;
3143
+ }
2203
3144
  function createLocalEditorDocumentFingerprint(document) {
2204
3145
  if (document == null)
2205
3146
  return null;
@@ -2221,11 +3162,13 @@ function disposeLocalEditorHarnessResources(state, ui, hierarchyKernel) {
2221
3162
  errors.push(error);
2222
3163
  }
2223
3164
  };
2224
- dispose(() => state.agentBridgeTransport?.dispose(), () => { state.agentBridgeTransport = null; });
2225
- dispose(() => state.unregisterAgentBridgeCapabilities?.(), () => {
2226
- state.unregisterAgentBridgeCapabilities = null;
3165
+ dispose(() => state.unregisterCommandCapabilities?.(), () => {
3166
+ state.unregisterCommandCapabilities = null;
3167
+ });
3168
+ dispose(() => disposeEditorWorld(state), () => {
3169
+ state.replaceProjectionSessionAuthority(null);
3170
+ state.session = null;
2227
3171
  });
2228
- dispose(() => disposeEditorWorld(state), () => { state.session = null; });
2229
3172
  dispose(() => ui.dispose());
2230
3173
  dispose(() => hierarchyKernel.setProviders([]));
2231
3174
  if (errors.length > 0) {
@@ -2274,7 +3217,7 @@ export function mergeLocalEditorHarnessInspectorComponentSections(input) {
2274
3217
  }),
2275
3218
  };
2276
3219
  }
2277
- function isAgentBridgeObjectSelectable(options, document, objectId, hierarchyItems) {
3220
+ function isEditorCommandObjectSelectable(options, document, objectId, hierarchyItems) {
2278
3221
  const inHierarchy = hierarchyItems.some(item => item.id === objectId);
2279
3222
  const hasProjectionNode = !!options.documentAdapter.getProjectionNode(document, objectId);
2280
3223
  if (!inHierarchy && !hasProjectionNode)
@@ -2678,10 +3621,31 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
2678
3621
  },
2679
3622
  onProjectionReady(event) {
2680
3623
  getLocalEditorEditingContext(state).handleProjectionReady(event.nodeId);
3624
+ if (isProjectionInspectorRefreshSuppressed(state, event.nodeId, event.batchId)) {
3625
+ renderPreviewUi({
3626
+ status: state.status,
3627
+ statusTone: state.statusToneStatus === state.status ? state.statusTone : 'default',
3628
+ statusDetails: state.statusToneStatus === state.status ? state.statusDetails : '',
3629
+ session: createUiSessionState(state),
3630
+ });
3631
+ return;
3632
+ }
2681
3633
  renderUi();
2682
3634
  },
2683
3635
  onProjectionBatchSettled(event) {
2684
3636
  getLocalEditorEditingContext(state).handleProjectionBatchSettled(event.batchId);
3637
+ const suppressInspectorRefresh = state.suppressedProjectionInspectorRefreshBatchIds.has(event.batchId)
3638
+ || (event.nodeIds ?? []).some(nodeId => state.suppressedProjectionInspectorRefreshNodeBatches.has(nodeId));
3639
+ releaseProjectionInspectorRefreshSuppression(state, event.batchId);
3640
+ if (suppressInspectorRefresh) {
3641
+ renderPreviewUi({
3642
+ status: state.status,
3643
+ statusTone: state.statusToneStatus === state.status ? state.statusTone : 'default',
3644
+ statusDetails: state.statusToneStatus === state.status ? state.statusDetails : '',
3645
+ session: createUiSessionState(state),
3646
+ });
3647
+ return;
3648
+ }
2685
3649
  renderUi();
2686
3650
  },
2687
3651
  });
@@ -2710,27 +3674,41 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
2710
3674
  state.statusTone = policyStatus ? 'warning' : 'default';
2711
3675
  state.statusToneStatus = state.status;
2712
3676
  state.statusDetails = '';
2713
- render();
3677
+ renderPreviewUi({
3678
+ status: state.status,
3679
+ statusTone: state.statusTone,
3680
+ statusDetails: state.statusDetails,
3681
+ transformTool: {
3682
+ activeTool: event.tool,
3683
+ activeSpace: event.space,
3684
+ activeConstraint: event.constraint,
3685
+ dragPhase: 'dragging',
3686
+ draggingNodeId: event.nodeId ?? event.activeId ?? null,
3687
+ },
3688
+ });
3689
+ requestEditorSceneFrame(state, 'gizmo-drag-start');
2714
3690
  },
2715
3691
  onDragUpdate(event) {
2716
3692
  updateActiveTransformPreview(state, event);
2717
- syncMarkerOverlayToSceneArtifacts(state, options, state.session?.getState().selection ?? { selectedIds: [], activeId: null });
3693
+ syncMarkerOverlayToSceneArtifacts(state, options, state.session?.getSelection() ?? { selectedIds: [], activeId: null });
2718
3694
  state.gizmo?.refreshSelection();
2719
- preview('gizmo-drag-update');
3695
+ scheduleInspectorPresentationPreview(state, options, renderPreviewUi, 'gizmo-drag-update');
3696
+ requestEditorSceneFrame(state, 'gizmo-drag-update');
2720
3697
  },
2721
3698
  onDragEnd(event) {
2722
3699
  sceneViewInteraction.endGizmoDrag();
2723
3700
  clearActiveTransformPreview(state);
2724
- const changed = getLocalEditorEditingContext(state).commitTransform(event);
2725
- if (!changed)
2726
- requestEditorSceneFrame(state, 'gizmo-drag-end');
2727
- renderUi();
3701
+ void state.runBrowserAuthoringMutation('transform.gizmo.commit', JSON.stringify(event), authoringState => getLocalEditorEditingContext(authoringState).commitTransform(event)).then(changed => {
3702
+ if (!changed)
3703
+ requestEditorSceneFrame(state, 'gizmo-drag-end');
3704
+ renderUi();
3705
+ }).catch(error => reportBrowserAuthoringFailure(state, renderUi, error));
2728
3706
  },
2729
3707
  onDragCancel(event) {
2730
3708
  sceneViewInteraction.endGizmoDrag();
2731
3709
  clearActiveTransformPreview(state);
2732
- if (event.duplicate && cancelDuplicateDrag(state, options)) {
2733
- render();
3710
+ if (event.duplicate) {
3711
+ void state.runBrowserAuthoringMutation('transform.duplicate.cancel', JSON.stringify(event), authoringState => cancelDuplicateDrag(authoringState, options)).then(() => render()).catch(error => reportBrowserAuthoringFailure(state, render, error));
2734
3712
  return;
2735
3713
  }
2736
3714
  state.status = event.targetIds.length > 1
@@ -2741,8 +3719,9 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
2741
3719
  onDragBlocked(event) {
2742
3720
  sceneViewInteraction.endGizmoDrag();
2743
3721
  clearActiveTransformPreview(state);
2744
- if (event.duplicate)
2745
- cancelDuplicateDrag(state, options);
3722
+ if (event.duplicate) {
3723
+ void state.runBrowserAuthoringMutation('transform.duplicate.cancel', JSON.stringify(event), authoringState => cancelDuplicateDrag(authoringState, options)).catch(error => reportBrowserAuthoringFailure(state, renderUi, error));
3724
+ }
2746
3725
  requestEditorSceneFrame(state, 'gizmo-drag-blocked');
2747
3726
  state.status = formatBlockedGizmoTransformStatus(event);
2748
3727
  state.statusTone = 'warning';
@@ -2750,10 +3729,17 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
2750
3729
  renderUi();
2751
3730
  },
2752
3731
  onDuplicateDragStart(input) {
2753
- const result = getLocalEditorEditingContext(state).beginDuplicateDrag(input);
2754
- if (!result)
3732
+ const result = state.tryRunBrowserAuthoringMutationImmediately('transform.duplicate.begin', JSON.stringify(input), session => getLocalEditorEditingContext(state).beginDuplicateDrag(input, session));
3733
+ if (!result.ok) {
3734
+ state.status = `Duplicate drag unavailable: ${result.reason}`;
3735
+ state.statusTone = 'warning';
3736
+ state.statusToneStatus = state.status;
2755
3737
  renderUi();
2756
- return result;
3738
+ return null;
3739
+ }
3740
+ if (!result.value)
3741
+ renderUi();
3742
+ return result.value;
2757
3743
  },
2758
3744
  });
2759
3745
  state.gizmo = gizmo;
@@ -2865,8 +3851,10 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
2865
3851
  return;
2866
3852
  }
2867
3853
  if (event.state.intent === 'placement') {
2868
- if (commitArmedPlacement(state, options, event.originalEvent))
2869
- render();
3854
+ void state.runBrowserAuthoringMutation('asset.place', JSON.stringify({ x: event.originalEvent.clientX, y: event.originalEvent.clientY }), authoringState => commitArmedPlacement(authoringState, options, event.originalEvent)).then(changed => {
3855
+ if (changed)
3856
+ render();
3857
+ }).catch(error => reportBrowserAuthoringFailure(state, render, error));
2870
3858
  return;
2871
3859
  }
2872
3860
  if (event.state.intent === 'view-plane-move') {
@@ -3002,7 +3990,7 @@ function disposeEditorWorld(state) {
3002
3990
  });
3003
3991
  run(() => clearPrefabStageForEditorTeardown(state));
3004
3992
  run(() => cancelScheduledSelectionInspectorRefresh(state));
3005
- state.activeTransformPreview = null;
3993
+ clearActiveTransformPreview(state);
3006
3994
  disposeField(() => state.sceneViewInteraction, () => { state.sceneViewInteraction = null; });
3007
3995
  if (state.resizeHandler) {
3008
3996
  run(() => {
@@ -3202,7 +4190,7 @@ function requestEditorPreviewFrame(state, options, renderPreviewUi, reason) {
3202
4190
  function updateActiveTransformPreview(state, event) {
3203
4191
  const activeId = event.activeId ?? event.nodeId ?? event.targetIds[0] ?? null;
3204
4192
  if (!activeId) {
3205
- state.activeTransformPreview = null;
4193
+ clearActiveTransformPreview(state);
3206
4194
  return;
3207
4195
  }
3208
4196
  state.activeTransformPreview = {
@@ -3215,6 +4203,74 @@ function updateActiveTransformPreview(state, event) {
3215
4203
  }
3216
4204
  function clearActiveTransformPreview(state) {
3217
4205
  state.activeTransformPreview = null;
4206
+ cancelInspectorPresentationPreview(state);
4207
+ }
4208
+ function cancelInspectorPresentationPreview(state) {
4209
+ state.inspectorPresentationGeneration += 1;
4210
+ state.inspectorPresentationPatches = null;
4211
+ if (state.inspectorPresentationFrame !== null) {
4212
+ globalThis.cancelAnimationFrame?.(state.inspectorPresentationFrame);
4213
+ state.inspectorPresentationFrame = null;
4214
+ }
4215
+ }
4216
+ function scheduleInspectorPresentationPreview(state, options, renderPreviewUi, _reason) {
4217
+ const preview = state.activeTransformPreview;
4218
+ const transform = preview?.activeId ? preview.transforms[preview.activeId] : null;
4219
+ if (!preview?.activeId || !transform || !isEditorTransformTrsSnapshot(transform))
4220
+ return;
4221
+ const generation = state.inspectorPresentationGeneration;
4222
+ const targetId = preview.activeId;
4223
+ state.inspectorPresentationPatches = createTransformInspectorPresentationPatches(targetId, transform, generation);
4224
+ if (state.inspectorPresentationFrame !== null)
4225
+ return;
4226
+ const publish = () => {
4227
+ if (generation !== state.inspectorPresentationGeneration)
4228
+ return;
4229
+ state.inspectorPresentationFrame = null;
4230
+ const patches = state.inspectorPresentationPatches;
4231
+ state.inspectorPresentationPatches = null;
4232
+ if (!patches?.length || !state.world)
4233
+ return;
4234
+ renderPreviewUi({ inspectorPresentationPatches: patches });
4235
+ };
4236
+ if (typeof globalThis.requestAnimationFrame === 'function') {
4237
+ state.inspectorPresentationFrame = globalThis.requestAnimationFrame(publish);
4238
+ }
4239
+ else {
4240
+ publish();
4241
+ }
4242
+ }
4243
+ function createTransformInspectorPresentationPatches(targetId, transform, generation) {
4244
+ const patches = [];
4245
+ for (const group of ['position', 'rotation', 'scale']) {
4246
+ for (const axis of ['x', 'y', 'z']) {
4247
+ const rawValue = transform[group][axis];
4248
+ patches.push({
4249
+ targetId,
4250
+ propertyPath: `transform.${group}.${axis}`,
4251
+ value: group === 'rotation' ? radiansToInspectorDegrees(rawValue) : rawValue,
4252
+ generation,
4253
+ });
4254
+ }
4255
+ }
4256
+ return patches;
4257
+ }
4258
+ function createHistoryTransformInspectorPresentationPatches(state, document, selection) {
4259
+ const activeId = selection?.activeId ?? null;
4260
+ if (!activeId || !selection?.selectedIds.includes(activeId))
4261
+ return [];
4262
+ const transform = state.projection?.readNodeTransform(activeId) ?? null;
4263
+ if (!transform)
4264
+ return [];
4265
+ const preview = {
4266
+ targetIds: [...selection.selectedIds],
4267
+ activeId,
4268
+ transforms: { [activeId]: cloneEditorTransformSnapshot(transform) },
4269
+ };
4270
+ const resolved = getLocalEditorEditingContext(state).resolveInspectorPreviewTransform(document, activeId, transform, preview);
4271
+ return resolved && isEditorTransformTrsSnapshot(resolved)
4272
+ ? createTransformInspectorPresentationPatches(activeId, resolved, state.inspectorPresentationGeneration)
4273
+ : [];
3218
4274
  }
3219
4275
  function scheduleProjectionShadowPreviewRebuild(state, reason) {
3220
4276
  if (state.projectionShadowPreviewRebuildFrame !== null) {
@@ -3283,8 +4339,9 @@ async function runExclusiveLocalEditorDocumentOperation(state, render, operation
3283
4339
  render();
3284
4340
  }
3285
4341
  }
3286
- async function runExclusiveResult(state, render, action, executorStates) {
3287
- const lease = acquireLocalEditorBusyOperationLease(state, render, executorStates);
4342
+ async function runExclusiveResult(state, render, action, executorStates, options = {}) {
4343
+ const renderBoundary = options.renderBoundary !== false;
4344
+ const lease = acquireLocalEditorBusyOperationLease(state, render, executorStates, { renderBoundary });
3288
4345
  let failed = false;
3289
4346
  try {
3290
4347
  return await action(lease.executor);
@@ -3296,22 +4353,25 @@ async function runExclusiveResult(state, render, action, executorStates) {
3296
4353
  finally {
3297
4354
  lease.release();
3298
4355
  state.busy = false;
3299
- try {
3300
- render();
3301
- }
3302
- catch (error) {
3303
- if (!failed)
3304
- throw error;
4356
+ if (renderBoundary || failed) {
4357
+ try {
4358
+ render();
4359
+ }
4360
+ catch (error) {
4361
+ if (!failed)
4362
+ throw error;
4363
+ }
3305
4364
  }
3306
4365
  }
3307
4366
  }
3308
- function acquireLocalEditorBusyOperationLease(state, render, executorStates) {
4367
+ function acquireLocalEditorBusyOperationLease(state, render, executorStates, options = {}) {
3309
4368
  if (state.busy)
3310
4369
  throw new Error('Local editor host is busy. Retry this operation.');
3311
4370
  state.busy = true;
3312
4371
  const lease = createLocalEditorBusyOperationLease(state, render, executorStates);
3313
4372
  try {
3314
- render();
4373
+ if (options.renderBoundary !== false)
4374
+ render();
3315
4375
  return lease;
3316
4376
  }
3317
4377
  catch (error) {
@@ -3544,7 +4604,7 @@ function consumeMarkerRelationPickSelectionCommand(state, options, command, rend
3544
4604
  renderUi();
3545
4605
  return true;
3546
4606
  }
3547
- const handled = patchMarkerRelation(state, options, {
4607
+ const relationInput = {
3548
4608
  action: 'create',
3549
4609
  type: pick.type ?? '',
3550
4610
  fromKind: pick.from.kind,
@@ -3552,16 +4612,18 @@ function consumeMarkerRelationPickSelectionCommand(state, options, command, rend
3552
4612
  toKind: endpoint.kind,
3553
4613
  toId: endpoint.id,
3554
4614
  directed: pick.directed,
3555
- });
3556
- if (handled && state.markerRelationPick) {
3557
- state.markerRelationPick = {
3558
- ...state.markerRelationPick,
3559
- status: state.status,
3560
- statusTone: state.statusTone,
3561
- };
3562
- }
3563
- if (renderUi)
3564
- renderUi();
4615
+ };
4616
+ void state.runBrowserAuthoringMutation('marker.relation.pick.create', JSON.stringify(relationInput), authoringState => patchMarkerRelation(authoringState, options, relationInput)).then(handled => {
4617
+ if (handled && state.markerRelationPick) {
4618
+ state.markerRelationPick = {
4619
+ ...state.markerRelationPick,
4620
+ status: state.status,
4621
+ statusTone: state.statusTone,
4622
+ };
4623
+ }
4624
+ if (renderUi)
4625
+ renderUi();
4626
+ }).catch(error => reportBrowserAuthoringFailure(state, renderUi ?? (() => undefined), error));
3565
4627
  return true;
3566
4628
  }
3567
4629
  function readMarkerRelationPickTargetId(command) {
@@ -3617,13 +4679,14 @@ function renameSceneGraphNode(state, options, intent) {
3617
4679
  label: patch.label ?? `Rename ${intent.id}`,
3618
4680
  patch: patch.patch,
3619
4681
  targetId: intent.id,
3620
- ...withProjectionImpact(patch.projectionImpact),
4682
+ ...withProjectionImpact(createMetadataProjectionImpact([intent.id])),
4683
+ ...withPresentationImpact({ hierarchy: { labelIds: [intent.id] }, inspector: ['summary'] }),
3621
4684
  });
3622
4685
  if (!result.documentChanged) {
3623
4686
  state.status = `Rename unchanged: ${intent.id}`;
3624
4687
  return 'rejected';
3625
4688
  }
3626
- rebuildProjectionFromDocument(state, options, result.workingDocument, result.selection);
4689
+ applyRenameProjectionImpact(state, options, result.workingDocument, result.selection, [intent.id], 'scene-rename');
3627
4690
  state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
3628
4691
  state.status = patch.label ?? `Renamed ${intent.id}`;
3629
4692
  return 'changed';
@@ -4139,7 +5202,7 @@ function sanitizeSelection(state, options, document, selection) {
4139
5202
  const hierarchy = graph
4140
5203
  ? createMarkerGraphHierarchyItems(documentHierarchy, graph, createMarkerGraphHierarchyVisibility(state))
4141
5204
  : documentHierarchy;
4142
- const selectedIds = selection.selectedIds.filter(id => isAgentBridgeObjectSelectable(options, document, id, hierarchy));
5205
+ const selectedIds = selection.selectedIds.filter(id => isEditorCommandObjectSelectable(options, document, id, hierarchy));
4143
5206
  const activeId = selection.activeId && selectedIds.includes(selection.activeId)
4144
5207
  ? selection.activeId
4145
5208
  : selectedIds[selectedIds.length - 1] ?? null;
@@ -4792,7 +5855,9 @@ async function handleBrowserAssetAction(state, options, input) {
4792
5855
  return enterPrefabStage(state, options, input);
4793
5856
  }
4794
5857
  if (input.actionId === 'asset.add-to-scene') {
4795
- return input.assetId ? addAssetToDocument(state, options, input.assetId).ok : false;
5858
+ return input.assetId
5859
+ ? state.runBrowserAuthoringMutation('object.createFromAsset', JSON.stringify({ assetId: input.assetId, source: 'asset-browser-action' }), authoringState => addAssetToDocument(authoringState, options, input.assetId).ok)
5860
+ : false;
4796
5861
  }
4797
5862
  if (input.actionId === 'asset.place') {
4798
5863
  if (!input.assetId)
@@ -4805,7 +5870,7 @@ async function handleBrowserAssetAction(state, options, input) {
4805
5870
  }
4806
5871
  return armAssetPlacement(state, options, input.assetId);
4807
5872
  }
4808
- const patched = patchBrowserAssetAction(state, options, input);
5873
+ const patched = await state.runBrowserAuthoringMutation(input.actionId, JSON.stringify(input), authoringState => patchBrowserAssetAction(authoringState, options, input));
4809
5874
  if (patched)
4810
5875
  return true;
4811
5876
  state.status = `Asset action unavailable: ${input.actionId}`;
@@ -4817,11 +5882,11 @@ async function handleInspectorAction(state, options, input) {
4817
5882
  if (!input.actionId.startsWith('prefab.')) {
4818
5883
  const activeId = input.targetId ?? state.session?.getState().selection.activeId ?? null;
4819
5884
  const selectedIds = input.targetIds ?? (activeId ? [activeId] : []);
4820
- return patchBrowserAssetAction(state, options, {
5885
+ return state.runBrowserAuthoringMutation(input.actionId, JSON.stringify([input, activeId, selectedIds]), authoringState => patchBrowserAssetAction(authoringState, options, {
4821
5886
  actionId: input.actionId,
4822
5887
  fieldPath: input.path,
4823
5888
  value: input.params,
4824
- }, { activeId, selectedIds });
5889
+ }, { activeId, selectedIds }));
4825
5890
  }
4826
5891
  const assetId = readInspectorActionParamString(input, 'assetId');
4827
5892
  const browserAssetId = readInspectorActionParamString(input, 'browserAssetId')
@@ -4872,7 +5937,7 @@ function readInspectorActionParamString(input, key) {
4872
5937
  const value = input.params?.[key];
4873
5938
  return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
4874
5939
  }
4875
- async function enterPrefabStage(state, options, input) {
5940
+ async function enterPrefabStage(state, options, input, serializedAccess) {
4876
5941
  if (state.mode !== 'editor')
4877
5942
  return false;
4878
5943
  const sessionState = state.session?.getState({ cloneDocuments: false });
@@ -4897,13 +5962,14 @@ async function enterPrefabStage(state, options, input) {
4897
5962
  return true;
4898
5963
  }
4899
5964
  if (previousPrefabStage) {
4900
- const exited = await exitPrefabStage(state, options, 'reject');
5965
+ const exited = await exitPrefabStage(state, options, 'reject', serializedAccess);
4901
5966
  if (!exited)
4902
5967
  return true;
4903
5968
  }
4904
5969
  const parentSession = state.session;
4905
5970
  if (!parentSession)
4906
5971
  throw new Error('Prefab Stage requires an active parent Session.');
5972
+ const expectedParentRevision = parentSession.getState({ cloneDocuments: false }).documentRevision;
4907
5973
  const prefabScopeRevision = ++state.nextPrefabScopeRevision;
4908
5974
  const documentScopeId = state.scopeOperations
4909
5975
  ? `editor:document:prefab:${prefabScopeRevision}`
@@ -4958,12 +6024,32 @@ async function enterPrefabStage(state, options, input) {
4958
6024
  selectedItemIds: ['prefab-root'],
4959
6025
  restoreSceneCameraPreviewEnabled: previousPrefabStage?.restoreSceneCameraPreviewEnabled ?? state.sceneCameraPreviewEnabled,
4960
6026
  };
4961
- const previousSceneContext = getLocalEditorEditingContextById(state, 'fps.editor.context.scene').getSnapshot();
6027
+ const previousSceneContext = state.toolManager?.snapshot().context
6028
+ ?? getLocalEditorEditingContextById(state, 'fps.editor.context.scene').getSnapshot();
6029
+ let projectionAuthoritySwitched = false;
4962
6030
  try {
4963
6031
  await rebindLocalEditorToolContext(state, createLocalEditorPrefabEditingContextSnapshot(state, nextPrefabStage));
6032
+ if (state.authoringHost) {
6033
+ await state.authoringHost.switchSession(transaction.draftSession, {
6034
+ expectedDocumentRevision: expectedParentRevision,
6035
+ ...(serializedAccess ? { serializedAccess } : {}),
6036
+ });
6037
+ }
6038
+ else {
6039
+ state.replaceProjectionSessionAuthority(transaction.draftSession);
6040
+ projectionAuthoritySwitched = true;
6041
+ }
4964
6042
  }
4965
6043
  catch (error) {
4966
6044
  const rollbackErrors = [error];
6045
+ if (projectionAuthoritySwitched) {
6046
+ try {
6047
+ state.replaceProjectionSessionAuthority(parentSession);
6048
+ }
6049
+ catch (authorityRollbackError) {
6050
+ rollbackErrors.push(authorityRollbackError);
6051
+ }
6052
+ }
4967
6053
  try {
4968
6054
  await rebindLocalEditorToolContext(state, previousSceneContext);
4969
6055
  }
@@ -4997,7 +6083,7 @@ async function enterPrefabStage(state, options, input) {
4997
6083
  syncTransformOperationSettingsToSceneArtifacts(state, 'prefab-stage-enter-operation-settings');
4998
6084
  return true;
4999
6085
  }
5000
- async function exitPrefabStage(state, options, dirtyAction = 'reject') {
6086
+ async function exitPrefabStage(state, options, dirtyAction = 'reject', serializedAccess) {
5001
6087
  if (!state.prefabStage)
5002
6088
  return false;
5003
6089
  const transactionSnapshot = state.prefabStage.transaction.snapshot();
@@ -5009,13 +6095,25 @@ async function exitPrefabStage(state, options, dirtyAction = 'reject') {
5009
6095
  state.statusDetails = 'Save or discard the Prefab changes before returning to the Scene.';
5010
6096
  return false;
5011
6097
  }
5012
- state.prefabStage.transaction.discard();
6098
+ state.prefabStage.transaction.assertParentUnchanged();
6099
+ const checkpoint = state.prefabStage.transaction.draftSession.getState({ cloneDocuments: false });
6100
+ if (state.authoringHost) {
6101
+ await state.authoringHost.reset(checkpoint.persistedDocument, checkpoint.source, {
6102
+ expectedDocumentRevision: checkpoint.documentRevision,
6103
+ ...(serializedAccess ? { serializedAccess } : {}),
6104
+ });
6105
+ }
6106
+ else {
6107
+ await state.runBrowserAuthoringMutation('prefab.stage.discard', JSON.stringify({ documentRevision: checkpoint.documentRevision }), authoringState => authoringState.session?.reset(checkpoint.persistedDocument, checkpoint.source));
6108
+ }
5013
6109
  }
5014
6110
  const sessionState = state.session?.getState({ cloneDocuments: false });
5015
6111
  const label = sessionState?.workingDocument
5016
6112
  ? resolvePrefabStageDescriptorForDocument(state, options, sessionState.workingDocument)?.label ?? state.prefabStage.descriptor.label
5017
6113
  : state.prefabStage.descriptor.label;
5018
6114
  const prefabStage = state.prefabStage;
6115
+ const expectedDraftRevision = prefabStage.transaction.draftSession
6116
+ .getState({ cloneDocuments: false }).documentRevision;
5019
6117
  const restoreSceneCameraPreviewEnabled = prefabStage.restoreSceneCameraPreviewEnabled;
5020
6118
  const currentSceneContext = getLocalEditorEditingContextById(state, 'fps.editor.context.scene').getSnapshot();
5021
6119
  const parentDocumentRevision = prefabStage.transaction.parentSession
@@ -5024,17 +6122,48 @@ async function exitPrefabStage(state, options, dirtyAction = 'reject') {
5024
6122
  ...currentSceneContext,
5025
6123
  revision: parentDocumentRevision + state.editorStateRevision + 1,
5026
6124
  };
6125
+ const prefabContext = state.toolManager?.snapshot().context
6126
+ ?? createLocalEditorPrefabEditingContextSnapshot(state, prefabStage);
6127
+ let projectionAuthoritySwitched = false;
5027
6128
  await closePrefabStageLifecycleScopes(state, prefabStage);
5028
6129
  try {
5029
6130
  await rebindLocalEditorToolContext(state, sceneContext);
6131
+ if (state.authoringHost) {
6132
+ await state.authoringHost.switchSession(prefabStage.transaction.parentSession, {
6133
+ expectedDocumentRevision: expectedDraftRevision,
6134
+ ...(serializedAccess ? { serializedAccess } : {}),
6135
+ });
6136
+ }
6137
+ else {
6138
+ state.replaceProjectionSessionAuthority(prefabStage.transaction.parentSession);
6139
+ projectionAuthoritySwitched = true;
6140
+ }
5030
6141
  }
5031
6142
  catch (error) {
6143
+ const rollbackErrors = [error];
6144
+ if (projectionAuthoritySwitched) {
6145
+ try {
6146
+ state.replaceProjectionSessionAuthority(prefabStage.transaction.draftSession);
6147
+ }
6148
+ catch (authorityRollbackError) {
6149
+ rollbackErrors.push(authorityRollbackError);
6150
+ }
6151
+ }
5032
6152
  try {
5033
6153
  await openPrefabStageLifecycleScopes(state, prefabStage);
5034
6154
  }
5035
6155
  catch (rollbackError) {
6156
+ rollbackErrors.push(rollbackError);
6157
+ }
6158
+ try {
6159
+ await rebindLocalEditorToolContext(state, prefabContext);
6160
+ }
6161
+ catch (rollbackError) {
6162
+ rollbackErrors.push(rollbackError);
6163
+ }
6164
+ if (rollbackErrors.length > 1) {
5036
6165
  const combined = new Error('Prefab Tool context exit and lifecycle scope rollback failed.');
5037
- combined.errors = [error, rollbackError];
6166
+ combined.errors = rollbackErrors;
5038
6167
  throw combined;
5039
6168
  }
5040
6169
  throw error;
@@ -5042,6 +6171,13 @@ async function exitPrefabStage(state, options, dirtyAction = 'reject') {
5042
6171
  state.session = prefabStage.transaction.parentSession;
5043
6172
  state.prefabStage = null;
5044
6173
  state.editorStateRevision += 1;
6174
+ let reconciliationError;
6175
+ try {
6176
+ await state.reconcilePendingProjectServiceSnapshot();
6177
+ }
6178
+ catch (error) {
6179
+ reconciliationError = error;
6180
+ }
5045
6181
  activateLocalEditorSceneViewContext(state, getLocalEditorEditingContext(state).getViewStateKey(), false);
5046
6182
  const parentSessionState = state.session.getState({ cloneDocuments: false });
5047
6183
  if (parentSessionState.workingDocument) {
@@ -5053,10 +6189,12 @@ async function exitPrefabStage(state, options, dirtyAction = 'reject') {
5053
6189
  syncSceneCameraPreview(state, options);
5054
6190
  else
5055
6191
  state.sceneCameraPreview?.setActive(false);
5056
- state.status = `Exited Prefab ${label}`;
5057
- state.statusTone = 'default';
6192
+ state.status = reconciliationError ? 'Project Editor Service reconciliation failed' : `Exited Prefab ${label}`;
6193
+ state.statusTone = reconciliationError ? 'error' : 'default';
5058
6194
  state.statusToneStatus = state.status;
5059
- state.statusDetails = '';
6195
+ state.statusDetails = reconciliationError
6196
+ ? stringifyLocalEditorError(reconciliationError) ?? 'Unknown project snapshot reconciliation error'
6197
+ : '';
5060
6198
  return true;
5061
6199
  }
5062
6200
  async function rebindLocalEditorToolContext(state, nextContext = getLocalEditorEditingContext(state).getSnapshot()) {
@@ -5097,7 +6235,7 @@ async function openPrefabStageLifecycleScopes(state, stage) {
5097
6235
  parentId: stage.sessionScopeId,
5098
6236
  });
5099
6237
  state.scopeOperations.addScopeResource(stage.previewScopeId, () => {
5100
- state.activeTransformPreview = null;
6238
+ clearActiveTransformPreview(state);
5101
6239
  });
5102
6240
  }
5103
6241
  function selectPrefabStageItem(state, itemId) {
@@ -5550,13 +6688,13 @@ function clearArmedPlacement(state) {
5550
6688
  state.armedPlacement = null;
5551
6689
  state.gizmo?.setPlacementMarker(null);
5552
6690
  }
5553
- function addAssetToDocument(state, options, assetId, createOptions = {}) {
6691
+ function addAssetToDocument(state, options, assetId, createOptions = {}, sessionOverride) {
5554
6692
  if (state.mode !== 'editor') {
5555
6693
  state.status = 'Asset creation rejected: editor is not active';
5556
6694
  return { ok: false, assetId, changed: false, status: state.status, error: 'editor_not_active' };
5557
6695
  }
5558
6696
  cancelActiveOperation(state);
5559
- const session = state.session;
6697
+ const session = sessionOverride ?? state.session;
5560
6698
  const beforeDocument = session?.getState().workingDocument;
5561
6699
  if (!session || !beforeDocument) {
5562
6700
  state.status = 'Asset creation rejected: document is not loaded';
@@ -5618,13 +6756,13 @@ function addAssetToDocument(state, options, assetId, createOptions = {}) {
5618
6756
  createdId: createdId ?? null,
5619
6757
  };
5620
6758
  }
5621
- function patchSerializedProperty(state, options, input) {
6759
+ function patchSerializedProperty(state, options, input, onCommitted) {
5622
6760
  if (state.mode !== 'editor')
5623
6761
  return false;
5624
6762
  cancelActiveOperation(state);
5625
- return getLocalEditorEditingContext(state).patchInspectorProperty(input);
6763
+ return getLocalEditorEditingContext(state).patchInspectorProperty(input, onCommitted);
5626
6764
  }
5627
- function patchSceneSerializedProperty(state, options, input) {
6765
+ function patchSceneSerializedProperty(state, options, input, onCommitted) {
5628
6766
  if (!state.session)
5629
6767
  return false;
5630
6768
  const sessionState = state.session.getState({ cloneDocuments: false });
@@ -5656,6 +6794,7 @@ function patchSceneSerializedProperty(state, options, input) {
5656
6794
  if (!patch)
5657
6795
  return false;
5658
6796
  const changedIds = patch.changedIds ?? targetIds;
6797
+ const serializedNamePatch = isSerializedNamePath(payload.path);
5659
6798
  const projectionMetadataPatch = isSerializedProjectionMetadataPath(payload.path);
5660
6799
  const projectionMetadataSyncIds = projectionMetadataPatch
5661
6800
  ? resolveSerializedProjectionMetadataSyncIds(options, document, payload.path, changedIds)
@@ -5667,15 +6806,18 @@ function patchSceneSerializedProperty(state, options, input) {
5667
6806
  label: patch.label ?? `Patch ${payload.path} on ${targetIds.length} objects`,
5668
6807
  patch: patch.patch,
5669
6808
  targetId: sessionState.selection.activeId ?? undefined,
5670
- ...withProjectionImpact(resolveProjectionImpact(patch, patch.reprojectIds?.length
5671
- ? createReprojectProjectionImpact(patch.reprojectIds)
5672
- : shouldReprojectInspectorEdit
5673
- ? createReprojectProjectionImpact(changedIds)
5674
- : isSerializedTransformPath(payload.path)
5675
- ? createTransformProjectionImpact(targetIds)
5676
- : projectionMetadataPatch
5677
- ? createMetadataProjectionImpact(projectionMetadataSyncIds)
5678
- : undefined)),
6809
+ ...withProjectionImpact(serializedNamePatch
6810
+ ? createMetadataProjectionImpact(changedIds)
6811
+ : resolveProjectionImpact(patch, patch.reprojectIds?.length
6812
+ ? createReprojectProjectionImpact(patch.reprojectIds)
6813
+ : shouldReprojectInspectorEdit
6814
+ ? createReprojectProjectionImpact(changedIds)
6815
+ : isSerializedTransformPath(payload.path)
6816
+ ? createTransformProjectionImpact(targetIds)
6817
+ : projectionMetadataPatch
6818
+ ? createMetadataProjectionImpact(projectionMetadataSyncIds)
6819
+ : undefined)),
6820
+ ...withPresentationImpact(resolveSerializedPresentationImpact(payload.path, changedIds)),
5679
6821
  }, largeProjectionMetadataPatch
5680
6822
  ? {
5681
6823
  assumeDocumentChanged: true,
@@ -5687,15 +6829,27 @@ function patchSceneSerializedProperty(state, options, input) {
5687
6829
  if (!result.documentChanged)
5688
6830
  return false;
5689
6831
  const workingDocument = result.workingDocument;
6832
+ state.status = patch.label ?? `Patch ${payload.path} on ${targetIds.length} objects`;
6833
+ if (isSerializedTransformPath(payload.path))
6834
+ onCommitted?.();
5690
6835
  const projectionSyncIds = projectionMetadataPatch
5691
6836
  ? resolveSerializedProjectionMetadataSyncIds(options, workingDocument, payload.path, changedIds)
5692
6837
  : isSerializedTransformPath(payload.path)
5693
6838
  ? targetIds
5694
6839
  : changedIds;
5695
- if (patch.reprojectIds?.length)
5696
- reprojectProjectionForChangedIds(state, options, workingDocument, patch.reprojectIds);
5697
- else if (shouldReprojectInspectorEdit)
5698
- reprojectProjectionForChangedIds(state, options, workingDocument, changedIds);
6840
+ if (serializedNamePatch) {
6841
+ applyRenameProjectionImpact(state, options, workingDocument, result.selection, changedIds, 'inspector-name');
6842
+ }
6843
+ else if (patch.reprojectIds?.length) {
6844
+ reprojectProjectionForChangedIds(state, options, workingDocument, patch.reprojectIds, {
6845
+ refreshInspector: input.refreshInspector !== false,
6846
+ });
6847
+ }
6848
+ else if (shouldReprojectInspectorEdit) {
6849
+ reprojectProjectionForChangedIds(state, options, workingDocument, changedIds, {
6850
+ refreshInspector: input.refreshInspector !== false,
6851
+ });
6852
+ }
5699
6853
  else if (projectionMetadataPatch) {
5700
6854
  syncProjectionMetadataForChangedIds(state, options, workingDocument, projectionSyncIds, {
5701
6855
  deferShadowPreviewRefresh: largeProjectionMetadataPatch,
@@ -5708,7 +6862,6 @@ function patchSceneSerializedProperty(state, options, input) {
5708
6862
  state.gizmo?.refreshSelection();
5709
6863
  }
5710
6864
  state.summary = summarizeDocument(options, workingDocument, state.session.getSource());
5711
- state.status = patch.label ?? `Patch ${payload.path} on ${targetIds.length} objects`;
5712
6865
  return true;
5713
6866
  }
5714
6867
  const patch = options.documentAdapter.createSerializedPropertyPatch({
@@ -5726,6 +6879,7 @@ function patchSceneSerializedProperty(state, options, input) {
5726
6879
  if (!patch)
5727
6880
  return false;
5728
6881
  const changedIds = patch.changedIds ?? [patch.changedId ?? payload.targetId];
6882
+ const serializedNamePatch = isSerializedNamePath(payload.path);
5729
6883
  const projectionMetadataPatch = isSerializedProjectionMetadataPath(payload.path);
5730
6884
  const projectionMetadataSyncIds = projectionMetadataPatch
5731
6885
  ? resolveSerializedProjectionMetadataSyncIds(options, document, payload.path, changedIds)
@@ -5734,20 +6888,34 @@ function patchSceneSerializedProperty(state, options, input) {
5734
6888
  type: 'document.patch',
5735
6889
  label: patch.label ?? `Patch ${payload.path}`,
5736
6890
  patch: patch.patch,
5737
- ...withProjectionImpact(resolveProjectionImpact(patch, patch.reprojectIds?.length
5738
- ? createReprojectProjectionImpact(patch.reprojectIds)
5739
- : shouldReprojectInspectorEdit
5740
- ? createReprojectProjectionImpact(changedIds)
5741
- : isSerializedTransformPath(payload.path)
5742
- ? createTransformProjectionImpact([patch.changedId ?? payload.targetId])
5743
- : projectionMetadataPatch
5744
- ? createMetadataProjectionImpact(projectionMetadataSyncIds)
5745
- : undefined)),
6891
+ ...withProjectionImpact(serializedNamePatch
6892
+ ? createMetadataProjectionImpact(changedIds)
6893
+ : resolveProjectionImpact(patch, patch.reprojectIds?.length
6894
+ ? createReprojectProjectionImpact(patch.reprojectIds)
6895
+ : shouldReprojectInspectorEdit
6896
+ ? createReprojectProjectionImpact(changedIds)
6897
+ : isSerializedTransformPath(payload.path)
6898
+ ? createTransformProjectionImpact([patch.changedId ?? payload.targetId])
6899
+ : projectionMetadataPatch
6900
+ ? createMetadataProjectionImpact(projectionMetadataSyncIds)
6901
+ : undefined)),
6902
+ ...withPresentationImpact(resolveSerializedPresentationImpact(payload.path, changedIds)),
5746
6903
  });
5747
- if (patch.reprojectIds?.length)
5748
- reprojectProjectionForChangedIds(state, options, result.workingDocument, patch.reprojectIds);
6904
+ state.status = patch.label ?? `Patched ${payload.path}`;
6905
+ if (result.documentChanged && isSerializedTransformPath(payload.path))
6906
+ onCommitted?.();
6907
+ if (result.documentChanged && serializedNamePatch) {
6908
+ applyRenameProjectionImpact(state, options, result.workingDocument, result.selection, changedIds, 'inspector-name');
6909
+ }
6910
+ else if (patch.reprojectIds?.length) {
6911
+ reprojectProjectionForChangedIds(state, options, result.workingDocument, patch.reprojectIds, {
6912
+ refreshInspector: input.refreshInspector !== false,
6913
+ });
6914
+ }
5749
6915
  else if (shouldReprojectInspectorEdit) {
5750
- reprojectProjectionForChangedIds(state, options, result.workingDocument, changedIds);
6916
+ reprojectProjectionForChangedIds(state, options, result.workingDocument, changedIds, {
6917
+ refreshInspector: input.refreshInspector !== false,
6918
+ });
5751
6919
  }
5752
6920
  else if (projectionMetadataPatch) {
5753
6921
  const syncIds = resolveSerializedProjectionMetadataSyncIds(options, result.workingDocument, payload.path, changedIds);
@@ -5764,7 +6932,6 @@ function patchSceneSerializedProperty(state, options, input) {
5764
6932
  state.gizmo?.refreshSelection();
5765
6933
  }
5766
6934
  state.summary = summarizeDocument(options, result.workingDocument, state.session.getSource());
5767
- state.status = patch.label ?? `Patched ${payload.path}`;
5768
6935
  return true;
5769
6936
  }
5770
6937
  function patchPrefabSerializedProperty(state, options, input) {
@@ -6469,6 +7636,7 @@ function applyRenderingDocumentPatch(state, options, document, patch, fallbackLa
6469
7636
  patch: patch.patch,
6470
7637
  targetId: patch.changedId,
6471
7638
  ...withProjectionImpact(projectionImpact),
7639
+ ...withPresentationImpact({ rendering: true }),
6472
7640
  });
6473
7641
  if (!result.documentChanged) {
6474
7642
  return { changed: false, workingDocument: result.workingDocument, label };
@@ -6574,6 +7742,9 @@ function isSerializedTransformPath(path) {
6574
7742
  || path.startsWith('transform.rotation.')
6575
7743
  || path.startsWith('transform.scale.');
6576
7744
  }
7745
+ function isSerializedNamePath(path) {
7746
+ return path === 'name';
7747
+ }
6577
7748
  function isSerializedProjectionMetadataPath(path) {
6578
7749
  return isSerializedShadowMetadataPath(path);
6579
7750
  }
@@ -6586,12 +7757,30 @@ function resolveSerializedProjectionMetadataSyncIds(options, document, path, cha
6586
7757
  }
6587
7758
  return uniqueProjectionIds(changedIds);
6588
7759
  }
6589
- function beginDuplicateDrag(state, options, input) {
6590
- if (state.mode !== 'editor' || !state.session)
7760
+ function isSerializedMaterialPath(path) {
7761
+ return path.startsWith('overrides.material')
7762
+ || path.startsWith('material.')
7763
+ || path.startsWith('scene.materialAssets.');
7764
+ }
7765
+ function resolveSerializedPresentationImpact(path, targetIds = []) {
7766
+ if (isSerializedNamePath(path)) {
7767
+ const labelIds = uniqueProjectionIds(targetIds);
7768
+ return labelIds.length > 0
7769
+ ? { hierarchy: { labelIds }, inspector: ['summary'] }
7770
+ : undefined;
7771
+ }
7772
+ if (isSerializedTransformPath(path))
7773
+ return { inspector: ['transform'] };
7774
+ if (isSerializedMaterialPath(path))
7775
+ return { inspector: ['material'] };
7776
+ return undefined;
7777
+ }
7778
+ function beginDuplicateDrag(state, options, input, sessionOverride) {
7779
+ if (state.mode !== 'editor' || !(sessionOverride ?? state.session))
6591
7780
  return null;
6592
7781
  if (state.duplicateDrag)
6593
7782
  return null;
6594
- const session = state.session;
7783
+ const session = sessionOverride ?? state.session;
6595
7784
  const document = session.getState().workingDocument;
6596
7785
  const patch = options.documentAdapter.createDuplicateSelectionPatch?.({
6597
7786
  document,
@@ -6619,7 +7808,7 @@ function beginDuplicateDrag(state, options, input) {
6619
7808
  const selectedIds = (patch.selectedIds?.length ? patch.selectedIds : patch.createdIds)
6620
7809
  .filter(id => isNodeSelectableInDocument(options, result.workingDocument, id));
6621
7810
  if (selectedIds.length === 0) {
6622
- const undone = state.session.undo();
7811
+ const undone = session.undo();
6623
7812
  if (undone)
6624
7813
  rebuildProjectionFromDocument(state, options, undone.workingDocument, originalSelection);
6625
7814
  state.status = 'Duplicate drag rejected: duplicated selection is not selectable';
@@ -6628,7 +7817,7 @@ function beginDuplicateDrag(state, options, input) {
6628
7817
  const activeId = patch.activeId && selectedIds.includes(patch.activeId)
6629
7818
  ? patch.activeId
6630
7819
  : selectedIds[selectedIds.length - 1] ?? null;
6631
- const selectionResult = state.session.dispatch({
7820
+ const selectionResult = session.dispatch({
6632
7821
  type: 'selection.replace',
6633
7822
  selectedIds,
6634
7823
  activeId,
@@ -7222,6 +8411,9 @@ function formatAssetLabel(asset, fallback) {
7222
8411
  const label = record?.label ?? record?.displayName ?? record?.name;
7223
8412
  return typeof label === 'string' && label.trim() ? label : fallback;
7224
8413
  }
8414
+ function withPresentationImpact(impact) {
8415
+ return impact ? { presentationImpact: impact } : {};
8416
+ }
7225
8417
  function withProjectionImpact(impact) {
7226
8418
  return impact ? { projectionImpact: impact } : {};
7227
8419
  }
@@ -7234,6 +8426,18 @@ function createTransformProjectionImpact(ids) {
7234
8426
  function createMetadataProjectionImpact(ids) {
7235
8427
  return createIncrementalProjectionImpact({ syncMetadataIds: ids });
7236
8428
  }
8429
+ function applyRenameProjectionImpact(state, options, document, selection, ids, reason) {
8430
+ const impact = createMetadataProjectionImpact(ids);
8431
+ const applied = applyProjectionImpactToDocument(state, options, {
8432
+ document,
8433
+ selection,
8434
+ impact,
8435
+ reason,
8436
+ syncWorld: false,
8437
+ });
8438
+ if (!applied)
8439
+ rebuildProjectionFromDocument(state, options, document, selection);
8440
+ }
7237
8441
  function createReprojectProjectionImpact(ids) {
7238
8442
  return createIncrementalProjectionImpact({ reprojectIds: ids });
7239
8443
  }
@@ -7356,6 +8560,7 @@ function createHistoryOperationResult(state, input) {
7356
8560
  },
7357
8561
  status: input.status,
7358
8562
  projection: createHistoryProjectionResult(input.waitForProjection, null, state),
8563
+ ...(input.result?.transaction.presentationImpact ? { presentationImpact: input.result.transaction.presentationImpact } : {}),
7359
8564
  };
7360
8565
  }
7361
8566
  function createHistoryProjectionResult(waitPolicy, idleState, state) {
@@ -7373,10 +8578,41 @@ function applyHistoryProjectionImpact(state, options, result, operation) {
7373
8578
  rebuildProjection(state, options, result);
7374
8579
  return;
7375
8580
  }
7376
- const applied = applyIncrementalHistoryProjectionImpact(state, options, result, operation, impact);
8581
+ const applied = applyIncrementalHistoryProjectionImpact(state, options, result, operation, impact, !isRenameTransactionImpact(result.transaction));
7377
8582
  if (!applied)
7378
8583
  rebuildProjection(state, options, result);
7379
8584
  }
8585
+ function isRenameTransactionImpact(transaction) {
8586
+ return isRenameImpact(transaction);
8587
+ }
8588
+ function resolveLocalEditorDocumentStructureChange(classify, command) {
8589
+ if (isRenameImpact(command))
8590
+ return false;
8591
+ return classify?.(command) ?? true;
8592
+ }
8593
+ function isRenameImpact(input) {
8594
+ const presentation = input.presentationImpact;
8595
+ const projection = input.projectionImpact;
8596
+ if (!presentation
8597
+ || presentation.hierarchy === 'full'
8598
+ || !presentation.hierarchy
8599
+ || presentation.rendering === true
8600
+ || !Array.isArray(presentation.inspector)
8601
+ || presentation.inspector.length !== 1
8602
+ || presentation.inspector[0] !== 'summary'
8603
+ || !projection
8604
+ || projection.type !== 'incremental')
8605
+ return false;
8606
+ const labelIds = uniqueProjectionIds(presentation.hierarchy.labelIds);
8607
+ const metadataIds = uniqueProjectionIds(projection.syncMetadataIds);
8608
+ return labelIds.length > 0
8609
+ && labelIds.length === metadataIds.length
8610
+ && labelIds.every(id => metadataIds.includes(id))
8611
+ && uniqueProjectionIds(projection.syncTransformIds).length === 0
8612
+ && uniqueProjectionIds(projection.reprojectIds).length === 0
8613
+ && uniqueProjectionIds(projection.createdNodeIds).length === 0
8614
+ && uniqueProjectionIds(projection.deletedNodeIds).length === 0;
8615
+ }
7380
8616
  function applyProjectionImpactToDocument(state, options, input) {
7381
8617
  if (!input.impact || input.impact.type === 'rebuild')
7382
8618
  return false;
@@ -7389,12 +8625,13 @@ function applyProjectionImpactToDocument(state, options, input) {
7389
8625
  projectNodeIds: input.impact.createdNodeIds,
7390
8626
  removeNodeIds: input.impact.deletedNodeIds,
7391
8627
  reason: input.reason,
8628
+ syncWorld: input.syncWorld,
7392
8629
  });
7393
8630
  }
7394
8631
  function rebuildProjection(state, options, result) {
7395
8632
  rebuildProjectionFromDocument(state, options, result.workingDocument, result.selection);
7396
8633
  }
7397
- function applyIncrementalHistoryProjectionImpact(state, options, result, operation, impact) {
8634
+ function applyIncrementalHistoryProjectionImpact(state, options, result, operation, impact, syncWorld) {
7398
8635
  const projectNodeIds = operation === 'redo'
7399
8636
  ? uniqueProjectionIds(impact.createdNodeIds)
7400
8637
  : uniqueProjectionIds(impact.deletedNodeIds);
@@ -7410,6 +8647,7 @@ function applyIncrementalHistoryProjectionImpact(state, options, result, operati
7410
8647
  projectNodeIds,
7411
8648
  removeNodeIds,
7412
8649
  reason: 'history-impact',
8650
+ syncWorld,
7413
8651
  });
7414
8652
  }
7415
8653
  function applyIncrementalProjectionImpactToDocument(state, options, input) {
@@ -7498,14 +8736,16 @@ function applyIncrementalProjectionImpactToDocument(state, options, input) {
7498
8736
  applyPreviewVisibilityStateToProjection(state);
7499
8737
  const sanitized = sanitizeSelection(state, options, input.document, input.selection);
7500
8738
  syncSelectionToProjection(state, options, sanitized ?? input.selection);
7501
- syncEditorWorldAppearanceFromDocument(state, options, input.document, `world-appearance-${input.reason}`);
7502
- if (syncMetadataNodes.length > 0) {
7503
- syncEditorWorldRenderingFromDocument(state, options, input.document, `world-rendering-${input.reason}`, {
7504
- staticProjectedMetadataChanged: true,
7505
- });
7506
- }
7507
- else {
7508
- syncEditorWorldRenderingFromDocument(state, options, input.document, `world-rendering-${input.reason}`, true);
8739
+ if (input.syncWorld !== false) {
8740
+ syncEditorWorldAppearanceFromDocument(state, options, input.document, `world-appearance-${input.reason}`);
8741
+ if (syncMetadataNodes.length > 0) {
8742
+ syncEditorWorldRenderingFromDocument(state, options, input.document, `world-rendering-${input.reason}`, {
8743
+ staticProjectedMetadataChanged: true,
8744
+ });
8745
+ }
8746
+ else {
8747
+ syncEditorWorldRenderingFromDocument(state, options, input.document, `world-rendering-${input.reason}`, true);
8748
+ }
7509
8749
  }
7510
8750
  invalidateEditorScene(state, `projection-${input.reason}`, options);
7511
8751
  return true;
@@ -7697,7 +8937,7 @@ function syncProjectionMetadataForChangedIds(state, options, document, changedId
7697
8937
  }
7698
8938
  invalidateEditorScene(state, 'projection-sync-metadata-ids');
7699
8939
  }
7700
- function reprojectProjectionForChangedIds(state, options, document, changedIds) {
8940
+ function reprojectProjectionForChangedIds(state, options, document, changedIds, syncOptions = {}) {
7701
8941
  if (state.prefabStage) {
7702
8942
  rebuildProjectionFromDocument(state, options, document, EMPTY_EDITOR_SELECTION);
7703
8943
  return;
@@ -7706,8 +8946,18 @@ function reprojectProjectionForChangedIds(state, options, document, changedIds)
7706
8946
  const projectedNodes = resolveProjectionNodesForTarget(options, document, changedId);
7707
8947
  if (projectedNodes) {
7708
8948
  removeSceneProjectionTarget(state, changedId);
7709
- for (const projectedNode of projectedNodes)
8949
+ for (const projectedNode of projectedNodes) {
7710
8950
  state.projection?.projectNode(projectedNode);
8951
+ if (syncOptions.refreshInspector === false) {
8952
+ const batch = state.projection?.getLastProjectionBatch();
8953
+ if (batch) {
8954
+ suppressProjectionInspectorRefreshForBatch(state, batch.batchId, projectedNode.id);
8955
+ void batch.settled.then(() => {
8956
+ releaseProjectionInspectorRefreshSuppression(state, batch.batchId);
8957
+ });
8958
+ }
8959
+ }
8960
+ }
7711
8961
  }
7712
8962
  }
7713
8963
  applyPreviewVisibilityStateToProjection(state);
@@ -7716,6 +8966,24 @@ function reprojectProjectionForChangedIds(state, options, document, changedIds)
7716
8966
  syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-reproject-changed-ids', true);
7717
8967
  invalidateEditorScene(state, 'projection-reproject-changed-ids', options);
7718
8968
  }
8969
+ function suppressProjectionInspectorRefreshForBatch(state, batchId, nodeId) {
8970
+ state.suppressedProjectionInspectorRefreshBatchIds.add(batchId);
8971
+ const batches = state.suppressedProjectionInspectorRefreshNodeBatches.get(nodeId) ?? new Set();
8972
+ batches.add(batchId);
8973
+ state.suppressedProjectionInspectorRefreshNodeBatches.set(nodeId, batches);
8974
+ }
8975
+ function isProjectionInspectorRefreshSuppressed(state, nodeId, batchId) {
8976
+ return (batchId !== undefined && state.suppressedProjectionInspectorRefreshBatchIds.has(batchId))
8977
+ || state.suppressedProjectionInspectorRefreshNodeBatches.has(nodeId);
8978
+ }
8979
+ function releaseProjectionInspectorRefreshSuppression(state, batchId) {
8980
+ state.suppressedProjectionInspectorRefreshBatchIds.delete(batchId);
8981
+ for (const [nodeId, batches] of state.suppressedProjectionInspectorRefreshNodeBatches) {
8982
+ batches.delete(batchId);
8983
+ if (batches.size === 0)
8984
+ state.suppressedProjectionInspectorRefreshNodeBatches.delete(nodeId);
8985
+ }
8986
+ }
7719
8987
  function removeLocalEditorMaterialProfileDiagnostics(state, projectionNodeId) {
7720
8988
  state.materialProfileDiagnostics = state.materialProfileDiagnostics.filter(diagnostic => diagnostic.locator?.objectId !== projectionNodeId);
7721
8989
  }
@@ -7826,8 +9094,7 @@ function syncViewportMeasurementState(state) {
7826
9094
  if (measurement)
7827
9095
  state.viewportMeasurement = measurement;
7828
9096
  }
7829
- function hasSceneCameraPreviewRig(state, options) {
7830
- const document = state.session?.getState().workingDocument ?? null;
9097
+ function hasSceneCameraPreviewRig(options, document) {
7831
9098
  return !!document && !!options.documentAdapter.getSceneCameraPreviewRig?.(document);
7832
9099
  }
7833
9100
  function applyActiveTransformPreviewToInspectorObject(state, options, document, inspectorObject) {
@@ -8053,7 +9320,7 @@ function tryFreezeHierarchyContributionDocument(document) {
8053
9320
  return null;
8054
9321
  }
8055
9322
  }
8056
- export function createLocalEditorSceneHierarchyScopeAdapter(state, options) {
9323
+ export function createLocalEditorSceneHierarchyScopeAdapter(state, options, applyRenamePresentation) {
8057
9324
  const clipboard = new Map();
8058
9325
  return {
8059
9326
  id: 'fps.editor.product.scene-hierarchy',
@@ -8061,7 +9328,7 @@ export function createLocalEditorSceneHierarchyScopeAdapter(state, options) {
8061
9328
  canProvide: () => !state.prefabStage,
8062
9329
  getTree(cache) {
8063
9330
  const sessionState = state.session?.getState({ cloneDocuments: false });
8064
- const structureKey = `scene:${String(sessionState?.documentStructureRevision ?? 0)}:${String(state.markerGraphVisibilityRevision)}`;
9331
+ const structureKey = `scene:${String(sessionState?.documentStructureRevision ?? 0)}:${String(state.markerGraphVisibilityRevision)}:${String(state.hierarchyPresentationFallbackRevision)}`;
8065
9332
  const revision = resolveLocalEditorHierarchyRevision(state);
8066
9333
  if (cache?.structureKey === structureKey && cache.snapshot.revision === revision)
8067
9334
  return cache;
@@ -8163,7 +9430,8 @@ export function createLocalEditorSceneHierarchyScopeAdapter(state, options) {
8163
9430
  : []),
8164
9431
  ];
8165
9432
  },
8166
- async executeOperation({ snapshot, invocation, operation, render, getSnapshot }) {
9433
+ async executeOperation({ snapshot, invocation, operation, render, getSnapshot, state: operationState }) {
9434
+ const state = operationState;
8167
9435
  const resolve = (rowId) => this.resolveTarget(snapshot, rowId);
8168
9436
  if (invocation.operationId === 'hierarchy.node.focus') {
8169
9437
  const target = invocation.contextNodeId ? resolve(invocation.contextNodeId) : null;
@@ -8239,7 +9507,7 @@ export function createLocalEditorSceneHierarchyScopeAdapter(state, options) {
8239
9507
  if (!targetId || !name || snapshot.readonly)
8240
9508
  return createHarnessHierarchyOperationResult(false, false, snapshot.revision, 'hierarchy.rename.invalid', 'Rename target or name is invalid.');
8241
9509
  const outcome = renameSceneGraphNode(state, options, { id: resolve(targetId)?.authoredId ?? targetId, name });
8242
- if (outcome === 'changed')
9510
+ if (outcome === 'changed' && !applyRenamePresentation?.({ id: targetId, name }))
8243
9511
  render();
8244
9512
  return outcome === 'changed'
8245
9513
  ? createHarnessHierarchyOperationResult(true, true, getSnapshot().revision)
@@ -8407,7 +9675,8 @@ export function createLocalEditorPrefabHierarchyScopeAdapter(state, options) {
8407
9675
  : []),
8408
9676
  ].flat();
8409
9677
  },
8410
- async executeOperation({ snapshot, invocation, operation, render, getSnapshot, executorStates }) {
9678
+ async executeOperation({ snapshot, invocation, operation, render, getSnapshot, executorStates, state: operationState }) {
9679
+ const state = operationState;
8411
9680
  const resolve = (rowId) => this.resolveTarget(snapshot, rowId);
8412
9681
  if (invocation.operationId === 'hierarchy.node.focus') {
8413
9682
  const target = invocation.contextNodeId ? resolve(invocation.contextNodeId) : null;
@@ -8679,6 +9948,10 @@ export function createLocalEditorHarnessHierarchyScopeProvider(state, options, r
8679
9948
  applySelection: applyHierarchySelection,
8680
9949
  listOperations,
8681
9950
  async executeOperation(invocation) {
9951
+ const serializedAccess = getSerializedHierarchyInvocationAccess(invocation);
9952
+ const serializedOperationState = serializedAccess === undefined
9953
+ ? state
9954
+ : createSerializedHierarchyOperationState(state, serializedAccess);
8682
9955
  const validated = validateContext(invocation);
8683
9956
  if ('ok' in validated)
8684
9957
  return validated;
@@ -8698,13 +9971,16 @@ export function createLocalEditorHarnessHierarchyScopeProvider(state, options, r
8698
9971
  ? invocation.input.visibleIds.filter((id) => typeof id === 'string')
8699
9972
  : [...validated.nodeById.keys()];
8700
9973
  const selectableIds = requestedIds.filter(id => validated.nodeById.get(id)?.selectable === true);
8701
- return applyHierarchySelection({
9974
+ const apply = () => applyHierarchySelection({
8702
9975
  ...invocation,
8703
9976
  selectedIds: selectableIds,
8704
9977
  activeId: selectableIds.includes(invocation.activeId ?? '')
8705
9978
  ? invocation.activeId
8706
9979
  : selectableIds[selectableIds.length - 1] ?? null,
8707
9980
  });
9981
+ return serializedAccess === undefined
9982
+ ? apply()
9983
+ : callWithEditorAuthoringSerializedAccess(serializedAccess, apply);
8708
9984
  }
8709
9985
  const contextNodeId = invocation.contextNodeId;
8710
9986
  const contextNode = contextNodeId ? validated.nodeById.get(contextNodeId) : null;
@@ -8728,11 +10004,14 @@ export function createLocalEditorHarnessHierarchyScopeProvider(state, options, r
8728
10004
  selectedIds = descendants;
8729
10005
  }
8730
10006
  const selectableIds = selectedIds.filter(id => validated.nodeById.get(id)?.selectable === true);
8731
- return applyHierarchySelection({
10007
+ const apply = () => applyHierarchySelection({
8732
10008
  ...invocation,
8733
10009
  selectedIds: selectableIds,
8734
10010
  activeId: selectableIds[0] ?? null,
8735
10011
  });
10012
+ return serializedAccess === undefined
10013
+ ? apply()
10014
+ : callWithEditorAuthoringSerializedAccess(serializedAccess, apply);
8736
10015
  }
8737
10016
  const contribution = (options.hierarchy?.operations ?? []).find(operation => operation.id === invocation.operationId);
8738
10017
  if (contribution) {
@@ -8742,36 +10021,48 @@ export function createLocalEditorHarnessHierarchyScopeProvider(state, options, r
8742
10021
  const beforeRevision = validated.revision;
8743
10022
  try {
8744
10023
  return await runExclusiveResult(state, render, async () => {
8745
- const preparedSnapshot = validateContext(invocation);
8746
- if ('ok' in preparedSnapshot)
8747
- return preparedSnapshot;
8748
- const visibleContext = createOperationContributionContext(preparedSnapshot, invocation, invocation.input);
8749
- if (!visibleContext || contribution.visible?.(visibleContext) === false) {
8750
- return createHarnessHierarchyOperationResult(false, false, preparedSnapshot.revision, 'hierarchy.operation.unsupported', `Unsupported Hierarchy operation: ${invocation.operationId}`);
8751
- }
8752
- const disabledContext = createOperationContributionContext(preparedSnapshot, invocation, invocation.input);
8753
- const disabled = disabledContext ? contribution.disabled?.(disabledContext) : true;
8754
- if (disabled === true || typeof disabled === 'string') {
8755
- return createHarnessHierarchyOperationResult(false, false, preparedSnapshot.revision, 'hierarchy.operation.disabled', typeof disabled === 'string' ? disabled : 'Hierarchy operation is disabled.');
8756
- }
8757
- const runContext = createOperationContributionContext(preparedSnapshot, invocation, invocation.input, 'isolated');
8758
- if (!runContext) {
8759
- return createHarnessHierarchyOperationResult(false, false, preparedSnapshot.revision, 'hierarchy.operation.unsupported', `Unsupported Hierarchy operation: ${invocation.operationId}`);
8760
- }
8761
- const contributionResult = await contribution.run(runContext);
8762
- const commitSnapshot = validateContext(invocation);
8763
- if ('ok' in commitSnapshot) {
8764
- return createHarnessHierarchyOperationResult(false, false, commitSnapshot.revision, 'hierarchy.operation.stale-before-commit', 'Hierarchy scope changed while the operation was being prepared; no patch was applied.');
8765
- }
8766
- const handled = applyHierarchyOperationContributionResult(state, options, contributionResult, contribution.label);
8767
- if (handled)
8768
- render();
8769
- const current = getSnapshot();
8770
- const changed = current.revision > beforeRevision;
8771
- if (handled && (contribution.effect !== 'authoring' || changed)) {
8772
- return createHarnessHierarchyOperationResult(true, changed, current.revision);
8773
- }
8774
- return createHarnessHierarchyOperationResult(false, changed, current.revision, 'hierarchy.operation.rejected', `${contribution.label} was rejected.`);
10024
+ const executeContribution = async (operationState = state) => {
10025
+ const preparedSnapshot = validateContext(invocation);
10026
+ if ('ok' in preparedSnapshot)
10027
+ return preparedSnapshot;
10028
+ const visibleContext = createOperationContributionContext(preparedSnapshot, invocation, invocation.input);
10029
+ if (!visibleContext || contribution.visible?.(visibleContext) === false) {
10030
+ return createHarnessHierarchyOperationResult(false, false, preparedSnapshot.revision, 'hierarchy.operation.unsupported', `Unsupported Hierarchy operation: ${invocation.operationId}`);
10031
+ }
10032
+ const disabledContext = createOperationContributionContext(preparedSnapshot, invocation, invocation.input);
10033
+ const disabled = disabledContext ? contribution.disabled?.(disabledContext) : true;
10034
+ if (disabled === true || typeof disabled === 'string') {
10035
+ return createHarnessHierarchyOperationResult(false, false, preparedSnapshot.revision, 'hierarchy.operation.disabled', typeof disabled === 'string' ? disabled : 'Hierarchy operation is disabled.');
10036
+ }
10037
+ const runContext = createOperationContributionContext(preparedSnapshot, invocation, invocation.input, 'isolated');
10038
+ if (!runContext) {
10039
+ return createHarnessHierarchyOperationResult(false, false, preparedSnapshot.revision, 'hierarchy.operation.unsupported', `Unsupported Hierarchy operation: ${invocation.operationId}`);
10040
+ }
10041
+ const contributionResult = await contribution.run(runContext);
10042
+ const commitSnapshot = validateContext(invocation);
10043
+ if ('ok' in commitSnapshot) {
10044
+ return createHarnessHierarchyOperationResult(false, false, commitSnapshot.revision, 'hierarchy.operation.stale-before-commit', 'Hierarchy scope changed while the operation was being prepared; no patch was applied.');
10045
+ }
10046
+ if (isLocalEditorHarnessPatchResult(contributionResult)
10047
+ && contribution.effect !== undefined
10048
+ && contribution.effect !== 'authoring') {
10049
+ return createHarnessHierarchyOperationResult(false, false, commitSnapshot.revision, 'hierarchy.operation.effect-mismatch', `${contribution.label} declared effect '${contribution.effect}' and cannot return an authoring patch.`);
10050
+ }
10051
+ const handled = applyHierarchyOperationContributionResult(operationState, options, contributionResult, contribution.label);
10052
+ if (handled)
10053
+ render();
10054
+ const current = getSnapshot();
10055
+ const changed = current.revision > beforeRevision;
10056
+ if (handled && (contribution.effect !== 'authoring' || changed)) {
10057
+ return createHarnessHierarchyOperationResult(true, changed, current.revision);
10058
+ }
10059
+ return createHarnessHierarchyOperationResult(false, changed, current.revision, 'hierarchy.operation.rejected', `${contribution.label} was rejected.`);
10060
+ };
10061
+ return contribution.effect === 'view'
10062
+ || contribution.effect === 'selection'
10063
+ || serializedAccess !== undefined
10064
+ ? executeContribution(serializedOperationState)
10065
+ : state.runBrowserAuthoringMutation(invocation.operationId, JSON.stringify([invocation, validated.scopeKey, validated.revision]), executeContribution);
8775
10066
  }, executorStates);
8776
10067
  }
8777
10068
  catch (error) {
@@ -8785,21 +10076,29 @@ export function createLocalEditorHarnessHierarchyScopeProvider(state, options, r
8785
10076
  const beforeRevision = validated.revision;
8786
10077
  try {
8787
10078
  return await runExclusiveResult(state, render, async () => {
8788
- const preparedSnapshot = validateContext(invocation);
8789
- if ('ok' in preparedSnapshot)
8790
- return preparedSnapshot;
8791
- return adapter.executeOperation({
8792
- snapshot: preparedSnapshot,
8793
- context: invocation,
8794
- invocation,
8795
- operation: listedOperation,
8796
- state,
8797
- options,
8798
- render,
8799
- getSnapshot,
8800
- executorStates,
8801
- });
8802
- }, executorStates);
10079
+ const executeAdapter = async (operationState = state) => {
10080
+ const preparedSnapshot = validateContext(invocation);
10081
+ if ('ok' in preparedSnapshot)
10082
+ return preparedSnapshot;
10083
+ return adapter.executeOperation({
10084
+ snapshot: preparedSnapshot,
10085
+ context: invocation,
10086
+ invocation,
10087
+ operation: listedOperation,
10088
+ state: operationState,
10089
+ options,
10090
+ render,
10091
+ getSnapshot,
10092
+ executorStates,
10093
+ });
10094
+ };
10095
+ return listedOperation.effect === 'authoring' && serializedAccess === undefined
10096
+ ? state.runBrowserAuthoringMutation(invocation.operationId, JSON.stringify([invocation, validated.scopeKey, validated.revision]), executeAdapter)
10097
+ : executeAdapter(serializedOperationState);
10098
+ }, executorStates, {
10099
+ renderBoundary: !(adapter.id === 'fps.editor.product.scene-hierarchy'
10100
+ && invocation.operationId === 'hierarchy.node.rename'),
10101
+ });
8803
10102
  }
8804
10103
  catch (error) {
8805
10104
  const current = getSnapshot();
@@ -8808,6 +10107,28 @@ export function createLocalEditorHarnessHierarchyScopeProvider(state, options, r
8808
10107
  },
8809
10108
  };
8810
10109
  }
10110
+ function getSerializedHierarchyInvocationAccess(invocation) {
10111
+ return invocation[SERIALIZED_HIERARCHY_INVOCATION];
10112
+ }
10113
+ function createSerializedHierarchyOperationState(state, serializedAccess) {
10114
+ const session = state.session;
10115
+ if (!session)
10116
+ return state;
10117
+ const authorizedSession = createEditorAuthoringSerializedSessionView(session, serializedAccess);
10118
+ return new Proxy(state, {
10119
+ get(target, property, receiver) {
10120
+ if (property === 'session')
10121
+ return authorizedSession;
10122
+ return Reflect.get(target, property, receiver);
10123
+ },
10124
+ set(target, property, value, receiver) {
10125
+ if (property === 'session') {
10126
+ throw new Error('Serialized Hierarchy operations cannot replace the active Session.');
10127
+ }
10128
+ return Reflect.set(target, property, value, receiver);
10129
+ },
10130
+ });
10131
+ }
8811
10132
  const BUILT_IN_HIERARCHY_OPERATION_IDS = new Set([
8812
10133
  'hierarchy.node.focus',
8813
10134
  'hierarchy.node.rename',
@@ -8857,6 +10178,13 @@ function createHarnessHierarchyOperationResult(ok, changed, revision, code, mess
8857
10178
  : Object.freeze([]),
8858
10179
  });
8859
10180
  }
10181
+ function reportBrowserAuthoringFailure(state, render, error) {
10182
+ state.status = 'Editor command failed';
10183
+ state.statusTone = 'error';
10184
+ state.statusToneStatus = state.status;
10185
+ state.statusDetails = error instanceof Error ? error.message : String(error);
10186
+ render();
10187
+ }
8860
10188
  function sameHierarchySelection(left, right) {
8861
10189
  return left.length === right.length && left.every((id, index) => id === right[index]);
8862
10190
  }
@@ -8884,6 +10212,32 @@ function getCachedLocalEditorHierarchyItems(state, options, document) {
8884
10212
  }
8885
10213
  return state.hierarchyItemsCache;
8886
10214
  }
10215
+ function resolveAuthoritativeHierarchyLabels(state, options, document, ids) {
10216
+ const cachedItems = getCachedLocalEditorHierarchyItems(state, options, document);
10217
+ const cachedById = new Map(cachedItems.map(item => [item.id, item]));
10218
+ const labels = {};
10219
+ let changed = false;
10220
+ const nextItems = [...cachedItems];
10221
+ for (const id of ids) {
10222
+ const cached = cachedById.get(id);
10223
+ const projectedName = options.documentAdapter.getProjectionNode(document, id)?.name;
10224
+ const label = typeof projectedName === 'string' && projectedName.trim()
10225
+ ? projectedName
10226
+ : cached?.label;
10227
+ if (!label)
10228
+ continue;
10229
+ labels[id] = label;
10230
+ if (cached && cached.label !== label) {
10231
+ const index = nextItems.findIndex(item => item.id === id);
10232
+ if (index >= 0)
10233
+ nextItems[index] = Object.freeze({ ...cached, label, name: label });
10234
+ changed = true;
10235
+ }
10236
+ }
10237
+ if (changed)
10238
+ state.hierarchyItemsCache = Object.freeze(nextItems);
10239
+ return labels;
10240
+ }
8887
10241
  function getLocalEditorDiagnosticContext(state) {
8888
10242
  return {
8889
10243
  scopeId: state.prefabStage
@@ -9149,6 +10503,23 @@ function toBrowserStatusDiagnostic(diagnostic, context) {
9149
10503
  ...(diagnostic.details ? { details: normalizeEditorDiagnosticDetails(diagnostic.details) } : {}),
9150
10504
  };
9151
10505
  }
10506
+ function createUiSessionState(state) {
10507
+ const sessionState = state.session?.getState({ cloneDocuments: false });
10508
+ const projectSnapshot = state.prefabStage ? null : state.projectAuthoringSnapshot;
10509
+ return sessionState
10510
+ ? {
10511
+ source: sessionState.source,
10512
+ dirty: state.prefabStage?.transaction.snapshot().documentDirty
10513
+ ?? projectSnapshot?.dirty
10514
+ ?? sessionState.dirty,
10515
+ canUndo: projectSnapshot?.canUndo ?? sessionState.canUndo,
10516
+ canRedo: projectSnapshot?.canRedo ?? sessionState.canRedo,
10517
+ history: projectSnapshot
10518
+ ? { entries: projectSnapshot.history.entries.map(entry => ({ ...entry })) }
10519
+ : sessionState.history,
10520
+ }
10521
+ : null;
10522
+ }
9152
10523
  function createUiState(state, options, hierarchySnapshot, toolManager) {
9153
10524
  const sessionState = state.session?.getState({ cloneDocuments: false });
9154
10525
  const document = sessionState?.workingDocument ?? null;
@@ -9188,7 +10559,6 @@ function createUiState(state, options, hierarchySnapshot, toolManager) {
9188
10559
  const uiState = {
9189
10560
  mode: state.mode,
9190
10561
  busy: state.busy,
9191
- agentBridge: state.agentBridgeStatus,
9192
10562
  status: state.status,
9193
10563
  statusTone: state.statusToneStatus === state.status ? state.statusTone : 'default',
9194
10564
  statusDetails: state.statusToneStatus === state.status ? state.statusDetails : '',
@@ -9207,6 +10577,7 @@ function createUiState(state, options, hierarchySnapshot, toolManager) {
9207
10577
  inspectorObject: previewInspectorObject,
9208
10578
  inspectorMultiObject: previewInspectorMultiObject,
9209
10579
  inspectorPending: false,
10580
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
9210
10581
  renderingPanel: document
9211
10582
  ? options.documentAdapter.getRenderingPanelState?.(document, {
9212
10583
  scene: state.world?.scene ?? null,
@@ -9251,22 +10622,14 @@ function createUiState(state, options, hierarchySnapshot, toolManager) {
9251
10622
  performanceStats: cloneLocalEditorPerformanceStats(state.performanceStats),
9252
10623
  sceneCameraPreview: {
9253
10624
  enabled: state.sceneCameraPreviewEnabled,
9254
- available: hasSceneCameraPreviewRig(state, options),
10625
+ available: hasSceneCameraPreviewRig(options, document),
9255
10626
  },
9256
10627
  prefabStage: createBrowserPrefabStageState(state, options, document),
9257
10628
  grid: {
9258
10629
  visible: state.gridVisible,
9259
10630
  available: !!state.grid,
9260
10631
  },
9261
- session: sessionState
9262
- ? {
9263
- source: sessionState.source,
9264
- dirty: state.prefabStage?.transaction.snapshot().documentDirty ?? sessionState.dirty,
9265
- canUndo: sessionState.canUndo,
9266
- canRedo: sessionState.canRedo,
9267
- history: sessionState.history,
9268
- }
9269
- : null,
10632
+ session: createUiSessionState(state),
9270
10633
  };
9271
10634
  uiState.documentRevision = sessionState?.documentRevision ?? 0;
9272
10635
  const diagnosticContext = getLocalEditorDiagnosticContext(state);
@@ -9342,25 +10705,25 @@ function createBrowserPrefabStageState(state, options, document, includeInspecto
9342
10705
  previewNodeId: descriptor.previewNodeId,
9343
10706
  };
9344
10707
  }
9345
- function summarizeAgentBridgePrefabStage(state, options) {
10708
+ function summarizeEditorCommandPrefabStage(state, options) {
9346
10709
  const document = state.session?.getState({ cloneDocuments: false }).workingDocument ?? null;
9347
10710
  const stage = createBrowserPrefabStageState(state, options, document);
9348
10711
  if (!stage)
9349
10712
  return null;
9350
- return createAgentBridgePrefabStageState(stage);
10713
+ return createEditorCommandPrefabStageState(stage);
9351
10714
  }
9352
- function summarizeAgentBridgePrefabStageDetails(state, options) {
10715
+ function summarizeEditorCommandPrefabStageDetails(state, options) {
9353
10716
  const document = state.session?.getState({ cloneDocuments: false }).workingDocument ?? null;
9354
10717
  const stage = createBrowserPrefabStageState(state, options, document);
9355
10718
  if (!stage)
9356
10719
  return null;
9357
10720
  return {
9358
- stage: createAgentBridgePrefabStageState(stage),
9359
- nodeDetails: createAgentBridgePrefabNodeDetails(stage.structureItems),
10721
+ stage: createEditorCommandPrefabStageState(stage),
10722
+ nodeDetails: createEditorCommandPrefabNodeDetails(stage.structureItems),
9360
10723
  inspectorObject: stage.inspectorObject ?? null,
9361
10724
  };
9362
10725
  }
9363
- function createAgentBridgePrefabNodeDetails(items) {
10726
+ function createEditorCommandPrefabNodeDetails(items) {
9364
10727
  const details = [];
9365
10728
  const stack = [...items].reverse();
9366
10729
  while (stack.length > 0) {
@@ -9389,7 +10752,7 @@ function createAgentBridgePrefabNodeDetails(items) {
9389
10752
  }
9390
10753
  return details;
9391
10754
  }
9392
- function createAgentBridgePrefabStageState(stage) {
10755
+ function createEditorCommandPrefabStageState(stage) {
9393
10756
  return {
9394
10757
  assetId: stage.assetId,
9395
10758
  ...(stage.browserAssetId ? { browserAssetId: stage.browserAssetId } : {}),
@@ -9405,7 +10768,7 @@ function createAgentBridgePrefabStageState(stage) {
9405
10768
  canDiscard: stage.canDiscard,
9406
10769
  };
9407
10770
  }
9408
- function createAgentBridgePrefabSummaryFromStage(stage) {
10771
+ function createEditorCommandPrefabSummaryFromStage(stage) {
9409
10772
  return {
9410
10773
  assetId: stage.assetId,
9411
10774
  ...(stage.browserAssetId ? { browserAssetId: stage.browserAssetId } : {}),
@@ -9416,7 +10779,7 @@ function createAgentBridgePrefabSummaryFromStage(stage) {
9416
10779
  status: 'ready',
9417
10780
  };
9418
10781
  }
9419
- function isSameAgentBridgePrefabStage(left, right) {
10782
+ function isSameEditorCommandPrefabStage(left, right) {
9420
10783
  if (!left || !right)
9421
10784
  return false;
9422
10785
  return left.assetId === right.assetId
@@ -9583,10 +10946,10 @@ function createLocalEditorEditingContextProviders(state, options) {
9583
10946
  return focusProjectionNode(state, activeId);
9584
10947
  },
9585
10948
  commitTransform: event => commitSceneGizmoTransform(state, options, event),
9586
- beginDuplicateDrag: input => beginDuplicateDrag(state, options, input),
10949
+ beginDuplicateDrag: (input, session) => beginDuplicateDrag(state, options, input, session),
9587
10950
  getInspectorObject: (document, targetId) => createSceneEditingContextInspectorObject(state, options, document, targetId),
9588
10951
  getInspectorMultiObject: (document, targetIds, activeId) => createSceneEditingContextInspectorMultiObject(state, options, document, targetIds, activeId),
9589
- patchInspectorProperty: input => patchSceneSerializedProperty(state, options, input),
10952
+ patchInspectorProperty: (input, onCommitted) => patchSceneSerializedProperty(state, options, input, onCommitted),
9590
10953
  resolveInspectorPreviewTransform(document, targetId, transform, preview) {
9591
10954
  const createInspectorPreview = options.documentAdapter.createTransformInspectorPreview;
9592
10955
  if (!document || !createInspectorPreview)
@@ -9799,6 +11162,7 @@ function createUiToolPresentationPreviewState(state, snapshot) {
9799
11162
  };
9800
11163
  }
9801
11164
  function publishLocalEditorSelectionPresentation(state, options, renderPreviewUi, reason) {
11165
+ cancelInspectorPresentationPreview(state);
9802
11166
  syncViewportSpatialOverlay(state, options);
9803
11167
  renderPreviewUi(createUiSelectionPresentationPreviewState(state, options));
9804
11168
  requestEditorSceneFrame(state, reason);
@@ -9811,6 +11175,7 @@ function createUiSelectionPresentationPreviewState(state, options) {
9811
11175
  const transformSelectionIsLarge = isLargeSelection(selectedIds);
9812
11176
  const document = state.session?.getState({ cloneDocuments: false }).workingDocument ?? null;
9813
11177
  return {
11178
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
9814
11179
  selectedIds,
9815
11180
  activeId,
9816
11181
  selectionSummary: {
@@ -9837,6 +11202,7 @@ function createUiSelectionInspectorPreviewState(state, options) {
9837
11202
  const document = state.session?.getState({ cloneDocuments: false }).workingDocument ?? null;
9838
11203
  if (!document) {
9839
11204
  return {
11205
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
9840
11206
  inspectorObject: null,
9841
11207
  inspectorMultiObject: null,
9842
11208
  inspectorPending: false,
@@ -9846,6 +11212,7 @@ function createUiSelectionInspectorPreviewState(state, options) {
9846
11212
  if (state.prefabStage) {
9847
11213
  const prefabStage = createBrowserPrefabStageState(state, options, document);
9848
11214
  return {
11215
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
9849
11216
  prefabStage,
9850
11217
  inspectorObject: prefabStage?.inspectorObject ?? null,
9851
11218
  inspectorMultiObject: null,
@@ -9854,6 +11221,7 @@ function createUiSelectionInspectorPreviewState(state, options) {
9854
11221
  }
9855
11222
  const editingContext = getLocalEditorEditingContext(state);
9856
11223
  return {
11224
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
9857
11225
  inspectorObject: editingContext.getInspectorObject(document),
9858
11226
  inspectorMultiObject: editingContext.getInspectorMultiObject(document),
9859
11227
  inspectorPending: false,
@@ -9861,10 +11229,6 @@ function createUiSelectionInspectorPreviewState(state, options) {
9861
11229
  }
9862
11230
  function cancelScheduledSelectionInspectorRefresh(state) {
9863
11231
  state.selectionInspectorRefreshGeneration += 1;
9864
- if (state.selectionInspectorRefreshFrame !== null) {
9865
- globalThis.cancelAnimationFrame?.(state.selectionInspectorRefreshFrame);
9866
- state.selectionInspectorRefreshFrame = null;
9867
- }
9868
11232
  if (state.selectionInspectorRefreshTimeout !== null) {
9869
11233
  globalThis.clearTimeout(state.selectionInspectorRefreshTimeout);
9870
11234
  state.selectionInspectorRefreshTimeout = null;
@@ -9892,18 +11256,7 @@ function scheduleSelectionInspectorRefresh(state, options, renderPreviewUi) {
9892
11256
  });
9893
11257
  }
9894
11258
  };
9895
- const deferUntilAfterFrame = () => {
9896
- state.selectionInspectorRefreshFrame = null;
9897
- if (generation !== state.selectionInspectorRefreshGeneration || !state.world)
9898
- return;
9899
- state.selectionInspectorRefreshTimeout = globalThis.setTimeout(publish, 0);
9900
- };
9901
- if (typeof globalThis.requestAnimationFrame === 'function') {
9902
- state.selectionInspectorRefreshFrame = globalThis.requestAnimationFrame(deferUntilAfterFrame);
9903
- }
9904
- else {
9905
- state.selectionInspectorRefreshTimeout = globalThis.setTimeout(publish, 0);
9906
- }
11259
+ state.selectionInspectorRefreshTimeout = globalThis.setTimeout(publish, 0);
9907
11260
  }
9908
11261
  function isLocalEditorSelectionDebugEnabled() {
9909
11262
  return typeof globalThis.location?.search === 'string'
@@ -9935,6 +11288,7 @@ function createUiPreviewState(state, options) {
9935
11288
  ? applyActiveTransformPreviewToInspectorObject(state, options, document, withRuntimeInspectorSections(state, options, document, inspectorObjectBase))
9936
11289
  : null;
9937
11290
  return {
11291
+ inspectorPresentationGeneration: state.inspectorPresentationGeneration,
9938
11292
  status: state.status,
9939
11293
  statusTone: state.statusToneStatus === state.status ? state.statusTone : 'default',
9940
11294
  statusDetails: state.statusToneStatus === state.status ? state.statusDetails : '',