evomeme 0.1.9 → 0.1.10-beta.13

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 (477) hide show
  1. package/README.md +2 -0
  2. package/dist/canvas-public/app.js +509 -5999
  3. package/dist/canvas-public/assets/presets/catalog.json +47 -0
  4. package/dist/canvas-public/assets/presets/tiles/A_01_CaoDi.png +0 -0
  5. package/dist/canvas-public/assets/presets/tiles/A_02_ShaMo.png +0 -0
  6. package/dist/canvas-public/assets/presets/tiles/A_03_TianKong.png +0 -0
  7. package/dist/canvas-public/assets/presets/tiles/A_04_ShuiBo.png +0 -0
  8. package/dist/canvas-public/assets/presets/tiles/A_05_HaiMian.png +0 -0
  9. package/dist/canvas-public/assets/presets/tiles/A_06_MuDiBan.png +0 -0
  10. package/dist/canvas-public/assets/presets/tiles/A_07_ShiZhuan.png +0 -0
  11. package/dist/canvas-public/assets/presets/tiles/A_08_AnRongYan.png +0 -0
  12. package/dist/canvas-public/assets/presets/tiles/A_09_QingTaiShi.png +0 -0
  13. package/dist/canvas-public/assets/presets/tiles/A_10_ShenHai.png +0 -0
  14. package/dist/canvas-public/assets/presets/tiles/A_11_HuiYanShi.png +0 -0
  15. package/dist/canvas-public/assets/presets/tiles/A_12_HongTu.png +0 -0
  16. package/dist/canvas-public/assets/presets/tiles/A_13_GengDi.png +0 -0
  17. package/dist/canvas-public/assets/presets/tiles/A_14_HuoShanYan.png +0 -0
  18. package/dist/canvas-public/assets/presets/tiles/A_15_NiTu.png +0 -0
  19. package/dist/canvas-public/assets/presets/tiles/A_16_HuangSha.png +0 -0
  20. package/dist/canvas-public/assets/presets/tiles/A_17_CongLin.png +0 -0
  21. package/dist/canvas-public/assets/presets/tiles/A_18_ZiYanShi.png +0 -0
  22. package/dist/canvas-public/assets/presets/tiles/A_19_MoGuTu.png +0 -0
  23. package/dist/canvas-public/assets/presets/tiles/A_20_HuaDian.png +0 -0
  24. package/dist/canvas-public/assets/presets/tiles/A_21_HaiLang.png +0 -0
  25. package/dist/canvas-public/assets/presets/tiles/A_22_GanTu.png +0 -0
  26. package/dist/canvas-public/assets/presets/tiles/A_23_QingTaiShiBan.png +0 -0
  27. package/dist/canvas-public/assets/presets/tiles/A_24_XingKong.png +0 -0
  28. package/dist/canvas-public/assets/presets/tiles/A_25_MoFaFuWen.png +0 -0
  29. package/dist/canvas-public/assets/presets/tiles/A_26_ShiTuYeDi.png +0 -0
  30. package/dist/canvas-public/assets/presets/tiles/A_27_LanZhuan.png +0 -0
  31. package/dist/canvas-public/assets/presets/tiles/A_28_HongZhuan.png +0 -0
  32. package/dist/canvas-public/assets/presets/tiles/A_29_YueQiu.png +0 -0
  33. package/dist/canvas-public/assets/presets/tiles/A_30_JinShuGe.png +0 -0
  34. package/dist/canvas-public/assets/presets/tiles/A_31_JinShuJingGai.png +0 -0
  35. package/dist/canvas-public/assets/presets/tiles/A_32_ShuZiChuang.png +0 -0
  36. package/dist/canvas-public/canvas/actions.js +144 -0
  37. package/dist/canvas-public/canvas/camera.js +28 -0
  38. package/dist/canvas-public/canvas/chrome.js +14 -0
  39. package/dist/canvas-public/canvas/data.js +123 -0
  40. package/dist/canvas-public/canvas/dependencies.js +19 -0
  41. package/dist/canvas-public/canvas/interactions.js +544 -0
  42. package/dist/canvas-public/canvas/panel-resize.js +5 -0
  43. package/dist/canvas-public/canvas/renderer.js +297 -0
  44. package/dist/canvas-public/canvas/selection.js +62 -0
  45. package/dist/canvas-public/canvas/serialization.js +118 -0
  46. package/dist/canvas-public/canvas/sync.js +202 -0
  47. package/dist/canvas-public/core/api.js +32 -0
  48. package/dist/canvas-public/core/feature-registry.js +29 -0
  49. package/dist/canvas-public/core/feature-state.js +228 -0
  50. package/dist/canvas-public/core/layout.js +133 -0
  51. package/dist/canvas-public/core/operations.js +38 -0
  52. package/dist/canvas-public/core/project.js +203 -0
  53. package/dist/canvas-public/core/state.js +27 -0
  54. package/dist/canvas-public/dialogs/context-menu.js +118 -0
  55. package/dist/canvas-public/elements/base.js +100 -0
  56. package/dist/canvas-public/elements/connections.js +105 -0
  57. package/dist/canvas-public/elements/drag.js +53 -0
  58. package/dist/canvas-public/elements/image.js +259 -0
  59. package/dist/canvas-public/elements/loading-recovery.js +5 -0
  60. package/dist/canvas-public/elements/loading.js +21 -0
  61. package/dist/canvas-public/elements/map.js +64 -0
  62. package/dist/canvas-public/elements/registry.js +58 -0
  63. package/dist/canvas-public/elements/replacement.js +78 -0
  64. package/dist/canvas-public/elements/resize.js +210 -0
  65. package/dist/canvas-public/elements/task-pipeline.js +235 -0
  66. package/dist/canvas-public/elements/types.js +21 -0
  67. package/dist/canvas-public/elements/ui.js +14 -0
  68. package/dist/canvas-public/elements/video.js +64 -0
  69. package/dist/canvas-public/features/assets/local-assets.js +72 -0
  70. package/dist/canvas-public/features/assets/official-assets.js +539 -0
  71. package/dist/canvas-public/features/editing/canvas-size.js +532 -0
  72. package/dist/canvas-public/features/editing/color-key.js +471 -0
  73. package/dist/canvas-public/features/editing/cutout.js +20 -0
  74. package/dist/canvas-public/features/editing/interactive-image-editing.js +982 -0
  75. package/dist/canvas-public/features/editing/layer-decomposition.js +45 -0
  76. package/dist/canvas-public/features/editing/manual-split.js +115 -0
  77. package/dist/canvas-public/features/editing/perfect-pixel.js +299 -0
  78. package/dist/canvas-public/features/editing/scroll-preview.js +130 -0
  79. package/dist/canvas-public/features/editing/scroll-seam-elimination.js +71 -0
  80. package/dist/canvas-public/features/editing/seam-elimination.js +46 -0
  81. package/dist/canvas-public/features/editing/smart-split.js +49 -0
  82. package/dist/canvas-public/features/editing/source-preview.js +43 -0
  83. package/dist/canvas-public/features/editing/video-cycle.js +49 -0
  84. package/dist/canvas-public/features/editing/video-frames.js +35 -0
  85. package/dist/canvas-public/features/frame-editor/frame-editing.js +755 -0
  86. package/dist/canvas-public/features/frame-editor/frame-model.js +63 -0
  87. package/dist/canvas-public/features/frame-editor/playback.js +83 -0
  88. package/dist/canvas-public/features/frame-editor/preview.js +151 -0
  89. package/dist/canvas-public/features/generation/animation.js +272 -0
  90. package/dist/canvas-public/features/generation/asset-batch.js +102 -0
  91. package/dist/canvas-public/features/generation/direction.js +163 -0
  92. package/dist/canvas-public/features/generation/fields.js +73 -0
  93. package/dist/canvas-public/features/generation/game-ui.js +64 -0
  94. package/dist/canvas-public/features/generation/image.js +96 -0
  95. package/dist/canvas-public/features/generation/scroll-image.js +157 -0
  96. package/dist/canvas-public/features/generation/seamless-tile.js +52 -0
  97. package/dist/canvas-public/features/generation/tile-transition-atlas.js +79 -0
  98. package/dist/canvas-public/features/generation/video.js +105 -0
  99. package/dist/canvas-public/features/map/creation.js +73 -0
  100. package/dist/canvas-public/features/map/tile-map-editor.js +2520 -0
  101. package/dist/canvas-public/features/registry.js +59 -0
  102. package/dist/canvas-public/index.html +84 -259
  103. package/dist/canvas-public/inspector/controls.js +49 -0
  104. package/dist/canvas-public/inspector/frame-preview.js +11 -0
  105. package/dist/canvas-public/inspector/inspector.js +190 -0
  106. package/dist/canvas-public/inspector/media-inspector.js +517 -0
  107. package/dist/canvas-public/inspector/media-preview.js +56 -0
  108. package/dist/canvas-public/inspector/number-stepper.js +96 -0
  109. package/dist/canvas-public/inspector/resize.js +88 -0
  110. package/dist/canvas-public/inspector/scroll-preview.js +11 -0
  111. package/dist/canvas-public/inspector/ui-inspector.js +14 -0
  112. package/dist/canvas-public/language/en-US.js +1059 -0
  113. package/dist/canvas-public/language/index.js +91 -0
  114. package/dist/canvas-public/language/zh-CN.js +1059 -0
  115. package/dist/canvas-public/map/export.js +10 -0
  116. package/dist/canvas-public/map/inspector.js +8 -0
  117. package/dist/canvas-public/map/model.js +409 -0
  118. package/dist/canvas-public/map/tile-map-editor.js +2 -0
  119. package/dist/canvas-public/media/metadata.js +183 -0
  120. package/dist/canvas-public/media/support.js +183 -0
  121. package/dist/canvas-public/styles/base.css +49 -0
  122. package/dist/canvas-public/styles/canvas.css +127 -0
  123. package/dist/canvas-public/styles/controls.css +203 -0
  124. package/dist/canvas-public/styles/dialogs.css +521 -0
  125. package/dist/canvas-public/styles/feature-form.css +409 -0
  126. package/dist/canvas-public/styles/inspector.css +441 -0
  127. package/dist/canvas-public/styles/layout.css +344 -0
  128. package/dist/canvas-public/styles/map.css +11 -0
  129. package/dist/canvas-public/styles/nodes.css +636 -0
  130. package/dist/canvas-public/styles.css +9 -2247
  131. package/dist/canvas-public/ui/controls.js +222 -0
  132. package/dist/canvas-public/ui/elements.js +9 -0
  133. package/dist/canvas-public/ui/feature-controller.js +296 -0
  134. package/dist/canvas-public/ui/feature-form.js +402 -0
  135. package/dist/canvas-public/ui/settings.js +84 -0
  136. package/dist/canvas-public/ui/toolbar.js +55 -0
  137. package/dist/canvas-public/vendor/pixi-tilemap.js +126 -0
  138. package/dist/canvas-public/vendor/pixi.min.js +2340 -0
  139. package/dist/canvas-public/workers/color-key-worker.js +289 -0
  140. package/dist/image-workers/perfect-pixel/perfect-pixel-core.js +441 -0
  141. package/dist/image-workers/perfect-pixel/perfect-pixel-worker.cjs +10 -0
  142. package/dist/index.js +46710 -12400
  143. package/dist/skills/en-US/evomeme-canvas/SKILL.md +52 -0
  144. package/dist/skills/en-US/evomeme-canvas/agents/openai.yaml +7 -0
  145. package/dist/skills/en-US/evomeme-cloud-fs/SKILL.md +203 -0
  146. package/dist/skills/en-US/evomeme-cloud-fs/agents/openai.yaml +7 -0
  147. package/dist/skills/en-US/evomeme-cover/SKILL.md +131 -0
  148. package/dist/skills/en-US/evomeme-cover/agents/openai.yaml +7 -0
  149. package/dist/skills/en-US/evomeme-game-maker/SKILL.md +288 -0
  150. package/dist/skills/en-US/evomeme-game-maker/agents/openai.yaml +4 -0
  151. package/dist/skills/en-US/evomeme-login/SKILL.md +97 -0
  152. package/dist/skills/en-US/evomeme-login/agents/openai.yaml +7 -0
  153. package/dist/skills/en-US/evomeme-mcp/SKILL.md +71 -0
  154. package/dist/skills/en-US/evomeme-mcp/agents/openai.yaml +7 -0
  155. package/dist/skills/en-US/evomeme-mcp/references/troubleshooting.md +55 -0
  156. package/dist/skills/en-US/evomeme-native-bridge-sdk/SKILL.md +286 -0
  157. package/dist/skills/en-US/evomeme-native-bridge-sdk/agents/openai.yaml +8 -0
  158. package/dist/skills/en-US/evomeme-tos-upload/SKILL.md +47 -0
  159. package/dist/skills/en-US/evomeme-tos-upload/agents/openai.yaml +5 -0
  160. package/dist/skills/en-US/evomeme-ui/SKILL.md +123 -0
  161. package/dist/skills/en-US/evomeme-ui/agents/openai.yaml +4 -0
  162. package/dist/skills/en-US/evomeme-ui/references/host-layout.md +69 -0
  163. package/dist/skills/en-US/game-art/SKILL.md +121 -0
  164. package/dist/skills/en-US/game-art/agents/openai.yaml +8 -0
  165. package/dist/skills/en-US/game-asset-align/SKILL.md +47 -0
  166. package/dist/skills/en-US/game-asset-align/agents/openai.yaml +8 -0
  167. package/dist/skills/en-US/game-asset-align/references/alignment-contract.md +61 -0
  168. package/dist/skills/en-US/game-asset-align/references/blender-scene-pipeline.md +35 -0
  169. package/dist/skills/en-US/game-asset-align/references/canonical-package.md +26 -0
  170. package/dist/skills/en-US/game-asset-align/references/engine-capabilities.md +14 -0
  171. package/dist/skills/en-US/game-asset-align/references/lightweight-review.md +34 -0
  172. package/dist/skills/en-US/game-asset-align/references/repair-policy.md +30 -0
  173. package/dist/skills/en-US/game-asset-align/references/tool-contract.md +69 -0
  174. package/dist/skills/en-US/game-asset-align/references/visual-review.md +26 -0
  175. package/dist/skills/en-US/game-asset-batch-generator/SKILL.md +117 -0
  176. package/dist/skills/en-US/game-asset-batch-generator/agents/openai.yaml +7 -0
  177. package/dist/skills/en-US/game-asset-image-generator/SKILL.md +86 -0
  178. package/dist/skills/en-US/game-asset-image-generator/agents/openai.yaml +7 -0
  179. package/dist/skills/en-US/game-asset-image-generator/references/background.md +34 -0
  180. package/dist/skills/en-US/game-asset-image-generator/references/static-game-assets.md +38 -0
  181. package/dist/skills/en-US/game-asset-mesh-generator/SKILL.md +35 -0
  182. package/dist/skills/en-US/game-asset-mesh-generator/agents/openai.yaml +7 -0
  183. package/dist/skills/en-US/game-asset-mesh-generator/references/animation-presets.md +313 -0
  184. package/dist/skills/en-US/game-asset-mesh-generator/references/generation-contracts.md +131 -0
  185. package/dist/skills/en-US/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
  186. package/dist/skills/en-US/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
  187. package/dist/skills/en-US/game-asset-mesh-generator/references/threejs-integration.md +113 -0
  188. package/dist/skills/en-US/game-asset-mesh-generator/references/tool-reference.md +67 -0
  189. package/dist/skills/en-US/game-audio/SKILL.md +121 -0
  190. package/dist/skills/en-US/game-audio/agents/openai.yaml +4 -0
  191. package/{skills → dist/skills/en-US}/game-audio/references/bgm-cdn.md +7 -11
  192. package/{skills → dist/skills/en-US}/game-audio/references/sfx-cdn.md +6 -4
  193. package/dist/skills/en-US/game-character-view-generator/SKILL.md +110 -0
  194. package/dist/skills/en-US/game-character-view-generator/agents/openai.yaml +7 -0
  195. package/dist/skills/en-US/game-demo/SKILL.md +103 -0
  196. package/dist/skills/en-US/game-demo/agents/openai.yaml +7 -0
  197. package/dist/skills/en-US/game-image-layer-decomposer/SKILL.md +99 -0
  198. package/dist/skills/en-US/game-image-layer-decomposer/agents/openai.yaml +7 -0
  199. package/dist/skills/en-US/game-image-splitter/SKILL.md +114 -0
  200. package/dist/skills/en-US/game-image-splitter/agents/openai.yaml +7 -0
  201. package/dist/skills/en-US/game-maker/SKILL.md +111 -0
  202. package/dist/skills/en-US/game-maker/agents/openai.yaml +4 -0
  203. package/dist/skills/en-US/game-perfect-pixel/SKILL.md +123 -0
  204. package/dist/skills/en-US/game-perfect-pixel/agents/openai.yaml +7 -0
  205. package/dist/skills/en-US/game-scroll-generator/SKILL.md +110 -0
  206. package/dist/skills/en-US/game-scroll-generator/agents/openai.yaml +7 -0
  207. package/dist/skills/en-US/game-test/SKILL.md +58 -0
  208. package/dist/skills/en-US/game-test/agents/openai.yaml +7 -0
  209. package/dist/skills/en-US/game-tile-generator/SKILL.md +152 -0
  210. package/dist/skills/en-US/game-tile-generator/agents/openai.yaml +7 -0
  211. package/dist/skills/en-US/game-ui/SKILL.md +177 -0
  212. package/dist/skills/en-US/game-ui/agents/openai.yaml +4 -0
  213. package/dist/skills/en-US/game-ui-generator/SKILL.md +82 -0
  214. package/dist/skills/en-US/game-ui-generator/agents/openai.yaml +7 -0
  215. package/dist/skills/en-US/game-ui-generator/references/component-patterns.md +65 -0
  216. package/dist/skills/en-US/game-ui-generator/references/runtime-integration.md +45 -0
  217. package/dist/skills/en-US/game-video-cycle/SKILL.md +98 -0
  218. package/dist/skills/en-US/game-video-cycle/agents/openai.yaml +7 -0
  219. package/dist/skills/en-US/game-video-generator/SKILL.md +78 -0
  220. package/dist/skills/en-US/game-video-generator/agents/openai.yaml +7 -0
  221. package/dist/skills/en-US/godot-3d-game/SKILL.md +41 -0
  222. package/dist/skills/en-US/godot-3d-game/agents/openai.yaml +8 -0
  223. package/dist/skills/en-US/godot-3d-game/references/alignment-gates.md +26 -0
  224. package/dist/skills/en-US/godot-3d-game/references/appearance-alignment.md +54 -0
  225. package/dist/skills/en-US/godot-3d-game/references/build-and-qa.md +22 -0
  226. package/dist/skills/en-US/godot-3d-game/references/canonical-asset-runtime.md +27 -0
  227. package/dist/skills/en-US/godot-3d-game/references/import-and-wrapper-scenes.md +20 -0
  228. package/dist/skills/en-US/godot-3d-game/references/physics-colliders.md +13 -0
  229. package/dist/skills/en-US/godot-3d-game/references/project-structure.md +22 -0
  230. package/dist/skills/en-US/godot-3d-game/references/skeleton-animation.md +16 -0
  231. package/dist/skills/en-US/phaser-2d-game/SKILL.md +127 -0
  232. package/dist/skills/en-US/phaser-2d-game/agents/openai.yaml +8 -0
  233. package/dist/skills/en-US/phaser-2d-game/references/engine-performance.md +64 -0
  234. package/dist/skills/en-US/phaser-2d-game/references/engine-physics.md +139 -0
  235. package/dist/skills/en-US/phaser-2d-game/references/engine-runtime.md +129 -0
  236. package/dist/skills/en-US/phaser-2d-game/references/engine-spritesheets.md +99 -0
  237. package/dist/skills/en-US/phaser-2d-game/references/engine-tilemaps.md +80 -0
  238. package/dist/skills/en-US/phaser-2d-game/references/file-structure.md +94 -0
  239. package/dist/skills/en-US/phaser-2d-graphics-builder/SKILL.md +68 -0
  240. package/dist/skills/en-US/phaser-2d-graphics-builder/agents/openai.yaml +7 -0
  241. package/dist/skills/en-US/threejs-3d-game/SKILL.md +117 -0
  242. package/dist/skills/en-US/threejs-3d-game/agents/openai.yaml +7 -0
  243. package/dist/skills/en-US/threejs-3d-game/references/engine-asset-pipeline.md +155 -0
  244. package/dist/skills/en-US/threejs-3d-game/references/engine-audio.md +70 -0
  245. package/dist/skills/en-US/threejs-3d-game/references/engine-input-camera.md +84 -0
  246. package/dist/skills/en-US/threejs-3d-game/references/engine-performance.md +96 -0
  247. package/dist/skills/en-US/threejs-3d-game/references/engine-physics.md +142 -0
  248. package/dist/skills/en-US/threejs-3d-game/references/engine-runtime.md +199 -0
  249. package/dist/skills/en-US/threejs-3d-game/references/file-structure.md +353 -0
  250. package/dist/skills/en-US/threejs-aaa-graphics-builder/SKILL.md +62 -0
  251. package/dist/skills/en-US/threejs-aaa-graphics-builder/agents/openai.yaml +7 -0
  252. package/{skills → dist/skills/zh-CN}/evomeme-cloud-fs/SKILL.md +33 -6
  253. package/{skills → dist/skills/zh-CN}/evomeme-cover/SKILL.md +7 -8
  254. package/{skills → dist/skills/zh-CN}/evomeme-game-maker/SKILL.md +91 -88
  255. package/{skills → dist/skills/zh-CN}/evomeme-login/SKILL.md +5 -0
  256. package/dist/skills/zh-CN/evomeme-mcp/SKILL.md +71 -0
  257. package/dist/skills/zh-CN/evomeme-mcp/agents/openai.yaml +7 -0
  258. package/dist/skills/zh-CN/evomeme-mcp/references/troubleshooting.md +55 -0
  259. package/{skills → dist/skills/zh-CN}/evomeme-tos-upload/SKILL.md +6 -5
  260. package/dist/skills/zh-CN/evomeme-ui/SKILL.md +123 -0
  261. package/dist/skills/zh-CN/evomeme-ui/agents/openai.yaml +4 -0
  262. package/dist/skills/zh-CN/evomeme-ui/references/host-layout.md +69 -0
  263. package/dist/skills/zh-CN/game-asset-align/SKILL.md +47 -0
  264. package/dist/skills/zh-CN/game-asset-align/agents/openai.yaml +8 -0
  265. package/dist/skills/zh-CN/game-asset-align/references/alignment-contract.md +61 -0
  266. package/dist/skills/zh-CN/game-asset-align/references/blender-scene-pipeline.md +35 -0
  267. package/dist/skills/zh-CN/game-asset-align/references/canonical-package.md +26 -0
  268. package/dist/skills/zh-CN/game-asset-align/references/engine-capabilities.md +14 -0
  269. package/dist/skills/zh-CN/game-asset-align/references/lightweight-review.md +34 -0
  270. package/dist/skills/zh-CN/game-asset-align/references/repair-policy.md +30 -0
  271. package/dist/skills/zh-CN/game-asset-align/references/tool-contract.md +69 -0
  272. package/dist/skills/zh-CN/game-asset-align/references/visual-review.md +26 -0
  273. package/dist/skills/zh-CN/game-asset-batch-generator/SKILL.md +117 -0
  274. package/dist/skills/zh-CN/game-asset-batch-generator/agents/openai.yaml +7 -0
  275. package/dist/skills/zh-CN/game-asset-image-generator/SKILL.md +86 -0
  276. package/{skills → dist/skills/zh-CN}/game-asset-image-generator/references/background.md +3 -1
  277. package/{skills → dist/skills/zh-CN}/game-asset-image-generator/references/static-game-assets.md +2 -2
  278. package/dist/skills/zh-CN/game-asset-mesh-generator/SKILL.md +35 -0
  279. package/dist/skills/zh-CN/game-asset-mesh-generator/agents/openai.yaml +7 -0
  280. package/dist/skills/zh-CN/game-asset-mesh-generator/references/animation-presets.md +313 -0
  281. package/dist/skills/zh-CN/game-asset-mesh-generator/references/generation-contracts.md +131 -0
  282. package/dist/skills/zh-CN/game-asset-mesh-generator/references/image-generator-workflows.md +65 -0
  283. package/dist/skills/zh-CN/game-asset-mesh-generator/references/postprocess-contracts.md +187 -0
  284. package/dist/skills/zh-CN/game-asset-mesh-generator/references/threejs-integration.md +113 -0
  285. package/dist/skills/zh-CN/game-asset-mesh-generator/references/tool-reference.md +67 -0
  286. package/dist/skills/zh-CN/game-audio/SKILL.md +121 -0
  287. package/dist/skills/zh-CN/game-audio/agents/openai.yaml +4 -0
  288. package/dist/skills/zh-CN/game-audio/references/bgm-cdn.md +123 -0
  289. package/dist/skills/zh-CN/game-audio/references/sfx-cdn.md +79 -0
  290. package/dist/skills/zh-CN/game-character-view-generator/SKILL.md +110 -0
  291. package/dist/skills/zh-CN/game-character-view-generator/agents/openai.yaml +7 -0
  292. package/dist/skills/zh-CN/game-image-layer-decomposer/SKILL.md +99 -0
  293. package/dist/skills/zh-CN/game-image-layer-decomposer/agents/openai.yaml +7 -0
  294. package/dist/skills/zh-CN/game-image-splitter/SKILL.md +114 -0
  295. package/dist/skills/zh-CN/game-image-splitter/agents/openai.yaml +7 -0
  296. package/dist/skills/zh-CN/game-perfect-pixel/SKILL.md +123 -0
  297. package/dist/skills/zh-CN/game-perfect-pixel/agents/openai.yaml +7 -0
  298. package/dist/skills/zh-CN/game-scroll-generator/SKILL.md +110 -0
  299. package/dist/skills/zh-CN/game-scroll-generator/agents/openai.yaml +7 -0
  300. package/dist/skills/zh-CN/game-test/references/checklists/playtest-qa.md +12 -0
  301. package/dist/skills/zh-CN/game-tile-generator/SKILL.md +151 -0
  302. package/dist/skills/zh-CN/game-tile-generator/agents/openai.yaml +7 -0
  303. package/{skills → dist/skills/zh-CN}/game-ui/SKILL.md +9 -1
  304. package/dist/skills/zh-CN/game-ui/references/checklists/game-ui-quality.md +14 -0
  305. package/dist/skills/zh-CN/game-ui/references/checklists/hud-readability.md +12 -0
  306. package/dist/skills/zh-CN/game-ui/references/checklists/mobile-input.md +12 -0
  307. package/dist/skills/zh-CN/game-ui/references/checklists/responsive-ui-fit.md +12 -0
  308. package/dist/skills/zh-CN/game-ui/references/ui-patterns.md +140 -0
  309. package/dist/skills/zh-CN/game-ui/references/ui-prompt-templates.md +63 -0
  310. package/dist/skills/zh-CN/game-ui-generator/SKILL.md +82 -0
  311. package/dist/skills/zh-CN/game-ui-generator/agents/openai.yaml +7 -0
  312. package/dist/skills/zh-CN/game-ui-generator/references/component-patterns.md +65 -0
  313. package/dist/skills/zh-CN/game-ui-generator/references/runtime-integration.md +45 -0
  314. package/dist/skills/zh-CN/game-video-cycle/SKILL.md +98 -0
  315. package/dist/skills/zh-CN/game-video-cycle/agents/openai.yaml +7 -0
  316. package/dist/skills/zh-CN/game-video-generator/SKILL.md +78 -0
  317. package/dist/skills/zh-CN/game-video-generator/agents/openai.yaml +7 -0
  318. package/dist/skills/zh-CN/godot-3d-game/SKILL.md +41 -0
  319. package/dist/skills/zh-CN/godot-3d-game/agents/openai.yaml +8 -0
  320. package/dist/skills/zh-CN/godot-3d-game/references/alignment-gates.md +26 -0
  321. package/dist/skills/zh-CN/godot-3d-game/references/appearance-alignment.md +54 -0
  322. package/dist/skills/zh-CN/godot-3d-game/references/build-and-qa.md +22 -0
  323. package/dist/skills/zh-CN/godot-3d-game/references/canonical-asset-runtime.md +27 -0
  324. package/dist/skills/zh-CN/godot-3d-game/references/import-and-wrapper-scenes.md +20 -0
  325. package/dist/skills/zh-CN/godot-3d-game/references/physics-colliders.md +13 -0
  326. package/dist/skills/zh-CN/godot-3d-game/references/project-structure.md +22 -0
  327. package/dist/skills/zh-CN/godot-3d-game/references/skeleton-animation.md +16 -0
  328. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-1.jpeg +0 -0
  329. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-2.jpeg +0 -0
  330. package/dist/skills/zh-CN/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  331. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/compose-quality.md +10 -0
  332. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/performance-safe-visual-detail.md +9 -0
  333. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/premium-game-quality-gate.md +15 -0
  334. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/sprite-quality.md +10 -0
  335. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/technical-art-quality.md +9 -0
  336. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/checklists/visual-scorecard.md +9 -0
  337. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/compose-recipes.md +71 -0
  338. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/graphics-prompt-templates.md +33 -0
  339. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/implementation-blueprint.md +82 -0
  340. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/sprite-recipes.md +90 -0
  341. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/technical-art.md +67 -0
  342. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/vfx-recipes.md +67 -0
  343. package/dist/skills/zh-CN/phaser-2d-graphics-builder/references/visual-scorecard.md +129 -0
  344. package/{skills → dist/skills/zh-CN}/threejs-3d-game/SKILL.md +6 -2
  345. package/dist/skills/zh-CN/threejs-3d-game/references/engine-asset-pipeline.md +155 -0
  346. package/{skills → dist/skills/zh-CN}/threejs-aaa-graphics-builder/SKILL.md +6 -6
  347. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-1.jpg +0 -0
  348. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-2.jpg +0 -0
  349. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  350. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/aaa-game-quality-gate.md +23 -0
  351. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/aaa-visual-scorecard.md +19 -0
  352. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/material-lighting-quality.md +14 -0
  353. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/performance-safe-visual-detail.md +14 -0
  354. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/procedural-model-quality.md +15 -0
  355. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/checklists/technical-art-quality.md +17 -0
  356. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/graphics-prompt-templates.md +236 -0
  357. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/implementation-blueprint.md +161 -0
  358. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/model-recipes.md +146 -0
  359. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/render-recipes.md +90 -0
  360. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/shader-cookbook.md +292 -0
  361. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/technical-art.md +167 -0
  362. package/dist/skills/zh-CN/threejs-aaa-graphics-builder/references/visual-scorecard.md +147 -0
  363. package/dist/stage-blender/bake_materials.py +102 -0
  364. package/dist/stage-blender/export_scene.py +275 -0
  365. package/dist/stage-blender/preview_camera.py +90 -0
  366. package/dist/stage-blender/repair_mesh.py +112 -0
  367. package/dist/stage-public/app.js +759 -0
  368. package/dist/stage-public/baked/bakePilot.js +44 -0
  369. package/dist/stage-public/baked/bakedScene.js +129 -0
  370. package/dist/stage-public/baked/sceneAppearance.js +225 -0
  371. package/dist/stage-public/baked/sceneCamera.js +45 -0
  372. package/dist/stage-public/comparison-contract.js +28 -0
  373. package/dist/stage-public/delivery.html +4 -0
  374. package/dist/stage-public/delivery.js +17 -0
  375. package/dist/stage-public/godot/appearance.gd +170 -0
  376. package/dist/stage-public/godot/preview.gd +79 -0
  377. package/dist/stage-public/index.html +141 -0
  378. package/dist/stage-public/review-app.js +289 -0
  379. package/dist/stage-public/review-runtime.js +63 -0
  380. package/dist/stage-public/review.css +24 -0
  381. package/dist/stage-public/review.html +47 -0
  382. package/dist/stage-public/scene-runtime.js +212 -0
  383. package/dist/stage-public/sceneContractRuntime.js +235 -0
  384. package/dist/stage-public/styles.css +328 -0
  385. package/dist/stage-public/three-appearance.js +104 -0
  386. package/dist/stage-public/vendor/three170/LICENSE +21 -0
  387. package/dist/stage-public/vendor/three170/build/three.module.js +54571 -0
  388. package/dist/stage-public/vendor/three170/examples/jsm/controls/OrbitControls.js +1523 -0
  389. package/dist/stage-public/vendor/three170/examples/jsm/environments/RoomEnvironment.js +144 -0
  390. package/dist/stage-public/vendor/three170/examples/jsm/libs/fflate.module.js +2672 -0
  391. package/dist/stage-public/vendor/three170/examples/jsm/loaders/EXRLoader.js +2563 -0
  392. package/dist/stage-public/vendor/three170/examples/jsm/loaders/GLTFLoader.js +4727 -0
  393. package/dist/stage-public/vendor/three170/examples/jsm/utils/BufferGeometryUtils.js +1373 -0
  394. package/dist/tile-transition-mask.svg +40 -0
  395. package/package.json +24 -8
  396. package/dist/canvas-public/cycle-detect.js +0 -166
  397. package/dist/canvas-public/smart-split.js +0 -185
  398. package/skills/game-asset-image-generator/SKILL.md +0 -104
  399. package/skills/game-audio/SKILL.md +0 -82
  400. package/skills/game-audio/agents/openai.yaml +0 -4
  401. package/skills/threejs-3d-game/references/engine-asset-pipeline.md +0 -116
  402. /package/{skills → dist/skills/en-US}/game-test/references/checklists/playtest-qa.md +0 -0
  403. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/game-ui-quality.md +0 -0
  404. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/hud-readability.md +0 -0
  405. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/mobile-input.md +0 -0
  406. /package/{skills → dist/skills/en-US}/game-ui/references/checklists/responsive-ui-fit.md +0 -0
  407. /package/{skills → dist/skills/en-US}/game-ui/references/ui-patterns.md +0 -0
  408. /package/{skills → dist/skills/en-US}/game-ui/references/ui-prompt-templates.md +0 -0
  409. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-1.jpeg +0 -0
  410. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-2.jpeg +0 -0
  411. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  412. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/compose-quality.md +0 -0
  413. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/performance-safe-visual-detail.md +0 -0
  414. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/premium-game-quality-gate.md +0 -0
  415. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/sprite-quality.md +0 -0
  416. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/technical-art-quality.md +0 -0
  417. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/checklists/visual-scorecard.md +0 -0
  418. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/compose-recipes.md +0 -0
  419. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/graphics-prompt-templates.md +0 -0
  420. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/implementation-blueprint.md +0 -0
  421. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/sprite-recipes.md +0 -0
  422. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/technical-art.md +0 -0
  423. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/vfx-recipes.md +0 -0
  424. /package/{skills → dist/skills/en-US}/phaser-2d-graphics-builder/references/visual-scorecard.md +0 -0
  425. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-1.jpg +0 -0
  426. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-2.jpg +0 -0
  427. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  428. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/aaa-game-quality-gate.md +0 -0
  429. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/aaa-visual-scorecard.md +0 -0
  430. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/material-lighting-quality.md +0 -0
  431. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/performance-safe-visual-detail.md +0 -0
  432. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/procedural-model-quality.md +0 -0
  433. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/checklists/technical-art-quality.md +0 -0
  434. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/graphics-prompt-templates.md +0 -0
  435. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/implementation-blueprint.md +0 -0
  436. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/model-recipes.md +0 -0
  437. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/render-recipes.md +0 -0
  438. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/shader-cookbook.md +0 -0
  439. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/technical-art.md +0 -0
  440. /package/{skills → dist/skills/en-US}/threejs-aaa-graphics-builder/references/visual-scorecard.md +0 -0
  441. /package/{skills → dist/skills/zh-CN}/evomeme-canvas/SKILL.md +0 -0
  442. /package/{skills → dist/skills/zh-CN}/evomeme-canvas/agents/openai.yaml +0 -0
  443. /package/{skills → dist/skills/zh-CN}/evomeme-cloud-fs/agents/openai.yaml +0 -0
  444. /package/{skills → dist/skills/zh-CN}/evomeme-cover/agents/openai.yaml +0 -0
  445. /package/{skills → dist/skills/zh-CN}/evomeme-game-maker/agents/openai.yaml +0 -0
  446. /package/{skills → dist/skills/zh-CN}/evomeme-login/agents/openai.yaml +0 -0
  447. /package/{skills → dist/skills/zh-CN}/evomeme-native-bridge-sdk/SKILL.md +0 -0
  448. /package/{skills → dist/skills/zh-CN}/evomeme-native-bridge-sdk/agents/openai.yaml +0 -0
  449. /package/{skills → dist/skills/zh-CN}/evomeme-tos-upload/agents/openai.yaml +0 -0
  450. /package/{skills → dist/skills/zh-CN}/game-art/SKILL.md +0 -0
  451. /package/{skills → dist/skills/zh-CN}/game-art/agents/openai.yaml +0 -0
  452. /package/{skills → dist/skills/zh-CN}/game-asset-image-generator/agents/openai.yaml +0 -0
  453. /package/{skills → dist/skills/zh-CN}/game-demo/SKILL.md +0 -0
  454. /package/{skills → dist/skills/zh-CN}/game-demo/agents/openai.yaml +0 -0
  455. /package/{skills → dist/skills/zh-CN}/game-maker/SKILL.md +0 -0
  456. /package/{skills → dist/skills/zh-CN}/game-maker/agents/openai.yaml +0 -0
  457. /package/{skills → dist/skills/zh-CN}/game-test/SKILL.md +0 -0
  458. /package/{skills → dist/skills/zh-CN}/game-test/agents/openai.yaml +0 -0
  459. /package/{skills → dist/skills/zh-CN}/game-ui/agents/openai.yaml +0 -0
  460. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/SKILL.md +0 -0
  461. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/agents/openai.yaml +0 -0
  462. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-performance.md +0 -0
  463. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-physics.md +0 -0
  464. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-runtime.md +0 -0
  465. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-spritesheets.md +0 -0
  466. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/engine-tilemaps.md +0 -0
  467. /package/{skills → dist/skills/zh-CN}/phaser-2d-game/references/file-structure.md +0 -0
  468. /package/{skills → dist/skills/zh-CN}/phaser-2d-graphics-builder/SKILL.md +0 -0
  469. /package/{skills → dist/skills/zh-CN}/phaser-2d-graphics-builder/agents/openai.yaml +0 -0
  470. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/agents/openai.yaml +0 -0
  471. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-audio.md +0 -0
  472. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-input-camera.md +0 -0
  473. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-performance.md +0 -0
  474. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-physics.md +0 -0
  475. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/engine-runtime.md +0 -0
  476. /package/{skills → dist/skills/zh-CN}/threejs-3d-game/references/file-structure.md +0 -0
  477. /package/{skills → dist/skills/zh-CN}/threejs-aaa-graphics-builder/agents/openai.yaml +0 -0
