@fps-games/editor 0.1.5-beta.5 → 0.1.7-beta.1

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 (469) hide show
  1. package/dist/agent-bridge/capabilities/assets.d.ts +8 -0
  2. package/dist/agent-bridge/capabilities/assets.d.ts.map +1 -0
  3. package/dist/agent-bridge/capabilities/assets.js +226 -0
  4. package/dist/agent-bridge/capabilities/assets.js.map +1 -0
  5. package/dist/agent-bridge/capabilities/editor-state.d.ts +5 -0
  6. package/dist/agent-bridge/capabilities/editor-state.d.ts.map +1 -0
  7. package/dist/agent-bridge/capabilities/editor-state.js +72 -0
  8. package/dist/agent-bridge/capabilities/editor-state.js.map +1 -0
  9. package/dist/agent-bridge/capabilities/host-lifecycle.d.ts +4 -0
  10. package/dist/agent-bridge/capabilities/host-lifecycle.d.ts.map +1 -0
  11. package/dist/agent-bridge/capabilities/host-lifecycle.js +87 -0
  12. package/dist/agent-bridge/capabilities/host-lifecycle.js.map +1 -0
  13. package/dist/agent-bridge/capabilities/prefabs.d.ts +5 -0
  14. package/dist/agent-bridge/capabilities/prefabs.d.ts.map +1 -0
  15. package/dist/agent-bridge/capabilities/prefabs.js +735 -0
  16. package/dist/agent-bridge/capabilities/prefabs.js.map +1 -0
  17. package/dist/agent-bridge/capabilities/scene-objects.d.ts +9 -0
  18. package/dist/agent-bridge/capabilities/scene-objects.d.ts.map +1 -0
  19. package/dist/agent-bridge/capabilities/scene-objects.js +394 -0
  20. package/dist/agent-bridge/capabilities/scene-objects.js.map +1 -0
  21. package/dist/agent-bridge/capabilities/selection.d.ts +5 -0
  22. package/dist/agent-bridge/capabilities/selection.d.ts.map +1 -0
  23. package/dist/agent-bridge/capabilities/selection.js +57 -0
  24. package/dist/agent-bridge/capabilities/selection.js.map +1 -0
  25. package/dist/agent-bridge/capabilities/shadows.d.ts +5 -0
  26. package/dist/agent-bridge/capabilities/shadows.d.ts.map +1 -0
  27. package/dist/agent-bridge/capabilities/shadows.js +1876 -0
  28. package/dist/agent-bridge/capabilities/shadows.js.map +1 -0
  29. package/dist/agent-bridge/capabilities/transform.d.ts +5 -0
  30. package/dist/agent-bridge/capabilities/transform.d.ts.map +1 -0
  31. package/dist/agent-bridge/capabilities/transform.js +162 -0
  32. package/dist/agent-bridge/capabilities/transform.js.map +1 -0
  33. package/dist/agent-bridge/capabilities/ui-regions.d.ts +4 -0
  34. package/dist/agent-bridge/capabilities/ui-regions.d.ts.map +1 -0
  35. package/dist/agent-bridge/capabilities/ui-regions.js +168 -0
  36. package/dist/agent-bridge/capabilities/ui-regions.js.map +1 -0
  37. package/dist/agent-bridge/capabilities/viewport.d.ts +5 -0
  38. package/dist/agent-bridge/capabilities/viewport.d.ts.map +1 -0
  39. package/dist/agent-bridge/capabilities/viewport.js +407 -0
  40. package/dist/agent-bridge/capabilities/viewport.js.map +1 -0
  41. package/dist/agent-bridge/capability-sources.d.ts +6 -0
  42. package/dist/agent-bridge/capability-sources.d.ts.map +1 -0
  43. package/dist/agent-bridge/capability-sources.js +26 -0
  44. package/dist/agent-bridge/capability-sources.js.map +1 -0
  45. package/dist/agent-bridge/diagnostics.d.ts +12 -0
  46. package/dist/agent-bridge/diagnostics.d.ts.map +1 -0
  47. package/dist/agent-bridge/diagnostics.js +45 -0
  48. package/dist/agent-bridge/diagnostics.js.map +1 -0
  49. package/dist/agent-bridge/index.d.ts +5 -0
  50. package/dist/agent-bridge/index.d.ts.map +1 -0
  51. package/dist/agent-bridge/index.js +5 -0
  52. package/dist/agent-bridge/index.js.map +1 -0
  53. package/dist/agent-bridge/register.d.ts +4 -0
  54. package/dist/agent-bridge/register.d.ts.map +1 -0
  55. package/dist/agent-bridge/register.js +908 -0
  56. package/dist/agent-bridge/register.js.map +1 -0
  57. package/dist/agent-bridge/shared.d.ts +189 -0
  58. package/dist/agent-bridge/shared.d.ts.map +1 -0
  59. package/dist/agent-bridge/shared.js +39 -0
  60. package/dist/agent-bridge/shared.js.map +1 -0
  61. package/dist/agent-bridge/source-types.d.ts +90 -0
  62. package/dist/agent-bridge/source-types.d.ts.map +1 -0
  63. package/dist/agent-bridge/source-types.js +2 -0
  64. package/dist/agent-bridge/source-types.js.map +1 -0
  65. package/dist/build-info.json +17 -17
  66. package/dist/local-editor-agent-actions.d.ts +8 -1
  67. package/dist/local-editor-agent-actions.d.ts.map +1 -1
  68. package/dist/local-editor-agent-actions.js +171 -12
  69. package/dist/local-editor-agent-actions.js.map +1 -1
  70. package/dist/local-editor-agent-bridge-transport.d.ts +95 -0
  71. package/dist/local-editor-agent-bridge-transport.d.ts.map +1 -0
  72. package/dist/local-editor-agent-bridge-transport.js +340 -0
  73. package/dist/local-editor-agent-bridge-transport.js.map +1 -0
  74. package/dist/local-editor-agent-bridge.d.ts +2 -0
  75. package/dist/local-editor-agent-bridge.d.ts.map +1 -0
  76. package/dist/local-editor-agent-bridge.js +2 -0
  77. package/dist/local-editor-agent-bridge.js.map +1 -0
  78. package/dist/local-editor-agent-context.d.ts +29 -1
  79. package/dist/local-editor-agent-context.d.ts.map +1 -1
  80. package/dist/local-editor-agent-context.js +403 -0
  81. package/dist/local-editor-agent-context.js.map +1 -1
  82. package/dist/local-editor-harness.d.ts +128 -6
  83. package/dist/local-editor-harness.d.ts.map +1 -1
  84. package/dist/local-editor-harness.js +2730 -151
  85. package/dist/local-editor-harness.js.map +1 -1
  86. package/dist/local-editor-marker-graph.d.ts +92 -0
  87. package/dist/local-editor-marker-graph.d.ts.map +1 -0
  88. package/dist/local-editor-marker-graph.js +1432 -0
  89. package/dist/local-editor-marker-graph.js.map +1 -0
  90. package/dist/local-editor-performance-stats.js +1 -1
  91. package/dist/local-editor-performance-stats.js.map +1 -1
  92. package/dist/playable-babylon-facade.d.ts +2 -2
  93. package/dist/playable-babylon-facade.d.ts.map +1 -1
  94. package/dist/playable-babylon-facade.js +1 -1
  95. package/dist/playable-babylon-facade.js.map +1 -1
  96. package/dist/playable-babylon-rendering.js +20 -0
  97. package/dist/playable-babylon-rendering.js.map +1 -1
  98. package/dist/playable-local-editor-host.d.ts +57 -7
  99. package/dist/playable-local-editor-host.d.ts.map +1 -1
  100. package/dist/playable-local-editor-host.js +25 -1
  101. package/dist/playable-local-editor-host.js.map +1 -1
  102. package/dist/playable-sdk.d.ts +4 -2
  103. package/dist/playable-sdk.d.ts.map +1 -1
  104. package/dist/playable-sdk.js +2 -1
  105. package/dist/playable-sdk.js.map +1 -1
  106. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-system.d.ts.map +1 -1
  107. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-system.js +6 -4
  108. package/node_modules/@fps-games/babylon-renderer/dist/shadows/blob-shadow-system.js.map +1 -1
  109. package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-shaders.d.ts.map +1 -1
  110. package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-shaders.js +103 -1
  111. package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-shaders.js.map +1 -1
  112. package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-system.d.ts.map +1 -1
  113. package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-system.js +330 -27
  114. package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-system.js.map +1 -1
  115. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-artifact-system.d.ts.map +1 -1
  116. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-artifact-system.js +159 -0
  117. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-artifact-system.js.map +1 -1
  118. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-baker.js +8 -1
  119. package/node_modules/@fps-games/babylon-renderer/dist/shadows/static-projected-shadow-baker.js.map +1 -1
  120. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.d.ts +53 -2
  121. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.d.ts.map +1 -1
  122. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.js +1 -0
  123. package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.js.map +1 -1
  124. package/node_modules/@fps-games/babylon-renderer/package.json +1 -1
  125. package/node_modules/@fps-games/editor-babylon/dist/camera-controller.d.ts.map +1 -1
  126. package/node_modules/@fps-games/editor-babylon/dist/camera-controller.js +11 -0
  127. package/node_modules/@fps-games/editor-babylon/dist/camera-controller.js.map +1 -1
  128. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts +3 -1
  129. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts.map +1 -1
  130. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js +114 -18
  131. package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js.map +1 -1
  132. package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts +1 -0
  133. package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts.map +1 -1
  134. package/node_modules/@fps-games/editor-babylon/dist/editor-world.js +3 -0
  135. package/node_modules/@fps-games/editor-babylon/dist/editor-world.js.map +1 -1
  136. package/node_modules/@fps-games/editor-babylon/dist/index.d.ts +3 -0
  137. package/node_modules/@fps-games/editor-babylon/dist/index.d.ts.map +1 -1
  138. package/node_modules/@fps-games/editor-babylon/dist/index.js +3 -0
  139. package/node_modules/@fps-games/editor-babylon/dist/index.js.map +1 -1
  140. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.d.ts +52 -0
  141. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.d.ts.map +1 -0
  142. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js +1395 -0
  143. package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js.map +1 -0
  144. package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.d.ts.map +1 -1
  145. package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.js +4 -65
  146. package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.js.map +1 -1
  147. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.d.ts +28 -0
  148. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.d.ts.map +1 -0
  149. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.js +235 -0
  150. package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.js.map +1 -0
  151. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.d.ts +37 -0
  152. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.d.ts.map +1 -0
  153. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.js +451 -0
  154. package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.js.map +1 -0
  155. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.d.ts.map +1 -1
  156. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js +14 -3
  157. package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js.map +1 -1
  158. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.d.ts +1 -0
  159. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.d.ts.map +1 -1
  160. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.js +20 -4
  161. package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.js.map +1 -1
  162. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.d.ts +3 -6
  163. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.d.ts.map +1 -1
  164. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.js +4 -37
  165. package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.js.map +1 -1
  166. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +74 -1
  167. package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts.map +1 -1
  168. package/node_modules/@fps-games/editor-babylon/dist/projection.js +641 -52
  169. package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
  170. package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.d.ts +11 -0
  171. package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.d.ts.map +1 -1
  172. package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.js +113 -29
  173. package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.js.map +1 -1
  174. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.d.ts +13 -1
  175. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.d.ts.map +1 -1
  176. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.js +543 -3
  177. package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.js.map +1 -1
  178. package/node_modules/@fps-games/editor-babylon/package.json +5 -5
  179. package/node_modules/@fps-games/editor-browser/dist/index.d.ts +8 -2
  180. package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
  181. package/node_modules/@fps-games/editor-browser/dist/index.js +4 -0
  182. package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
  183. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.d.ts +5 -0
  184. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.d.ts.map +1 -0
  185. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js +23 -0
  186. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js.map +1 -0
  187. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js +1 -1
  188. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js.map +1 -1
  189. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts.map +1 -1
  190. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js +76 -9
  191. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js.map +1 -1
  192. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.d.ts.map +1 -1
  193. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.js +3 -2
  194. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.js.map +1 -1
  195. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-command-adapter.d.ts +21 -0
  196. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-command-adapter.d.ts.map +1 -0
  197. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-command-adapter.js +75 -0
  198. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-command-adapter.js.map +1 -0
  199. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +2 -0
  200. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -1
  201. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +326 -72
  202. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -1
  203. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.d.ts +5 -0
  204. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.d.ts.map +1 -0
  205. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.js +92 -0
  206. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.js.map +1 -0
  207. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts +4 -1
  208. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts.map +1 -1
  209. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js +22 -7
  210. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js.map +1 -1
  211. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts +4 -0
  212. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts.map +1 -1
  213. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js +24 -11
  214. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js.map +1 -1
  215. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts +1 -1
  216. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts.map +1 -1
  217. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js +7 -0
  218. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js.map +1 -1
  219. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.js +1 -1
  220. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.js.map +1 -1
  221. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts +4 -0
  222. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
  223. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +1471 -29
  224. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
  225. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.d.ts +70 -0
  226. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.d.ts.map +1 -0
  227. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js +171 -0
  228. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js.map +1 -0
  229. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts +1 -1
  230. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts.map +1 -1
  231. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js +2 -2
  232. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js.map +1 -1
  233. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts.map +1 -1
  234. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js +9 -2
  235. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js.map +1 -1
  236. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +301 -3
  237. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
  238. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts +12 -1
  239. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts.map +1 -1
  240. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js +13 -1
  241. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js.map +1 -1
  242. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +6 -4
  243. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
  244. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +1501 -122
  245. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
  246. package/node_modules/@fps-games/editor-browser/package.json +3 -3
  247. package/node_modules/@fps-games/editor-core/dist/agent-bridge.d.ts +18 -0
  248. package/node_modules/@fps-games/editor-core/dist/agent-bridge.d.ts.map +1 -0
  249. package/node_modules/@fps-games/editor-core/dist/agent-bridge.js +102 -0
  250. package/node_modules/@fps-games/editor-core/dist/agent-bridge.js.map +1 -0
  251. package/node_modules/@fps-games/editor-core/dist/index.d.ts +1 -0
  252. package/node_modules/@fps-games/editor-core/dist/index.d.ts.map +1 -1
  253. package/node_modules/@fps-games/editor-core/dist/index.js +1 -0
  254. package/node_modules/@fps-games/editor-core/dist/index.js.map +1 -1
  255. package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts +1 -1
  256. package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts.map +1 -1
  257. package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts +3 -1
  258. package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts.map +1 -1
  259. package/node_modules/@fps-games/editor-core/dist/viewport-tools.js +1 -0
  260. package/node_modules/@fps-games/editor-core/dist/viewport-tools.js.map +1 -1
  261. package/node_modules/@fps-games/editor-core/package.json +2 -2
  262. package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
  263. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/config.d.ts +3 -0
  264. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/config.d.ts.map +1 -0
  265. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/config.js +9 -0
  266. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/config.js.map +1 -0
  267. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/generated-module.d.ts +6 -0
  268. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/generated-module.d.ts.map +1 -0
  269. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/generated-module.js +89 -0
  270. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/generated-module.js.map +1 -0
  271. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/identity.d.ts +18 -0
  272. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/identity.d.ts.map +1 -0
  273. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/identity.js +121 -0
  274. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/identity.js.map +1 -0
  275. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/index.d.ts +4 -0
  276. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/index.d.ts.map +1 -0
  277. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/index.js +3 -0
  278. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/index.js.map +1 -0
  279. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/metadata.d.ts +27 -0
  280. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/metadata.d.ts.map +1 -0
  281. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/metadata.js +782 -0
  282. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/metadata.js.map +1 -0
  283. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/node.d.ts +5 -0
  284. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/node.d.ts.map +1 -0
  285. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/node.js +5 -0
  286. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/node.js.map +1 -0
  287. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/registry.d.ts +80 -0
  288. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/registry.d.ts.map +1 -0
  289. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/registry.js +600 -0
  290. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/registry.js.map +1 -0
  291. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/types.d.ts +51 -0
  292. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/types.d.ts.map +1 -0
  293. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/types.js +2 -0
  294. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-database/types.js.map +1 -0
  295. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.d.ts.map +1 -1
  296. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js +17 -3
  297. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js.map +1 -1
  298. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.d.ts.map +1 -1
  299. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.js +2 -1
  300. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.js.map +1 -1
  301. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/types.d.ts +3 -2
  302. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/types.d.ts.map +1 -1
  303. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.d.ts +1 -1
  304. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.d.ts.map +1 -1
  305. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.js +48 -20
  306. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.js.map +1 -1
  307. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.d.ts +1 -1
  308. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.d.ts.map +1 -1
  309. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.js +1 -1
  310. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.js.map +1 -1
  311. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/placement.d.ts.map +1 -1
  312. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/placement.js +2 -1
  313. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/placement.js.map +1 -1
  314. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/types.d.ts +3 -0
  315. package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/types.d.ts.map +1 -1
  316. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.d.ts +97 -2
  317. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.d.ts.map +1 -1
  318. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js +1143 -8
  319. package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js.map +1 -1
  320. package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts +145 -2
  321. package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts.map +1 -1
  322. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts +1 -1
  323. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts.map +1 -1
  324. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js +1 -1
  325. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js.map +1 -1
  326. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.d.ts.map +1 -1
  327. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js +24 -9
  328. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js.map +1 -1
  329. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts +6 -0
  330. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts.map +1 -1
  331. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js +31 -0
  332. package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js.map +1 -1
  333. package/node_modules/@fps-games/editor-playable-sdk/dist/index.d.ts +3 -0
  334. package/node_modules/@fps-games/editor-playable-sdk/dist/index.d.ts.map +1 -1
  335. package/node_modules/@fps-games/editor-playable-sdk/dist/index.js +3 -0
  336. package/node_modules/@fps-games/editor-playable-sdk/dist/index.js.map +1 -1
  337. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.d.ts +82 -2
  338. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.d.ts.map +1 -1
  339. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.js +396 -22
  340. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/document-sections.js.map +1 -1
  341. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/index.d.ts +1 -1
  342. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/index.d.ts.map +1 -1
  343. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/index.js +1 -1
  344. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/index.js.map +1 -1
  345. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/validation.d.ts.map +1 -1
  346. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/validation.js +0 -3
  347. package/node_modules/@fps-games/editor-playable-sdk/dist/inspector/validation.js.map +1 -1
  348. package/node_modules/@fps-games/editor-playable-sdk/dist/lighting-debug/math.d.ts +29 -1
  349. package/node_modules/@fps-games/editor-playable-sdk/dist/lighting-debug/math.d.ts.map +1 -1
  350. package/node_modules/@fps-games/editor-playable-sdk/dist/lighting-debug/math.js +143 -0
  351. package/node_modules/@fps-games/editor-playable-sdk/dist/lighting-debug/math.js.map +1 -1
  352. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.d.ts.map +1 -1
  353. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js +3 -7
  354. package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js.map +1 -1
  355. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.d.ts.map +1 -1
  356. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js +6 -0
  357. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js.map +1 -1
  358. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/types.d.ts +2 -10
  359. package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/types.d.ts.map +1 -1
  360. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.d.ts.map +1 -1
  361. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js +107 -11
  362. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js.map +1 -1
  363. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/reducer.d.ts.map +1 -1
  364. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/reducer.js +4 -1
  365. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/reducer.js.map +1 -1
  366. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/transform.d.ts.map +1 -1
  367. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/transform.js +19 -1
  368. package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/transform.js.map +1 -1
  369. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/panel.d.ts.map +1 -1
  370. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/panel.js +19 -3
  371. package/node_modules/@fps-games/editor-playable-sdk/dist/rendering-profile/panel.js.map +1 -1
  372. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/assets.d.ts +1 -2
  373. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/assets.d.ts.map +1 -1
  374. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/assets.js +0 -14
  375. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/assets.js.map +1 -1
  376. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/import-plan.d.ts +1 -7
  377. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/import-plan.d.ts.map +1 -1
  378. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/import-plan.js +3 -32
  379. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/import-plan.js.map +1 -1
  380. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.d.ts.map +1 -1
  381. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js +82 -63
  382. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js.map +1 -1
  383. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts +7 -23
  384. package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts.map +1 -1
  385. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/compiler.d.ts +5 -0
  386. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/compiler.d.ts.map +1 -0
  387. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/compiler.js +996 -0
  388. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/compiler.js.map +1 -0
  389. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/index.d.ts +3 -0
  390. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/index.d.ts.map +1 -0
  391. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/index.js +3 -0
  392. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/index.js.map +1 -0
  393. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/types.d.ts +152 -0
  394. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/types.d.ts.map +1 -0
  395. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/types.js +2 -0
  396. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-compiler/types.js.map +1 -0
  397. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.d.ts.map +1 -1
  398. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.js +44 -2
  399. package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.js.map +1 -1
  400. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.d.ts +1 -0
  401. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.d.ts.map +1 -1
  402. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.js +244 -12
  403. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.js.map +1 -1
  404. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.d.ts.map +1 -1
  405. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.js +216 -67
  406. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.js.map +1 -1
  407. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.d.ts.map +1 -1
  408. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.js +7 -4
  409. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.js.map +1 -1
  410. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/patch.d.ts.map +1 -1
  411. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/patch.js +45 -1
  412. package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/patch.js.map +1 -1
  413. package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.d.ts.map +1 -1
  414. package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.js +2 -1
  415. package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.js.map +1 -1
  416. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/index.d.ts +5 -0
  417. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/index.d.ts.map +1 -0
  418. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/index.js +5 -0
  419. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/index.js.map +1 -0
  420. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/legacy.d.ts +21 -0
  421. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/legacy.d.ts.map +1 -0
  422. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/legacy.js +158 -0
  423. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/legacy.js.map +1 -0
  424. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/quality.d.ts +5 -0
  425. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/quality.d.ts.map +1 -0
  426. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/quality.js +83 -0
  427. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/quality.js.map +1 -0
  428. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/resolver.d.ts +35 -0
  429. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/resolver.d.ts.map +1 -0
  430. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/resolver.js +363 -0
  431. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/resolver.js.map +1 -0
  432. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/types.d.ts +105 -0
  433. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/types.d.ts.map +1 -0
  434. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/types.js +6 -0
  435. package/node_modules/@fps-games/editor-playable-sdk/dist/shadow-system/types.js.map +1 -0
  436. package/node_modules/@fps-games/editor-playable-sdk/dist/static-shadows/artifact.d.ts +3 -0
  437. package/node_modules/@fps-games/editor-playable-sdk/dist/static-shadows/artifact.d.ts.map +1 -1
  438. package/node_modules/@fps-games/editor-playable-sdk/dist/static-shadows/artifact.js +44 -0
  439. package/node_modules/@fps-games/editor-playable-sdk/dist/static-shadows/artifact.js.map +1 -1
  440. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/asset-authoring.d.ts +26 -0
  441. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/asset-authoring.d.ts.map +1 -0
  442. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/asset-authoring.js +105 -0
  443. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/asset-authoring.js.map +1 -0
  444. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts +3 -0
  445. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts.map +1 -1
  446. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js +3 -0
  447. package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js.map +1 -1
  448. package/node_modules/@fps-games/editor-playable-sdk/package.json +3 -3
  449. package/node_modules/@fps-games/editor-protocol/dist/agent-bridge.d.ts +1127 -0
  450. package/node_modules/@fps-games/editor-protocol/dist/agent-bridge.d.ts.map +1 -0
  451. package/node_modules/@fps-games/editor-protocol/dist/agent-bridge.js +2180 -0
  452. package/node_modules/@fps-games/editor-protocol/dist/agent-bridge.js.map +1 -0
  453. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.d.ts +68 -0
  454. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.d.ts.map +1 -0
  455. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.js +23 -0
  456. package/node_modules/@fps-games/editor-protocol/dist/agent-trace.js.map +1 -0
  457. package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts +57 -3
  458. package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts.map +1 -1
  459. package/node_modules/@fps-games/editor-protocol/dist/agent.js.map +1 -1
  460. package/node_modules/@fps-games/editor-protocol/dist/index.d.ts +21 -0
  461. package/node_modules/@fps-games/editor-protocol/dist/index.d.ts.map +1 -1
  462. package/node_modules/@fps-games/editor-protocol/dist/index.js +3 -0
  463. package/node_modules/@fps-games/editor-protocol/dist/index.js.map +1 -1
  464. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.d.ts +128 -0
  465. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.d.ts.map +1 -0
  466. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.js +2 -0
  467. package/node_modules/@fps-games/editor-protocol/dist/marker-graph.js.map +1 -0
  468. package/node_modules/@fps-games/editor-protocol/package.json +1 -1
  469. package/package.json +8 -8
