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,289 @@
1
+ import { createReviewView } from './review-runtime.js';
2
+ import { compareSpatial, compareStructural } from './comparison-contract.js';
3
+
4
+ const $ = id => document.getElementById(id);
5
+ // The task chooses the scene. Never enumerate packages or substitute another asset.
6
+ const assetId = new URLSearchParams(location.search).get('asset');
7
+ let view, check, cameraChoices = [], aspect = 1.88, busy = false, referenceUrl, cameraEpoch = 0;
8
+ let renderTimer, renderEpoch = 0, currentJob, interacting = false, needsRender = false;
9
+ const apiBase = `/api/packages/${encodeURIComponent(assetId)}`;
10
+ let targetEngine = new URLSearchParams(location.search).get('engine') === 'godot' ? 'godot' : 'three_js';
11
+ let stageView, viewedCapture, godotUrl, hasReference = false;
12
+ $('target-engine').value = targetEngine;
13
+ const engineName = () => targetEngine === 'godot' ? 'Godot' : 'Three.js';
14
+ function renderAdaptation() {
15
+ const report = check.adaptation?.[targetEngine], panel = $('adaptation-panel');
16
+ panel.hidden = !report;
17
+ if (!report) return;
18
+ const labels = {mapped:'已映射',baked:'已烘焙',approximate:'采用近似',missing:'尚未实现',unverified:'待核对'};
19
+ $('adaptation-summary').textContent = `${report.summary.missing || 0} 项尚未实现 · ${report.summary.approximate || 0} 项采用近似`;
20
+ const source = check.contract?.source_features?.rendering;
21
+ $('adaptation-scope').textContent = `${engineName()} · ${report.renderer} | Blender ${source?.engine || '源设置未记录'} · ${source?.preview_quality === 'final' ? '源采样参考图' : '快速参考图'}。自由视角使用快速原生渲染。`;
22
+ $('adaptation-items').replaceChildren();
23
+ for (const item of report.items) {
24
+ const row = document.createElement('div'); row.className = 'adaptation-row';
25
+ const title = document.createElement('div'); title.className = 'adaptation-title';
26
+ const name = document.createElement('strong'); name.textContent = item.label;
27
+ const badge = document.createElement('span'); badge.className = `adaptation-badge ${item.status}`; badge.textContent = labels[item.status];
28
+ title.append(name,badge);
29
+ const description = document.createElement('p'); description.textContent = item.detail;
30
+ const action = document.createElement('p'); action.className = 'adaptation-action'; action.textContent = item.action;
31
+ const buttons = document.createElement('div'); buttons.className = 'adaptation-buttons';
32
+ const inspect = document.createElement('button'); inspect.textContent = '查看相关机位';
33
+ inspect.onclick = () => {
34
+ if (busy) return;
35
+ const index = cameraChoices.findIndex(c => c.camera?.id === item.camera_id);
36
+ $('camera').value = String(Math.max(index,0));
37
+ setCamera(Math.max(index,0)).catch(failed);
38
+ document.querySelector('.viewer').scrollIntoView({behavior:'smooth',block:'start'});
39
+ };
40
+ const request = document.createElement('button'); request.textContent = '记录修复要求';
41
+ request.onclick = async () => {
42
+ if (busy || !view || !stageView) { message('当前画面就绪后可记录修复要求。'); return; }
43
+ request.disabled = true;
44
+ try {
45
+ await post('feedback',{revision:check.revision,presentation:view.capture().presentation,
46
+ object_name:(item.object_names || []).join(', '),instruction:`目标 ${engineName()},修复「${item.label}」:${item.detail} ${item.action}`});
47
+ request.textContent = '已记录,回对话继续'; message('修复要求已保存在当前资产中;回到建模对话继续处理。');
48
+ } catch(error) { message(error.message); request.disabled = false; }
49
+ };
50
+ buttons.append(inspect,request); row.append(title,description,action,buttons); $('adaptation-items').append(row);
51
+ }
52
+ panel.open = report.summary.missing > 0;
53
+ }
54
+ function resetConfirmation() { stageView = null; viewedCapture = null; $('confirm-asset').disabled = true; $('confirmation-state').textContent = '当前画面待确认'; document.querySelector('.confirmation-bar').classList.remove('confirmed'); }
55
+ async function post(endpoint, body) {
56
+ const response = await fetch(`${apiBase}/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
57
+ const result = await response.json(); if (!response.ok || !result.ok) throw new Error(result.error || 'Stage request failed.'); return result;
58
+ }
59
+ async function finishView() {
60
+ const epoch = renderEpoch, engine = targetEngine;
61
+ resetConfirmation();
62
+ const capture = view.capture(); let native;
63
+ if (engine === 'godot') {
64
+ $('godot-image').hidden = true; $('godot-pending').hidden = false; $('godot-progress').textContent = 'Godot 正在渲染当前机位…';
65
+ try {
66
+ native = await post('godot-preview', { revision: check.revision, presentation: capture.presentation });
67
+ if (epoch !== renderEpoch || engine !== targetEngine) return;
68
+ const image = await verifiedImage(native.image_url,native);
69
+ if (epoch !== renderEpoch) { URL.revokeObjectURL(image.url); return; }
70
+ if (godotUrl) URL.revokeObjectURL(godotUrl); godotUrl = image.url;
71
+ $('godot-image').src = image.url; $('godot-image').hidden = false; $('godot-pending').hidden = true;
72
+ capture.screenshot = await fetch(image.url).then(r => r.blob()).then(blob => new Promise(resolve => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.readAsDataURL(blob); }));
73
+ $('engine-render-label').textContent = `原生 ${native.native.version}`;
74
+ $('confirmation-note').textContent = [hasReference ? '请核对 Godot 的材质、灯光和阴影,再确认当前版本。' : '未提供 Blender 基准;只确认目标引擎效果,不代表与 Blender 画面一致。',...native.profile.warnings].join(' ');
75
+ } catch (error) {
76
+ if (epoch === renderEpoch) { $('godot-progress').textContent = error.message; message(error.message); }
77
+ throw error;
78
+ }
79
+ }
80
+ if (epoch !== renderEpoch || engine !== targetEngine) return;
81
+ const receipt = await post('stage-view', { revision: check.revision, presentation: capture.presentation, engine, godot_preview_id: native?.id });
82
+ if (epoch !== renderEpoch || engine !== targetEngine) return;
83
+ stageView = receipt; viewedCapture = capture;
84
+ if (!hasReference) checkState('appearance', {state:'warn',label:'目标引擎已载入',detail:'未提供 Blender 基准;请查看当前材质与灯光效果。'});
85
+ $('confirmation-state').textContent = `${engineName()} 当前版本待用户确认`;
86
+ $('confirm-asset').disabled = false;
87
+ }
88
+ const message = text => { $('status').textContent = text; $('loading-text').textContent = text; };
89
+ function checkState(kind, value) { $(`${kind}-state`).className = `check-state ${value.state}`; $(`${kind}-state`).textContent = value.label; $(`${kind}-detail`).textContent = value.detail; }
90
+ function split(value) {
91
+ const n = Math.max(0, Math.min(100, value));
92
+ $('comparison').style.setProperty('--split', `${n}%`);
93
+ $('divider').setAttribute('aria-valuenow', String(Math.round(n)));
94
+ $('divider').setAttribute('aria-valuetext', `左侧 Blender ${Math.round(n)}%,右侧 ${engineName()} ${Math.round(100 - n)}%`);
95
+ document.querySelector('.source-label').hidden = n === 0;
96
+ document.querySelector('.engine-label').hidden = n === 100;
97
+ }
98
+ function fit() {
99
+ const w = Math.min($('stage').clientWidth, $('stage').clientHeight * aspect);
100
+ $('comparison').style.width = `${w}px`; $('comparison').style.height = `${w / aspect}px`;
101
+ }
102
+ const resizeObserver = new ResizeObserver(fit); resizeObserver.observe($('stage'));
103
+ function drag(event) { const r = $('comparison').getBoundingClientRect(); split((event.clientX - r.left) / r.width * 100); }
104
+ $('divider').addEventListener('pointerdown', event => { event.stopPropagation(); if (event.button !== 0 || busy) return; event.preventDefault(); $('divider').focus({ preventScroll: true }); $('divider').setPointerCapture(event.pointerId); drag(event); });
105
+ $('divider').addEventListener('pointermove', event => { event.stopPropagation(); if ($('divider').hasPointerCapture(event.pointerId)) drag(event); });
106
+ for (const type of ['pointerup', 'pointercancel']) $('divider').addEventListener(type, event => { event.stopPropagation(); if ($('divider').hasPointerCapture(event.pointerId)) $('divider').releasePointerCapture(event.pointerId); });
107
+ $('divider').addEventListener('keydown', event => {
108
+ const current = Number($('divider').getAttribute('aria-valuenow'));
109
+ const values = { ArrowLeft: current - 2, ArrowDown: current - 2, ArrowRight: current + 2, ArrowUp: current + 2, Home: 0, End: 100 };
110
+ if (event.key in values) { event.preventDefault(); split(values[event.key]); }
111
+ });
112
+ async function verifiedImage(file, fingerprint) {
113
+ const response = await fetch(fingerprint ? file : `${apiBase}/files/${file}?revision=${check.revision}`);
114
+ if (!response.ok) throw new Error('Failed to read the Blender reference.');
115
+ const bytes = await response.arrayBuffer(), expected = fingerprint || check.files[file];
116
+ const hash = [...new Uint8Array(await crypto.subtle.digest('SHA-256', bytes))].map(n => n.toString(16).padStart(2, '0')).join('');
117
+ if (!expected || hash !== expected.sha256 || bytes.byteLength !== expected.bytes) throw new Error('The Blender reference changed. Reload the scene.');
118
+ const url = URL.createObjectURL(new Blob([bytes], { type: 'image/png' })), img = new Image(); img.src = url;
119
+ try { await img.decode(); } catch (error) { URL.revokeObjectURL(url); throw error; }
120
+ return { url, width: img.naturalWidth, height: img.naturalHeight };
121
+ }
122
+ function cancelJob(id) { if (id) fetch(`${apiBase}/blender-preview/${id}/cancel`, { method: 'POST', keepalive: true }).catch(() => {}); }
123
+ function invalidatePreview() { ++renderEpoch; clearTimeout(renderTimer); cancelJob(currentJob); currentJob = null; needsRender = false; resetConfirmation(); if (targetEngine === 'godot') { $('godot-image').hidden = true; $('godot-pending').hidden = false; $('godot-progress').textContent = '当前机位待更新…'; } }
124
+ function nativePending(text, error = false) {
125
+ $('blender-view').classList.add('pending'); $('blender-pending').hidden = false;
126
+ $('blender-pending').classList.toggle('error', error); $('blender-progress').textContent = text;
127
+ $('retry-preview').hidden = !error; $('view-state').textContent = error ? 'Blender 视角更新失败' : '正在同步当前视角';
128
+ $('blender-label').textContent = error ? '当前视角未就绪' : '正在更新机位';
129
+ checkState('appearance', { state: 'warn', label: '当前视角待更新', detail: 'Three.js 已更新;等待同机位 Blender 原生渲染。' });
130
+ }
131
+ function nativeReady(image, label) {
132
+ if (referenceUrl) URL.revokeObjectURL(referenceUrl);
133
+ referenceUrl = image.url; $('blender-image').src = image.url;
134
+ $('blender-view').classList.remove('pending'); $('blender-pending').hidden = true;
135
+ $('blender-label').textContent = label; $('view-state').textContent = '对齐后的实际效果';
136
+ checkState('appearance', { state: 'warn', label: '显示已应用 · 待比对', detail: view.mode === 'baked' ? '已应用源烘焙与显示配方 · 拖动对比渲染差异' : '已应用目标引擎材质 · 拖动对比渲染差异' });
137
+ }
138
+ function cameraChanged() {
139
+ if (busy || !view) return;
140
+ invalidatePreview(); needsRender = true; $('camera').value = 'free';
141
+ nativePending(interacting ? '松开后更新 Blender 视角' : '准备渲染当前视角…');
142
+ $('camera-note').textContent = '自由视角 · 共享相机';
143
+ message('Three.js 实时响应;Blender 停止操作后按同一机位重新渲染。');
144
+ if (!interacting) schedulePreview();
145
+ }
146
+ function schedulePreview() { clearTimeout(renderTimer); if (needsRender) renderTimer = setTimeout(renderPreview, 450); }
147
+ async function renderPreview() {
148
+ if (!view || busy || interacting) return;
149
+ if (!hasReference) {
150
+ needsRender = false;
151
+ try { await finishView(); } catch (error) { message(error.message); }
152
+ return;
153
+ }
154
+ needsRender = false;
155
+ const epoch = renderEpoch, revision = check.revision, camera = view.getCamera();
156
+ nativePending('Blender 正在渲染当前视角…');
157
+ const started = performance.now();
158
+ try {
159
+ const response = await fetch(`${apiBase}/blender-preview`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ revision, camera }) });
160
+ let job = await response.json();
161
+ if (epoch !== renderEpoch) { if (job.id) cancelJob(job.id); return; }
162
+ if (!response.ok || !job.ok) throw new Error(job.error || 'Blender preview request failed.');
163
+ currentJob = job.id;
164
+ while (job.state === 'queued' || job.state === 'rendering') {
165
+ if (epoch !== renderEpoch) return;
166
+ const seconds = Math.round((performance.now() - started) / 1000);
167
+ $('blender-progress').textContent = `${job.state === 'queued' ? '等待上一帧结束' : 'Blender 正在渲染'} · ${seconds} 秒`;
168
+ await new Promise(resolve => setTimeout(resolve, 700));
169
+ if (epoch !== renderEpoch) return;
170
+ const result = await fetch(`${apiBase}/blender-preview/${job.id}`); job = await result.json();
171
+ if (!result.ok || !job.ok) throw new Error(job.error || 'Failed to read Blender render progress.');
172
+ }
173
+ if (epoch !== renderEpoch) return;
174
+ if (job.state !== 'ready') throw new Error(job.error || 'This view was superseded. Retry the current view.');
175
+ if (job.revision !== revision || JSON.stringify(job.camera) !== JSON.stringify(camera)) throw new Error('Blender 返回机位与当前视角不一致');
176
+ const image = await verifiedImage(job.image_url, job);
177
+ if (epoch !== renderEpoch) { URL.revokeObjectURL(image.url); return; }
178
+ if (Math.abs(image.width / image.height - aspect) > .003) { URL.revokeObjectURL(image.url); throw new Error('Blender 返回画幅与当前机位不一致'); }
179
+ nativeReady(image, `Blender · ${job.receipt.samples} samples 预览`);
180
+ await finishView();
181
+ $('camera-note').textContent = `自由视角 · ${image.width} × ${image.height} · ${job.receipt.seconds} 秒`;
182
+ message('两侧已同步到当前机位。Blender 为原生快速预览;可继续旋转、平移、缩放或拖动分隔线。');
183
+ currentJob = null;
184
+ } catch (error) {
185
+ if (epoch !== renderEpoch) return;
186
+ nativePending('当前视角未生成,可重试或切回参考机位。', true); message(error.message); currentJob = null;
187
+ }
188
+ }
189
+ $('retry-preview').addEventListener('pointerdown', e => e.stopPropagation());
190
+ $('retry-preview').onclick = () => { invalidatePreview(); renderPreview(); };
191
+ async function setCamera(index) {
192
+ const choice = cameraChoices[index], epoch = ++cameraEpoch;
193
+ if (!choice) return;
194
+ invalidatePreview(); view?.setInteractive(false);
195
+ busy = true; $('camera').disabled = true; $('reload').disabled = true; $('target-engine').disabled = true; $('comparison').hidden = true; $('loading').hidden = false;
196
+ $('loading').classList.remove('error');
197
+ message('对齐 Blender 与 Three.js 的机位和画幅…');
198
+ try {
199
+ if (!choice.file) {
200
+ view.resetCamera(); aspect = view.getCamera().aspect; fit(); split(0);
201
+ $('comparison').hidden = false; $('loading').hidden = true;
202
+ $('view-state').textContent = '目标引擎实际预览'; $('camera-note').textContent = '未提供 Blender 基准,当前只核对目标引擎效果';
203
+ $('confirmation-note').textContent = '未提供 Blender 基准。请查看目标引擎实际材质与灯光;此确认不代表与 Blender 画面一致。';
204
+ await finishView();
205
+ message('当前为目标引擎预览。可旋转、平移、缩放,查看后确认当前版本。');
206
+ return;
207
+ }
208
+ const image = await verifiedImage(choice.file);
209
+ if (epoch !== cameraEpoch) { URL.revokeObjectURL(image.url); return; }
210
+ aspect = choice.camera.aspect_ratio;
211
+ if (Math.abs(image.width / image.height - aspect) > .001) { URL.revokeObjectURL(image.url); throw new Error('Blender 基准画幅与契约相机不一致'); }
212
+ nativeReady(image, 'Blender · 原生渲染');
213
+ view.setCamera(choice.camera); fit(); $('comparison').hidden = false;
214
+ await finishView();
215
+ $('loading').hidden = true; $('view-state').textContent = '对齐后的实际效果';
216
+ $('camera-note').textContent = `${choice.label} · ${image.width} × ${image.height} 基准画幅`;
217
+ message('拖动画面改变共享机位;停止操作后 Blender 更新。拖动分隔线对比两侧效果。');
218
+ } finally { busy = false; view?.setInteractive(true); $('camera').disabled = false; $('reload').disabled = false; $('target-engine').disabled = false; }
219
+ }
220
+ function failed(error) {
221
+ resetConfirmation();
222
+ $('comparison').hidden = true; $('loading').hidden = false; $('loading').classList.add('error'); $('view-state').textContent = '对比未就绪';
223
+ for (const kind of ['spatial', 'structural']) if ($(`${kind}-state`).textContent === '检查中') checkState(kind, { state: 'warn', label: '未完成', detail: '本次导入未完成,尚不能判断契约是否满足。' });
224
+ checkState('appearance', { state: 'error', label: '无法对比', detail: '两侧需要同一场景、同一机位的有效结果。' });
225
+ message(error.message); console.error(error);
226
+ }
227
+ async function load() {
228
+ if (busy) return;
229
+ if (!assetId) { failed(new Error('当前任务未指定场景。请由建模任务传入 asset_id 后打开视窗。')); return; }
230
+ busy = true; $('reload').disabled = true; $('camera').disabled = true; $('target-engine').disabled = true; $('comparison').hidden = true; $('loading').hidden = false; $('loading').classList.remove('error');
231
+ $('engine-name').textContent = engineName(); $('godot-image').hidden = true; $('godot-pending').hidden = targetEngine !== 'godot';
232
+ document.title = `场景对齐 · Blender / ${engineName()}`;
233
+ document.querySelector('.viewer').setAttribute('aria-label', `当前场景的 Blender 与 ${engineName()} 对比`);
234
+ $('divider').setAttribute('aria-label', `Blender 与 ${engineName()} 对比滑条`);
235
+ $('engine-view').style.visibility = targetEngine === 'godot' ? 'hidden' : '';
236
+ $('engine-render-label').textContent = targetEngine === 'godot' ? '等待原生渲染' : '当前引擎渲染';
237
+ invalidatePreview(); ++cameraEpoch; interacting = false; $('comparison').classList.remove('orbiting');
238
+ $('revision').textContent = '';
239
+ view?.dispose(); view = null; check = null; $('title').textContent = assetId === 'windworn_sanctuary' ? '风蚀圣域' : assetId; $('asset-id').textContent = assetId;
240
+ for (const kind of ['spatial', 'structural', 'appearance']) checkState(kind, { state: '', label: '检查中', detail: '读取当前资产契约…' });
241
+ message('检查当前场景的模型、资产契约与显示依赖…');
242
+ try {
243
+ const response = await fetch(`/api/packages/${encodeURIComponent(assetId)}/review`, { method: 'POST' });
244
+ const result = await response.json(); if (!response.ok || !result.ok) throw new Error(result.error || 'Scene validation failed.'); check = result;
245
+ if (!check.technical_ok) throw new Error(`模型格式存在 ${check.report.issues.numErrors} 个错误,请在建模任务中修复后重新导入。`);
246
+ const contract = check.contract;
247
+ const reference = contract?.source_of_truth?.reference_camera || 'reference';
248
+ cameraChoices = (contract?.cameras || []).map(camera => ({ camera, label: camera.label || (camera.id === reference ? '参考机位' : camera.id === 'overview' ? '全景机位' : camera.id),
249
+ file: camera.reference_render || (camera.id === reference ? contract.source_of_truth?.reference_render || 'preview/blender_reference.png' : camera.id === 'overview' ? 'preview/blender_overview.png' : null) })).filter(c => c.file && check.files[c.file]);
250
+ hasReference = cameraChoices.length > 0;
251
+ if (!hasReference) cameraChoices = [{camera:null,label:'模型预览',file:null}];
252
+ $('divider').hidden = !hasReference;
253
+ $('blender-view').hidden = !hasReference;
254
+ document.querySelector('.frame-note').textContent = hasReference ? '同机位 · 同画幅' : '目标引擎预览';
255
+ $('camera').replaceChildren(...cameraChoices.map((choice, i) => { const o = document.createElement('option'); o.value = String(i); o.textContent = choice.label; return o; }));
256
+ const free = document.createElement('option'); free.value = 'free'; free.textContent = '自由视角'; free.disabled = true; $('camera').append(free);
257
+ aspect = cameraChoices[0].camera?.aspect_ratio || 1.6; fit();
258
+ $('comparison').hidden = false; $('comparison').style.visibility = 'hidden';
259
+ view = await createReviewView({ container: $('engine-view'), assetBase: `${apiBase}/files/`, asset: check.asset, contract, files: check.files, profile: check.profiles?.three_js,
260
+ interactive: false, controlElement: $('comparison'), onCameraChange: cameraChanged,
261
+ onCameraStart: () => { interacting = true; $('comparison').classList.add('orbiting'); clearTimeout(renderTimer); },
262
+ onCameraEnd: () => { interacting = false; $('comparison').classList.remove('orbiting'); schedulePreview(); }, onProgress: message });
263
+ const recipe = { translation_metres: view.root.position.toArray(), rotation_quaternion_xyzw: view.root.quaternion.toArray(), scale_xyz: view.root.scale.toArray() };
264
+ checkState('spatial', compareSpatial(contract, view.stats, recipe));
265
+ checkState('structural', compareStructural(contract, view.stats));
266
+ renderAdaptation();
267
+ $('revision').textContent = `资产 ${check.files['model.glb'].sha256.slice(0, 10)}${contract ? ` · 契约 r${contract.contract_revision || 1}` : ''}`;
268
+ $('comparison').style.visibility = ''; split(50); await setCamera(0);
269
+ } catch (error) { failed(error); }
270
+ finally { busy = false; $('reload').disabled = false; $('target-engine').disabled = false; }
271
+ }
272
+ $('reload').onclick = load;
273
+ $('target-engine').onchange = () => { targetEngine = $('target-engine').value; invalidatePreview(); if (!busy) load(); };
274
+ $('confirm-asset').onclick = async () => {
275
+ if (!stageView || !viewedCapture || busy || interacting) return;
276
+ busy = true; $('confirm-asset').disabled = true; $('reload').disabled = true; $('camera').disabled = true; $('target-engine').disabled = true; view.setInteractive(false);
277
+ try {
278
+ const result = await post('confirmation', { accepted: true, revision: check.revision, stage_view_token: stageView.token, ...viewedCapture });
279
+ check.revision = result.revision;
280
+ $('confirmation-state').textContent = `${engineName()} 当前版本已确认,可以导入`;
281
+ document.querySelector('.confirmation-bar').classList.add('confirmed');
282
+ message('确认已保存。可回到原任务继续导入;修改模型、材质、灯光或显示配置后,必须回 Stage 重新确认。');
283
+ stageView = null; viewedCapture = null;
284
+ } catch (error) { resetConfirmation(); message(error.message); }
285
+ finally { busy = false; $('reload').disabled = false; $('camera').disabled = false; $('target-engine').disabled = false; view.setInteractive(true); }
286
+ };
287
+ $('camera').onchange = () => setCamera(Number($('camera').value)).catch(failed);
288
+ window.addEventListener('pagehide', () => { ++cameraEpoch; invalidatePreview(); resizeObserver.disconnect(); view?.dispose(); if (referenceUrl) URL.revokeObjectURL(referenceUrl); });
289
+ load();
@@ -0,0 +1,63 @@
1
+ /** Viewer UI. Scene appearance is shared with synchronized game projects. */
2
+ import * as THREE from 'three';
3
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
4
+ import { loadCanonicalScene, applySceneCamera } from './scene-runtime.js';
5
+
6
+ export async function createReviewView({ container, assetBase, asset, contract = null, files, presentation, profile = null,
7
+ interactive = true, controlElement, onCameraChange = () => {}, onCameraStart = () => {}, onCameraEnd = () => {}, onProgress = () => {} }) {
8
+ const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
9
+ renderer.setPixelRatio(Math.min(devicePixelRatio, 1.5));
10
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
11
+ renderer.toneMapping = THREE.AgXToneMapping;
12
+ renderer.toneMappingExposure = 1;
13
+ const frame = document.createElement('div');
14
+ frame.className = 'render-frame'; frame.append(renderer.domElement); container.replaceChildren(frame);
15
+ const world = new THREE.Scene(), camera = new THREE.PerspectiveCamera(40, 1.6, .01, 2000);
16
+ const controls = new OrbitControls(camera, controlElement || renderer.domElement);
17
+ controls.enableDamping = false; controls.enabled = interactive;
18
+ let scene, observer, disposed = false;
19
+ try {
20
+ scene = await loadCanonicalScene({ renderer, world, assetBase, asset, contract, files, presentation, profile, onProgress });
21
+ const { root, stats, inventory, mode } = scene;
22
+ const center = new THREE.Box3(new THREE.Vector3().fromArray(stats.bounds.min), new THREE.Vector3().fromArray(stats.bounds.max)).getCenter(new THREE.Vector3());
23
+ const radius = Math.max(...stats.size, .1);
24
+ controls.target.copy(center); camera.position.copy(center).add(new THREE.Vector3(.9, .55, 1.3).multiplyScalar(radius)); camera.lookAt(center);
25
+ camera.near = Math.max(.001, radius / 2000); camera.far = Math.max(100, radius * 20);
26
+ let aspect = presentation?.camera.aspect || contract?.cameras?.[0]?.aspect_ratio || 1.6;
27
+ function render() { if (!disposed) scene.render(camera); }
28
+ function setCamera(value) {
29
+ if (!value) return;
30
+ applySceneCamera(camera, value); controls.target.fromArray(value.target || value.look_at); aspect = camera.aspect; render();
31
+ }
32
+ function getCamera() { return { position: camera.position.toArray(), quaternion: camera.quaternion.toArray(), target: controls.target.toArray(), fov: camera.fov, aspect: camera.aspect, near: camera.near, far: camera.far }; }
33
+ function resize() {
34
+ const width = Math.max(1, Math.min(container.clientWidth, container.clientHeight * aspect));
35
+ frame.style.width = `${width}px`; frame.style.height = `${width / aspect}px`;
36
+ renderer.setSize(width, width / aspect, true); camera.aspect = aspect; camera.updateProjectionMatrix(); render();
37
+ }
38
+ controls.addEventListener('change', () => { render(); onCameraChange(getCamera()); });
39
+ controls.addEventListener('start', onCameraStart); controls.addEventListener('end', onCameraEnd);
40
+ observer = new ResizeObserver(resize); observer.observe(container);
41
+ setCamera(presentation?.camera || contract?.cameras?.find(c => c.id === 'reference') || contract?.cameras?.[0]);
42
+ const initialCamera = getCamera(); resize(); onProgress('编译显示材质…'); await renderer.compileAsync(world, camera); render();
43
+ return {
44
+ root, world, camera, renderer, inventory, mode, stats, setCamera, getCamera, render,
45
+ setInteractive: enabled => { controls.enabled = enabled; }, resetCamera: () => setCamera(initialCamera),
46
+ diagnostic(kind) { scene.diagnostic(kind); render(); },
47
+ focus(index) {
48
+ const object = inventory[index]?.object; if (!object) return;
49
+ const box = new THREE.Box3().setFromObject(object, true), c = box.getCenter(new THREE.Vector3()), r = Math.max(box.getSize(new THREE.Vector3()).length(), 1);
50
+ controls.target.copy(c); camera.position.copy(c).add(new THREE.Vector3(1, .7, 1).multiplyScalar(r)); camera.lookAt(c); render();
51
+ },
52
+ capture() {
53
+ if (scene.diagnosticActive) throw new Error('Switch back to the material view before confirming.'); render();
54
+ return { screenshot: renderer.domElement.toDataURL('image/png'), presentation: { renderer: 'evomeme.review.v1', three_revision: THREE.REVISION, mode, camera: getCamera(),
55
+ recipe: { translation_metres: root.position.toArray(), rotation_quaternion_xyzw: root.quaternion.toArray(), scale_xyz: root.scale.toArray(), algorithm_version: 'evomeme.review.v1' } } };
56
+ }, dispose
57
+ };
58
+ } catch (error) { dispose(); throw error; }
59
+ function dispose() {
60
+ if (disposed) return; disposed = true;
61
+ observer?.disconnect(); controls.dispose(); scene?.dispose(); renderer.dispose(); frame.remove();
62
+ }
63
+ }
@@ -0,0 +1,24 @@
1
+ :root{color-scheme:dark;--bg:#131819;--panel:#1a2022;--line:#303b3d;--ink:#e2e9e5;--muted:#94a39e;--mint:#b6dccc;--orange:#edb080}
2
+ .adaptation-panel{margin-top:14px;border:1px solid var(--line);border-radius:6px;background:var(--panel)}
3
+ .adaptation-panel summary{padding:15px 18px;cursor:pointer;font-weight:550;display:flex;gap:18px;justify-content:space-between;list-style:revert}
4
+ .adaptation-panel summary:focus-visible{outline:2px solid var(--mint);outline-offset:3px}
5
+ #adaptation-summary{font-size:11px;color:var(--orange);font-weight:400}
6
+ .adaptation-scope,.adaptation-footnote{padding:0 18px;font-size:11px;line-height:1.8;color:var(--muted)}
7
+ .adaptation-items{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:var(--line);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
8
+ .adaptation-row{padding:15px 18px;background:var(--panel);min-width:0}
9
+ .adaptation-title{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
10
+ .adaptation-title strong{font-size:12px;font-weight:550;overflow-wrap:anywhere}
11
+ .adaptation-badge{font-size:10px;white-space:nowrap;color:var(--mint)}
12
+ .adaptation-badge.missing{color:#ef9b8d}.adaptation-badge.approximate,.adaptation-badge.unverified{color:var(--orange)}
13
+ .adaptation-row p{font-size:11px;line-height:1.75;margin:9px 0 0;color:var(--ink)}
14
+ .adaptation-row .adaptation-action{color:var(--muted)}
15
+ .adaptation-buttons{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}.adaptation-buttons button{font-size:11px;padding:6px 9px}
16
+ @media(max-width:800px){.adaptation-items{grid-template-columns:1fr}.adaptation-panel summary{align-items:flex-start;flex-direction:column;gap:7px}}
17
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:13px 'Segoe UI','Microsoft YaHei UI',sans-serif}button,select{font:inherit;color:inherit;background:var(--panel);border:1px solid #465250;border-radius:5px}button{padding:9px 14px;cursor:pointer}button:hover{border-color:var(--mint)}button:disabled,select:disabled{opacity:.45;cursor:default}button span{font-size:18px;margin-right:5px}select{padding:6px 29px 6px 10px;max-width:160px}button:focus-visible,select:focus-visible{outline:2px solid var(--mint);outline-offset:4px}[hidden]{display:none!important}
18
+ .comparison-app{width:min(1680px,100%);margin:auto;padding:25px 30px 12px}header{display:flex;align-items:center;gap:24px;padding-bottom:22px}.scene-title{flex:1;min-width:0}.eyebrow{margin:0 0 9px;font-size:10px;font-weight:600;letter-spacing:1.6px;color:var(--muted)}.eyebrow span{color:#586c63;padding:0 10px}h1{font-size:25px;letter-spacing:.3px;margin:0;font-weight:550}.asset-id{margin:7px 0 0;font-size:11px;color:var(--muted)}.task-context{display:flex;align-items:center;gap:8px;font-size:12px}.source-dot,.engine-dot{width:6px;height:6px;border-radius:50%;background:var(--orange)}.engine-dot{background:var(--mint)}.arrow{margin:0 8px;color:#73867e}
19
+ .viewer{border:1px solid var(--line);border-radius:7px;overflow:hidden;background:#0d1213}.view-toolbar{height:45px;display:flex;gap:12px;align-items:center;padding:0 17px;border-bottom:1px solid var(--line);background:var(--panel);font-size:11px;color:var(--muted)}.live-state{color:var(--ink);flex:1;display:flex;align-items:center;gap:8px}.live-state:before{content:'';display:inline-block;width:5px;height:5px;background:var(--mint);border-radius:50%}.frame-note{padding-left:14px;border-left:1px solid var(--line)}.stage{position:relative;height:clamp(290px,calc(100dvh - 335px),870px);min-height:290px;display:grid;place-items:center;overflow:hidden;background:#0c1112}.comparison{position:relative;overflow:hidden;isolation:isolate;touch-action:none;cursor:ew-resize;background:#0c1112}.engine-view{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none}.render-frame canvas{display:block}.blender-view{position:absolute;inset:0;clip-path:inset(0 calc(100% - var(--split)) 0 0);pointer-events:none}.blender-view img{width:100%;height:100%;display:block;object-fit:fill;user-select:none}.source-label,.engine-label{position:absolute;top:15px;padding:9px 12px;border-radius:4px;display:flex;align-items:center;gap:9px;pointer-events:none;background:#13201be3;box-shadow:0 2px 12px #0002;backdrop-filter:blur(10px);font-size:10px}.source-label{left:15px}.engine-label{right:15px}.source-label b{color:#ffd4af}.engine-label b{color:#d4f5e6}.source-label b,.engine-label b{font-size:12px}.source-label span,.engine-label span{color:#c5d2cc}.divider{position:absolute;top:0;bottom:0;left:var(--split);width:38px;transform:translateX(-50%);cursor:ew-resize;touch-action:none;outline:none}.divider:before{content:'';position:absolute;left:18px;width:2px;height:100%;background:#eef6ec;box-shadow:0 0 8px #0007}.grip{position:absolute;top:50%;left:0;transform:translateY(-50%);width:38px;height:43px;border:1px solid #e8f2e3;border-radius:8px;background:#e8f2e3;box-shadow:0 4px 16px #0618105c;display:flex;align-items:center;justify-content:center;gap:6px;color:#30483c;font-size:23px;font-family:Georgia,serif}.grip i{height:15px;border-left:1px solid #b3c4b8}.divider:focus-visible .grip{outline:3px solid #efbc78;outline-offset:4px}.loading{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;color:var(--muted);padding:35px;text-align:center}.loading p{line-height:1.8;overflow-wrap:anywhere}.loader{width:22px;height:22px;border:1px solid #344a3f;border-top-color:var(--mint);border-radius:50%;animation:spin 1s linear infinite}.loading.error .loader{display:none}@keyframes spin{to{transform:rotate(360deg)}}
20
+ .view-caption{height:38px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 17px;background:var(--panel);font-size:10px;color:var(--muted)}.view-caption>span:first-child{display:flex;align-items:center;gap:8px}.split-symbol{display:inline-block;width:11px;height:11px;border:1px solid #78938a;background:linear-gradient(90deg,#a9cabb 50%,transparent 50%)}.contract-strip{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;margin-top:17px;border:1px solid var(--line);border-radius:6px;background:var(--panel)}article{padding:16px 18px;min-width:0}article+article{border-left:1px solid var(--line)}.check-heading{display:flex;align-items:center;gap:9px}.check-number{font-size:10px;color:#687e73;font-variant-numeric:tabular-nums}h2{font-size:13px;font-weight:500;margin:0;flex:1}h2 small{font-size:10px;margin-left:7px;color:var(--muted);font-weight:400}.check-state{font-size:10px;color:var(--muted);white-space:nowrap}.check-state.ok{color:var(--mint)}.check-state.warn{color:#e7c48c}.check-state.error{color:#eda69a}article p{font-size:11px;line-height:1.7;color:var(--muted);margin:11px 0 0}footer{display:flex;justify-content:space-between;gap:12px;align-items:baseline;color:#778a81;font-size:10px}footer p{line-height:1.6;overflow-wrap:anywhere}#revision{white-space:nowrap;font-variant-numeric:tabular-nums}
21
+ @media(min-height:1000px){.comparison-app{padding-top:32px}.stage{height:calc(100dvh - 355px)}}@media(max-width:800px){.comparison-app{padding:18px 16px 10px}.task-context{display:none}header{gap:10px}h1{font-size:22px}.frame-note{display:none}.source-label span,.engine-label span{display:none}.contract-strip{grid-template-columns:1fr}article{padding:12px 16px}article+article{border-left:0;border-top:1px solid var(--line)}article p{margin-top:6px}.view-caption{height:auto;min-height:36px;flex-wrap:wrap;padding:10px 12px}#camera-note{display:none}footer{align-items:flex-start}#revision{display:none}.stage{height:clamp(250px,52dvh,500px);min-height:250px}}
22
+ @media(prefers-reduced-motion:reduce){.loader{animation:none}}
23
+ .confirmation-bar{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-top:14px;padding:14px 18px;border:1px solid var(--line);border-radius:6px;background:var(--panel)}.confirmation-bar strong{font-size:12px;font-weight:550}.confirmation-bar p{color:var(--muted);font-size:11px;line-height:1.7;margin:6px 0 0}.confirmation-bar button{white-space:nowrap;background:#29473d;border-color:#608d78}.native-engine-image{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}.native-engine-pending{position:absolute;inset:0;background:#132021;display:grid;place-content:center;justify-items:center;gap:14px;color:var(--muted);pointer-events:none}.confirmation-bar.confirmed{border-color:#608d78}.stage{height:clamp(290px,calc(100dvh - 440px),870px)}@media(max-width:800px){.task-context{display:flex}.task-context>span,.task-context>label{display:none}.confirmation-bar{align-items:stretch;flex-direction:column;gap:10px}.stage{height:clamp(250px,48dvh,500px)}}
24
+ .comparison{cursor:grab}.comparison.orbiting{cursor:grabbing}.blender-pending{position:absolute;inset:0;background:radial-gradient(ellipse at 25% 50%,#22302e,#131c1d 75%)}.pending-content{position:absolute;left:calc(var(--split)/2);top:50%;transform:translate(-50%,-50%);width:220px;max-width:calc(var(--split) - 24px);display:flex;align-items:center;flex-direction:column;gap:14px;text-align:center;color:var(--muted);font-size:12px;line-height:1.7}.pending-content p{margin:0}.pending-content button{pointer-events:auto;font-size:11px;padding:7px 11px}.blender-pending.error .loader{display:none}.blender-view.pending img{visibility:hidden}
@@ -0,0 +1,47 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>场景对齐 · Blender / Three.js</title>
7
+ <link rel="stylesheet" href="./review.css">
8
+ <script type="importmap">{"imports":{"three":"./vendor/three170/build/three.module.js","three/addons/":"./vendor/three170/examples/jsm/"}}</script>
9
+ </head>
10
+ <body>
11
+ <main class="comparison-app">
12
+ <header>
13
+ <div class="scene-title"><p class="eyebrow">SCENE ALIGNMENT <span>/</span> 场景对齐</p><h1 id="title">载入当前场景</h1><p id="asset-id" class="asset-id"></p></div>
14
+ <div class="task-context"><span class="source-dot"></span>Blender<span class="arrow">→</span><label for="target-engine">目标引擎</label><select id="target-engine"><option value="three_js">Three.js</option><option value="godot">Godot 4</option></select></div>
15
+ <button id="reload" disabled><span aria-hidden="true">↻</span> 重新载入</button>
16
+ </header>
17
+ <section class="viewer" aria-label="当前场景的 Blender 与 Three.js 对比">
18
+ <div class="view-toolbar"><span class="live-state" id="view-state">正在检查场景</span><label for="camera">对比机位</label><select id="camera" disabled><option>参考机位</option></select><span class="frame-note">同机位 · 同画幅</span></div>
19
+ <div id="stage" class="stage">
20
+ <div id="comparison" class="comparison" style="--split:50%" hidden>
21
+ <div id="engine-view" class="engine-view"></div><img id="godot-image" class="native-engine-image" alt="当前机位的 Godot 原生渲染" hidden><div id="godot-pending" class="native-engine-pending" hidden><span class="loader" aria-hidden="true"></span><p id="godot-progress">Godot 正在渲染…</p></div>
22
+ <div id="blender-view" class="blender-view"><img id="blender-image" alt="当前机位的 Blender 原生渲染" draggable="false"><div id="blender-pending" class="blender-pending" hidden><div class="pending-content"><span class="loader" aria-hidden="true"></span><p id="blender-progress" role="status">正在更新当前视角…</p><button id="retry-preview" hidden>重试当前视角</button></div></div></div>
23
+ <div class="source-label"><b>Blender</b><span id="blender-label">Cycles · 原生渲染</span></div>
24
+ <div class="engine-label"><b id="engine-name">Three.js</b><span id="engine-render-label">当前引擎渲染</span></div>
25
+ <div id="divider" class="divider" role="slider" tabindex="0" aria-label="Blender 与 Three.js 对比滑条" aria-valuemin="0" aria-valuemax="100" aria-valuenow="50" aria-valuetext="左侧 Blender 50%,右侧 Three.js 50%" aria-orientation="horizontal"><span class="grip" aria-hidden="true">‹<i></i>›</span></div>
26
+ </div>
27
+ <div class="loading" id="loading"><span class="loader" aria-hidden="true"></span><p id="loading-text">读取当前任务的场景与资产契约…</p></div>
28
+ </div>
29
+ <div class="view-caption"><span>左键旋转 · 右键平移 · 滚轮缩放 · 拖动分隔线对比</span><span id="camera-note">共享机位 · Blender 停止拖动后更新</span></div>
30
+ </section>
31
+ <details id="adaptation-panel" class="adaptation-panel" hidden>
32
+ <summary><span>效果转换与待核对项</span><span id="adaptation-summary"></span></summary>
33
+ <p id="adaptation-scope" class="adaptation-scope"></p>
34
+ <div id="adaptation-items" class="adaptation-items"></div>
35
+ <p class="adaptation-footnote">这些记录说明转换方式;是否接受当前视觉效果,由你查看两侧画面后决定。</p>
36
+ </details>
37
+ <section class="contract-strip" aria-label="资产契约对齐状态">
38
+ <article><div class="check-heading"><span class="check-number">01</span><h2>空间 <small>Spatial</small></h2><span id="spatial-state" class="check-state">检查中</span></div><p id="spatial-detail">位置、旋转、尺寸与原点</p></article>
39
+ <article><div class="check-heading"><span class="check-number">02</span><h2>结构 <small>Structural</small></h2><span id="structural-state" class="check-state">检查中</span></div><p id="structural-detail">场景节点、网格与材质关联</p></article>
40
+ <article><div class="check-heading"><span class="check-number">03</span><h2>视觉 <small>Appearance</small></h2><span id="appearance-state" class="check-state">检查中</span></div><p id="appearance-detail">材质与显示配方、实际渲染差异</p></article>
41
+ </section>
42
+ <section class="confirmation-bar" aria-label="导入前确认"><div><strong id="confirmation-state">导入前必须在 Stage 确认</strong><p id="confirmation-note">Blender 建模完成后,先查看空间、结构、材质和灯光效果,再确认当前版本。修改后需重新确认。</p></div><button id="confirm-asset" disabled>确认当前版本,允许导入</button></section>
43
+ <footer><p id="status" role="status" aria-live="polite">仅检查当前任务的场景。</p><span id="revision"></span></footer>
44
+ </main>
45
+ <script type="module" src="./review-app.js"></script>
46
+ </body>
47
+ </html>