@@ -0,0 +1,759 @@
1
+ import * as THREE from 'three';
2
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3
+ import { TransformControls } from 'three/addons/controls/TransformControls.js';
4
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
5
+ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
6
+ import {
7
+ applyContractCamera,
8
+ findCamera,
9
+ letterboxElement,
10
+ makeTurquoiseSkyTexture,
11
+ rebuildAtmosphere,
12
+ rebuildContractLights,
13
+ } from './sceneContractRuntime.js';
14
+
15
+ const RUNTIME_FORWARD = new THREE.Vector3(0, 0, -1);
16
+ const SKY = 0xb7c9d6;
17
+ const SKY_FOG = 0xc5d4de;
18
+ const GRASS = 0x8fa88a;
19
+ const GRID_MAIN = 0x6f856c;
20
+ const GRID_SUB = 0x9bb096;
21
+
22
+ const els = {
23
+ list: document.getElementById('package-list'),
24
+ status: document.getElementById('status'),
25
+ refresh: document.getElementById('refresh-btn'),
26
+ inspector: document.getElementById('inspector'),
27
+ title: document.getElementById('asset-title'),
28
+ path: document.getElementById('asset-path'),
29
+ type: document.getElementById('field-type'),
30
+ forward: document.getElementById('field-forward'),
31
+ pivot: document.getElementById('field-pivot'),
32
+ scale: document.getElementById('field-scale'),
33
+ yaw: document.getElementById('field-yaw'),
34
+ pitch: document.getElementById('field-pitch'),
35
+ roll: document.getElementById('field-roll'),
36
+ notes: document.getElementById('field-notes'),
37
+ runtimeMeta: document.getElementById('runtime-meta'),
38
+ save: document.getElementById('save-btn'),
39
+ exportBtn: document.getElementById('export-btn'),
40
+ stage: document.querySelector('.stage'),
41
+ viewport: document.getElementById('viewport'),
42
+ frame: document.getElementById('viewport-frame'),
43
+ gizmoBar: document.getElementById('gizmo-bar'),
44
+ cameraBar: document.getElementById('camera-bar'),
45
+ };
46
+
47
+ /** @type {{ asset_id: string, asset: any, package_dir: string, model_url?: string, scene_contract_url?: string|null, has_scene_contract?: boolean } | null} */
48
+ let selected = null;
49
+ /** @type {any | null} */
50
+ let sceneContract = null;
51
+ /** @type {THREE.Object3D | null} */
52
+ let currentRoot = null;
53
+ /** @type {THREE.Object3D | null} */
54
+ let currentModel = null;
55
+ /** @type {import('three/addons/loaders/GLTFLoader.js').GLTF | null} */
56
+ let currentGltf = null;
57
+ let syncingFromGizmo = false;
58
+ let activeCameraId = 'reference';
59
+ let contractMode = false;
60
+
61
+ const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
62
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
63
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
64
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
65
+ renderer.toneMappingExposure = 1.05;
66
+ els.viewport.appendChild(renderer.domElement);
67
+
68
+ const scene = new THREE.Scene();
69
+ scene.background = new THREE.Color(SKY);
70
+ scene.fog = new THREE.Fog(SKY_FOG, 80, 420);
71
+ const pmrem = new THREE.PMREMGenerator(renderer);
72
+ scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.015).texture;
73
+
74
+ const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 500);
75
+ camera.position.set(2.2, 1.6, 2.8);
76
+
77
+ const controls = new OrbitControls(camera, renderer.domElement);
78
+ controls.enableDamping = true;
79
+ controls.target.set(0, 0.8, 0);
80
+
81
+ const transformControls = new TransformControls(camera, renderer.domElement);
82
+ transformControls.setMode('rotate');
83
+ transformControls.setSpace('local');
84
+ transformControls.setSize(0.95);
85
+ const transformHelper = transformControls.getHelper();
86
+ scene.add(transformHelper);
87
+ transformControls.enabled = false;
88
+ transformHelper.visible = false;
89
+
90
+ transformControls.addEventListener('dragging-changed', (event) => {
91
+ controls.enabled = !event.value && (!contractMode || activeCameraId === 'free');
92
+ if (!event.value && currentRoot && selected && !contractMode) {
93
+ syncFormFromObject();
94
+ updateRuntimeMeta(currentRoot, { ...selected.asset, ...formAssetPatch() });
95
+ }
96
+ });
97
+
98
+ transformControls.addEventListener('objectChange', () => {
99
+ if (!currentRoot || !selected || contractMode) return;
100
+ if (transformControls.getMode() === 'rotate') {
101
+ syncFormFromObject();
102
+ updateRuntimeMeta(currentRoot, { ...selected.asset, ...formAssetPatch() });
103
+ }
104
+ });
105
+
106
+ const defaultLightRoot = new THREE.Group();
107
+ defaultLightRoot.name = 'default_lights';
108
+ scene.add(defaultLightRoot);
109
+ defaultLightRoot.add(new THREE.HemisphereLight(0xd7e2ea, 0x8fa88a, 0.95));
110
+ const defaultKey = new THREE.DirectionalLight(0xf2eee6, 0.7);
111
+ defaultKey.position.set(4, 8, 2);
112
+ defaultLightRoot.add(defaultKey);
113
+ const defaultFill = new THREE.DirectionalLight(0xc9d5df, 0.28);
114
+ defaultFill.position.set(-4, 3, -3);
115
+ defaultLightRoot.add(defaultFill);
116
+
117
+ const contractLightRoot = new THREE.Group();
118
+ contractLightRoot.name = 'contract_lights';
119
+ contractLightRoot.visible = false;
120
+ scene.add(contractLightRoot);
121
+
122
+ const fxRoot = new THREE.Group();
123
+ fxRoot.name = 'atmosphere_fx';
124
+ fxRoot.visible = false;
125
+ scene.add(fxRoot);
126
+
127
+ const sky = new THREE.Mesh(
128
+ new THREE.SphereGeometry(400, 32, 16),
129
+ new THREE.MeshBasicMaterial({
130
+ map: makeTurquoiseSkyTexture(),
131
+ side: THREE.BackSide,
132
+ depthWrite: false,
133
+ })
134
+ );
135
+ scene.add(sky);
136
+
137
+ const ground = new THREE.Mesh(
138
+ new THREE.CircleGeometry(28, 64),
139
+ new THREE.MeshStandardMaterial({ color: GRASS, roughness: 0.95, metalness: 0 })
140
+ );
141
+ ground.rotation.x = -Math.PI / 2;
142
+ ground.position.y = -0.001;
143
+ scene.add(ground);
144
+
145
+ const horizon = new THREE.Mesh(
146
+ new THREE.RingGeometry(22, 28, 64),
147
+ new THREE.MeshBasicMaterial({
148
+ color: SKY_FOG,
149
+ transparent: true,
150
+ opacity: 0.45,
151
+ side: THREE.DoubleSide,
152
+ depthWrite: false,
153
+ })
154
+ );
155
+ horizon.rotation.x = -Math.PI / 2;
156
+ horizon.position.y = 0.01;
157
+ scene.add(horizon);
158
+
159
+ const grid = new THREE.GridHelper(12, 12, GRID_MAIN, GRID_SUB);
160
+ grid.position.y = 0.015;
161
+ for (const mat of Array.isArray(grid.material) ? grid.material : [grid.material]) {
162
+ mat.transparent = true;
163
+ mat.opacity = 0.22;
164
+ mat.depthWrite = false;
165
+ }
166
+ scene.add(grid);
167
+
168
+ const axis = new THREE.AxesHelper(1.2);
169
+ scene.add(axis);
170
+
171
+ const loader = new GLTFLoader();
172
+
173
+ function resize() {
174
+ if (contractMode && sceneContract) {
175
+ const cam = findCamera(sceneContract, activeCameraId === 'free' ? 'reference' : activeCameraId);
176
+ const aspect = cam?.aspect_ratio || 1.88;
177
+ const { width, height } = letterboxElement(els.frame, els.stage, aspect);
178
+ camera.aspect = aspect;
179
+ if (activeCameraId !== 'free' && cam) {
180
+ camera.fov = cam.vertical_fov_degrees;
181
+ camera.near = cam.near;
182
+ camera.far = cam.far;
183
+ }
184
+ camera.updateProjectionMatrix();
185
+ renderer.setSize(width, height, false);
186
+ return;
187
+ }
188
+ els.frame.style.width = '100%';
189
+ els.frame.style.height = '100%';
190
+ const { clientWidth: w, clientHeight: h } = els.viewport;
191
+ camera.aspect = w / Math.max(h, 1);
192
+ camera.updateProjectionMatrix();
193
+ renderer.setSize(w, h, false);
194
+ }
195
+ window.addEventListener('resize', resize);
196
+ resize();
197
+
198
+ function animate() {
199
+ requestAnimationFrame(animate);
200
+ // OrbitControls.update() always rewrites camera from spherical lookAt(target).
201
+ // Skip it while a contract camera is locked, or the authored quaternion is lost.
202
+ if (!contractMode || activeCameraId === 'free') {
203
+ controls.update();
204
+ }
205
+ const motes = fxRoot.children.find((c) => c.isPoints);
206
+ if (motes) motes.rotation.y += 0.00035;
207
+ renderer.render(scene, camera);
208
+ }
209
+ animate();
210
+
211
+ function axisVector(axisName) {
212
+ const map = {
213
+ '+x': [1, 0, 0],
214
+ '-x': [-1, 0, 0],
215
+ '+y': [0, 1, 0],
216
+ '-y': [0, -1, 0],
217
+ '+z': [0, 0, 1],
218
+ '-z': [0, 0, -1],
219
+ };
220
+ const v = map[axisName] || [0, 0, 1];
221
+ return new THREE.Vector3(v[0], v[1], v[2]);
222
+ }
223
+
224
+ function runtimeYaw(forwardAxis) {
225
+ switch (forwardAxis) {
226
+ case '+z':
227
+ return Math.PI;
228
+ case '-z':
229
+ return 0;
230
+ case '+x':
231
+ return Math.PI / 2;
232
+ case '-x':
233
+ return -Math.PI / 2;
234
+ default:
235
+ return 0;
236
+ }
237
+ }
238
+
239
+ function measureBounds(object) {
240
+ object.updateMatrixWorld(true);
241
+ const box = new THREE.Box3().setFromObject(object);
242
+ return {
243
+ min: box.min,
244
+ max: box.max,
245
+ size: box.getSize(new THREE.Vector3()),
246
+ center: box.getCenter(new THREE.Vector3()),
247
+ };
248
+ }
249
+
250
+ function detachGizmo() {
251
+ transformControls.detach();
252
+ transformControls.enabled = false;
253
+ transformHelper.visible = false;
254
+ els.gizmoBar.hidden = true;
255
+ }
256
+
257
+ function attachGizmo(object) {
258
+ if (contractMode) {
259
+ detachGizmo();
260
+ return;
261
+ }
262
+ transformControls.attach(object);
263
+ transformControls.enabled = true;
264
+ transformHelper.visible = true;
265
+ els.gizmoBar.hidden = false;
266
+ }
267
+
268
+ function syncFormFromObject() {
269
+ if (!currentRoot) return;
270
+ syncingFromGizmo = true;
271
+ const forward = els.forward.value || '+z';
272
+ const euler = new THREE.Euler().setFromQuaternion(currentRoot.quaternion.clone(), 'YXZ');
273
+ let yawOff = THREE.MathUtils.radToDeg(euler.y - runtimeYaw(forward));
274
+ yawOff = ((yawOff + 540) % 360) - 180;
275
+ els.yaw.value = yawOff.toFixed(1);
276
+ els.pitch.value = THREE.MathUtils.radToDeg(euler.x).toFixed(1);
277
+ els.roll.value = THREE.MathUtils.radToDeg(euler.z).toFixed(1);
278
+ syncingFromGizmo = false;
279
+ }
280
+
281
+ function updateRuntimeMeta(object, asset) {
282
+ const fitted = measureBounds(object);
283
+ const yaw =
284
+ runtimeYaw(asset.forward_axis) + THREE.MathUtils.degToRad(asset.yaw_offset_degrees || 0);
285
+ const authored = axisVector(asset.forward_axis);
286
+ const lines = [
287
+ `authored forward: ${asset.forward_axis} ${vec(authored)}`,
288
+ `runtime forward: -Z ${vec(RUNTIME_FORWARD)}`,
289
+ `pivot: ${asset.pivot || 'ground_center'} scale_hint: ${asset.scale_hint_metres ?? 'null'}`,
290
+ `runtime_yaw_deg: ${THREE.MathUtils.radToDeg(yaw).toFixed(1)}`,
291
+ `offsets °: yaw ${Number(asset.yaw_offset_degrees || 0).toFixed(1)} / pitch ${Number(asset.pitch_offset_degrees || 0).toFixed(1)} / roll ${Number(asset.roll_offset_degrees || 0).toFixed(1)}`,
292
+ `bounds_m: ${fitted.size.x.toFixed(2)} × ${fitted.size.y.toFixed(2)} × ${fitted.size.z.toFixed(2)}`,
293
+ `verified_by: ${asset.verified_by}`,
294
+ `alignment: ${asset.alignment?.overall_status || 'legacy'} · ready=${Boolean(asset.alignment?.ready)}`,
295
+ ];
296
+ if (sceneContract) {
297
+ lines.push(`scene_contract: ${sceneContract.schema} · cam=${activeCameraId}`);
298
+ lines.push('appearance: three.js approximation (not Cycles parity)');
299
+ }
300
+ els.runtimeMeta.textContent = lines.join('\n');
301
+ }
302
+
303
+ function setBoardExtrasVisible(visible) {
304
+ ground.visible = visible;
305
+ horizon.visible = visible;
306
+ grid.visible = visible;
307
+ axis.visible = visible;
308
+ defaultLightRoot.visible = visible;
309
+ }
310
+
311
+ function fitEnvironment(radius) {
312
+ const r = Math.max(radius, 0.5);
313
+ camera.near = Math.max(r / 800, 0.01);
314
+ camera.far = Math.max(r * 25, 500);
315
+ camera.updateProjectionMatrix();
316
+ if (scene.fog) {
317
+ scene.fog.near = r * 3.5;
318
+ scene.fog.far = r * 12;
319
+ }
320
+ sky.scale.setScalar(Math.max(r / 40, 1));
321
+ const groundScale = Math.max((r * 4) / 28, 1);
322
+ ground.scale.setScalar(groundScale);
323
+ horizon.scale.setScalar(groundScale);
324
+ grid.scale.setScalar(Math.max(r / 6, 1));
325
+ axis.scale.setScalar(Math.max(r / 30, 1));
326
+ controls.minDistance = Math.max(r * 0.04, 0.05);
327
+ controls.maxDistance = r * 15;
328
+ }
329
+
330
+ function frameCamera(object) {
331
+ const fitted = measureBounds(object);
332
+ const radius = Math.max(fitted.size.x, fitted.size.y, fitted.size.z, 0.5);
333
+ fitEnvironment(radius);
334
+ controls.target.copy(fitted.center);
335
+ camera.position.set(
336
+ fitted.center.x + radius * 1.15,
337
+ fitted.center.y + radius * 0.55,
338
+ fitted.center.z + radius * 1.35
339
+ );
340
+ controls.update();
341
+ }
342
+
343
+ /**
344
+ * Follow plugin_client applyCanonicalOrientation + contract rules:
345
+ * pivot=origin / scale_hint=null → identity root, no recenter, no height normalize.
346
+ */
347
+ function applyOrientation(object, asset, { frame = false } = {}) {
348
+ object.rotation.set(0, 0, 0);
349
+ object.scale.set(1, 1, 1);
350
+ object.position.set(0, 0, 0);
351
+ object.updateMatrixWorld(true);
352
+
353
+ const recipe = asset.alignment?.ready && asset.alignment?.recipe;
354
+ if (recipe) {
355
+ object.position.fromArray(recipe.translation_metres);
356
+ object.quaternion.fromArray(recipe.rotation_quaternion_xyzw);
357
+ object.scale.fromArray(recipe.scale_xyz);
358
+ object.updateMatrixWorld(true);
359
+ if (frame && !contractMode) frameCamera(object);
360
+ updateRuntimeMeta(object, asset);
361
+ return;
362
+ }
363
+
364
+ const pivot = asset.pivot || 'ground_center';
365
+ let bounds = measureBounds(object);
366
+
367
+ if (pivot === 'ground_center' || pivot === 'bounds_center') {
368
+ object.position.x -= bounds.center.x;
369
+ object.position.z -= bounds.center.z;
370
+ }
371
+ if (pivot === 'ground_center') {
372
+ object.position.y -= bounds.min.y;
373
+ } else if (pivot === 'bounds_center') {
374
+ object.position.y -= bounds.center.y;
375
+ }
376
+ // pivot === 'origin' → keep world origin
377
+
378
+ const yaw = runtimeYaw(asset.forward_axis) + THREE.MathUtils.degToRad(asset.yaw_offset_degrees || 0);
379
+ object.rotation.order = 'YXZ';
380
+ object.rotation.y = yaw;
381
+ object.rotation.x = THREE.MathUtils.degToRad(asset.pitch_offset_degrees || 0);
382
+ object.rotation.z = THREE.MathUtils.degToRad(asset.roll_offset_degrees || 0);
383
+ object.updateMatrixWorld(true);
384
+
385
+ if (asset.scale_hint_metres && Number(asset.scale_hint_metres) > 0) {
386
+ bounds = measureBounds(object);
387
+ if (bounds.size.y > 1e-6) {
388
+ object.scale.multiplyScalar(Number(asset.scale_hint_metres) / bounds.size.y);
389
+ object.updateMatrixWorld(true);
390
+ if (pivot === 'ground_center') {
391
+ bounds = measureBounds(object);
392
+ object.position.y -= bounds.min.y;
393
+ }
394
+ }
395
+ }
396
+
397
+ bounds = measureBounds(object);
398
+ const radius = Math.max(bounds.size.x, bounds.size.y, bounds.size.z, 0.5);
399
+ if (contractMode) {
400
+ setBoardExtrasVisible(false);
401
+ // Keep contract fog/camera; only enlarge sky dome for large maps.
402
+ sky.scale.setScalar(Math.max(radius / 40, 1));
403
+ if (sceneContract && activeCameraId !== 'free') {
404
+ applyContractCamera(camera, controls, findCamera(sceneContract, activeCameraId));
405
+ resize();
406
+ }
407
+ } else if (frame) {
408
+ setBoardExtrasVisible(true);
409
+ frameCamera(object);
410
+ } else {
411
+ setBoardExtrasVisible(true);
412
+ fitEnvironment(radius);
413
+ }
414
+ updateRuntimeMeta(object, asset);
415
+ }
416
+
417
+ function enableVertexColors(root) {
418
+ root.traverse((obj) => {
419
+ if (!obj.isMesh || !obj.geometry?.attributes?.color) return;
420
+ const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
421
+ for (const mat of mats) {
422
+ if (!mat) continue;
423
+ mat.vertexColors = true;
424
+ // Meadow materials ship without baseColorFactor; keep albedo neutral so COLOR_0 shows.
425
+ if (mat.color) mat.color.set(0xffffff);
426
+ mat.needsUpdate = true;
427
+ }
428
+ });
429
+ }
430
+
431
+ function enableContractPresentation(contract) {
432
+ contractMode = true;
433
+ sceneContract = contract;
434
+ els.frame.classList.add('letterbox');
435
+ els.cameraBar.hidden = false;
436
+ detachGizmo();
437
+ setBoardExtrasVisible(false);
438
+ defaultLightRoot.visible = false;
439
+ contractLightRoot.visible = true;
440
+ fxRoot.visible = true;
441
+ rebuildContractLights(contractLightRoot, contract);
442
+ rebuildAtmosphere(fxRoot, contract);
443
+
444
+ // Avoid RoomEnvironment washout on vertex-colored untextured meshes.
445
+ scene.environment = null;
446
+ const exposure = contract.blender_render?.exposure;
447
+ renderer.toneMappingExposure = 0.85 + (typeof exposure === 'number' ? exposure : 0.05);
448
+ if (scene.fog) {
449
+ scene.fog.color.set(0xb7c6d0);
450
+ scene.fog.near = 90;
451
+ scene.fog.far = 420;
452
+ }
453
+ sky.scale.setScalar(1);
454
+ activeCameraId = 'reference';
455
+ highlightCameraButton('reference');
456
+ applyContractCamera(camera, controls, findCamera(contract, 'reference'));
457
+ controls.enabled = false;
458
+ resize();
459
+ }
460
+
461
+ function disableContractPresentation() {
462
+ contractMode = false;
463
+ sceneContract = null;
464
+ els.frame.classList.remove('letterbox');
465
+ els.cameraBar.hidden = true;
466
+ contractLightRoot.visible = false;
467
+ fxRoot.visible = false;
468
+ defaultLightRoot.visible = true;
469
+ setBoardExtrasVisible(true);
470
+ scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.015).texture;
471
+ renderer.toneMappingExposure = 1.0;
472
+ controls.enabled = true;
473
+ activeCameraId = 'free';
474
+ resize();
475
+ }
476
+
477
+ function highlightCameraButton(id) {
478
+ for (const btn of els.cameraBar.querySelectorAll('[data-camera]')) {
479
+ btn.classList.toggle('active', btn.dataset.camera === id);
480
+ }
481
+ }
482
+
483
+ function vec(v) {
484
+ return `(${v.x.toFixed(0)}, ${v.y.toFixed(0)}, ${v.z.toFixed(0)})`;
485
+ }
486
+
487
+ async function api(pathname, init) {
488
+ const response = await fetch(pathname, {
489
+ headers: { 'Content-Type': 'application/json' },
490
+ ...init,
491
+ });
492
+ const data = await response.json();
493
+ if (!response.ok || data.ok === false) {
494
+ throw new Error(data.error || `HTTP ${response.status}`);
495
+ }
496
+ return data;
497
+ }
498
+
499
+ async function refreshList() {
500
+ els.status.textContent = '加载列表…';
501
+ const data = await api('/api/packages');
502
+ els.list.innerHTML = '';
503
+ if (!data.packages.length) {
504
+ els.status.textContent = '还没有 canonical 包。用 MCP create 或放入 GLB。';
505
+ return;
506
+ }
507
+ for (const item of data.packages) {
508
+ const btn = document.createElement('button');
509
+ btn.type = 'button';
510
+ btn.dataset.id = item.asset_id;
511
+ btn.innerHTML = `<span class="id">${item.asset_id}</span><span class="sub">${item.asset.type} · ${item.asset.alignment?.overall_status || 'legacy'} · ${item.asset.verified_by}</span>`;
512
+ btn.addEventListener('click', () => selectPackage(item.asset_id));
513
+ els.list.appendChild(btn);
514
+ }
515
+ els.status.textContent = `${data.packages.length} 个资产包`;
516
+ if (selected) highlight(selected.asset_id);
517
+ }
518
+
519
+ function highlight(assetId) {
520
+ for (const btn of els.list.querySelectorAll('button')) {
521
+ btn.classList.toggle('active', btn.dataset.id === assetId);
522
+ }
523
+ }
524
+
525
+ function fillForm(asset) {
526
+ els.title.textContent = asset.asset_id;
527
+ els.type.value = asset.type || 'prop';
528
+ els.forward.value = asset.forward_axis || '+z';
529
+ if (els.pivot) els.pivot.value = asset.pivot || 'ground_center';
530
+ els.scale.value =
531
+ asset.scale_hint_metres === null || asset.scale_hint_metres === undefined
532
+ ? ''
533
+ : String(asset.scale_hint_metres);
534
+ els.yaw.value = String(asset.yaw_offset_degrees || 0);
535
+ els.pitch.value = String(asset.pitch_offset_degrees || 0);
536
+ els.roll.value = String(asset.roll_offset_degrees || 0);
537
+ els.notes.value = asset.notes || '';
538
+ }
539
+
540
+ async function selectPackage(assetId) {
541
+ if (!assetId) {
542
+ els.status.textContent = '无效资产 ID';
543
+ return;
544
+ }
545
+ els.status.textContent = `加载 ${assetId}…`;
546
+ const data = await api(`/api/packages/${encodeURIComponent(assetId)}`);
547
+ selected = {
548
+ ...data,
549
+ asset_id: data.asset_id || data.asset?.asset_id || assetId,
550
+ };
551
+ highlight(selected.asset_id);
552
+ els.inspector.hidden = false;
553
+ els.path.textContent = data.package_dir;
554
+ fillForm(data.asset);
555
+
556
+ sceneContract = null;
557
+ if (data.has_scene_contract && data.scene_contract_url) {
558
+ const contractRes = await fetch(data.scene_contract_url);
559
+ if (!contractRes.ok) throw new Error(`Failed to read scene.contract.json (${contractRes.status})`);
560
+ sceneContract = await contractRes.json();
561
+ }
562
+
563
+ await loadModel(data.model_url, data.asset, sceneContract);
564
+ els.status.textContent = sceneContract
565
+ ? `已选中 ${selected.asset_id} · 契约相机 ${activeCameraId}`
566
+ : `已选中 ${selected.asset_id} · 拖动物体轴向手柄`;
567
+ }
568
+
569
+ async function loadModel(modelUrl, asset, contract) {
570
+ detachGizmo();
571
+ if (currentRoot) {
572
+ scene.remove(currentRoot);
573
+ disposeObject(currentRoot);
574
+ currentRoot = null;
575
+ currentModel = null;
576
+ currentGltf = null;
577
+ }
578
+
579
+ const gltf = await loader.loadAsync(`${modelUrl}?t=${Date.now()}`);
580
+ currentGltf = gltf;
581
+ currentModel = gltf.scene;
582
+ enableVertexColors(currentModel);
583
+ currentRoot = new THREE.Group();
584
+ currentRoot.add(currentModel);
585
+ scene.add(currentRoot);
586
+
587
+ if (contract) {
588
+ enableContractPresentation(contract);
589
+ applyOrientation(currentRoot, asset, { frame: false });
590
+ // Prefer embedded glTF cameras (same transforms as contract) after root is identity.
591
+ syncCameraFromGltf(gltf, activeCameraId === 'free' ? 'reference' : activeCameraId, contract);
592
+ } else {
593
+ disableContractPresentation();
594
+ applyOrientation(currentRoot, asset, { frame: true });
595
+ attachGizmo(currentRoot);
596
+ }
597
+ }
598
+
599
+ function syncCameraFromGltf(gltf, cameraId, contract) {
600
+ const camDef = findCamera(contract, cameraId);
601
+ const index = typeof camDef?.gltf_camera_index === 'number' ? camDef.gltf_camera_index : 0;
602
+ const embedded = gltf.cameras?.[index];
603
+ currentRoot?.updateMatrixWorld(true);
604
+ if (embedded) {
605
+ embedded.updateWorldMatrix(true, false);
606
+ const wp = new THREE.Vector3();
607
+ const wq = new THREE.Quaternion();
608
+ embedded.getWorldPosition(wp);
609
+ embedded.getWorldQuaternion(wq);
610
+ camera.position.copy(wp);
611
+ camera.quaternion.copy(wq);
612
+ camera.fov = embedded.fov;
613
+ camera.near = embedded.near;
614
+ camera.far = embedded.far;
615
+ if (camDef?.look_at) controls.target.fromArray(camDef.look_at);
616
+ resize();
617
+ return;
618
+ }
619
+ applyContractCamera(camera, controls, camDef);
620
+ resize();
621
+ }
622
+
623
+ function disposeObject(root) {
624
+ root.traverse((obj) => {
625
+ if (obj.geometry) obj.geometry.dispose?.();
626
+ if (obj.material) {
627
+ const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
628
+ for (const mat of mats) mat.dispose?.();
629
+ }
630
+ });
631
+ }
632
+
633
+ function formAssetPatch() {
634
+ const scaleRaw = els.scale.value.trim();
635
+ const patch = {
636
+ type: els.type.value,
637
+ forward_axis: els.forward.value,
638
+ pivot: els.pivot?.value || 'ground_center',
639
+ scale_hint_metres: scaleRaw === '' ? null : Number(scaleRaw),
640
+ yaw_offset_degrees: Number(els.yaw.value) || 0,
641
+ pitch_offset_degrees: Number(els.pitch.value) || 0,
642
+ roll_offset_degrees: Number(els.roll.value) || 0,
643
+ notes: els.notes.value,
644
+ verified_by: 'user_board',
645
+ };
646
+ if (currentRoot) {
647
+ currentRoot.updateMatrixWorld(true);
648
+ patch.alignment_recipe = {
649
+ translation_metres: currentRoot.position.toArray(),
650
+ rotation_quaternion_xyzw: currentRoot.quaternion.toArray(),
651
+ scale_xyz: currentRoot.scale.toArray(),
652
+ algorithm_version: 'evomeme.stage.v2',
653
+ };
654
+ }
655
+ return patch;
656
+ }
657
+
658
+ els.refresh.addEventListener('click', () => {
659
+ refreshList().catch((error) => {
660
+ els.status.textContent = error.message;
661
+ });
662
+ });
663
+
664
+ els.save.addEventListener('click', async () => {
665
+ if (!selected?.asset_id) return;
666
+ try {
667
+ els.status.textContent = '保存中…';
668
+ const data = await api(`/api/packages/${encodeURIComponent(selected.asset_id)}`, {
669
+ method: 'PUT',
670
+ body: JSON.stringify(formAssetPatch()),
671
+ });
672
+ selected = {
673
+ ...selected,
674
+ asset_id: data.asset_id || data.asset?.asset_id || selected.asset_id,
675
+ asset: data.asset,
676
+ package_dir: data.package_dir,
677
+ };
678
+ fillForm(data.asset);
679
+ if (currentRoot) applyOrientation(currentRoot, data.asset, { frame: !contractMode });
680
+ els.status.textContent = '已写入 asset.json 并刷新预览';
681
+ await refreshList();
682
+ } catch (error) {
683
+ els.status.textContent = error.message;
684
+ }
685
+ });
686
+
687
+ els.exportBtn.addEventListener('click', async () => {
688
+ if (!selected?.asset_id) return;
689
+ try {
690
+ const data = await api(`/api/packages/${encodeURIComponent(selected.asset_id)}/export`, {
691
+ method: 'POST',
692
+ body: '{}',
693
+ });
694
+ els.status.textContent = `通用包:${data.export_dir}`;
695
+ alert(`${data.note}\n\n${data.export_dir}`);
696
+ } catch (error) {
697
+ els.status.textContent = error.message;
698
+ }
699
+ });
700
+
701
+ for (const el of [els.forward, els.scale, els.yaw, els.pitch, els.roll, els.type, els.pivot].filter(Boolean)) {
702
+ const onEdit = () => {
703
+ if (syncingFromGizmo || !currentRoot || !selected) return;
704
+ applyOrientation(currentRoot, { ...selected.asset, ...formAssetPatch(), alignment: null }, { frame: false });
705
+ };
706
+ el.addEventListener('change', onEdit);
707
+ el.addEventListener('input', onEdit);
708
+ }
709
+
710
+ els.gizmoBar?.addEventListener('click', (event) => {
711
+ const target = event.target;
712
+ if (!(target instanceof HTMLElement)) return;
713
+ const mode = target.dataset.gizmo;
714
+ if (mode === 'rotate' || mode === 'translate') {
715
+ transformControls.setMode(mode);
716
+ for (const btn of els.gizmoBar.querySelectorAll('[data-gizmo]')) {
717
+ btn.classList.toggle('active', btn === target);
718
+ }
719
+ return;
720
+ }
721
+ if (target.dataset.space !== undefined) {
722
+ const next = transformControls.space === 'local' ? 'world' : 'local';
723
+ transformControls.setSpace(next);
724
+ target.textContent = next === 'local' ? '局部' : '世界';
725
+ target.dataset.space = next;
726
+ }
727
+ });
728
+
729
+ els.cameraBar?.addEventListener('click', (event) => {
730
+ const target = event.target;
731
+ if (!(target instanceof HTMLElement) || !target.dataset.camera || !sceneContract) return;
732
+ const id = target.dataset.camera;
733
+ activeCameraId = id;
734
+ highlightCameraButton(id);
735
+ if (id === 'free') {
736
+ controls.enabled = true;
737
+ const ref = findCamera(sceneContract, 'reference');
738
+ if (ref) {
739
+ camera.fov = ref.vertical_fov_degrees;
740
+ camera.near = ref.near;
741
+ camera.far = ref.far;
742
+ camera.updateProjectionMatrix();
743
+ }
744
+ els.status.textContent = '自由观察(轨道);点 reference 可回到契约构图';
745
+ return;
746
+ }
747
+ controls.enabled = false;
748
+ if (currentGltf) {
749
+ syncCameraFromGltf(currentGltf, id, sceneContract);
750
+ } else {
751
+ applyContractCamera(camera, controls, findCamera(sceneContract, id));
752
+ resize();
753
+ }
754
+ els.status.textContent = `契约相机:${id}`;
755
+ });
756
+
757
+ refreshList().catch((error) => {
758
+ els.status.textContent = error.message;
759
+ });