@@ -2,6 +2,7 @@ import { createLocalEditorIcon, isLocalEditorIconName, } from './local-editor-ui
2
2
  import { createBadge, createAssetList, createDockTab, createEditorInputStyle, createEmptyState, createListItemBlock, createPanelHeader, createPropertyRow, createToolbarButton, createTreeView, createTreeViewItem, } from './local-editor-ui-primitives.js';
3
3
  import { configureLocalEditorDockPanelContent, createLocalEditorDockPanelViewport, renderLocalEditorDockTabStrip, } from './local-editor-ui-dock-panel.js';
4
4
  import { clearElement, createButton, toTitle } from './local-editor-ui-shared.js';
5
+ import { resolveLocalEditorAssetBrowserTab, resolveLocalEditorBrowserAssetKind, } from './local-editor-ui-asset-kind.js';
5
6
  import { createLocalEditorBrowserInspectorVectorCopyPayload, } from './local-editor-inspector-clipboard.js';
6
7
  import { resolveLocalEditorBrowserInspectorVectorGroupPathFromProperties, } from './local-editor-inspector-groups.js';
7
8
  const BOTTOM_DOCK_TAB_ICONS = {
@@ -11,6 +12,7 @@ const BOTTOM_DOCK_TAB_ICONS = {
11
12
  const RIGHT_DOCK_TAB_ICONS = {
12
13
  inspector: 'inspector',
13
14
  rendering: 'world',
15
+ markers: 'view-overlay',
14
16
  };
15
17
  const LEFT_DOCK_TAB_ICONS = {
16
18
  hierarchy: 'hierarchy',
@@ -18,6 +20,7 @@ const LEFT_DOCK_TAB_ICONS = {
18
20
  const ASSET_BROWSER_TABS = [
19
21
  { id: 'all', label: '全部', title: '显示全部项目资产' },
20
22
  { id: 'models', label: '模型', title: '显示模型资产' },
23
+ { id: 'prefabs', label: '预制体', title: '显示预制体资产' },
21
24
  { id: 'materials', label: '材质', title: '显示项目材质资产' },
22
25
  { id: 'textures', label: '贴图', title: '显示贴图资产' },
23
26
  ];
@@ -27,7 +30,11 @@ export function renderHierarchyPanel(doc, panel, state, rename, drop) {
27
30
  createGroupButton.dataset.editorHierarchyCreateGroup = 'true';
28
31
  createGroupButton.style.padding = '3px 7px';
29
32
  createGroupButton.style.fontSize = '11px';
30
- panel.appendChild(createPanelHeader(doc, 'Hierarchy', [createGroupButton], 'hierarchy'));
33
+ const createMarkerButton = createToolbarButton(doc, '+ Marker', 'view-overlay');
34
+ createMarkerButton.dataset.editorHierarchyCreateMarker = 'true';
35
+ createMarkerButton.style.padding = '3px 7px';
36
+ createMarkerButton.style.fontSize = '11px';
37
+ panel.appendChild(createPanelHeader(doc, 'Hierarchy', [createGroupButton, createMarkerButton], 'hierarchy'));
31
38
  const list = createTreeView(doc);
32
39
  for (const item of state.hierarchy) {
33
40
  const isRenaming = rename?.id === item.id;
@@ -143,6 +150,7 @@ export function renderWorkbenchRightDockTabs(doc, panel, activeTab, panels = [])
143
150
  fallbackTabs: [
144
151
  { id: 'inspector', title: '检查器', area: 'right' },
145
152
  { id: 'rendering', title: '渲染', area: 'right' },
153
+ { id: 'markers', title: '标记', area: 'right' },
146
154
  ],
147
155
  iconById: RIGHT_DOCK_TAB_ICONS,
148
156
  panel,
@@ -150,6 +158,30 @@ export function renderWorkbenchRightDockTabs(doc, panel, activeTab, panels = [])
150
158
  tabStyle: tabHeaderButtonStyle,
151
159
  });
152
160
  }
161
+ export function renderMarkerPanel(doc, panel, state) {
162
+ clearElement(panel);
163
+ configureLocalEditorDockPanelContent(panel);
164
+ const markerPanel = state.markerPanel ?? null;
165
+ panel.appendChild(createPanelHeader(doc, markerPanel?.title ?? 'Markers', [], 'view-overlay'));
166
+ const viewport = createLocalEditorDockPanelViewport(doc, 'editorMarkersScrollViewport', { gap: '8px' });
167
+ if (!markerPanel) {
168
+ viewport.appendChild(createEmptyState(doc, '当前项目没有提供 marker graph capability。'));
169
+ panel.appendChild(viewport);
170
+ return;
171
+ }
172
+ viewport.appendChild(createMarkerPanelSummary(doc, markerPanel));
173
+ viewport.appendChild(createMarkerRelationCreateBlock(doc, markerPanel));
174
+ viewport.appendChild(createMarkerRelationListBlock(doc, markerPanel.relations ?? []));
175
+ viewport.appendChild(createMarkerTypeCreateBlock(doc));
176
+ const list = doc.createElement('div');
177
+ list.style.cssText = 'display:flex;flex-direction:column;gap:6px';
178
+ for (const markerType of markerPanel.markerTypes)
179
+ list.appendChild(createMarkerTypeRow(doc, markerType));
180
+ if (markerPanel.markerTypes.length === 0)
181
+ list.appendChild(createEmptyState(doc, '还没有 marker type。'));
182
+ viewport.appendChild(list);
183
+ panel.appendChild(viewport);
184
+ }
153
185
  export function renderWorkbenchLeftDockTabs(doc, panel, activeTab, panels = []) {
154
186
  renderLocalEditorDockTabStrip(doc, {
155
187
  activeId: activeTab,
@@ -164,6 +196,481 @@ export function renderWorkbenchLeftDockTabs(doc, panel, activeTab, panels = [])
164
196
  tabStyle: tabHeaderButtonStyle,
165
197
  });
166
198
  }
199
+ function createMarkerPanelSummary(doc, panel) {
200
+ const block = createListItemBlock(doc);
201
+ block.style.cssText = [
202
+ block.style.cssText,
203
+ 'display:flex',
204
+ 'flex-direction:column',
205
+ 'gap:7px',
206
+ ].filter(Boolean).join(';');
207
+ const counters = doc.createElement('div');
208
+ counters.style.cssText = 'display:flex;align-items:center;gap:5px;flex-wrap:wrap';
209
+ counters.appendChild(createBadge(doc, `${panel.markersCount ?? 0} markers`, { compact: true, icon: 'view-overlay' }));
210
+ counters.appendChild(createBadge(doc, `${panel.relationsCount ?? 0} relations`, { compact: true, icon: 'hierarchy' }));
211
+ counters.appendChild(createBadge(doc, `${panel.markerTypes.length} types`, { compact: true, icon: 'object' }));
212
+ block.appendChild(counters);
213
+ if (panel.summary || panel.status) {
214
+ const summary = doc.createElement('div');
215
+ summary.textContent = panel.status ?? panel.summary ?? '';
216
+ const statusColor = panel.statusTone === 'error'
217
+ ? 'var(--fps-editor-danger-text)'
218
+ : panel.statusTone === 'warning'
219
+ ? 'var(--fps-editor-warn)'
220
+ : panel.statusTone === 'success'
221
+ ? 'var(--fps-editor-success)'
222
+ : 'var(--fps-editor-muted)';
223
+ summary.style.cssText = [
224
+ `color:${statusColor}`,
225
+ 'font-size:11px',
226
+ 'font-weight:700',
227
+ 'line-height:1.45',
228
+ ].join(';');
229
+ block.appendChild(summary);
230
+ }
231
+ return block;
232
+ }
233
+ function createMarkerRelationCreateBlock(doc, panel) {
234
+ const block = createListItemBlock(doc);
235
+ block.dataset.editorMarkerRelationCreate = 'true';
236
+ block.style.cssText = [
237
+ block.style.cssText,
238
+ 'display:flex',
239
+ 'flex-direction:column',
240
+ 'gap:7px',
241
+ ].filter(Boolean).join(';');
242
+ const header = createMarkerPanelSectionHeader(doc, '关系');
243
+ header.appendChild(createBadge(doc, panel.activeMarkerId ? '当前标记' : '未选中标记', {
244
+ compact: true,
245
+ tone: panel.activeMarkerId ? 'success' : 'default',
246
+ }));
247
+ block.appendChild(header);
248
+ if (panel.markersVisible === false) {
249
+ block.appendChild(createMarkerRelationVisibilityHint(doc, '标记视图已关闭,关系线暂不显示'));
250
+ }
251
+ const endpoints = panel.relationEndpointOptions ?? [];
252
+ const relationTypes = panel.relationTypes ?? [];
253
+ const grid = doc.createElement('div');
254
+ grid.style.cssText = 'display:grid;grid-template-columns:72px minmax(0,1fr);gap:6px 8px;align-items:center';
255
+ grid.appendChild(createMarkerTypeFieldLabel(doc, '起点'));
256
+ grid.appendChild(createMarkerRelationEndpointSelect(doc, 'from', endpoints, panel.activeMarkerId ?? null));
257
+ grid.appendChild(createMarkerTypeFieldLabel(doc, '关系'));
258
+ grid.appendChild(createMarkerRelationTypeSelect(doc, relationTypes));
259
+ grid.appendChild(createMarkerTypeFieldLabel(doc, '终点'));
260
+ grid.appendChild(createMarkerRelationEndpointSelect(doc, 'to', endpoints, null));
261
+ grid.appendChild(createMarkerTypeFieldLabel(doc, '方向'));
262
+ grid.appendChild(createMarkerRelationDirectionSelect(doc));
263
+ block.appendChild(grid);
264
+ if (panel.relationPick?.active) {
265
+ block.appendChild(createMarkerRelationPickStatus(doc, panel));
266
+ }
267
+ const actions = doc.createElement('div');
268
+ actions.style.cssText = 'display:flex;justify-content:flex-end;gap:6px;flex-wrap:wrap';
269
+ const selectedRelationCount = (panel.relations ?? []).filter(relation => relation.selected).length;
270
+ const deleteConnectedButton = createToolbarButton(doc, '删除当前标记全部关系', 'discard');
271
+ deleteConnectedButton.type = 'button';
272
+ deleteConnectedButton.dataset.editorMarkerRelationAction = 'delete-connected';
273
+ if (panel.activeMarkerId)
274
+ deleteConnectedButton.dataset.editorMarkerRelationMarkerId = panel.activeMarkerId;
275
+ deleteConnectedButton.disabled = !panel.activeMarkerId || selectedRelationCount === 0;
276
+ deleteConnectedButton.title = deleteConnectedButton.disabled
277
+ ? '当前标记没有可删除的一跳关系。'
278
+ : '删除当前标记的一跳关系,不删除标记或对象。';
279
+ deleteConnectedButton.dataset.editorTooltip = deleteConnectedButton.title;
280
+ deleteConnectedButton.style.padding = '4px 8px';
281
+ actions.appendChild(deleteConnectedButton);
282
+ const pickButton = createToolbarButton(doc, '视口拾取', 'view-overlay');
283
+ pickButton.type = 'button';
284
+ pickButton.dataset.editorMarkerRelationAction = 'pick-start';
285
+ pickButton.disabled = endpoints.length === 0 || relationTypes.length === 0;
286
+ pickButton.title = pickButton.disabled
287
+ ? '至少需要一个端点和一个关系类型。'
288
+ : '进入视口拾取关系端点模式。';
289
+ pickButton.dataset.editorTooltip = pickButton.title;
290
+ pickButton.style.padding = '4px 8px';
291
+ actions.appendChild(pickButton);
292
+ if (panel.relationPick?.active) {
293
+ const cancelPickButton = createToolbarButton(doc, '取消拾取', 'discard');
294
+ cancelPickButton.type = 'button';
295
+ cancelPickButton.dataset.editorMarkerRelationAction = 'pick-cancel';
296
+ cancelPickButton.title = '退出关系端点拾取。';
297
+ cancelPickButton.dataset.editorTooltip = cancelPickButton.title;
298
+ cancelPickButton.style.padding = '4px 8px';
299
+ actions.appendChild(cancelPickButton);
300
+ }
301
+ const button = createToolbarButton(doc, '创建关系', 'hierarchy');
302
+ button.type = 'button';
303
+ button.dataset.editorMarkerRelationAction = 'create';
304
+ button.disabled = endpoints.length < 2 || relationTypes.length === 0;
305
+ button.title = button.disabled
306
+ ? '至少需要两个端点和一个关系类型。'
307
+ : '创建 marker 关系';
308
+ button.dataset.editorTooltip = button.title;
309
+ button.style.padding = '4px 8px';
310
+ actions.appendChild(button);
311
+ block.appendChild(actions);
312
+ return block;
313
+ }
314
+ function createMarkerRelationVisibilityHint(doc, text) {
315
+ const hint = doc.createElement('div');
316
+ hint.textContent = text;
317
+ hint.style.cssText = [
318
+ 'color:var(--fps-editor-warn)',
319
+ 'border:1px solid rgba(251, 191, 36, 0.45)',
320
+ 'border-radius:6px',
321
+ 'background:rgba(251, 191, 36, 0.10)',
322
+ 'padding:6px 8px',
323
+ 'font-size:11px',
324
+ 'font-weight:800',
325
+ 'line-height:1.35',
326
+ ].join(';');
327
+ return hint;
328
+ }
329
+ function createMarkerRelationPickStatus(doc, panel) {
330
+ const relationPick = panel.relationPick;
331
+ const status = doc.createElement('div');
332
+ const tone = relationPick?.statusTone ?? 'success';
333
+ const color = tone === 'warning'
334
+ ? 'var(--fps-editor-warn)'
335
+ : tone === 'error'
336
+ ? 'var(--fps-editor-danger-text)'
337
+ : tone === 'success'
338
+ ? 'var(--fps-editor-success)'
339
+ : 'var(--fps-editor-muted)';
340
+ status.textContent = relationPick?.status
341
+ ?? (relationPick?.from
342
+ ? '正在拾取关系终点。'
343
+ : '正在拾取关系起点。');
344
+ status.style.cssText = [
345
+ `color:${color}`,
346
+ 'border:1px solid var(--fps-editor-border)',
347
+ 'border-radius:6px',
348
+ 'background:rgba(42, 125, 255, 0.10)',
349
+ 'padding:6px 8px',
350
+ 'font-size:11px',
351
+ 'font-weight:800',
352
+ 'line-height:1.35',
353
+ ].join(';');
354
+ return status;
355
+ }
356
+ function createMarkerRelationListBlock(doc, relations) {
357
+ const block = createListItemBlock(doc);
358
+ block.style.cssText = [
359
+ block.style.cssText,
360
+ 'display:flex',
361
+ 'flex-direction:column',
362
+ 'gap:7px',
363
+ ].filter(Boolean).join(';');
364
+ const header = createMarkerPanelSectionHeader(doc, '关系列表');
365
+ header.appendChild(createBadge(doc, `${relations.length}`, { compact: true, icon: 'hierarchy' }));
366
+ block.appendChild(header);
367
+ if (relations.length === 0) {
368
+ block.appendChild(createEmptyState(doc, '还没有关系。'));
369
+ return block;
370
+ }
371
+ const list = doc.createElement('div');
372
+ list.style.cssText = 'display:flex;flex-direction:column;gap:6px';
373
+ const sortedRelations = [...relations].sort((left, right) => {
374
+ if (left.selected !== right.selected)
375
+ return left.selected ? -1 : 1;
376
+ return left.typeLabel.localeCompare(right.typeLabel) || left.id.localeCompare(right.id);
377
+ });
378
+ for (const relation of sortedRelations)
379
+ list.appendChild(createMarkerRelationRow(doc, relation));
380
+ block.appendChild(list);
381
+ return block;
382
+ }
383
+ function createMarkerRelationRow(doc, relation) {
384
+ const row = doc.createElement('div');
385
+ row.dataset.editorMarkerRelationRow = relation.id;
386
+ row.dataset.editorMarkerRelationAction = 'select';
387
+ row.tabIndex = 0;
388
+ row.style.cssText = [
389
+ `border:1px solid ${relation.active ? 'var(--fps-editor-accent-strong)' : 'var(--fps-editor-border)'}`,
390
+ 'border-radius:6px',
391
+ `background:${relation.active ? 'rgba(42, 125, 255, 0.16)' : 'var(--fps-editor-surface-subtle)'}`,
392
+ 'padding:7px',
393
+ 'display:flex',
394
+ 'flex-direction:column',
395
+ 'gap:6px',
396
+ 'cursor:pointer',
397
+ relation.selected ? 'box-shadow:inset 3px 0 0 var(--fps-editor-accent)' : '',
398
+ relation.active ? 'outline:1px solid rgba(90, 170, 255, 0.35)' : '',
399
+ ].filter(Boolean).join(';');
400
+ row.title = '点击高亮这条关系线';
401
+ row.dataset.editorTooltip = row.title;
402
+ const header = doc.createElement('div');
403
+ header.style.cssText = 'display:flex;align-items:center;gap:6px;min-width:0';
404
+ const title = doc.createElement('div');
405
+ title.textContent = relation.typeLabel;
406
+ title.style.cssText = 'flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fps-editor-text-strong);font-size:12px;font-weight:900';
407
+ header.appendChild(title);
408
+ header.appendChild(createBadge(doc, relation.directed ? '有方向' : '无方向', { compact: true }));
409
+ if (relation.selected)
410
+ header.appendChild(createBadge(doc, '一跳', { compact: true, tone: 'success' }));
411
+ if (relation.active)
412
+ header.appendChild(createBadge(doc, '高亮', { compact: true, tone: 'success' }));
413
+ row.appendChild(header);
414
+ const body = doc.createElement('div');
415
+ body.style.cssText = 'display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:6px;font-size:11px;font-weight:800';
416
+ body.appendChild(createMarkerRelationEndpointPill(doc, relation.from));
417
+ const arrow = doc.createElement('div');
418
+ arrow.textContent = relation.directed ? '->' : '--';
419
+ arrow.style.cssText = 'color:var(--fps-editor-muted);font-family:var(--fps-editor-mono-font);font-weight:900';
420
+ body.appendChild(arrow);
421
+ body.appendChild(createMarkerRelationEndpointPill(doc, relation.to));
422
+ row.appendChild(body);
423
+ if (relation.status) {
424
+ const status = doc.createElement('div');
425
+ status.textContent = relation.status;
426
+ status.style.cssText = [
427
+ `color:${relation.statusTone === 'warning' ? 'var(--fps-editor-warn)' : 'var(--fps-editor-danger-text)'}`,
428
+ 'font-size:11px',
429
+ 'font-weight:800',
430
+ 'line-height:1.35',
431
+ ].join(';');
432
+ row.appendChild(status);
433
+ }
434
+ const actions = doc.createElement('div');
435
+ actions.style.cssText = 'display:flex;justify-content:flex-end;gap:6px;flex-wrap:wrap';
436
+ const reverseButton = createToolbarButton(doc, '反转', 'rotate');
437
+ reverseButton.type = 'button';
438
+ reverseButton.dataset.editorMarkerRelationAction = 'reverse';
439
+ reverseButton.style.padding = '4px 8px';
440
+ reverseButton.title = '交换关系起点和终点。';
441
+ reverseButton.dataset.editorTooltip = reverseButton.title;
442
+ actions.appendChild(reverseButton);
443
+ const deleteButton = createToolbarButton(doc, '删除', 'discard');
444
+ deleteButton.type = 'button';
445
+ deleteButton.dataset.editorMarkerRelationAction = 'delete';
446
+ deleteButton.style.padding = '4px 8px';
447
+ actions.appendChild(deleteButton);
448
+ row.appendChild(actions);
449
+ return row;
450
+ }
451
+ function createMarkerRelationEndpointPill(doc, endpoint) {
452
+ const pill = doc.createElement('div');
453
+ const kindLabel = endpoint.kind === 'marker'
454
+ ? '标记'
455
+ : endpoint.kind === 'scene-object'
456
+ ? '对象'
457
+ : endpoint.kind;
458
+ const hidden = endpoint.visibility === 'hidden';
459
+ const label = `${kindLabel} · ${endpoint.label || endpoint.id}${hidden ? ' · 已隐藏' : ''}`;
460
+ pill.textContent = label;
461
+ pill.title = endpoint.status ? `${label} · ${endpoint.status}` : label;
462
+ pill.dataset.editorTooltip = pill.title;
463
+ pill.style.cssText = [
464
+ 'min-width:0',
465
+ 'overflow:hidden',
466
+ 'text-overflow:ellipsis',
467
+ 'white-space:nowrap',
468
+ 'border:1px solid var(--fps-editor-border)',
469
+ 'border-radius:999px',
470
+ 'padding:3px 6px',
471
+ `color:${endpoint.disabled ? 'var(--fps-editor-warn)' : hidden ? 'var(--fps-editor-muted)' : 'var(--fps-editor-text)'}`,
472
+ `background:${hidden ? 'var(--fps-editor-surface-subtle)' : 'var(--fps-editor-surface)'}`,
473
+ hidden ? 'opacity:0.72' : '',
474
+ ].join(';');
475
+ return pill;
476
+ }
477
+ function createMarkerRelationEndpointSelect(doc, field, endpoints, preferredMarkerId) {
478
+ const select = doc.createElement('select');
479
+ select.dataset.editorMarkerRelationField = field;
480
+ select.style.cssText = createInspectorInputStyle();
481
+ for (const endpoint of endpoints) {
482
+ const option = doc.createElement('option');
483
+ option.value = encodeMarkerRelationEndpointValue(endpoint.kind, endpoint.id);
484
+ option.textContent = formatMarkerRelationEndpointOptionLabel(endpoint);
485
+ option.dataset.editorMarkerRelationEndpointKind = endpoint.kind;
486
+ option.dataset.editorMarkerRelationEndpointId = endpoint.id;
487
+ option.disabled = endpoint.disabled === true;
488
+ if (preferredMarkerId && endpoint.kind === 'marker' && endpoint.id === preferredMarkerId)
489
+ option.selected = true;
490
+ select.appendChild(option);
491
+ }
492
+ select.disabled = endpoints.length === 0;
493
+ return select;
494
+ }
495
+ function createMarkerRelationTypeSelect(doc, relationTypes) {
496
+ const select = doc.createElement('select');
497
+ select.dataset.editorMarkerRelationField = 'type';
498
+ select.style.cssText = createInspectorInputStyle();
499
+ for (const relationType of relationTypes) {
500
+ const option = doc.createElement('option');
501
+ option.value = relationType.type;
502
+ option.textContent = relationType.label || relationType.type;
503
+ option.dataset.editorMarkerRelationDirected = String(relationType.directed ?? true);
504
+ if (relationType.description)
505
+ option.title = relationType.description;
506
+ select.appendChild(option);
507
+ }
508
+ select.disabled = relationTypes.length === 0;
509
+ return select;
510
+ }
511
+ function createMarkerRelationDirectionSelect(doc) {
512
+ const select = doc.createElement('select');
513
+ select.dataset.editorMarkerRelationField = 'directed';
514
+ select.style.cssText = createInspectorInputStyle();
515
+ for (const [value, label] of [
516
+ ['default', '按类型默认'],
517
+ ['true', '有方向'],
518
+ ['false', '无方向'],
519
+ ]) {
520
+ const option = doc.createElement('option');
521
+ option.value = value;
522
+ option.textContent = label;
523
+ select.appendChild(option);
524
+ }
525
+ return select;
526
+ }
527
+ function createMarkerPanelSectionHeader(doc, text) {
528
+ const header = doc.createElement('div');
529
+ header.style.cssText = 'display:flex;align-items:center;gap:6px;min-width:0;color:var(--fps-editor-text-strong);font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:0';
530
+ const title = doc.createElement('div');
531
+ title.textContent = text;
532
+ title.style.cssText = 'flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
533
+ header.appendChild(title);
534
+ return header;
535
+ }
536
+ function formatMarkerRelationEndpointOptionLabel(endpoint) {
537
+ const kindLabel = endpoint.kind === 'marker'
538
+ ? '标记'
539
+ : endpoint.kind === 'scene-object'
540
+ ? '对象'
541
+ : endpoint.kind;
542
+ return `${kindLabel} · ${endpoint.label || endpoint.id}${endpoint.status ? ` · ${endpoint.status}` : ''}`;
543
+ }
544
+ function encodeMarkerRelationEndpointValue(kind, id) {
545
+ return `${kind}:${encodeURIComponent(id)}`;
546
+ }
547
+ function createMarkerTypeCreateBlock(doc) {
548
+ const block = createListItemBlock(doc);
549
+ block.dataset.editorMarkerTypeCreate = 'true';
550
+ block.style.cssText = [
551
+ block.style.cssText,
552
+ 'display:flex',
553
+ 'flex-direction:column',
554
+ 'gap:7px',
555
+ ].filter(Boolean).join(';');
556
+ const header = doc.createElement('div');
557
+ header.textContent = 'New Marker Type';
558
+ header.style.cssText = 'color:var(--fps-editor-text-strong);font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:0';
559
+ block.appendChild(header);
560
+ block.appendChild(createMarkerTypeFieldGrid(doc, {
561
+ prefix: 'create',
562
+ type: '',
563
+ label: '',
564
+ kind: 'region',
565
+ description: '',
566
+ typeReadOnly: false,
567
+ }));
568
+ const actions = doc.createElement('div');
569
+ actions.style.cssText = 'display:flex;justify-content:flex-end';
570
+ const button = createToolbarButton(doc, 'Create', 'object');
571
+ button.type = 'button';
572
+ button.dataset.editorMarkerTypeAction = 'create';
573
+ button.style.padding = '4px 8px';
574
+ actions.appendChild(button);
575
+ block.appendChild(actions);
576
+ return block;
577
+ }
578
+ function createMarkerTypeRow(doc, markerType) {
579
+ const block = createListItemBlock(doc);
580
+ block.dataset.editorMarkerTypeRow = markerType.type;
581
+ block.style.cssText = [
582
+ block.style.cssText,
583
+ 'display:flex',
584
+ 'flex-direction:column',
585
+ 'gap:7px',
586
+ ].filter(Boolean).join(';');
587
+ const header = doc.createElement('div');
588
+ header.style.cssText = 'display:flex;align-items:center;gap:6px;min-width:0';
589
+ const title = doc.createElement('div');
590
+ title.textContent = markerType.type;
591
+ title.style.cssText = 'flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fps-editor-text-strong);font-size:12px;font-weight:900';
592
+ header.appendChild(title);
593
+ if (markerType.builtIn)
594
+ header.appendChild(createBadge(doc, 'BUILT IN', { compact: true, tone: 'warning' }));
595
+ if ((markerType.usedByCount ?? 0) > 0)
596
+ header.appendChild(createBadge(doc, `${markerType.usedByCount} used`, { compact: true }));
597
+ block.appendChild(header);
598
+ block.appendChild(createMarkerTypeFieldGrid(doc, {
599
+ prefix: 'row',
600
+ type: markerType.type,
601
+ label: markerType.label,
602
+ kind: markerType.kind ?? '',
603
+ description: markerType.description ?? '',
604
+ typeReadOnly: true,
605
+ }));
606
+ const actions = doc.createElement('div');
607
+ actions.style.cssText = 'display:flex;align-items:center;justify-content:flex-end;gap:6px';
608
+ const updateButton = createToolbarButton(doc, 'Update', 'save');
609
+ updateButton.type = 'button';
610
+ updateButton.dataset.editorMarkerTypeAction = 'update';
611
+ updateButton.style.padding = '4px 8px';
612
+ actions.appendChild(updateButton);
613
+ const deleteButton = createToolbarButton(doc, 'Delete', 'discard');
614
+ deleteButton.type = 'button';
615
+ deleteButton.dataset.editorMarkerTypeAction = 'delete';
616
+ deleteButton.disabled = markerType.deletable === false;
617
+ deleteButton.title = markerType.deletable === false
618
+ ? (markerType.builtIn ? 'Built-in marker types cannot be deleted.' : 'Marker types currently in use cannot be deleted.')
619
+ : 'Delete marker type';
620
+ deleteButton.dataset.editorTooltip = deleteButton.title;
621
+ deleteButton.style.padding = '4px 8px';
622
+ actions.appendChild(deleteButton);
623
+ block.appendChild(actions);
624
+ return block;
625
+ }
626
+ function createMarkerTypeFieldGrid(doc, input) {
627
+ const grid = doc.createElement('div');
628
+ grid.style.cssText = 'display:grid;grid-template-columns:72px minmax(0,1fr);gap:6px 8px;align-items:center';
629
+ grid.appendChild(createMarkerTypeFieldLabel(doc, 'Type'));
630
+ grid.appendChild(createMarkerTypeTextInput(doc, input.prefix, 'type', input.type, input.typeReadOnly));
631
+ grid.appendChild(createMarkerTypeFieldLabel(doc, 'Label'));
632
+ grid.appendChild(createMarkerTypeTextInput(doc, input.prefix, 'label', input.label, false));
633
+ grid.appendChild(createMarkerTypeFieldLabel(doc, 'Kind'));
634
+ grid.appendChild(createMarkerTypeTextInput(doc, input.prefix, 'kind', input.kind, false));
635
+ grid.appendChild(createMarkerTypeFieldLabel(doc, 'Description'));
636
+ grid.appendChild(createMarkerTypeTextarea(doc, input.prefix, 'description', input.description));
637
+ return grid;
638
+ }
639
+ function createMarkerTypeFieldLabel(doc, text) {
640
+ const label = doc.createElement('div');
641
+ label.textContent = text;
642
+ label.style.cssText = 'color:var(--fps-editor-muted);font-size:11px;font-weight:800';
643
+ return label;
644
+ }
645
+ function createMarkerTypeTextInput(doc, prefix, field, value, readOnly) {
646
+ const input = doc.createElement('input');
647
+ input.type = 'text';
648
+ input.value = value;
649
+ input.dataset.editorMarkerTypeField = field;
650
+ input.dataset.editorMarkerTypeFieldPrefix = prefix;
651
+ input.readOnly = readOnly;
652
+ input.style.cssText = [
653
+ createInspectorInputStyle(),
654
+ readOnly ? 'opacity:0.72' : '',
655
+ ].filter(Boolean).join(';');
656
+ return input;
657
+ }
658
+ function createMarkerTypeTextarea(doc, prefix, field, value) {
659
+ const input = doc.createElement('textarea');
660
+ input.value = value;
661
+ input.rows = 2;
662
+ input.dataset.editorMarkerTypeField = field;
663
+ input.dataset.editorMarkerTypeFieldPrefix = prefix;
664
+ input.style.cssText = [
665
+ createInspectorInputStyle(),
666
+ 'height:auto',
667
+ 'min-height:46px',
668
+ 'resize:vertical',
669
+ 'padding:6px',
670
+ 'line-height:1.35',
671
+ ].join(';');
672
+ return input;
673
+ }
167
674
  export function renderRenderingPanel(doc, panel, state) {
168
675
  clearElement(panel);
169
676
  configureLocalEditorDockPanelContent(panel);
@@ -926,8 +1433,17 @@ function renderAssetBrowserContent(doc, panel, state, variant = 'list', activeTa
926
1433
  export function createAssetPreviewElement(doc, asset, variant) {
927
1434
  const size = variant === 'modal' ? 220 : variant === 'grid' ? 46 : 24;
928
1435
  const preview = asset.preview;
929
- if (!preview)
930
- return createLocalEditorIcon(doc, 'asset', { size: variant === 'grid' ? 18 : 16 });
1436
+ if (!preview) {
1437
+ const kind = resolveLocalEditorBrowserAssetKind(asset);
1438
+ const icon = kind === 'prefab'
1439
+ ? 'prefab'
1440
+ : kind === 'material'
1441
+ ? 'material-slot'
1442
+ : kind === 'texture'
1443
+ ? 'asset'
1444
+ : 'object';
1445
+ return createLocalEditorIcon(doc, icon, { size: variant === 'grid' ? 18 : 16 });
1446
+ }
931
1447
  const frame = doc.createElement('div');
932
1448
  frame.style.cssText = [
933
1449
  `width:${size}px`,
@@ -1072,19 +1588,7 @@ function appendAssetBrowserTabs(doc, panel, state, activeTab) {
1072
1588
  function filterAssetsByBrowserTab(assets, tab) {
1073
1589
  if (tab === 'all')
1074
1590
  return [...assets];
1075
- return assets.filter(asset => resolveAssetBrowserTab(asset) === tab);
1076
- }
1077
- function resolveAssetBrowserTab(asset) {
1078
- const kind = `${asset.kind ?? ''}`.trim().toLowerCase();
1079
- const id = `${asset.id ?? ''}`.trim().toLowerCase();
1080
- const assetId = `${asset.assetId ?? ''}`.trim().toLowerCase();
1081
- if (kind === 'material' || kind === 'materials' || id.startsWith('material:') || assetId.startsWith('material:')) {
1082
- return 'materials';
1083
- }
1084
- if (kind === 'texture' || kind === 'image' || kind === 'textures' || id.startsWith('texture:') || assetId.startsWith('texture:')) {
1085
- return 'textures';
1086
- }
1087
- return 'models';
1591
+ return assets.filter(asset => resolveLocalEditorAssetBrowserTab(asset) === tab);
1088
1592
  }
1089
1593
  function renderHistoryPanel(doc, panel, history) {
1090
1594
  configureLocalEditorDockPanelContent(panel);
@@ -1145,7 +1649,7 @@ export function renderInspectorPanel(doc, panel, state, filter = '', options = {
1145
1649
  const title = doc.createElement('h2');
1146
1650
  title.style.cssText = 'display:flex;align-items:center;gap:6px;font-size:13px;margin:-8px -8px 8px;padding:7px 8px;border-bottom:1px solid var(--fps-editor-divider);background:var(--fps-editor-chrome-dark);font-weight:800;color:var(--fps-editor-text-strong)';
1147
1651
  title.appendChild(createLocalEditorIcon(doc, 'inspector'));
1148
- title.appendChild(createHeadingLabel(doc, 'Inspector'));
1652
+ title.appendChild(createHeadingLabel(doc, options.title ?? 'Inspector'));
1149
1653
  panel.appendChild(title);
1150
1654
  const search = createInspectorSearchInput(doc, filter);
1151
1655
  panel.appendChild(search);
@@ -1157,7 +1661,7 @@ export function renderInspectorPanel(doc, panel, state, filter = '', options = {
1157
1661
  : state.inspectorObject ?? (state.serializedObject ? createLegacyInspectorObject(state.serializedObject) : null);
1158
1662
  if (!inspectorObject) {
1159
1663
  const empty = doc.createElement('div');
1160
- empty.textContent = '请从层级树或 Scene View 中选择一个 GameObject。';
1664
+ empty.textContent = options.emptyText ?? '请从层级树或 Scene View 中选择一个 GameObject 或 Marker。';
1161
1665
  empty.style.cssText = 'color:var(--fps-editor-muted);line-height:1.45';
1162
1666
  viewport.appendChild(empty);
1163
1667
  panel.appendChild(viewport);
@@ -1165,7 +1669,7 @@ export function renderInspectorPanel(doc, panel, state, filter = '', options = {
1165
1669
  return;
1166
1670
  }
1167
1671
  const controlRegistry = createLocalEditorBrowserInspectorControlRegistry(options.controls, options.controlConflict);
1168
- appendInspectorSummary(doc, viewport, inspectorObject, selectionCount);
1672
+ appendInspectorSummary(doc, viewport, inspectorObject, selectionCount, options.summaryTitle);
1169
1673
  const visibleSections = filterInspectorSections(inspectorObject.sections.filter(section => section.placement !== 'summary'), filter);
1170
1674
  if (visibleSections.length === 0) {
1171
1675
  if (!filter.trim() && inspectorObject.sections.length === 0) {
@@ -1220,17 +1724,21 @@ function createInspectorSearchInput(doc, value) {
1220
1724
  ].join(';');
1221
1725
  return input;
1222
1726
  }
1223
- function appendInspectorSummary(doc, panel, inspectorObject, selectionCount) {
1727
+ function appendInspectorSummary(doc, panel, inspectorObject, selectionCount, summaryTitle) {
1224
1728
  const block = createInspectorComponentBlock(doc);
1225
1729
  const header = doc.createElement('div');
1226
1730
  header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 8px';
1227
1731
  const title = doc.createElement('h3');
1228
- title.textContent = selectionCount > 1 ? 'Selection' : 'GameObject';
1732
+ const targetKind = inspectorObject.selection.targetKind;
1733
+ const markerLike = targetKind === 'marker' || targetKind === 'marker-graph';
1734
+ title.textContent = summaryTitle ?? (selectionCount > 1
1735
+ ? markerLike ? 'Markers' : 'Selection'
1736
+ : targetKind === 'marker-graph' ? 'Marker Graph' : targetKind === 'marker' ? 'Marker' : 'GameObject');
1229
1737
  title.style.cssText = 'font-size:12px;margin:0;font-weight:900;color:var(--fps-editor-text-strong)';
1230
1738
  header.appendChild(title);
1231
1739
  header.appendChild(createInspectorAccessBadge(doc, 'readonly'));
1232
1740
  block.appendChild(header);
1233
- appendReadOnlyRow(doc, block, selectionCount > 1 ? 'Selected' : 'Name', inspectorObject.label ?? inspectorObject.activeId ?? inspectorObject.targetIds[0] ?? '无');
1741
+ appendReadOnlyRow(doc, block, selectionCount > 1 ? 'Selected' : targetKind === 'marker' ? 'Label' : 'Name', inspectorObject.label ?? inspectorObject.activeId ?? inspectorObject.targetIds[0] ?? '无');
1234
1742
  appendReadOnlyRow(doc, block, 'Active ID', inspectorObject.activeId ?? '无');
1235
1743
  if (selectionCount > 1)
1236
1744
  appendReadOnlyRow(doc, block, 'Count', String(selectionCount));
@@ -1505,6 +2013,15 @@ function readInspectorControlOptionNumber(property, key) {
1505
2013
  const value = property.controlOptions?.[key];
1506
2014
  return typeof value === 'number' && Number.isFinite(value) ? value : 0;
1507
2015
  }
2016
+ function readInspectorControlOptionBoolean(property, key) {
2017
+ return property.controlOptions?.[key] === true;
2018
+ }
2019
+ function readInspectorControlOptionRecord(property, key) {
2020
+ const value = property.controlOptions?.[key];
2021
+ return value && typeof value === 'object' && !Array.isArray(value)
2022
+ ? value
2023
+ : null;
2024
+ }
1508
2025
  function readInspectorAssetPickerPreview(property, key) {
1509
2026
  const value = property.controlOptions?.[key];
1510
2027
  return isLocalEditorBrowserAssetPreview(value) ? value : undefined;
@@ -1590,6 +2107,20 @@ const builtinInspectorControlRegistrations = [
1590
2107
  customControl: 'rendering-action-button',
1591
2108
  render: ({ doc, property }) => createInspectorRenderingActionButtonControl(doc, property),
1592
2109
  },
2110
+ {
2111
+ id: 'builtin.inspector-action-button',
2112
+ order: 93,
2113
+ control: 'custom',
2114
+ customControl: 'inspector-action-button',
2115
+ render: ({ doc, target, property }) => createInspectorActionButtonControl(doc, target, property),
2116
+ },
2117
+ {
2118
+ id: 'builtin.marker-relation-editor',
2119
+ order: 91,
2120
+ control: 'custom',
2121
+ customControl: 'marker-relation-editor',
2122
+ render: ({ doc, property }) => createInspectorMarkerRelationEditorControl(doc, property),
2123
+ },
1593
2124
  {
1594
2125
  id: 'builtin.asset-picker-card',
1595
2126
  order: 95,
@@ -1598,9 +2129,16 @@ const builtinInspectorControlRegistrations = [
1598
2129
  render: ({ doc, target, property, bindInput }) => createInspectorAssetPickerCardControl(doc, target, property, bindInput),
1599
2130
  },
1600
2131
  {
1601
- id: 'builtin.material-slot-list',
2132
+ id: 'builtin.ground-decal-layout',
1602
2133
  order: 96,
1603
2134
  control: 'custom',
2135
+ customControl: 'ground-decal-layout',
2136
+ render: ({ doc, target, property, bindInput }) => createInspectorGroundDecalLayoutControl(doc, target, property, bindInput),
2137
+ },
2138
+ {
2139
+ id: 'builtin.material-slot-list',
2140
+ order: 97,
2141
+ control: 'custom',
1604
2142
  customControl: 'material-slot-list',
1605
2143
  render: ({ doc, property }) => createInspectorMaterialSlotListControl(doc, property),
1606
2144
  },
@@ -1627,7 +2165,7 @@ function appendInspectorPropertyRow(doc, parent, inspectorObject, property, cont
1627
2165
  const control = renderInspectorControl(doc, controlRegistry, inspectorObject, property);
1628
2166
  const access = getInspectorPropertyAccess(property);
1629
2167
  const effect = getInspectorPropertyEffect(property);
1630
- if (property.customControl === 'material-slot-list') {
2168
+ if (property.customControl === 'material-slot-list' || property.customControl === 'marker-relation-editor') {
1631
2169
  control.dataset.editorInspectorAccess = access;
1632
2170
  control.dataset.editorInspectorEffect = effect;
1633
2171
  control.title = createInspectorStatusTitle(access, effect, property.tooltip ?? property.disabledReason);
@@ -1710,6 +2248,8 @@ function localEditorBrowserInspectorControlSupports(registration, context) {
1710
2248
  return registration.supports?.(context) ?? true;
1711
2249
  }
1712
2250
  function normalizeInspectorRenderingActionProperty(property) {
2251
+ if (property.customControl && property.customControl !== 'rendering-action-button')
2252
+ return property;
1713
2253
  const actionId = readInspectorRenderingActionId(property);
1714
2254
  if (!actionId)
1715
2255
  return property;
@@ -1739,6 +2279,8 @@ function readInspectorRenderingActionId(property) {
1739
2279
  function isInspectorCustomControlFallback(customControl, property) {
1740
2280
  if (customControl !== 'rendering-action-button')
1741
2281
  return false;
2282
+ if (property.customControl)
2283
+ return false;
1742
2284
  return !!readInspectorRenderingActionId(property);
1743
2285
  }
1744
2286
  function appendInspectorVectorInputs(doc, parent, inspectorObject, label, properties, layout = 'block', options = {}) {
@@ -1760,10 +2302,13 @@ function appendInspectorVectorInputs(doc, parent, inspectorObject, label, proper
1760
2302
  actions.style.cssText = 'display:inline-flex;align-items:center;gap:5px;flex:0 0 auto';
1761
2303
  labelElement.appendChild(actions);
1762
2304
  const groupPath = resolveLocalEditorBrowserInspectorVectorGroupPathFromProperties(properties);
1763
- const locked = !!groupPath && options.lockedVectorGroups?.has(groupPath) === true;
2305
+ const forceLocked = properties.some(property => readInspectorControlOptionBoolean(property, 'forceVectorLock'));
2306
+ const locked = forceLocked || (!!groupPath && options.lockedVectorGroups?.has(groupPath) === true);
1764
2307
  if (groupPath && access === 'editable') {
1765
- const lockButton = createInspectorVectorHeaderButton(doc, locked ? `Unlock ${label} axes` : `Lock ${label} axes`, locked);
1766
- lockButton.dataset.editorInspectorVectorLock = groupPath;
2308
+ const lockButton = createInspectorVectorHeaderButton(doc, forceLocked ? `${label} axes are locked` : locked ? `Unlock ${label} axes` : `Lock ${label} axes`, locked);
2309
+ if (!forceLocked)
2310
+ lockButton.dataset.editorInspectorVectorLock = groupPath;
2311
+ lockButton.disabled = forceLocked;
1767
2312
  lockButton.dataset.editorInspectorVectorLockState = locked ? 'locked' : 'unlocked';
1768
2313
  lockButton.setAttribute('aria-pressed', locked ? 'true' : 'false');
1769
2314
  lockButton.appendChild(createLocalEditorIcon(doc, 'lock', { size: 12, strokeWidth: 2.2 }));
@@ -2001,7 +2546,8 @@ function createInspectorTextControl(doc, target, property) {
2001
2546
  function createInspectorBooleanControl(doc, target, property) {
2002
2547
  const input = createInspectorInputBase(doc, target, property);
2003
2548
  input.type = 'checkbox';
2004
- input.checked = property.mixed ? false : property.value === true;
2549
+ input.checked = property.value === true;
2550
+ input.indeterminate = property.mixed === true;
2005
2551
  input.style.cssText = [
2006
2552
  'width:16px',
2007
2553
  'height:16px',
@@ -2069,7 +2615,11 @@ function createInspectorColorControl(doc, target, property) {
2069
2615
  }
2070
2616
  function createInspectorOpenRightDockTabControl(doc, property) {
2071
2617
  const targetTabOption = readInspectorControlOptionString(property, 'targetTab');
2072
- const targetTab = targetTabOption === 'inspector' ? 'inspector' : 'rendering';
2618
+ const targetTab = targetTabOption === 'inspector'
2619
+ ? 'inspector'
2620
+ : targetTabOption === 'markers'
2621
+ ? 'markers'
2622
+ : 'rendering';
2073
2623
  const label = readInspectorControlOptionString(property, 'label')
2074
2624
  || (typeof property.value === 'string' ? property.value : property.label);
2075
2625
  const button = createToolbarButton(doc, label, RIGHT_DOCK_TAB_ICONS[targetTab]);
@@ -2110,6 +2660,56 @@ function createInspectorRenderingActionButtonControl(doc, property) {
2110
2660
  ].filter(Boolean).join(';');
2111
2661
  return button;
2112
2662
  }
2663
+ function createInspectorActionButtonControl(doc, target, property) {
2664
+ const actionId = readInspectorControlOptionString(property, 'actionId')
2665
+ || (typeof property.value === 'string' ? property.value : '');
2666
+ const iconName = readInspectorControlOptionString(property, 'icon');
2667
+ const icon = isLocalEditorIconName(iconName) ? iconName : 'asset';
2668
+ const label = readInspectorControlOptionString(property, 'label')
2669
+ || (typeof property.value === 'string' ? property.value : property.label);
2670
+ const button = createToolbarButton(doc, label, icon);
2671
+ button.type = 'button';
2672
+ if (actionId)
2673
+ button.dataset.editorInspectorAction = actionId;
2674
+ button.dataset.editorInspectorActionTargetId = target.activeId ?? '';
2675
+ button.dataset.editorInspectorActionTargetIds = JSON.stringify(target.targetIds);
2676
+ button.dataset.editorInspectorActionPath = property.path;
2677
+ const params = readInspectorControlOptionRecord(property, 'params');
2678
+ if (params)
2679
+ button.dataset.editorInspectorActionParams = JSON.stringify(params);
2680
+ button.disabled = property.readOnly === true
2681
+ || property.effect === 'unsupported'
2682
+ || property.controlOptions?.disabled === true
2683
+ || !actionId;
2684
+ button.title = property.tooltip ?? label;
2685
+ button.dataset.editorTooltip = button.title;
2686
+ button.style.cssText = [
2687
+ button.style.cssText,
2688
+ 'width:100%',
2689
+ 'justify-content:center',
2690
+ 'height:28px',
2691
+ ].filter(Boolean).join(';');
2692
+ return button;
2693
+ }
2694
+ function createInspectorMarkerRelationEditorControl(doc, property) {
2695
+ const panel = readInspectorMarkerRelationPanelState(property);
2696
+ const wrapper = doc.createElement('div');
2697
+ wrapper.style.cssText = 'display:flex;flex-direction:column;gap:8px;width:100%;min-width:0';
2698
+ if (!panel) {
2699
+ wrapper.appendChild(createEmptyState(doc, '当前 marker 没有可编辑的关系数据。'));
2700
+ return wrapper;
2701
+ }
2702
+ wrapper.appendChild(createMarkerRelationCreateBlock(doc, panel));
2703
+ wrapper.appendChild(createMarkerRelationListBlock(doc, (panel.relations ?? []).filter(relation => relation.selected)));
2704
+ return wrapper;
2705
+ }
2706
+ function readInspectorMarkerRelationPanelState(property) {
2707
+ const value = property.controlOptions?.markerPanel ?? property.value;
2708
+ if (!value || typeof value !== 'object' || Array.isArray(value))
2709
+ return null;
2710
+ const panel = value;
2711
+ return Array.isArray(panel.markerTypes) ? panel : null;
2712
+ }
2113
2713
  function createInspectorMaterialSlotListControl(doc, property) {
2114
2714
  const wrapper = doc.createElement('div');
2115
2715
  wrapper.dataset.editorInspectorMaterialSlotList = 'true';
@@ -2277,6 +2877,846 @@ function readStringField(record, key) {
2277
2877
  const value = record[key];
2278
2878
  return typeof value === 'string' ? value : '';
2279
2879
  }
2880
+ const inspectorGroundDecalLayoutImageCache = new Map();
2881
+ const INSPECTOR_GROUND_DECAL_LAYOUT_MIN_SIZE = 0.02;
2882
+ const INSPECTOR_GROUND_DECAL_LAYOUT_MAX_SIZE = 1.5;
2883
+ function createInspectorGroundDecalLayoutControl(doc, _target, property, bindInput) {
2884
+ const editable = isInspectorPropertyEditable(property);
2885
+ const layers = readInspectorGroundDecalLayoutLayers(property);
2886
+ const composite = isInspectorGroundDecalLayoutCompositeValue(property.value);
2887
+ const textureSize = readInspectorGroundDecalLayoutTextureSize(property.value);
2888
+ const mask = readInspectorGroundDecalLayoutMask(property.value);
2889
+ const aspectRatio = Math.max(0.2, Math.min(4, readInspectorControlOptionNumber(property, 'aspectRatio') || 1));
2890
+ const surfaceMaxWidth = Math.max(96, Math.round(220 * aspectRatio));
2891
+ const wrapper = doc.createElement('div');
2892
+ wrapper.dataset.editorInspectorGroundDecalLayout = 'true';
2893
+ wrapper.style.cssText = [
2894
+ 'display:flex',
2895
+ 'flex-direction:column',
2896
+ 'gap:8px',
2897
+ 'min-width:0',
2898
+ ].join(';');
2899
+ const input = doc.createElement('input');
2900
+ input.type = 'hidden';
2901
+ input.value = serializeInspectorGroundDecalLayoutValue(layers, composite);
2902
+ bindInput(input, { source: 'custom' });
2903
+ input.dataset.serializedJsonValue = 'true';
2904
+ wrapper.appendChild(input);
2905
+ const surface = doc.createElement('div');
2906
+ surface.dataset.editorInspectorGroundDecalLayoutSurface = 'true';
2907
+ surface.style.cssText = [
2908
+ 'position:relative',
2909
+ `width:min(100%, ${surfaceMaxWidth}px)`,
2910
+ 'max-width:100%',
2911
+ 'align-self:center',
2912
+ `aspect-ratio:${aspectRatio} / 1`,
2913
+ 'border:1px solid var(--fps-editor-editable-border)',
2914
+ 'border-radius:4px',
2915
+ 'background:linear-gradient(90deg, rgba(255,255,255,0.05) 1px, transparent 1px), linear-gradient(0deg, rgba(255,255,255,0.05) 1px, transparent 1px), var(--fps-editor-field)',
2916
+ 'background-size:20px 20px',
2917
+ 'overflow:hidden',
2918
+ editable ? 'cursor:grab' : 'cursor:not-allowed',
2919
+ ].join(';');
2920
+ wrapper.appendChild(surface);
2921
+ const previewCanvas = doc.createElement('canvas');
2922
+ previewCanvas.width = textureSize.width;
2923
+ previewCanvas.height = textureSize.height;
2924
+ previewCanvas.dataset.editorInspectorGroundDecalLayoutPreview = 'true';
2925
+ previewCanvas.style.cssText = [
2926
+ 'position:absolute',
2927
+ 'inset:0',
2928
+ 'width:100%',
2929
+ 'height:100%',
2930
+ 'pointer-events:none',
2931
+ ].join(';');
2932
+ surface.appendChild(previewCanvas);
2933
+ const guide = doc.createElement('div');
2934
+ guide.style.cssText = [
2935
+ 'position:absolute',
2936
+ 'inset:8px',
2937
+ 'border:1px solid var(--fps-editor-border-soft)',
2938
+ 'border-radius:8px',
2939
+ 'pointer-events:none',
2940
+ ].join(';');
2941
+ surface.appendChild(guide);
2942
+ const fields = doc.createElement('div');
2943
+ fields.style.cssText = [
2944
+ 'display:flex',
2945
+ 'flex-direction:column',
2946
+ 'gap:6px',
2947
+ 'min-width:0',
2948
+ ].join(';');
2949
+ wrapper.appendChild(fields);
2950
+ const boxByLayer = new Map();
2951
+ const fieldInputsByLayer = new Map();
2952
+ const editableLayers = layers.filter(isInspectorGroundDecalLayoutLayerEditable);
2953
+ const scaleLockedByLayer = new Map();
2954
+ for (const layer of editableLayers) {
2955
+ scaleLockedByLayer.set(layer, isInspectorGroundDecalLayoutUniformScaleLayer(layer));
2956
+ }
2957
+ let previewRenderVersion = 0;
2958
+ const renderPreview = () => {
2959
+ const version = ++previewRenderVersion;
2960
+ void renderInspectorGroundDecalLayoutPreview(previewCanvas, layers, textureSize, mask, () => version === previewRenderVersion);
2961
+ };
2962
+ const updateLayerRect = (layer, rect) => {
2963
+ layer.rect = normalizeInspectorGroundDecalLayoutRect(rect);
2964
+ const layerFieldInputs = fieldInputsByLayer.get(layer);
2965
+ if (layerFieldInputs)
2966
+ updateInspectorGroundDecalLayoutFieldInputs(layerFieldInputs, layer.rect);
2967
+ const layerBox = boxByLayer.get(layer);
2968
+ if (layerBox)
2969
+ renderInspectorGroundDecalLayoutBox(layerBox, layer.rect);
2970
+ renderPreview();
2971
+ };
2972
+ const commitLayout = () => {
2973
+ commitInspectorGroundDecalLayoutValue(input, layers, fieldInputsByLayer, composite);
2974
+ };
2975
+ let dragActive = false;
2976
+ const beginDrag = (event, layer, box, mode) => {
2977
+ if (!editable)
2978
+ return;
2979
+ if ('button' in event && event.button !== 0)
2980
+ return;
2981
+ event.preventDefault();
2982
+ event.stopPropagation();
2983
+ if (dragActive)
2984
+ return;
2985
+ dragActive = true;
2986
+ const bounds = surface.getBoundingClientRect();
2987
+ const startX = event.clientX;
2988
+ const startY = event.clientY;
2989
+ const start = layer.rect;
2990
+ const captureTarget = event.currentTarget instanceof HTMLElement ? event.currentTarget : box;
2991
+ const pointerId = 'pointerId' in event ? event.pointerId : null;
2992
+ if (pointerId !== null) {
2993
+ try {
2994
+ captureTarget.setPointerCapture(pointerId);
2995
+ }
2996
+ catch {
2997
+ // Capture can fail if the browser has already ended the pointer stream.
2998
+ }
2999
+ }
3000
+ surface.style.cursor = mode === 'move' ? 'grabbing' : 'nwse-resize';
3001
+ box.style.cursor = surface.style.cursor;
3002
+ const move = (moveEvent) => {
3003
+ moveEvent.preventDefault();
3004
+ const dx = bounds.width > 0 ? (moveEvent.clientX - startX) / bounds.width : 0;
3005
+ const dy = bounds.height > 0 ? (moveEvent.clientY - startY) / bounds.height : 0;
3006
+ const nextRect = mode === 'move'
3007
+ ? normalizeInspectorGroundDecalLayoutRect({
3008
+ ...start,
3009
+ x: start.x + dx,
3010
+ z: start.z - dy,
3011
+ })
3012
+ : resizeInspectorGroundDecalLayoutRect(layer, start, dx, dy, scaleLockedByLayer.get(layer) === true);
3013
+ updateLayerRect(layer, nextRect);
3014
+ };
3015
+ const end = () => {
3016
+ doc.defaultView?.removeEventListener('pointermove', move);
3017
+ doc.defaultView?.removeEventListener('mousemove', move);
3018
+ doc.defaultView?.removeEventListener('pointerup', end);
3019
+ doc.defaultView?.removeEventListener('mouseup', end);
3020
+ if (pointerId !== null) {
3021
+ try {
3022
+ captureTarget.releasePointerCapture(pointerId);
3023
+ }
3024
+ catch {
3025
+ // Ignore capture release errors after pointer cancellation.
3026
+ }
3027
+ }
3028
+ dragActive = false;
3029
+ surface.style.cursor = editable ? 'grab' : 'not-allowed';
3030
+ box.style.cursor = editable ? 'grab' : 'not-allowed';
3031
+ commitLayout();
3032
+ };
3033
+ doc.defaultView?.addEventListener('pointermove', move);
3034
+ doc.defaultView?.addEventListener('pointerup', end, { once: true });
3035
+ doc.defaultView?.addEventListener('mousemove', move);
3036
+ doc.defaultView?.addEventListener('mouseup', end, { once: true });
3037
+ };
3038
+ for (const layer of editableLayers) {
3039
+ const box = createInspectorGroundDecalLayoutBox(doc, layer, editable);
3040
+ surface.appendChild(box);
3041
+ boxByLayer.set(layer, box);
3042
+ const handle = box.querySelector('[data-editor-inspector-ground-decal-layout-resize]');
3043
+ box.addEventListener('pointerdown', (event) => {
3044
+ const target = event.target instanceof HTMLElement ? event.target : null;
3045
+ if (target?.closest('[data-editor-inspector-ground-decal-layout-resize]'))
3046
+ return;
3047
+ beginDrag(event, layer, box, 'move');
3048
+ });
3049
+ box.addEventListener('mousedown', (event) => {
3050
+ const target = event.target instanceof HTMLElement ? event.target : null;
3051
+ if (target?.closest('[data-editor-inspector-ground-decal-layout-resize]'))
3052
+ return;
3053
+ beginDrag(event, layer, box, 'move');
3054
+ });
3055
+ handle?.addEventListener('pointerdown', (event) => beginDrag(event, layer, box, 'resize'));
3056
+ handle?.addEventListener('mousedown', (event) => beginDrag(event, layer, box, 'resize'));
3057
+ let layerInputs;
3058
+ layerInputs = createInspectorGroundDecalLayoutLayerFields(doc, fields, layer, editable, (changedKey) => {
3059
+ const next = readRectFromLayoutFieldInputs(layerInputs, layer.rect, layer, changedKey, scaleLockedByLayer.get(layer) === true);
3060
+ if (!next)
3061
+ return;
3062
+ updateLayerRect(layer, next);
3063
+ }, (changedKey) => {
3064
+ const next = readRectFromLayoutFieldInputs(layerInputs, layer.rect, layer, changedKey, scaleLockedByLayer.get(layer) === true);
3065
+ if (!next)
3066
+ return;
3067
+ updateLayerRect(layer, next);
3068
+ commitLayout();
3069
+ }, isInspectorGroundDecalLayoutUniformScaleLayer(layer) ? {
3070
+ locked: scaleLockedByLayer.get(layer) === true,
3071
+ onChange: (locked) => {
3072
+ scaleLockedByLayer.set(layer, locked);
3073
+ if (!locked)
3074
+ return;
3075
+ updateLayerRect(layer, normalizeInspectorGroundDecalLayoutUniformRect(layer, layer.rect, 'width'));
3076
+ commitLayout();
3077
+ },
3078
+ } : undefined);
3079
+ fieldInputsByLayer.set(layer, layerInputs);
3080
+ renderInspectorGroundDecalLayoutBox(box, layer.rect);
3081
+ }
3082
+ renderPreview();
3083
+ return wrapper;
3084
+ }
3085
+ function readInspectorGroundDecalLayoutLayers(property) {
3086
+ if (isInspectorGroundDecalLayoutCompositeValue(property.value)) {
3087
+ const layers = property.value.layers
3088
+ .map((layer, fallbackIndex) => readInspectorGroundDecalLayoutLayer(layer, fallbackIndex))
3089
+ .filter((layer) => !!layer);
3090
+ if (layers.length > 0)
3091
+ return layers;
3092
+ }
3093
+ const label = readInspectorControlOptionString(property, 'layerLabel') || property.label || 'Ground decal layer';
3094
+ return [{
3095
+ index: 0,
3096
+ id: 'layer_0',
3097
+ role: 'layer',
3098
+ kind: 'unknown',
3099
+ label,
3100
+ rect: readInspectorGroundDecalLayoutRect(property.value),
3101
+ enabled: true,
3102
+ editable: true,
3103
+ zOrder: 0,
3104
+ }];
3105
+ }
3106
+ function isInspectorGroundDecalLayoutCompositeValue(value) {
3107
+ return isRecord(value) && Array.isArray(value.layers);
3108
+ }
3109
+ function readInspectorGroundDecalLayoutLayer(value, fallbackIndex) {
3110
+ if (!isRecord(value))
3111
+ return null;
3112
+ const index = Number.isInteger(value.index) && typeof value.index === 'number' && value.index >= 0
3113
+ ? value.index
3114
+ : fallbackIndex;
3115
+ const label = readStringField(value, 'label') || `Layer ${index + 1}`;
3116
+ return {
3117
+ index,
3118
+ id: readStringField(value, 'id') || `layer_${index}`,
3119
+ role: readStringField(value, 'role') || 'layer',
3120
+ kind: readStringField(value, 'kind') || 'unknown',
3121
+ label,
3122
+ rect: readInspectorGroundDecalLayoutRect(value.rect),
3123
+ enabled: readOptionalBooleanField(value, 'enabled') ?? true,
3124
+ editable: readOptionalBooleanField(value, 'editable') ?? true,
3125
+ zOrder: readOptionalNumberField(value, 'zOrder') ?? index,
3126
+ opacity: readOptionalNumberField(value, 'opacity') ?? undefined,
3127
+ scaleMode: readInspectorGroundDecalLayoutScaleMode(value.scaleMode),
3128
+ aspectRatio: readInspectorGroundDecalLayoutAspectRatio(value.aspectRatio),
3129
+ text: readStringField(value, 'text') || undefined,
3130
+ preview: readInspectorGroundDecalLayoutPreview(value.preview),
3131
+ };
3132
+ }
3133
+ function readInspectorGroundDecalLayoutScaleMode(value) {
3134
+ return value === 'uniform' || value === 'free' ? value : undefined;
3135
+ }
3136
+ function readInspectorGroundDecalLayoutAspectRatio(value) {
3137
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : undefined;
3138
+ }
3139
+ function readInspectorGroundDecalLayoutPreview(value) {
3140
+ if (!isRecord(value))
3141
+ return undefined;
3142
+ if (value.kind === 'image') {
3143
+ const url = readStringField(value, 'url');
3144
+ if (!url)
3145
+ return undefined;
3146
+ return {
3147
+ kind: 'image',
3148
+ url,
3149
+ alt: readStringField(value, 'alt') || undefined,
3150
+ tint: readInspectorGroundDecalLayoutColor(value.tint),
3151
+ };
3152
+ }
3153
+ if (value.kind === 'text') {
3154
+ return {
3155
+ kind: 'text',
3156
+ text: readStringField(value, 'text'),
3157
+ fontFamily: readStringField(value, 'fontFamily') || undefined,
3158
+ fontSize: readOptionalNumberField(value, 'fontSize') ?? undefined,
3159
+ fontWeight: readStringField(value, 'fontWeight') || undefined,
3160
+ color: readInspectorGroundDecalLayoutColor(value.color),
3161
+ strokeColor: readInspectorGroundDecalLayoutColor(value.strokeColor),
3162
+ strokeWidth: readOptionalNumberField(value, 'strokeWidth') ?? undefined,
3163
+ align: readInspectorGroundDecalLayoutTextAlign(value.align),
3164
+ baseline: readInspectorGroundDecalLayoutTextBaseline(value.baseline),
3165
+ };
3166
+ }
3167
+ if (value.kind === 'color') {
3168
+ const color = readInspectorGroundDecalLayoutColor(value.color);
3169
+ return color ? { kind: 'color', color } : undefined;
3170
+ }
3171
+ if (value.kind === 'progress') {
3172
+ const color = readInspectorGroundDecalLayoutColor(value.color);
3173
+ if (!color)
3174
+ return undefined;
3175
+ return {
3176
+ kind: 'progress',
3177
+ value: readOptionalNumberField(value, 'value') ?? 0,
3178
+ direction: readInspectorGroundDecalLayoutProgressDirection(value.direction),
3179
+ color,
3180
+ };
3181
+ }
3182
+ return undefined;
3183
+ }
3184
+ function readInspectorGroundDecalLayoutTextureSize(value) {
3185
+ if (!isRecord(value) || !isRecord(value.textureSize))
3186
+ return { width: 512, height: 512 };
3187
+ const width = readOptionalNumberField(value.textureSize, 'width') ?? 512;
3188
+ const height = readOptionalNumberField(value.textureSize, 'height') ?? 512;
3189
+ return {
3190
+ width: Math.max(1, Math.round(width)),
3191
+ height: Math.max(1, Math.round(height)),
3192
+ };
3193
+ }
3194
+ function readInspectorGroundDecalLayoutMask(value) {
3195
+ if (!isRecord(value) || !isRecord(value.mask))
3196
+ return undefined;
3197
+ return {
3198
+ enabled: readOptionalBooleanField(value.mask, 'enabled') ?? true,
3199
+ source: readStringField(value.mask, 'source') || undefined,
3200
+ cornerRadius: readOptionalNumberField(value.mask, 'cornerRadius') ?? undefined,
3201
+ padding: readOptionalNumberField(value.mask, 'padding') ?? undefined,
3202
+ };
3203
+ }
3204
+ function readInspectorGroundDecalLayoutColor(value) {
3205
+ if (!isRecord(value))
3206
+ return undefined;
3207
+ const r = readOptionalNumberField(value, 'r');
3208
+ const g = readOptionalNumberField(value, 'g');
3209
+ const b = readOptionalNumberField(value, 'b');
3210
+ if (r === null || g === null || b === null)
3211
+ return undefined;
3212
+ const a = readOptionalNumberField(value, 'a');
3213
+ return {
3214
+ r,
3215
+ g,
3216
+ b,
3217
+ ...(a === null ? {} : { a }),
3218
+ };
3219
+ }
3220
+ function readOptionalNumberField(record, key) {
3221
+ const value = record[key];
3222
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
3223
+ }
3224
+ function readOptionalBooleanField(record, key) {
3225
+ const value = record[key];
3226
+ return typeof value === 'boolean' ? value : null;
3227
+ }
3228
+ function readInspectorGroundDecalLayoutTextAlign(value) {
3229
+ return value === 'left' || value === 'center' || value === 'right' ? value : undefined;
3230
+ }
3231
+ function readInspectorGroundDecalLayoutTextBaseline(value) {
3232
+ return value === 'top' || value === 'middle' || value === 'bottom' ? value : undefined;
3233
+ }
3234
+ function readInspectorGroundDecalLayoutProgressDirection(value) {
3235
+ return value === 'leftToRight' || value === 'rightToLeft' || value === 'bottomToTop' || value === 'topToBottom'
3236
+ ? value
3237
+ : undefined;
3238
+ }
3239
+ function isInspectorGroundDecalLayoutLayerEditable(layer) {
3240
+ return layer.enabled && layer.editable;
3241
+ }
3242
+ function isInspectorGroundDecalLayoutUniformScaleLayer(layer) {
3243
+ return layer.scaleMode === 'uniform';
3244
+ }
3245
+ function createInspectorGroundDecalLayoutBox(doc, layer, editable) {
3246
+ const box = doc.createElement('div');
3247
+ box.dataset.editorInspectorGroundDecalLayoutBox = 'true';
3248
+ box.dataset.editorInspectorGroundDecalLayoutLayer = layer.id;
3249
+ box.title = layer.label;
3250
+ box.style.cssText = [
3251
+ 'position:absolute',
3252
+ 'box-sizing:border-box',
3253
+ 'border:2px solid var(--fps-editor-accent-strong)',
3254
+ 'background:rgba(65,145,255,0.08)',
3255
+ 'box-shadow:inset 0 0 0 1px rgba(0,0,0,0.32)',
3256
+ 'border-radius:3px',
3257
+ 'overflow:visible',
3258
+ editable ? 'cursor:grab' : 'cursor:not-allowed',
3259
+ 'touch-action:none',
3260
+ ].join(';');
3261
+ const handle = doc.createElement('button');
3262
+ handle.type = 'button';
3263
+ handle.dataset.editorInspectorGroundDecalLayoutResize = 'true';
3264
+ handle.disabled = !editable;
3265
+ handle.title = `Resize ${layer.label}`;
3266
+ handle.style.cssText = [
3267
+ 'position:absolute',
3268
+ 'right:0',
3269
+ 'bottom:0',
3270
+ 'width:10px',
3271
+ 'height:10px',
3272
+ 'box-sizing:border-box',
3273
+ 'padding:0',
3274
+ 'border:1px solid var(--fps-editor-accent-strong)',
3275
+ 'border-radius:3px',
3276
+ 'background:var(--fps-editor-panel)',
3277
+ editable ? 'cursor:nwse-resize' : 'cursor:not-allowed',
3278
+ 'touch-action:none',
3279
+ ].join(';');
3280
+ box.appendChild(handle);
3281
+ return box;
3282
+ }
3283
+ function createInspectorGroundDecalLayoutLayerFields(doc, parent, layer, editable, onInput, onCommit, scaleLock) {
3284
+ const row = doc.createElement('div');
3285
+ row.style.cssText = [
3286
+ 'display:grid',
3287
+ 'grid-template-columns:minmax(52px,0.9fr) repeat(4,minmax(0,1fr))',
3288
+ 'gap:6px',
3289
+ 'align-items:end',
3290
+ 'min-width:0',
3291
+ ].join(';');
3292
+ parent.appendChild(row);
3293
+ const name = doc.createElement('div');
3294
+ name.title = layer.label;
3295
+ name.style.cssText = [
3296
+ 'min-width:0',
3297
+ 'display:flex',
3298
+ 'align-items:center',
3299
+ 'gap:4px',
3300
+ 'color:var(--fps-editor-muted)',
3301
+ 'font-size:10px',
3302
+ 'font-weight:900',
3303
+ 'line-height:24px',
3304
+ ].join(';');
3305
+ const nameText = doc.createElement('span');
3306
+ nameText.textContent = layer.label;
3307
+ nameText.style.cssText = 'min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
3308
+ name.appendChild(nameText);
3309
+ if (scaleLock) {
3310
+ let locked = scaleLock.locked;
3311
+ const lockButton = createInspectorVectorHeaderButton(doc, '', locked);
3312
+ lockButton.style.width = '20px';
3313
+ lockButton.style.height = '20px';
3314
+ lockButton.style.flex = '0 0 auto';
3315
+ lockButton.disabled = !editable;
3316
+ lockButton.appendChild(createLocalEditorIcon(doc, 'lock', { size: 11, strokeWidth: 2.2 }));
3317
+ const applyLockState = () => {
3318
+ updateInspectorGroundDecalLayoutScaleLockButton(lockButton, layer, locked);
3319
+ };
3320
+ lockButton.addEventListener('click', (event) => {
3321
+ event.preventDefault();
3322
+ event.stopPropagation();
3323
+ if (!editable)
3324
+ return;
3325
+ locked = !locked;
3326
+ applyLockState();
3327
+ scaleLock.onChange(locked);
3328
+ });
3329
+ applyLockState();
3330
+ name.appendChild(lockButton);
3331
+ }
3332
+ row.appendChild(name);
3333
+ const inputs = new Map();
3334
+ for (const key of ['x', 'z', 'width', 'depth']) {
3335
+ const fieldLabel = key === 'width' ? 'W' : key === 'depth' ? 'D' : key.toUpperCase();
3336
+ const accessibleLabel = `${layer.label} ${key === 'width' ? 'width' : key === 'depth' ? 'depth' : key}`;
3337
+ const field = doc.createElement('label');
3338
+ field.style.cssText = [
3339
+ 'display:flex',
3340
+ 'flex-direction:column',
3341
+ 'gap:3px',
3342
+ 'min-width:0',
3343
+ 'color:var(--fps-editor-muted)',
3344
+ 'font-size:10px',
3345
+ 'font-weight:900',
3346
+ 'text-transform:uppercase',
3347
+ 'letter-spacing:0',
3348
+ ].join(';');
3349
+ field.textContent = fieldLabel;
3350
+ const fieldInput = doc.createElement('input');
3351
+ fieldInput.type = 'text';
3352
+ fieldInput.inputMode = 'decimal';
3353
+ fieldInput.setAttribute('aria-label', accessibleLabel);
3354
+ fieldInput.title = accessibleLabel;
3355
+ fieldInput.disabled = !editable;
3356
+ fieldInput.value = formatLocalEditorBrowserInspectorNumberValue(layer.rect[key]);
3357
+ fieldInput.style.cssText = createInspectorInputStyle();
3358
+ fieldInput.addEventListener('input', () => onInput(key));
3359
+ fieldInput.addEventListener('change', () => onCommit(key));
3360
+ fieldInput.addEventListener('keydown', (event) => {
3361
+ if (event.key !== 'Enter' || event.isComposing)
3362
+ return;
3363
+ event.preventDefault();
3364
+ event.stopPropagation();
3365
+ onCommit(key);
3366
+ });
3367
+ field.appendChild(fieldInput);
3368
+ row.appendChild(field);
3369
+ inputs.set(key, fieldInput);
3370
+ }
3371
+ return inputs;
3372
+ }
3373
+ function updateInspectorGroundDecalLayoutScaleLockButton(button, layer, locked) {
3374
+ const title = locked ? `Unlock ${layer.label} size ratio` : `Lock ${layer.label} size ratio`;
3375
+ button.title = title;
3376
+ button.dataset.editorTooltip = title;
3377
+ button.dataset.editorInspectorVectorLockState = locked ? 'locked' : 'unlocked';
3378
+ button.setAttribute('aria-pressed', locked ? 'true' : 'false');
3379
+ button.style.borderColor = locked ? 'var(--fps-editor-accent)' : 'var(--fps-editor-border)';
3380
+ button.style.background = locked ? 'var(--fps-editor-accent-soft)' : 'var(--fps-editor-button-bg)';
3381
+ button.style.color = locked ? 'var(--fps-editor-accent)' : 'var(--fps-editor-text)';
3382
+ }
3383
+ async function renderInspectorGroundDecalLayoutPreview(canvas, layers, textureSize, mask, shouldContinue) {
3384
+ if (canvas.width !== textureSize.width)
3385
+ canvas.width = textureSize.width;
3386
+ if (canvas.height !== textureSize.height)
3387
+ canvas.height = textureSize.height;
3388
+ const imageByLayer = new Map();
3389
+ await Promise.all(layers.map(async (layer) => {
3390
+ if (layer.preview?.kind !== 'image')
3391
+ return;
3392
+ imageByLayer.set(layer, await loadInspectorGroundDecalLayoutImage(canvas.ownerDocument, layer.preview.url));
3393
+ }));
3394
+ if (!shouldContinue())
3395
+ return;
3396
+ const ctx = canvas.getContext('2d');
3397
+ if (!ctx)
3398
+ return;
3399
+ ctx.clearRect(0, 0, textureSize.width, textureSize.height);
3400
+ ctx.save();
3401
+ applyInspectorGroundDecalLayoutMask(ctx, textureSize, mask);
3402
+ const sortedLayers = [...layers]
3403
+ .filter(layer => layer.enabled)
3404
+ .sort((left, right) => left.zOrder - right.zOrder || left.index - right.index);
3405
+ for (const layer of sortedLayers) {
3406
+ drawInspectorGroundDecalLayoutLayer(ctx, layer, textureSize, imageByLayer.get(layer) ?? null);
3407
+ }
3408
+ ctx.restore();
3409
+ }
3410
+ function applyInspectorGroundDecalLayoutMask(ctx, textureSize, mask) {
3411
+ if (mask?.enabled === false)
3412
+ return;
3413
+ const minSize = Math.min(textureSize.width, textureSize.height);
3414
+ const padding = Math.max(0, minSize * (mask?.padding ?? 0));
3415
+ const radius = Math.max(0, minSize * (mask?.cornerRadius ?? 0.18));
3416
+ roundedInspectorGroundDecalLayoutRectPath(ctx, padding, padding, textureSize.width - padding * 2, textureSize.height - padding * 2, radius);
3417
+ ctx.clip();
3418
+ }
3419
+ function drawInspectorGroundDecalLayoutLayer(ctx, layer, textureSize, image) {
3420
+ const preview = layer.preview;
3421
+ if (!preview)
3422
+ return;
3423
+ const rect = resolveInspectorGroundDecalLayoutPixelRect(layer.rect, textureSize);
3424
+ ctx.save();
3425
+ ctx.globalAlpha = layer.opacity ?? 1;
3426
+ if (preview.kind === 'color') {
3427
+ ctx.fillStyle = inspectorGroundDecalLayoutColorToCss(preview.color);
3428
+ ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
3429
+ }
3430
+ else if (preview.kind === 'progress') {
3431
+ drawInspectorGroundDecalLayoutProgress(ctx, preview, rect);
3432
+ }
3433
+ else if (preview.kind === 'image') {
3434
+ if (image) {
3435
+ drawInspectorGroundDecalLayoutImage(ctx, image, rect, preview.tint);
3436
+ }
3437
+ else {
3438
+ drawInspectorGroundDecalLayoutMissingTexture(ctx, rect);
3439
+ }
3440
+ }
3441
+ else if (preview.kind === 'text') {
3442
+ drawInspectorGroundDecalLayoutText(ctx, preview, rect);
3443
+ }
3444
+ ctx.restore();
3445
+ }
3446
+ function drawInspectorGroundDecalLayoutImage(ctx, image, rect, tint) {
3447
+ if (!tint) {
3448
+ ctx.drawImage(image, rect.x, rect.y, rect.width, rect.height);
3449
+ return;
3450
+ }
3451
+ const inheritedAlpha = ctx.globalAlpha;
3452
+ const tintAlpha = Math.max(0, Math.min(1, tint.a ?? 1));
3453
+ const totalAlpha = inheritedAlpha * tintAlpha;
3454
+ if (isInspectorGroundDecalLayoutWhiteTint(tint)) {
3455
+ ctx.globalAlpha = totalAlpha;
3456
+ ctx.drawImage(image, rect.x, rect.y, rect.width, rect.height);
3457
+ ctx.globalAlpha = inheritedAlpha;
3458
+ return;
3459
+ }
3460
+ const ownerDocument = ctx.canvas.ownerDocument;
3461
+ const canvas = ownerDocument.createElement('canvas');
3462
+ canvas.width = Math.max(1, Math.round(rect.width));
3463
+ canvas.height = Math.max(1, Math.round(rect.height));
3464
+ const tintContext = canvas.getContext('2d');
3465
+ if (!tintContext) {
3466
+ ctx.globalAlpha = totalAlpha;
3467
+ ctx.drawImage(image, rect.x, rect.y, rect.width, rect.height);
3468
+ ctx.globalAlpha = inheritedAlpha;
3469
+ return;
3470
+ }
3471
+ try {
3472
+ tintContext.drawImage(image, 0, 0, canvas.width, canvas.height);
3473
+ const imageData = tintContext.getImageData(0, 0, canvas.width, canvas.height);
3474
+ const data = imageData.data;
3475
+ const r = Math.max(0, Math.min(1, tint.r));
3476
+ const g = Math.max(0, Math.min(1, tint.g));
3477
+ const b = Math.max(0, Math.min(1, tint.b));
3478
+ for (let index = 0; index < data.length; index += 4) {
3479
+ data[index] = Math.round(data[index] * r);
3480
+ data[index + 1] = Math.round(data[index + 1] * g);
3481
+ data[index + 2] = Math.round(data[index + 2] * b);
3482
+ data[index + 3] = Math.round(data[index + 3] * totalAlpha);
3483
+ }
3484
+ tintContext.putImageData(imageData, 0, 0);
3485
+ ctx.globalAlpha = 1;
3486
+ ctx.drawImage(canvas, rect.x, rect.y, rect.width, rect.height);
3487
+ ctx.globalAlpha = inheritedAlpha;
3488
+ }
3489
+ catch {
3490
+ ctx.globalAlpha = totalAlpha;
3491
+ ctx.drawImage(image, rect.x, rect.y, rect.width, rect.height);
3492
+ ctx.globalAlpha = inheritedAlpha;
3493
+ }
3494
+ }
3495
+ function isInspectorGroundDecalLayoutWhiteTint(tint) {
3496
+ return tint.r >= 0.999 && tint.g >= 0.999 && tint.b >= 0.999;
3497
+ }
3498
+ function drawInspectorGroundDecalLayoutProgress(ctx, preview, rect) {
3499
+ const value = Math.max(0, Math.min(1, preview.value));
3500
+ const direction = preview.direction ?? 'bottomToTop';
3501
+ ctx.fillStyle = inspectorGroundDecalLayoutColorToCss(preview.color);
3502
+ if (direction === 'leftToRight') {
3503
+ ctx.fillRect(rect.x, rect.y, rect.width * value, rect.height);
3504
+ }
3505
+ else if (direction === 'rightToLeft') {
3506
+ const width = rect.width * value;
3507
+ ctx.fillRect(rect.x + rect.width - width, rect.y, width, rect.height);
3508
+ }
3509
+ else if (direction === 'topToBottom') {
3510
+ ctx.fillRect(rect.x, rect.y, rect.width, rect.height * value);
3511
+ }
3512
+ else {
3513
+ const height = rect.height * value;
3514
+ ctx.fillRect(rect.x, rect.y + rect.height - height, rect.width, height);
3515
+ }
3516
+ }
3517
+ function drawInspectorGroundDecalLayoutText(ctx, preview, rect) {
3518
+ const fontSize = preview.fontSize ?? Math.floor(rect.height * 0.72);
3519
+ const fontWeight = preview.fontWeight ?? '800';
3520
+ const fontFamily = preview.fontFamily ?? 'system-ui, Arial, sans-serif';
3521
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
3522
+ ctx.textAlign = preview.align ?? 'center';
3523
+ ctx.textBaseline = preview.baseline ?? 'middle';
3524
+ const x = resolveInspectorGroundDecalLayoutTextX(rect, ctx.textAlign);
3525
+ const y = resolveInspectorGroundDecalLayoutTextY(rect, ctx.textBaseline);
3526
+ if (preview.strokeColor && preview.strokeWidth) {
3527
+ ctx.lineWidth = preview.strokeWidth;
3528
+ ctx.strokeStyle = inspectorGroundDecalLayoutColorToCss(preview.strokeColor);
3529
+ ctx.strokeText(preview.text, x, y);
3530
+ }
3531
+ ctx.fillStyle = inspectorGroundDecalLayoutColorToCss(preview.color ?? { r: 1, g: 1, b: 1, a: 1 });
3532
+ ctx.fillText(preview.text, x, y);
3533
+ }
3534
+ function resolveInspectorGroundDecalLayoutPixelRect(rect, textureSize) {
3535
+ const width = rect.width * textureSize.width;
3536
+ const height = rect.depth * textureSize.height;
3537
+ const centerX = textureSize.width * (0.5 + rect.x);
3538
+ const centerY = textureSize.height * (0.5 - rect.z);
3539
+ return {
3540
+ x: centerX - width / 2,
3541
+ y: centerY - height / 2,
3542
+ width,
3543
+ height,
3544
+ };
3545
+ }
3546
+ function resolveInspectorGroundDecalLayoutTextX(rect, align) {
3547
+ if (align === 'left' || align === 'start')
3548
+ return rect.x;
3549
+ if (align === 'right' || align === 'end')
3550
+ return rect.x + rect.width;
3551
+ return rect.x + rect.width / 2;
3552
+ }
3553
+ function resolveInspectorGroundDecalLayoutTextY(rect, baseline) {
3554
+ if (baseline === 'top' || baseline === 'hanging')
3555
+ return rect.y;
3556
+ if (baseline === 'bottom' || baseline === 'alphabetic' || baseline === 'ideographic')
3557
+ return rect.y + rect.height;
3558
+ return rect.y + rect.height / 2;
3559
+ }
3560
+ function drawInspectorGroundDecalLayoutMissingTexture(ctx, rect) {
3561
+ ctx.fillStyle = 'rgba(255,0,80,0.55)';
3562
+ ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
3563
+ ctx.strokeStyle = 'rgba(255,255,255,0.9)';
3564
+ ctx.lineWidth = 4;
3565
+ ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
3566
+ }
3567
+ function roundedInspectorGroundDecalLayoutRectPath(ctx, x, y, width, height, radius) {
3568
+ const r = Math.max(0, Math.min(radius, width / 2, height / 2));
3569
+ ctx.beginPath();
3570
+ ctx.moveTo(x + r, y);
3571
+ ctx.lineTo(x + width - r, y);
3572
+ ctx.quadraticCurveTo(x + width, y, x + width, y + r);
3573
+ ctx.lineTo(x + width, y + height - r);
3574
+ ctx.quadraticCurveTo(x + width, y + height, x + width - r, y + height);
3575
+ ctx.lineTo(x + r, y + height);
3576
+ ctx.quadraticCurveTo(x, y + height, x, y + height - r);
3577
+ ctx.lineTo(x, y + r);
3578
+ ctx.quadraticCurveTo(x, y, x + r, y);
3579
+ ctx.closePath();
3580
+ }
3581
+ function inspectorGroundDecalLayoutColorToCss(color) {
3582
+ const r = Math.round(Math.max(0, Math.min(1, color.r)) * 255);
3583
+ const g = Math.round(Math.max(0, Math.min(1, color.g)) * 255);
3584
+ const b = Math.round(Math.max(0, Math.min(1, color.b)) * 255);
3585
+ const a = Math.max(0, Math.min(1, color.a ?? 1));
3586
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
3587
+ }
3588
+ function loadInspectorGroundDecalLayoutImage(doc, url) {
3589
+ const cached = inspectorGroundDecalLayoutImageCache.get(url);
3590
+ if (cached)
3591
+ return cached;
3592
+ const promise = new Promise((resolve) => {
3593
+ const image = doc.createElement('img');
3594
+ image.onload = () => resolve(image);
3595
+ image.onerror = () => resolve(null);
3596
+ image.src = url;
3597
+ });
3598
+ inspectorGroundDecalLayoutImageCache.set(url, promise);
3599
+ return promise;
3600
+ }
3601
+ function readInspectorGroundDecalLayoutRect(value) {
3602
+ if (!isRecord(value))
3603
+ return { x: 0, z: 0, width: 0.25, depth: 0.25 };
3604
+ return normalizeInspectorGroundDecalLayoutRect({
3605
+ x: readFiniteRecordNumber(value, 'x', 0),
3606
+ z: readFiniteRecordNumber(value, 'z', 0),
3607
+ width: readFiniteRecordNumber(value, 'width', 0.25),
3608
+ depth: readFiniteRecordNumber(value, 'depth', 0.25),
3609
+ });
3610
+ }
3611
+ function readFiniteRecordNumber(record, key, fallback) {
3612
+ const value = record[key];
3613
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
3614
+ }
3615
+ function normalizeInspectorGroundDecalLayoutRect(rect) {
3616
+ return {
3617
+ x: clampNumber(roundInspectorGroundDecalLayoutValue(rect.x), -1, 1),
3618
+ z: clampNumber(roundInspectorGroundDecalLayoutValue(rect.z), -1, 1),
3619
+ width: clampNumber(roundInspectorGroundDecalLayoutValue(rect.width), INSPECTOR_GROUND_DECAL_LAYOUT_MIN_SIZE, INSPECTOR_GROUND_DECAL_LAYOUT_MAX_SIZE),
3620
+ depth: clampNumber(roundInspectorGroundDecalLayoutValue(rect.depth), INSPECTOR_GROUND_DECAL_LAYOUT_MIN_SIZE, INSPECTOR_GROUND_DECAL_LAYOUT_MAX_SIZE),
3621
+ };
3622
+ }
3623
+ function resizeInspectorGroundDecalLayoutRect(layer, start, dx, dy, locked) {
3624
+ if (!locked) {
3625
+ return normalizeInspectorGroundDecalLayoutRect({
3626
+ ...start,
3627
+ width: start.width + dx * 2,
3628
+ depth: start.depth + dy * 2,
3629
+ });
3630
+ }
3631
+ const source = Math.abs(dx) >= Math.abs(dy) ? 'width' : 'depth';
3632
+ return normalizeInspectorGroundDecalLayoutUniformRect(layer, {
3633
+ ...start,
3634
+ width: start.width + dx * 2,
3635
+ depth: start.depth + dy * 2,
3636
+ }, source);
3637
+ }
3638
+ function normalizeInspectorGroundDecalLayoutUniformRect(layer, rect, source) {
3639
+ const aspectRatio = resolveInspectorGroundDecalLayoutAspectRatio(layer);
3640
+ const normalizedPosition = normalizeInspectorGroundDecalLayoutRect(rect);
3641
+ const candidateDepth = source === 'width' ? rect.width / aspectRatio : rect.depth;
3642
+ const minDepth = Math.max(INSPECTOR_GROUND_DECAL_LAYOUT_MIN_SIZE, INSPECTOR_GROUND_DECAL_LAYOUT_MIN_SIZE / aspectRatio);
3643
+ const maxDepth = Math.min(INSPECTOR_GROUND_DECAL_LAYOUT_MAX_SIZE, INSPECTOR_GROUND_DECAL_LAYOUT_MAX_SIZE / aspectRatio);
3644
+ const depth = clampNumber(candidateDepth, minDepth, maxDepth);
3645
+ return {
3646
+ ...normalizedPosition,
3647
+ width: roundInspectorGroundDecalLayoutValue(depth * aspectRatio),
3648
+ depth: roundInspectorGroundDecalLayoutValue(depth),
3649
+ };
3650
+ }
3651
+ function resolveInspectorGroundDecalLayoutAspectRatio(layer) {
3652
+ if (typeof layer.aspectRatio === 'number' && Number.isFinite(layer.aspectRatio) && layer.aspectRatio > 0) {
3653
+ return layer.aspectRatio;
3654
+ }
3655
+ if (layer.rect.depth > 0)
3656
+ return Math.max(0.001, layer.rect.width / layer.rect.depth);
3657
+ return 1;
3658
+ }
3659
+ function roundInspectorGroundDecalLayoutValue(value) {
3660
+ return Math.round(value * 1000) / 1000;
3661
+ }
3662
+ function renderInspectorGroundDecalLayoutBox(box, rect) {
3663
+ const left = (0.5 + rect.x - rect.width / 2) * 100;
3664
+ const top = (0.5 - rect.z - rect.depth / 2) * 100;
3665
+ box.style.left = `${left}%`;
3666
+ box.style.top = `${top}%`;
3667
+ box.style.width = `${rect.width * 100}%`;
3668
+ box.style.height = `${rect.depth * 100}%`;
3669
+ }
3670
+ function updateInspectorGroundDecalLayoutFieldInputs(inputs, rect) {
3671
+ for (const key of ['x', 'z', 'width', 'depth']) {
3672
+ const input = inputs.get(key);
3673
+ if (input)
3674
+ input.value = formatLocalEditorBrowserInspectorNumberValue(rect[key]);
3675
+ }
3676
+ }
3677
+ function readRectFromLayoutFieldInputs(inputs, fallback, layer, changedKey, locked) {
3678
+ const next = { ...fallback };
3679
+ for (const key of ['x', 'z', 'width', 'depth']) {
3680
+ const input = inputs.get(key);
3681
+ if (!input)
3682
+ continue;
3683
+ const value = parseLocalEditorBrowserInspectorNumberValue(input.value, 'final');
3684
+ if (value == null)
3685
+ return null;
3686
+ next[key] = value;
3687
+ }
3688
+ if (locked && (changedKey === 'width' || changedKey === 'depth')) {
3689
+ return normalizeInspectorGroundDecalLayoutUniformRect(layer, next, changedKey);
3690
+ }
3691
+ return normalizeInspectorGroundDecalLayoutRect(next);
3692
+ }
3693
+ function serializeInspectorGroundDecalLayoutValue(layers, composite) {
3694
+ if (!composite && layers.length === 1)
3695
+ return JSON.stringify(normalizeInspectorGroundDecalLayoutRect(layers[0].rect));
3696
+ return JSON.stringify({
3697
+ layers: layers.map(layer => ({
3698
+ index: layer.index,
3699
+ id: layer.id,
3700
+ role: layer.role,
3701
+ kind: layer.kind,
3702
+ label: layer.label,
3703
+ rect: normalizeInspectorGroundDecalLayoutRect(layer.rect),
3704
+ })),
3705
+ });
3706
+ }
3707
+ function commitInspectorGroundDecalLayoutValue(input, layers, fieldInputsByLayer, composite) {
3708
+ for (const layer of layers) {
3709
+ layer.rect = normalizeInspectorGroundDecalLayoutRect(layer.rect);
3710
+ const fieldInputs = fieldInputsByLayer.get(layer);
3711
+ if (fieldInputs)
3712
+ updateInspectorGroundDecalLayoutFieldInputs(fieldInputs, layer.rect);
3713
+ }
3714
+ const nextValue = serializeInspectorGroundDecalLayoutValue(layers, composite);
3715
+ if (input.value === nextValue)
3716
+ return;
3717
+ input.value = nextValue;
3718
+ input.dispatchEvent(new Event('change', { bubbles: true }));
3719
+ }
2280
3720
  function createInspectorAssetPickerCardControl(doc, _target, property, bindInput) {
2281
3721
  const wrapper = doc.createElement('div');
2282
3722
  wrapper.dataset.editorInspectorAssetPickerControl = 'true';
@@ -2464,6 +3904,8 @@ export function applyLocalEditorBrowserInspectorControlBinding(element, target,
2464
3904
  element.dataset.serializedCommitMode = property.commitMode;
2465
3905
  element.dataset.serializedPersistence = property.persistence;
2466
3906
  element.dataset.serializedEffect = getInspectorPropertyEffect(property);
3907
+ if (readInspectorControlOptionBoolean(property, 'forceVectorLock'))
3908
+ element.dataset.serializedForceVectorLock = 'true';
2467
3909
  if (property.disabledReason)
2468
3910
  element.dataset.serializedDisabledReason = property.disabledReason;
2469
3911
  if (options?.source)