agentshire 1.0.0

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 (406) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +437 -0
  4. package/README.zh-CN.md +441 -0
  5. package/ROADMAP.md +207 -0
  6. package/THIRD_PARTY_NOTICES.md +49 -0
  7. package/VISION.md +101 -0
  8. package/index.ts +386 -0
  9. package/openclaw.plugin.json +27 -0
  10. package/package.json +100 -0
  11. package/src/bridge/AGENTS.md +136 -0
  12. package/src/bridge/ActivityStream.ts +184 -0
  13. package/src/bridge/CitizenManager.ts +280 -0
  14. package/src/bridge/DirectorBridge.ts +1076 -0
  15. package/src/bridge/EventTranslator.ts +142 -0
  16. package/src/bridge/NpcEventQueue.ts +61 -0
  17. package/src/bridge/ReconnectManager.ts +42 -0
  18. package/src/bridge/RouteManager.ts +270 -0
  19. package/src/bridge/StateTracker.ts +100 -0
  20. package/src/bridge/ToolVfxMapper.ts +94 -0
  21. package/src/bridge/__tests__/ActivityStream.test.ts +156 -0
  22. package/src/bridge/__tests__/CitizenManager.test.ts +174 -0
  23. package/src/bridge/__tests__/EventTranslator.test.ts +292 -0
  24. package/src/bridge/__tests__/NpcEventQueue.test.ts +92 -0
  25. package/src/bridge/__tests__/RouteManager.test.ts +106 -0
  26. package/src/bridge/data/route-config.json +36 -0
  27. package/src/bridge/data/route-config.ts +19 -0
  28. package/src/bridge/implicit-chat.ts +192 -0
  29. package/src/bridge/index.ts +12 -0
  30. package/src/contracts/agent-state.ts +87 -0
  31. package/src/contracts/agui.ts +212 -0
  32. package/src/contracts/chat.ts +67 -0
  33. package/src/contracts/events.ts +170 -0
  34. package/src/contracts/index.ts +71 -0
  35. package/src/contracts/media.ts +77 -0
  36. package/src/contracts/registry.ts +32 -0
  37. package/src/plugin/AGENTS.md +192 -0
  38. package/src/plugin/__tests__/editor-serve-megapack.test.ts +150 -0
  39. package/src/plugin/__tests__/hook-translator.test.ts +197 -0
  40. package/src/plugin/auto-config.ts +131 -0
  41. package/src/plugin/channel.ts +399 -0
  42. package/src/plugin/chat-asset-resolver.ts +75 -0
  43. package/src/plugin/chat-session-watcher.ts +98 -0
  44. package/src/plugin/citizen-agent-manager.ts +178 -0
  45. package/src/plugin/citizen-chat-router.ts +83 -0
  46. package/src/plugin/citizen-workshop-manager.ts +96 -0
  47. package/src/plugin/custom-asset-manager.ts +250 -0
  48. package/src/plugin/editor-serve.ts +1368 -0
  49. package/src/plugin/group-discussion.ts +223 -0
  50. package/src/plugin/hook-translator.ts +202 -0
  51. package/src/plugin/llm-agent-proxy.ts +193 -0
  52. package/src/plugin/outbound-adapter.ts +187 -0
  53. package/src/plugin/paths.ts +33 -0
  54. package/src/plugin/plan-manager.ts +594 -0
  55. package/src/plugin/runtime.ts +9 -0
  56. package/src/plugin/session-history.ts +838 -0
  57. package/src/plugin/session-log-watcher.ts +221 -0
  58. package/src/plugin/soul-prompt-template.ts +290 -0
  59. package/src/plugin/subagent-tracker.ts +205 -0
  60. package/src/plugin/tools.ts +493 -0
  61. package/src/plugin/town-session.ts +40 -0
  62. package/src/plugin/ws-server.ts +680 -0
  63. package/src/town-souls.ts +134 -0
  64. package/town-frontend/dist/assets/CustomAssetStore-oi8aIurt.js +2 -0
  65. package/town-frontend/dist/assets/GLTFLoader-BA5RqSME.js +3804 -0
  66. package/town-frontend/dist/assets/OrbitControls-ZmySp9sQ.js +2 -0
  67. package/town-frontend/dist/assets/SettingsPanel-BO52reJe.js +2 -0
  68. package/town-frontend/dist/assets/SkeletonUtils-BCVmgslc.js +2 -0
  69. package/town-frontend/dist/assets/SkillLearnCard-Dk38iDpy.js +6 -0
  70. package/town-frontend/dist/assets/TopicSetupPanel-DLaLHB_Z.js +2 -0
  71. package/town-frontend/dist/assets/Trap-Bold-CT0JBE39.woff2 +0 -0
  72. package/town-frontend/dist/assets/Trap-SemiBold-R4_-Ld0j.woff2 +0 -0
  73. package/town-frontend/dist/assets/WeatherSystem-Cb3BvHes.js +550 -0
  74. package/town-frontend/dist/assets/avatars/char-female-a.webp +0 -0
  75. package/town-frontend/dist/assets/avatars/char-female-b.webp +0 -0
  76. package/town-frontend/dist/assets/avatars/char-female-c.webp +0 -0
  77. package/town-frontend/dist/assets/avatars/char-female-d.webp +0 -0
  78. package/town-frontend/dist/assets/avatars/char-female-e.webp +0 -0
  79. package/town-frontend/dist/assets/avatars/char-female-f.webp +0 -0
  80. package/town-frontend/dist/assets/avatars/char-male-a.webp +0 -0
  81. package/town-frontend/dist/assets/avatars/char-male-b.webp +0 -0
  82. package/town-frontend/dist/assets/avatars/char-male-c.webp +0 -0
  83. package/town-frontend/dist/assets/avatars/char-male-d.webp +0 -0
  84. package/town-frontend/dist/assets/avatars/char-male-e.webp +0 -0
  85. package/town-frontend/dist/assets/avatars/char-male-f.webp +0 -0
  86. package/town-frontend/dist/assets/avatars/char-pet-beaver.webp +0 -0
  87. package/town-frontend/dist/assets/avatars/char-pet-bee.webp +0 -0
  88. package/town-frontend/dist/assets/avatars/char-pet-bunny.webp +0 -0
  89. package/town-frontend/dist/assets/avatars/char-pet-cat.webp +0 -0
  90. package/town-frontend/dist/assets/avatars/char-pet-caterpillar.webp +0 -0
  91. package/town-frontend/dist/assets/avatars/char-pet-chick.webp +0 -0
  92. package/town-frontend/dist/assets/avatars/char-pet-cow.webp +0 -0
  93. package/town-frontend/dist/assets/avatars/char-pet-crab.webp +0 -0
  94. package/town-frontend/dist/assets/avatars/char-pet-deer.webp +0 -0
  95. package/town-frontend/dist/assets/avatars/char-pet-dog.webp +0 -0
  96. package/town-frontend/dist/assets/avatars/char-pet-elephant.webp +0 -0
  97. package/town-frontend/dist/assets/avatars/char-pet-fish.webp +0 -0
  98. package/town-frontend/dist/assets/avatars/char-pet-fox.webp +0 -0
  99. package/town-frontend/dist/assets/avatars/char-pet-giraffe.webp +0 -0
  100. package/town-frontend/dist/assets/avatars/char-pet-hog.webp +0 -0
  101. package/town-frontend/dist/assets/avatars/char-pet-koala.webp +0 -0
  102. package/town-frontend/dist/assets/avatars/char-pet-lion.webp +0 -0
  103. package/town-frontend/dist/assets/avatars/char-pet-monkey.webp +0 -0
  104. package/town-frontend/dist/assets/avatars/char-pet-panda.webp +0 -0
  105. package/town-frontend/dist/assets/avatars/char-pet-parrot.webp +0 -0
  106. package/town-frontend/dist/assets/avatars/char-pet-penguin.webp +0 -0
  107. package/town-frontend/dist/assets/avatars/char-pet-pig.webp +0 -0
  108. package/town-frontend/dist/assets/avatars/char-pet-polar.webp +0 -0
  109. package/town-frontend/dist/assets/avatars/char-pet-tiger.webp +0 -0
  110. package/town-frontend/dist/assets/character-catalog-DSmLtlNC.js +2 -0
  111. package/town-frontend/dist/assets/citizenEditor-DubGSJOQ.js +296 -0
  112. package/town-frontend/dist/assets/command-parser-BUd15Bmv.js +12 -0
  113. package/town-frontend/dist/assets/editor-B5QO0OtX.js +258 -0
  114. package/town-frontend/dist/assets/editor-Bk8g1NCD.css +1 -0
  115. package/town-frontend/dist/assets/index-BWfrufil.js +2 -0
  116. package/town-frontend/dist/assets/index-faS20RJk.js +6 -0
  117. package/town-frontend/dist/assets/logo-DJI6EtST.png +0 -0
  118. package/town-frontend/dist/assets/logo-title-AdKPZX5E.png +0 -0
  119. package/town-frontend/dist/assets/main-CqsN43aT.js +224 -0
  120. package/town-frontend/dist/assets/main-D7neuy3w.css +1 -0
  121. package/town-frontend/dist/assets/models/buildings/base.bin +0 -0
  122. package/town-frontend/dist/assets/models/buildings/base.gltf +136 -0
  123. package/town-frontend/dist/assets/models/buildings/bench.bin +0 -0
  124. package/town-frontend/dist/assets/models/buildings/bench.gltf +136 -0
  125. package/town-frontend/dist/assets/models/buildings/box_A.bin +0 -0
  126. package/town-frontend/dist/assets/models/buildings/box_A.gltf +136 -0
  127. package/town-frontend/dist/assets/models/buildings/box_B.bin +0 -0
  128. package/town-frontend/dist/assets/models/buildings/box_B.gltf +136 -0
  129. package/town-frontend/dist/assets/models/buildings/building_A.bin +0 -0
  130. package/town-frontend/dist/assets/models/buildings/building_A.gltf +136 -0
  131. package/town-frontend/dist/assets/models/buildings/building_A_withoutBase.bin +0 -0
  132. package/town-frontend/dist/assets/models/buildings/building_A_withoutBase.gltf +136 -0
  133. package/town-frontend/dist/assets/models/buildings/building_B.bin +0 -0
  134. package/town-frontend/dist/assets/models/buildings/building_B.gltf +136 -0
  135. package/town-frontend/dist/assets/models/buildings/building_B_withoutBase.bin +0 -0
  136. package/town-frontend/dist/assets/models/buildings/building_B_withoutBase.gltf +136 -0
  137. package/town-frontend/dist/assets/models/buildings/building_C.bin +0 -0
  138. package/town-frontend/dist/assets/models/buildings/building_C.gltf +136 -0
  139. package/town-frontend/dist/assets/models/buildings/building_C_withoutBase.bin +0 -0
  140. package/town-frontend/dist/assets/models/buildings/building_C_withoutBase.gltf +136 -0
  141. package/town-frontend/dist/assets/models/buildings/building_D.bin +0 -0
  142. package/town-frontend/dist/assets/models/buildings/building_D.gltf +136 -0
  143. package/town-frontend/dist/assets/models/buildings/building_D_withoutBase.bin +0 -0
  144. package/town-frontend/dist/assets/models/buildings/building_D_withoutBase.gltf +136 -0
  145. package/town-frontend/dist/assets/models/buildings/building_E.bin +0 -0
  146. package/town-frontend/dist/assets/models/buildings/building_E.gltf +136 -0
  147. package/town-frontend/dist/assets/models/buildings/building_E_withoutBase.bin +0 -0
  148. package/town-frontend/dist/assets/models/buildings/building_E_withoutBase.gltf +136 -0
  149. package/town-frontend/dist/assets/models/buildings/building_F.bin +0 -0
  150. package/town-frontend/dist/assets/models/buildings/building_F.gltf +136 -0
  151. package/town-frontend/dist/assets/models/buildings/building_F_withoutBase.bin +0 -0
  152. package/town-frontend/dist/assets/models/buildings/building_F_withoutBase.gltf +136 -0
  153. package/town-frontend/dist/assets/models/buildings/building_G.bin +0 -0
  154. package/town-frontend/dist/assets/models/buildings/building_G.gltf +136 -0
  155. package/town-frontend/dist/assets/models/buildings/building_G_withoutBase.bin +0 -0
  156. package/town-frontend/dist/assets/models/buildings/building_G_withoutBase.gltf +136 -0
  157. package/town-frontend/dist/assets/models/buildings/building_H.bin +0 -0
  158. package/town-frontend/dist/assets/models/buildings/building_H.gltf +136 -0
  159. package/town-frontend/dist/assets/models/buildings/building_H_withoutBase.bin +0 -0
  160. package/town-frontend/dist/assets/models/buildings/building_H_withoutBase.gltf +136 -0
  161. package/town-frontend/dist/assets/models/buildings/bush.bin +0 -0
  162. package/town-frontend/dist/assets/models/buildings/bush.gltf +136 -0
  163. package/town-frontend/dist/assets/models/buildings/car_hatchback.bin +0 -0
  164. package/town-frontend/dist/assets/models/buildings/car_hatchback.gltf +442 -0
  165. package/town-frontend/dist/assets/models/buildings/car_police.bin +0 -0
  166. package/town-frontend/dist/assets/models/buildings/car_police.gltf +442 -0
  167. package/town-frontend/dist/assets/models/buildings/car_sedan.bin +0 -0
  168. package/town-frontend/dist/assets/models/buildings/car_sedan.gltf +442 -0
  169. package/town-frontend/dist/assets/models/buildings/car_stationwagon.bin +0 -0
  170. package/town-frontend/dist/assets/models/buildings/car_stationwagon.gltf +442 -0
  171. package/town-frontend/dist/assets/models/buildings/car_taxi.bin +0 -0
  172. package/town-frontend/dist/assets/models/buildings/car_taxi.gltf +442 -0
  173. package/town-frontend/dist/assets/models/buildings/citybits_texture.png +0 -0
  174. package/town-frontend/dist/assets/models/buildings/dumpster.bin +0 -0
  175. package/town-frontend/dist/assets/models/buildings/dumpster.gltf +136 -0
  176. package/town-frontend/dist/assets/models/buildings/firehydrant.bin +0 -0
  177. package/town-frontend/dist/assets/models/buildings/firehydrant.gltf +136 -0
  178. package/town-frontend/dist/assets/models/buildings/road_corner.bin +0 -0
  179. package/town-frontend/dist/assets/models/buildings/road_corner.gltf +136 -0
  180. package/town-frontend/dist/assets/models/buildings/road_corner_curved.bin +0 -0
  181. package/town-frontend/dist/assets/models/buildings/road_corner_curved.gltf +136 -0
  182. package/town-frontend/dist/assets/models/buildings/road_junction.bin +0 -0
  183. package/town-frontend/dist/assets/models/buildings/road_junction.gltf +136 -0
  184. package/town-frontend/dist/assets/models/buildings/road_straight.bin +0 -0
  185. package/town-frontend/dist/assets/models/buildings/road_straight.gltf +136 -0
  186. package/town-frontend/dist/assets/models/buildings/road_straight_crossing.bin +0 -0
  187. package/town-frontend/dist/assets/models/buildings/road_straight_crossing.gltf +136 -0
  188. package/town-frontend/dist/assets/models/buildings/road_tsplit.bin +0 -0
  189. package/town-frontend/dist/assets/models/buildings/road_tsplit.gltf +136 -0
  190. package/town-frontend/dist/assets/models/buildings/streetlight.bin +0 -0
  191. package/town-frontend/dist/assets/models/buildings/streetlight.gltf +136 -0
  192. package/town-frontend/dist/assets/models/buildings/trafficlight_A.bin +0 -0
  193. package/town-frontend/dist/assets/models/buildings/trafficlight_A.gltf +136 -0
  194. package/town-frontend/dist/assets/models/buildings/trafficlight_B.bin +0 -0
  195. package/town-frontend/dist/assets/models/buildings/trafficlight_B.gltf +136 -0
  196. package/town-frontend/dist/assets/models/buildings/trafficlight_C.bin +0 -0
  197. package/town-frontend/dist/assets/models/buildings/trafficlight_C.gltf +136 -0
  198. package/town-frontend/dist/assets/models/buildings/trash_A.bin +0 -0
  199. package/town-frontend/dist/assets/models/buildings/trash_A.gltf +136 -0
  200. package/town-frontend/dist/assets/models/buildings/trash_B.bin +0 -0
  201. package/town-frontend/dist/assets/models/buildings/trash_B.gltf +136 -0
  202. package/town-frontend/dist/assets/models/buildings/watertower.bin +0 -0
  203. package/town-frontend/dist/assets/models/buildings/watertower.gltf +136 -0
  204. package/town-frontend/dist/assets/models/characters/Textures/colormap.png +0 -0
  205. package/town-frontend/dist/assets/models/characters/character-female-a.glb +0 -0
  206. package/town-frontend/dist/assets/models/characters/character-female-b.glb +0 -0
  207. package/town-frontend/dist/assets/models/characters/character-female-c.glb +0 -0
  208. package/town-frontend/dist/assets/models/characters/character-female-d.glb +0 -0
  209. package/town-frontend/dist/assets/models/characters/character-female-e.glb +0 -0
  210. package/town-frontend/dist/assets/models/characters/character-female-f.glb +0 -0
  211. package/town-frontend/dist/assets/models/characters/character-male-a.glb +0 -0
  212. package/town-frontend/dist/assets/models/characters/character-male-b.glb +0 -0
  213. package/town-frontend/dist/assets/models/characters/character-male-c.glb +0 -0
  214. package/town-frontend/dist/assets/models/characters/character-male-d.glb +0 -0
  215. package/town-frontend/dist/assets/models/characters/character-male-e.glb +0 -0
  216. package/town-frontend/dist/assets/models/characters/character-male-f.glb +0 -0
  217. package/town-frontend/dist/assets/models/characters/character-pet-beaver.glb +0 -0
  218. package/town-frontend/dist/assets/models/characters/character-pet-bee.glb +0 -0
  219. package/town-frontend/dist/assets/models/characters/character-pet-bunny.glb +0 -0
  220. package/town-frontend/dist/assets/models/characters/character-pet-cat.glb +0 -0
  221. package/town-frontend/dist/assets/models/characters/character-pet-caterpillar.glb +0 -0
  222. package/town-frontend/dist/assets/models/characters/character-pet-chick.glb +0 -0
  223. package/town-frontend/dist/assets/models/characters/character-pet-cow.glb +0 -0
  224. package/town-frontend/dist/assets/models/characters/character-pet-crab.glb +0 -0
  225. package/town-frontend/dist/assets/models/characters/character-pet-deer.glb +0 -0
  226. package/town-frontend/dist/assets/models/characters/character-pet-dog.glb +0 -0
  227. package/town-frontend/dist/assets/models/characters/character-pet-elephant.glb +0 -0
  228. package/town-frontend/dist/assets/models/characters/character-pet-fish.glb +0 -0
  229. package/town-frontend/dist/assets/models/characters/character-pet-fox.glb +0 -0
  230. package/town-frontend/dist/assets/models/characters/character-pet-giraffe.glb +0 -0
  231. package/town-frontend/dist/assets/models/characters/character-pet-hog.glb +0 -0
  232. package/town-frontend/dist/assets/models/characters/character-pet-koala.glb +0 -0
  233. package/town-frontend/dist/assets/models/characters/character-pet-lion.glb +0 -0
  234. package/town-frontend/dist/assets/models/characters/character-pet-monkey.glb +0 -0
  235. package/town-frontend/dist/assets/models/characters/character-pet-panda.glb +0 -0
  236. package/town-frontend/dist/assets/models/characters/character-pet-parrot.glb +0 -0
  237. package/town-frontend/dist/assets/models/characters/character-pet-penguin.glb +0 -0
  238. package/town-frontend/dist/assets/models/characters/character-pet-pig.glb +0 -0
  239. package/town-frontend/dist/assets/models/characters/character-pet-polar.glb +0 -0
  240. package/town-frontend/dist/assets/models/characters/character-pet-tiger.glb +0 -0
  241. package/town-frontend/dist/assets/models/characters/colormap.png +0 -0
  242. package/town-frontend/dist/assets/models/furniture/armchair.bin +0 -0
  243. package/town-frontend/dist/assets/models/furniture/armchair.gltf +136 -0
  244. package/town-frontend/dist/assets/models/furniture/armchair_pillows.bin +0 -0
  245. package/town-frontend/dist/assets/models/furniture/armchair_pillows.gltf +136 -0
  246. package/town-frontend/dist/assets/models/furniture/bed_double_A.bin +0 -0
  247. package/town-frontend/dist/assets/models/furniture/bed_double_A.gltf +136 -0
  248. package/town-frontend/dist/assets/models/furniture/bed_double_B.bin +0 -0
  249. package/town-frontend/dist/assets/models/furniture/bed_double_B.gltf +136 -0
  250. package/town-frontend/dist/assets/models/furniture/bed_single_A.bin +0 -0
  251. package/town-frontend/dist/assets/models/furniture/bed_single_A.gltf +136 -0
  252. package/town-frontend/dist/assets/models/furniture/bed_single_B.bin +0 -0
  253. package/town-frontend/dist/assets/models/furniture/bed_single_B.gltf +136 -0
  254. package/town-frontend/dist/assets/models/furniture/book_set.bin +0 -0
  255. package/town-frontend/dist/assets/models/furniture/book_set.gltf +136 -0
  256. package/town-frontend/dist/assets/models/furniture/book_single.bin +0 -0
  257. package/town-frontend/dist/assets/models/furniture/book_single.gltf +136 -0
  258. package/town-frontend/dist/assets/models/furniture/cabinet_medium.bin +0 -0
  259. package/town-frontend/dist/assets/models/furniture/cabinet_medium.gltf +136 -0
  260. package/town-frontend/dist/assets/models/furniture/cabinet_medium_decorated.bin +0 -0
  261. package/town-frontend/dist/assets/models/furniture/cabinet_medium_decorated.gltf +136 -0
  262. package/town-frontend/dist/assets/models/furniture/cabinet_small.bin +0 -0
  263. package/town-frontend/dist/assets/models/furniture/cabinet_small.gltf +136 -0
  264. package/town-frontend/dist/assets/models/furniture/cabinet_small_decorated.bin +0 -0
  265. package/town-frontend/dist/assets/models/furniture/cabinet_small_decorated.gltf +136 -0
  266. package/town-frontend/dist/assets/models/furniture/cactus_medium_A.bin +0 -0
  267. package/town-frontend/dist/assets/models/furniture/cactus_medium_A.gltf +136 -0
  268. package/town-frontend/dist/assets/models/furniture/cactus_medium_B.bin +0 -0
  269. package/town-frontend/dist/assets/models/furniture/cactus_medium_B.gltf +136 -0
  270. package/town-frontend/dist/assets/models/furniture/cactus_small_A.bin +0 -0
  271. package/town-frontend/dist/assets/models/furniture/cactus_small_A.gltf +136 -0
  272. package/town-frontend/dist/assets/models/furniture/cactus_small_B.bin +0 -0
  273. package/town-frontend/dist/assets/models/furniture/cactus_small_B.gltf +136 -0
  274. package/town-frontend/dist/assets/models/furniture/chair_A.bin +0 -0
  275. package/town-frontend/dist/assets/models/furniture/chair_A.gltf +136 -0
  276. package/town-frontend/dist/assets/models/furniture/chair_A_wood.bin +0 -0
  277. package/town-frontend/dist/assets/models/furniture/chair_A_wood.gltf +136 -0
  278. package/town-frontend/dist/assets/models/furniture/chair_B.bin +0 -0
  279. package/town-frontend/dist/assets/models/furniture/chair_B.gltf +136 -0
  280. package/town-frontend/dist/assets/models/furniture/chair_B_wood.bin +0 -0
  281. package/town-frontend/dist/assets/models/furniture/chair_B_wood.gltf +136 -0
  282. package/town-frontend/dist/assets/models/furniture/chair_C.bin +0 -0
  283. package/town-frontend/dist/assets/models/furniture/chair_C.gltf +136 -0
  284. package/town-frontend/dist/assets/models/furniture/chair_stool.bin +0 -0
  285. package/town-frontend/dist/assets/models/furniture/chair_stool.gltf +136 -0
  286. package/town-frontend/dist/assets/models/furniture/chair_stool_wood.bin +0 -0
  287. package/town-frontend/dist/assets/models/furniture/chair_stool_wood.gltf +136 -0
  288. package/town-frontend/dist/assets/models/furniture/couch.bin +0 -0
  289. package/town-frontend/dist/assets/models/furniture/couch.gltf +136 -0
  290. package/town-frontend/dist/assets/models/furniture/couch_pillows.bin +0 -0
  291. package/town-frontend/dist/assets/models/furniture/couch_pillows.gltf +136 -0
  292. package/town-frontend/dist/assets/models/furniture/furniturebits_texture.png +0 -0
  293. package/town-frontend/dist/assets/models/furniture/lamp_standing.bin +0 -0
  294. package/town-frontend/dist/assets/models/furniture/lamp_standing.gltf +136 -0
  295. package/town-frontend/dist/assets/models/furniture/lamp_table.bin +0 -0
  296. package/town-frontend/dist/assets/models/furniture/lamp_table.gltf +136 -0
  297. package/town-frontend/dist/assets/models/furniture/pictureframe_large_A.bin +0 -0
  298. package/town-frontend/dist/assets/models/furniture/pictureframe_large_A.gltf +136 -0
  299. package/town-frontend/dist/assets/models/furniture/pictureframe_large_B.bin +0 -0
  300. package/town-frontend/dist/assets/models/furniture/pictureframe_large_B.gltf +136 -0
  301. package/town-frontend/dist/assets/models/furniture/pictureframe_medium.bin +0 -0
  302. package/town-frontend/dist/assets/models/furniture/pictureframe_medium.gltf +136 -0
  303. package/town-frontend/dist/assets/models/furniture/pictureframe_small_A.bin +0 -0
  304. package/town-frontend/dist/assets/models/furniture/pictureframe_small_A.gltf +136 -0
  305. package/town-frontend/dist/assets/models/furniture/pictureframe_small_B.bin +0 -0
  306. package/town-frontend/dist/assets/models/furniture/pictureframe_small_B.gltf +136 -0
  307. package/town-frontend/dist/assets/models/furniture/pictureframe_small_C.bin +0 -0
  308. package/town-frontend/dist/assets/models/furniture/pictureframe_small_C.gltf +136 -0
  309. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_A.bin +0 -0
  310. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_A.gltf +136 -0
  311. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_B.bin +0 -0
  312. package/town-frontend/dist/assets/models/furniture/pictureframe_standing_B.gltf +136 -0
  313. package/town-frontend/dist/assets/models/furniture/pillow_A.bin +0 -0
  314. package/town-frontend/dist/assets/models/furniture/pillow_A.gltf +136 -0
  315. package/town-frontend/dist/assets/models/furniture/pillow_B.bin +0 -0
  316. package/town-frontend/dist/assets/models/furniture/pillow_B.gltf +136 -0
  317. package/town-frontend/dist/assets/models/furniture/rug_oval_A.bin +0 -0
  318. package/town-frontend/dist/assets/models/furniture/rug_oval_A.gltf +136 -0
  319. package/town-frontend/dist/assets/models/furniture/rug_oval_B.bin +0 -0
  320. package/town-frontend/dist/assets/models/furniture/rug_oval_B.gltf +136 -0
  321. package/town-frontend/dist/assets/models/furniture/rug_rectangle_A.bin +0 -0
  322. package/town-frontend/dist/assets/models/furniture/rug_rectangle_A.gltf +136 -0
  323. package/town-frontend/dist/assets/models/furniture/rug_rectangle_B.bin +0 -0
  324. package/town-frontend/dist/assets/models/furniture/rug_rectangle_B.gltf +136 -0
  325. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_A.bin +0 -0
  326. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_A.gltf +136 -0
  327. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_B.bin +0 -0
  328. package/town-frontend/dist/assets/models/furniture/rug_rectangle_stripes_B.gltf +136 -0
  329. package/town-frontend/dist/assets/models/furniture/shelf_A_big.bin +0 -0
  330. package/town-frontend/dist/assets/models/furniture/shelf_A_big.gltf +136 -0
  331. package/town-frontend/dist/assets/models/furniture/shelf_A_small.bin +0 -0
  332. package/town-frontend/dist/assets/models/furniture/shelf_A_small.gltf +136 -0
  333. package/town-frontend/dist/assets/models/furniture/shelf_B_large.bin +0 -0
  334. package/town-frontend/dist/assets/models/furniture/shelf_B_large.gltf +136 -0
  335. package/town-frontend/dist/assets/models/furniture/shelf_B_large_decorated.bin +0 -0
  336. package/town-frontend/dist/assets/models/furniture/shelf_B_large_decorated.gltf +136 -0
  337. package/town-frontend/dist/assets/models/furniture/shelf_B_small.bin +0 -0
  338. package/town-frontend/dist/assets/models/furniture/shelf_B_small.gltf +136 -0
  339. package/town-frontend/dist/assets/models/furniture/shelf_B_small_decorated.bin +0 -0
  340. package/town-frontend/dist/assets/models/furniture/shelf_B_small_decorated.gltf +136 -0
  341. package/town-frontend/dist/assets/models/furniture/table_low.bin +0 -0
  342. package/town-frontend/dist/assets/models/furniture/table_low.gltf +136 -0
  343. package/town-frontend/dist/assets/models/furniture/table_medium.bin +0 -0
  344. package/town-frontend/dist/assets/models/furniture/table_medium.gltf +136 -0
  345. package/town-frontend/dist/assets/models/furniture/table_medium_long.bin +0 -0
  346. package/town-frontend/dist/assets/models/furniture/table_medium_long.gltf +136 -0
  347. package/town-frontend/dist/assets/models/furniture/table_small.bin +0 -0
  348. package/town-frontend/dist/assets/models/furniture/table_small.gltf +136 -0
  349. package/town-frontend/dist/assets/models/props/bench.bin +0 -0
  350. package/town-frontend/dist/assets/models/props/bench.gltf +136 -0
  351. package/town-frontend/dist/assets/models/props/bush.bin +0 -0
  352. package/town-frontend/dist/assets/models/props/bush.gltf +136 -0
  353. package/town-frontend/dist/assets/models/props/capybara.glb +0 -0
  354. package/town-frontend/dist/assets/models/props/car_hatchback.bin +0 -0
  355. package/town-frontend/dist/assets/models/props/car_hatchback.gltf +442 -0
  356. package/town-frontend/dist/assets/models/props/car_sedan.bin +0 -0
  357. package/town-frontend/dist/assets/models/props/car_sedan.gltf +442 -0
  358. package/town-frontend/dist/assets/models/props/car_taxi.bin +0 -0
  359. package/town-frontend/dist/assets/models/props/car_taxi.gltf +442 -0
  360. package/town-frontend/dist/assets/models/props/citybits_texture.png +0 -0
  361. package/town-frontend/dist/assets/models/props/dumpster.bin +0 -0
  362. package/town-frontend/dist/assets/models/props/dumpster.gltf +136 -0
  363. package/town-frontend/dist/assets/models/props/firehydrant.bin +0 -0
  364. package/town-frontend/dist/assets/models/props/firehydrant.gltf +136 -0
  365. package/town-frontend/dist/assets/models/props/streetlight.bin +0 -0
  366. package/town-frontend/dist/assets/models/props/streetlight.gltf +136 -0
  367. package/town-frontend/dist/assets/models/props/trafficlight_A.bin +0 -0
  368. package/town-frontend/dist/assets/models/props/trafficlight_A.gltf +136 -0
  369. package/town-frontend/dist/assets/models/props/trash_A.bin +0 -0
  370. package/town-frontend/dist/assets/models/props/trash_A.gltf +136 -0
  371. package/town-frontend/dist/assets/models/props/watertower.bin +0 -0
  372. package/town-frontend/dist/assets/models/props/watertower.gltf +136 -0
  373. package/town-frontend/dist/assets/models/stage-deco/Flowers_1_D.glb +0 -0
  374. package/town-frontend/dist/assets/models/stage-deco/Flowers_2_B.glb +0 -0
  375. package/town-frontend/dist/assets/models/stage-deco/Grass_A_1.glb +0 -0
  376. package/town-frontend/dist/assets/models/stage-deco/Park_GrassHill_A.glb +0 -0
  377. package/town-frontend/dist/assets/models/stage-deco/Pebles_1_A_2.glb +0 -0
  378. package/town-frontend/dist/assets/music/bgm_day.mp3 +0 -0
  379. package/town-frontend/dist/assets/music/bgm_dusk.mp3 +0 -0
  380. package/town-frontend/dist/assets/music/bgm_night.mp3 +0 -0
  381. package/town-frontend/dist/assets/music/bgm_work.mp3 +0 -0
  382. package/town-frontend/dist/assets/office-whiteboard-idle-CyEwBrq_.webp +0 -0
  383. package/town-frontend/dist/assets/preview-B6hYEQij.js +2 -0
  384. package/town-frontend/dist/assets/town-BKesnERP.js +8888 -0
  385. package/town-frontend/dist/assets/town-BnswKsjF.css +1 -0
  386. package/town-frontend/dist/citizen-editor.html +166 -0
  387. package/town-frontend/dist/editor.html +227 -0
  388. package/town-frontend/dist/index.html +22 -0
  389. package/town-frontend/dist/preview.html +56 -0
  390. package/town-frontend/dist/town.html +165 -0
  391. package/town-frontend/dist/viewer.html +91 -0
  392. package/town-souls/CHEN.md +130 -0
  393. package/town-souls/CHENGZI.md +92 -0
  394. package/town-souls/CITIZEN_tpl.md +16 -0
  395. package/town-souls/DIANDIAN.md +92 -0
  396. package/town-souls/HAITANG.md +94 -0
  397. package/town-souls/QIQI.md +119 -0
  398. package/town-souls/SOUL.md +141 -0
  399. package/town-souls/SOUL_tpl.md +135 -0
  400. package/town-souls/XIAOLIE.md +107 -0
  401. package/town-souls/YAN.md +107 -0
  402. package/town-workspace/IDENTITY.md +5 -0
  403. package/town-workspace/SOUL.md +141 -0
  404. package/town-workspace/project-workflow.md +81 -0
  405. package/town-workspace/town-defaults.json +92 -0
  406. package/town-workspace/town-guide.md +45 -0
@@ -0,0 +1,296 @@
1
+ var Se=Object.defineProperty;var ke=(m,e,t)=>e in m?Se(m,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):m[e]=t;var d=(m,e,t)=>ke(m,typeof e!="symbol"?e+"":e,t);import{g as o,t as F,i as Le}from"./index-BWfrufil.js";/* empty css */import{b as Ee}from"./character-catalog-DSmLtlNC.js";import{x as X,a as ue,W as Y,h as W,P as xe,a6 as Me,S as Q,b as Z,g as Ae,z as Ie,V as E,a7 as Te,M as G,d as $e,a8 as Ne,A as K,D as P,k as Pe,T as ze,C as ee,o as U,a9 as Be,i as B,K as te,a1 as me,a3 as De,r as fe,G as Ue}from"./GLTFLoader-BA5RqSME.js";import{O as ie}from"./OrbitControls-ZmySp9sQ.js";function ve(){return{scale:2.8,rotationX:0,rotationY:0,rotationZ:0,offsetX:0,offsetY:0,offsetZ:0}}function q(m,e){const t=ve();if(e==="builtin")return t;if(e==="library")return t.scale=m>.01?2.8/m:2.8,t.rotationY=180,t;const i=2.8;return t.scale=m>.01?i/m:2.8,t}const z=["idle","walk","typing","wave","cheer","reading","frustrated","dancing"],re={idle:"Idle_A",walk:"Walk_A",typing:"Zombie_Atack_B",wave:"Pistol_Shoot",cheer:"Jump_B_Full",reading:"Pistol_Idle",frustrated:"Death_A",dancing:"Jump_C_Full"},O={互联网:["前端开发","后端开发","全栈开发","移动开发","架构设计","运维"],产品设计:["产品经理","UI设计","UX设计","交互设计"],自媒体:["内容运营","短视频创作","直播运营","文案写作","社群运营"],金融:["投资分析","风控合规","量化交易","财务管理"],电商:["电商运营","供应链","选品分析","用户增长"],教育:["课程设计","教学研究","知识管理"],市场营销:["品牌策略","广告投放","市场调研","SEO/SEM"],数据:["数据分析","数据工程","商业智能"],游戏:["游戏策划","游戏开发","游戏美术"],项目管理:["项目管理","质量保障"],通用:["通用助手"]},Fe=Object.keys(O);function oe(){const m=o()==="en";return{version:1,user:{name:m?"Mayor":"镇长",avatarId:"char-male-c",modelSource:"builtin"},steward:{name:"shire",avatarId:"char-female-b",modelSource:"builtin",bio:m?"Sharp and decisive manager, delegates tasks, never codes, orchestrates citizens":"干练御姐,做事利落,职业经理型,善于引导对话,不亲自写代码,通过调度居民完成任务",persona:"SOUL"},citizens:[{id:"citizen_1",name:m?"Yan":"岩",avatarId:"char-male-b",modelSource:"builtin",bio:m?"Quiet, logical, architecture purist with a global view":"沉稳寡言,逻辑至上,架构洁癖,全局视野,技术信仰者",industry:"互联网",specialty:m?"Architect":"架构设计",persona:"YAN",homeId:"house_a"},{id:"citizen_2",name:m?"Chengzi":"橙子",avatarId:"char-female-c",modelSource:"builtin",bio:m?"Fast thinker, ideas overflow, obsessed with UX empathy":"脑子转速极快,想法多到溢出来,开朗到有点吵,对用户体验有偏执的共情力",industry:"产品设计",specialty:m?"Product":"产品经理",persona:"CHENGZI",homeId:"house_b"},{id:"citizen_3",name:m?"Haitang":"海棠",avatarId:"char-female-e",modelSource:"builtin",bio:m?"Elegant, few words, extraordinary color perception":"安静优雅,审美洁癖,话少但每句都跟视觉有关,色彩感知力异常",industry:"产品设计",specialty:m?"UI Design":"UI设计",persona:"HAITANG",homeId:"house_c"},{id:"citizen_4",name:m?"Diandian":"点点",avatarId:"char-female-f",modelSource:"builtin",bio:m?"Warm and sunny, solid dev, team morale booster":"温暖阳光,邻家妹妹型程序员,技术扎实但从不炫耀,团队气氛担当",industry:"互联网",specialty:m?"Frontend":"前端开发",persona:"DIANDIAN",homeId:"house_a"},{id:"citizen_5",name:m?"Xiaolie":"小烈",avatarId:"char-male-e",modelSource:"builtin",bio:m?"Action-first, blazing fast, thrives on hard problems":"热血冲劲,行动派,先干再说,效率惊人,遇到技术难题像打BOSS一样兴奋",industry:"互联网",specialty:m?"Backend":"后端开发",persona:"XIAOLIE",homeId:"house_b"},{id:"citizen_6",name:m?"Qiqi":"柒柒",avatarId:"char-female-d",modelSource:"builtin",bio:m?"Creative fountain, knows what goes viral but values substance":"灵感涌泉,表达欲旺盛,自媒体老手,知道什么能火但更在意内容有没有价值",industry:"自媒体",specialty:m?"Content":"内容运营",persona:"QIQI",homeId:"house_c"},{id:"citizen_7",name:m?"Chen":"辰",avatarId:"char-male-d",modelSource:"builtin",bio:m?"Cold logic, speaks only in conclusions, data is religion":"冷静理性,数据洁癖,沉默但一开口就是结论,用数据说话是信仰不是习惯",industry:"数据",specialty:m?"Data":"数据分析",persona:"CHEN",homeId:"house_a"}]}}let Oe=100;function _e(){return`citizen_${Date.now()}_${Oe++}`}const Ge={idle:["idle","standby","rest"],walk:["walk","move","locomotion"],typing:["type","typing","work","attack"],wave:["wave","greet","hello","shoot"],cheer:["cheer","celebrate","jump","victory"],reading:["read","reading","book","pistol_idle"],frustrated:["frustrat","angry","death","sad"],dancing:["danc","dance"]},j={idle:"待机",walk:"行走",typing:"工作",wave:"打招呼",cheer:"庆祝",reading:"阅读",frustrated:"沮丧",dancing:"跳舞"};function R(m,e){const t=e?{...e}:{},i=m.map(s=>({orig:s,lc:s.toLowerCase()}));for(const s of z){if(t[s])continue;const n=Ge[s],a=i.find(r=>n.some(l=>r.lc.includes(l)));a&&(t[s]=a.orig)}return t}const Re=2,He=[{id:"char-male-a",gender:"male",tags:["male","man","boy","gentle","reliable","host","steward","manager","guide","butler","blue","calm","mature"],description:"Blue-toned male, mature and composed; fits steward, host, organizer, or gentleman roles."},{id:"char-male-b",gender:"male",tags:["male","boy","technical","engineer","coder","builder","focused","blue","clean","nerd","geek"],description:"Tech-oriented male, ideal for programmer, engineer, builder, or geek roles."},{id:"char-male-c",gender:"male",tags:["male","leader","player","mayor","protagonist","hero","owner","gold","distinctive","cool"],description:"Protagonist-style male with high visibility; reserved for the mayor / player."},{id:"char-male-d",gender:"male",tags:["male","boy","analyst","planner","strategist","thoughtful","gray","reserved","quiet"],description:"Analytical male, suits strategist, planner, or quiet thinker roles."},{id:"char-male-e",gender:"male",tags:["male","boy","adventurous","tester","explorer","energetic","red","active","sporty"],description:"Action-oriented male, great for tester, explorer, or sporty roles."},{id:"char-male-f",gender:"male",tags:["male","boy","general","versatile","friendly","purple","casual","easygoing"],description:"Versatile male, balanced style for generic residents or casual roles."},{id:"char-female-a",gender:"female",tags:["female","girl","artist","designer","creative","green","fresh","cute","anime"],description:"Fresh-styled female, ideal for artist, designer, or creative roles."},{id:"char-female-b",gender:"female",tags:["female","girl","technical","architect","engineer","blue","smart","cool"],description:"Tech-savvy female, suits architect, engineer, or sharp professional roles."},{id:"char-female-c",gender:"female",tags:["female","girl","writer","planner","content","orange","bright","cheerful"],description:"Expressive female, great for writer, planner, or outgoing communicator roles."},{id:"char-female-d",gender:"female",tags:["female","girl","creative","planner","dreamy","pink","lively","cute","kawaii","anime","adorable"],description:"Lively pink-toned female, perfect for creative, cute, or anime-style roles."},{id:"char-female-e",gender:"female",tags:["female","girl","animation","visual","elegant","purple","art","graceful"],description:"Elegant purple-toned female, fits animation, visual art, or graceful roles."},{id:"char-female-f",gender:"female",tags:["female","girl","general","warm","friendly","yellow","sunny","sweet"],description:"Warm yellow-toned female, ideal for friendly residents or sweet roles."}],qe={version:Re,models:He};function je(){return o()==="en"?qe:Ee}const ge="./assets/models",le="/ext-assets";let H=null,se=null,ce=null;function we(){const m=o();if(H&&ce===m)return;ce=m,H=(je().models??[]).map(t=>{var i;return{id:t.id,displayName:((i=t.description)==null?void 0:i.slice(0,12))??t.id,gender:t.gender,tags:t.tags??[],description:t.description??"",source:"builtin",meshUrl:`${ge}/characters/character-${t.id.replace("char-","")}.glb`,hasEmbeddedAnimations:!0}}),se=H.map(t=>({id:t.id,displayName:t.id.replace("char-","").replace("-"," "),source:"builtin",thumbnailUrl:`./assets/avatars/${t.id}.webp`,variants:[],colors:[],meshUrlPattern:t.meshUrl}))}const Ve=["beaver","bee","bunny","cat","caterpillar","chick","cow","crab","deer","dog","elephant","fish","fox","giraffe","hog","koala","lion","monkey","panda","parrot","penguin","pig","polar","tiger"],ye=Ve.map(m=>({id:`char-pet-${m}`,displayName:m,source:"builtin",thumbnailUrl:`./assets/avatars/char-pet-${m}.webp`,variants:[],colors:[],meshUrlPattern:`${ge}/characters/character-pet-${m}.glb`})),Je=[{id:"1",displayName:"Character 1",maxVariant:3,maxColor:16},{id:"2",displayName:"Character 2",maxVariant:6,maxColor:12},{id:"3",displayName:"Character 3",maxVariant:3,maxColor:8},{id:"4",displayName:"Character 4",maxVariant:1,maxColor:8},{id:"5",displayName:"Character 5",maxVariant:4,maxColor:8},{id:"6",displayName:"Character 6",maxVariant:4,maxColor:8},{id:"7",displayName:"Character 7",maxVariant:2,maxColor:10},{id:"8",displayName:"Character 8",maxVariant:5,maxColor:6},{id:"9",displayName:"Character 9",maxVariant:6,maxColor:10},{id:"10",displayName:"Character 10",maxVariant:5,maxColor:14},{id:"11",displayName:"Character 11",maxVariant:4,maxColor:11}],be=Je.map(m=>({id:`lib-${m.id}`,displayName:m.displayName,source:"library",thumbnailUrl:`${le}/Characters_1/thumbnails/lib-${m.id}.webp`,variants:Array.from({length:m.maxVariant},(e,t)=>t+1),colors:Array.from({length:m.maxColor},(e,t)=>t+1),meshUrlPattern:`${le}/Characters_1/gLTF/Characters/Character_${m.id}_{variant}_{color}.glb`}));function de(){return we(),[...se,...ye]}function Xe(){return be}function Ce(m=[]){we();const e=m.filter(t=>t.kind==="character").map(t=>({id:`custom-${t.id}`,displayName:t.name,source:"custom",thumbnailUrl:t.thumbnail,variants:[],colors:[],meshUrlPattern:`custom-assets/characters/${t.fileName}`}));return[...se,...ye,...be,...e]}function N(m,e,t){if(m.source==="builtin"||m.source==="custom")return m.meshUrlPattern;let i=m.meshUrlPattern;return i=i.replace("{variant}",String(e??m.variants[0]??1)),i=i.replace("{color}",String(t??m.colors[0]??1)),i}class Ye{constructor(e,t){d(this,"el");d(this,"config");d(this,"selection",null);d(this,"listeners",[]);d(this,"activeMenu",null);d(this,"onDeleteCallback",null);d(this,"avatarResolver",null);this.el=e,this.config=t,document.addEventListener("click",()=>this.closeActiveMenu()),this.render(),this.selectFirst()}onChange(e){this.listeners.push(e)}onDelete(e){this.onDeleteCallback=e}setAvatarResolver(e){this.avatarResolver=e}emit(){for(const e of this.listeners)e(this.selection)}getSelection(){return this.selection}getConfig(){return this.config}setConfig(e){this.config=e,this.render()}select(e){this.selection=e,this.updateSelectionUI(),this.emit()}addCitizen(e){const t=_e(),s=Object.keys(O)[0],n=O[s][0],a={id:t,name:e.trim()||(o()==="en"?"New Citizen":"新居民"),avatarId:"char-male-f",modelSource:"builtin",bio:"",industry:s,specialty:n,persona:"",homeId:""};this.config.citizens.push(a),this.render(),this.select({type:"citizen",id:t})}deleteCitizen(e){var t;this.config.citizens=this.config.citizens.filter(i=>i.id!==e),((t=this.selection)==null?void 0:t.type)==="citizen"&&this.selection.id===e&&this.selectFirst(),this.render()}selectFirst(){this.selection={type:"user"},this.emit()}closeActiveMenu(){this.activeMenu&&(this.activeMenu.remove(),this.activeMenu=null)}showItemMenu(e,t,i){this.closeActiveMenu();const s=document.createElement("div");s.className="cw-roster-menu";const n=document.createElement("button");n.className="cw-roster-menu-item danger",n.innerHTML=`<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>${o()==="en"?"Delete":"删除居民"}`,n.addEventListener("click",r=>{r.stopPropagation(),this.closeActiveMenu(),this.onDeleteCallback?this.onDeleteCallback(t):this.deleteCitizen(t)}),s.appendChild(n);const a=e.getBoundingClientRect();this.el.closest(".cw-roster").getBoundingClientRect(),s.style.position="fixed",s.style.left=`${a.right+4}px`,s.style.top=`${a.top-4}px`,a.right+140>window.innerWidth&&(s.style.left=`${a.left-140}px`),document.body.appendChild(s),this.activeMenu=s}render(){const e=this.el;e.innerHTML="";const t=this.config.user,i=this.config.steward;if(e.appendChild(this.renderGroup("",[this.renderItem({type:"user"},t.name,"",t.avatarUrl,t.avatarId)])),e.appendChild(this.renderGroup("",[this.renderItem({type:"steward"},i.name,i.bio?F("steward"):"",i.avatarUrl,i.avatarId)])),this.config.citizens.length>0){const s=this.config.citizens.map(n=>this.renderItem({type:"citizen",id:n.id},n.name,n.specialty||n.industry,n.avatarUrl,n.avatarId,!0));e.appendChild(this.renderGroup(o()==="en"?`Citizens (${this.config.citizens.length})`:`居民 (${this.config.citizens.length})`,s))}this.updateSelectionUI()}renderGroup(e,t){const i=document.createElement("div");if(i.className="cw-roster-group",e){const s=document.createElement("div");s.className="cw-roster-group-label",s.textContent=e,i.appendChild(s)}for(const s of t)i.appendChild(s);return i}resolveAvatarUrl(e,t){if(this.avatarResolver)return this.avatarResolver(e,t);if(e)return e;if(t){const i=Ce().find(s=>s.id===t);if(i!=null&&i.thumbnailUrl)return i.thumbnailUrl}return null}renderItem(e,t,i,s,n,a=!1){const r=document.createElement("div");r.className="cw-roster-item",r.dataset.type=e.type,e.type==="citizen"&&(r.dataset.id=e.id);const l=document.createElement("div");l.className="cw-roster-avatar";const p=this.resolveAvatarUrl(s,n);if(p){const c=document.createElement("img");c.src=p,c.alt=t,c.onerror=()=>{c.remove(),l.textContent=t.charAt(0)},l.appendChild(c)}else l.textContent=t.charAt(0);r.appendChild(l);const u=document.createElement("div");u.className="cw-roster-info";const h=document.createElement("div");if(h.className="cw-roster-name",h.textContent=t,u.appendChild(h),i){const c=document.createElement("div");c.className="cw-roster-meta",c.textContent=i,u.appendChild(c)}if(r.appendChild(u),a&&e.type==="citizen"){const c=document.createElement("button");c.className="cw-roster-more",c.innerHTML='<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/></svg>',c.addEventListener("click",v=>{v.stopPropagation();const f=this.config.citizens.find(g=>g.id===e.id);this.showItemMenu(c,e.id,(f==null?void 0:f.name)||"")}),r.appendChild(c)}return r.addEventListener("click",()=>this.select(e)),r}updateSelectionUI(){this.el.querySelectorAll(".cw-roster-item").forEach(e=>{const t=e,i=t.dataset.type,s=t.dataset.id;let n=!1;this.selection&&this.selection.type===i&&(i==="citizen"?n=this.selection.id===s:n=!0),t.classList.toggle("selected",n)})}}const he=.55,We="/ext-assets",T=2.4/2.8;class Qe{constructor(e){d(this,"container");d(this,"renderer");d(this,"scene");d(this,"camera");d(this,"controls");d(this,"loader",new X);d(this,"clock",new ue);d(this,"currentModel",null);d(this,"mixer",null);d(this,"currentAction",null);d(this,"animMap",new Map);d(this,"animQueue",[]);d(this,"animIdx",0);d(this,"animLoopTimer",0);d(this,"animId",0);d(this,"loadId",0);d(this,"active",!1);d(this,"currentScale",1);d(this,"onRawHeight",null);d(this,"loop",()=>{var t;if(!this.active)return;this.animId=requestAnimationFrame(this.loop);const e=this.clock.getDelta();(t=this.mixer)==null||t.update(e),this.animQueue.length>0&&(this.animLoopTimer+=e,this.animLoopTimer>3&&(this.animLoopTimer=0,this.playNextInQueue())),this.controls.update(),this.renderer.render(this.scene,this.camera)});this.container=e;const t=document.createElement("canvas");t.style.display="block",t.style.width="100%",t.style.height="100%",e.appendChild(t),this.renderer=new Y({canvas:t,antialias:!0,alpha:!1}),this.renderer.setPixelRatio(Math.min(devicePixelRatio,2)),this.renderer.outputColorSpace=W,this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=xe,this.renderer.toneMapping=Me,this.renderer.toneMappingExposure=1,this.scene=new Q,this.camera=new Z(32,1,.1,100),this.camera.position.set(0,4.5,9),this.controls=new ie(this.camera,t),this.controls.enableDamping=!0,this.controls.dampingFactor=.08,this.controls.target.set(0,1.2,0),this.controls.minDistance=4,this.controls.maxDistance=14,this.controls.maxPolarAngle=Math.PI*.48,this.controls.minPolarAngle=Math.PI*.15,this.controls.enablePan=!1,this.buildBackground(),this.buildLighting(),this.buildGrassPlatform(),new ResizeObserver(()=>{this.active&&this.resize()}).observe(e)}setOnRawHeight(e){this.onRawHeight=e}buildBackground(){const e=new Ae(50,32,32),t=new Ie({side:Te,depthWrite:!1,uniforms:{colorA:{value:new E(.866,.702,.412)},colorB:{value:new E(.835,.686,.431)}},vertexShader:`
2
+ varying vec2 vUv;
3
+ void main() {
4
+ vUv = uv;
5
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
6
+ }
7
+ `,fragmentShader:`
8
+ uniform vec3 colorA;
9
+ uniform vec3 colorB;
10
+ varying vec2 vUv;
11
+ void main() {
12
+ float t = 1.0 - (vUv.x * 0.4 + vUv.y * 0.6);
13
+ gl_FragColor = vec4(mix(colorA, colorB, t), 1.0);
14
+ }
15
+ `});this.scene.add(new G(e,t));const i=new $e(30,30),s=new Ne({opacity:.35}),n=new G(i,s);n.rotation.x=-Math.PI/2,n.position.y=-.02,n.receiveShadow=!0,this.scene.add(n)}buildLighting(){const e=new K(16774624,1);this.scene.add(e);const t=new P(16777215,1.8);t.position.set(6,8,5),t.castShadow=!0,t.shadow.mapSize.set(2048,2048),t.shadow.camera.near=.5,t.shadow.camera.far=30,t.shadow.camera.left=-12,t.shadow.camera.right=8,t.shadow.camera.top=10,t.shadow.camera.bottom=-8,t.shadow.bias=-3e-4,t.shadow.normalBias=.02,this.scene.add(t);const i=new P(16772829,.5);i.position.set(-5,6,-4),this.scene.add(i);const s=new P(16773341,.4);s.position.set(-2,4,-7),this.scene.add(s)}async buildGrassPlatform(){const e=new Pe({color:16777215,emissive:new ee(10066329),roughness:.35,metalness:0,side:ze}),t=.17,i=1.9,s=0,n=[],a=16;for(let h=0;h<=a;h++){const c=Math.PI/2-Math.PI*h/a;n.push(new U(i+t*Math.cos(c),s+t*Math.sin(c)))}const r=[new U(0,.08),new U(1.82,.08),...n,new U(1.82,-.2),new U(0,-.2)],l=new Be(r,128),p=new G(l,e);p.position.y=.15,p.receiveShadow=!0,p.castShadow=!0,this.scene.add(p);const u="assets/models/stage-deco";try{const c=(await this.loader.loadAsync(`${u}/Park_GrassHill_A.glb`)).scene;c.scale.setScalar(1),c.updateMatrixWorld(!0);const v=new B().setFromObject(c),f=Math.max(v.max.x-v.min.x,v.max.z-v.min.z)/2,w=f>.01?1.8/f:1;c.scale.setScalar(w),c.position.set(0,.3,0),c.traverse(b=>{b.isMesh&&(b.castShadow=!0,b.receiveShadow=!0)}),this.scene.add(c)}catch{}this.loadDeco(`${u}/Grass_A_1.glb`,.35,0,.54,-.6,.2),this.loadDeco(`${u}/Grass_A_1.glb`,.3,1.8,.54,.3,-.7),this.loadDeco(`${u}/Grass_A_1.glb`,.28,3,.52,.9,.5),this.loadDeco(`${u}/Grass_A_1.glb`,.35,3,.48,-.4,1.2),this.loadDeco(`${u}/Flowers_2_B.glb`,.6,.5,.5,-1,-.5),this.loadDeco(`${u}/Flowers_1_D.glb`,.6,0,.49,.45,1.2),this.loadDeco(`${u}/Pebles_1_A_2.glb`,.35,0,.53,.1,.7)}async loadDeco(e,t,i,s,n,a){try{const l=(await this.loader.loadAsync(e)).scene;l.scale.setScalar(t),l.position.set(n,s,a),l.rotation.y=i,l.traverse(p=>{p.isMesh&&(p.castShadow=!0,p.receiveShadow=!0)}),this.scene.add(l)}catch{}}start(){this.active||(this.active=!0,this.resize(),this.clock.start(),this.loop())}stop(){this.active=!1,cancelAnimationFrame(this.animId)}async setCharacter(e,t,i,s,n){var r;const a=++this.loadId;this.clearCharacter();try{const l=await this.loader.loadAsync(e);if(this.loadId!==a)return;const p=l.scene;p.scale.setScalar(1),p.updateMatrixWorld(!0);const u=new B().setFromObject(p),h=u.max.y-u.min.y,c=e.includes("Characters_1")?"library":e.includes("custom-assets")?"custom":"builtin";(r=this.onRawHeight)==null||r.call(this,h,c);const v=n??q(h,c),f=v.scale*T;if(this.currentScale=f,p.scale.setScalar(f),p.position.set(v.offsetX*T,v.offsetY*T+he,v.offsetZ*T),p.rotation.set(v.rotationX*Math.PI/180,v.rotationY*Math.PI/180,v.rotationZ*Math.PI/180),p.traverse(w=>{if(w.isMesh){w.castShadow=!0;const b=Array.isArray(w.material)?w.material:[w.material];for(const C of b)c==="custom"&&(C.transparent&&(C.transparent=!1,C.alphaTest=.5,C.opacity=1),C.alphaMap&&(C.alphaTest=Math.max(C.alphaTest,.5)),C.depthWrite=!0,C.side=te),(c==="custom"||c==="builtin")&&(C.onBeforeCompile=y=>{y.fragmentShader=y.fragmentShader.replace("#include <dithering_fragment>",`{
16
+ float luma = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114));
17
+ gl_FragColor.rgb = mix(vec3(luma), gl_FragColor.rgb, 1.6);
18
+ }
19
+ #include <dithering_fragment>`)},C.needsUpdate=!0)}}),this.loadId!==a)return;this.currentModel=p,this.scene.add(p);let g=l.animations.length>0?l.animations:t??[];if(g.length===0&&c==="library")try{const w=await this.loader.loadAsync(`${We}/Characters_1/gLTF/Animations/Animations.glb`);if(this.loadId!==a){this.scene.remove(p);return}g=w.animations}catch{}if(g.length===0&&s&&s.length>0)for(const w of s)try{const b=await this.loader.loadAsync(w);if(this.loadId!==a){this.scene.remove(p);return}g=[...g,...b.animations]}catch{}if(this.loadId!==a){this.scene.remove(p);return}if(g.length>0){if(this.mixer=new me(p),this.animMap.clear(),i&&Object.keys(i).length>0){const w=new Map;for(const b of g)w.set(b.name,b);for(const[b,C]of Object.entries(i)){if(!C)continue;const y=w.get(C);y&&this.animMap.set(b,y)}}else for(const w of g)this.animMap.set(w.name.toLowerCase(),w);this.buildAnimQueue(),this.animIdx=0,this.animLoopTimer=0,this.playNextInQueue()}this.fadeInCurrent()}catch(l){console.warn("[CharacterStage] Failed to load:",l)}}buildAnimQueue(){const e=["idle","wave","idle","walk","idle","typing","idle","cheer","idle","dancing","idle"];this.animQueue=[];for(const t of e)this.findClip(t)&&this.animQueue.push(t);this.animQueue.length===0&&this.animMap.size>0&&this.animQueue.push(this.animMap.keys().next().value)}playNextInQueue(){if(this.animQueue.length===0)return;const e=this.animQueue[this.animIdx%this.animQueue.length];e==="idle"?(this.playAnim(e),this.animLoopTimer=0,this.animIdx=(this.animIdx+1)%this.animQueue.length):this.playAnimOnce(e,()=>{this.animIdx=(this.animIdx+1)%this.animQueue.length,this.playNextInQueue()})}clearCharacter(){this.currentModel&&(this.scene.remove(this.currentModel),this.currentModel=null),this.mixer&&(this.mixer.stopAllAction(),this.mixer=null),this.animMap.clear(),this.currentAction=null,this.animQueue=[]}fadeOutCurrent(){return new Promise(e=>{if(!this.currentModel){e();return}const t=this.currentModel,i=t.scale.x,s=120,n=performance.now(),a=()=>{const r=Math.min((performance.now()-n)/s,1);t.scale.setScalar(i*(.85+.15*(1-r))),r<1?requestAnimationFrame(a):(this.clearCharacter(),e())};requestAnimationFrame(a)})}fadeInCurrent(){if(!this.currentModel)return;const e=this.currentModel,t=this.currentScale;e.scale.setScalar(t*.88);const i=180,s=performance.now(),n=()=>{const a=Math.min((performance.now()-s)/i,1);e.scale.setScalar(t*(.88+.12*a*(2-a))),a<1&&requestAnimationFrame(n)};requestAnimationFrame(n)}findClip(...e){for(const t of e)if(this.animMap.has(t))return this.animMap.get(t);for(const t of e)for(const[i,s]of this.animMap)if(i.includes(t))return s;return null}playAnim(e){if(!this.mixer)return;const t=this.findClip(e);if(!t)return;const i=this.mixer.clipAction(t);this.currentAction&&this.currentAction!==i?(i.reset().play(),this.currentAction.crossFadeTo(i,.3,!1)):i.reset().play(),this.currentAction=i}playAnimOnce(e,t){if(!this.mixer){t==null||t();return}const i=this.findClip(e);if(!i){t==null||t();return}const s=this.mixer.clipAction(i);s.reset(),s.setLoop(De,1),s.clampWhenFinished=!0,s.play(),this.currentAction&&this.currentAction!==s&&this.currentAction.crossFadeTo(s,.3,!1),this.currentAction=s;const n=()=>{var a;(a=this.mixer)==null||a.removeEventListener("finished",n),t==null||t()};this.mixer.addEventListener("finished",n)}resize(){const e=this.container.clientWidth,t=this.container.clientHeight;e>0&&t>0&&(this.renderer.setSize(e,t),this.camera.aspect=e/t,this.camera.updateProjectionMatrix())}applyTransform(e){if(!this.currentModel)return;const t=e.scale*T;this.currentScale=t,this.currentModel.scale.setScalar(t),this.currentModel.position.set(e.offsetX*T,e.offsetY*T+he,e.offsetZ*T),this.currentModel.rotation.set(e.rotationX*Math.PI/180,e.rotationY*Math.PI/180,e.rotationZ*Math.PI/180)}dispose(){this.stop(),this.clearCharacter(),this.renderer.dispose()}}const Ze=50*1024*1024,Ke=[".glb",".gltf"];class pe{constructor(){d(this,"overlay");d(this,"loader",new X);d(this,"previewRenderer",null);d(this,"previewScene",null);d(this,"previewCamera",null);d(this,"previewControls",null);d(this,"previewModel",null);d(this,"previewPivot",null);d(this,"previewCanvas",null);d(this,"animId",0);d(this,"currentFile",null);d(this,"basePreviewScale",1);d(this,"onComplete",null);d(this,"editingAsset",null);d(this,"detectedClipNames",[]);this.overlay=this.createOverlay(),document.getElementById("editor-root").appendChild(this.overlay)}open(e){this.currentFile=null,this.editingAsset=null,this.onComplete=e,this.overlay.classList.add("open"),this.showStep1()}openEdit(e,t){this.currentFile=null,this.editingAsset=null,this.onComplete=t,this.overlay.classList.add("open"),this.loadAssetAndShowEdit(e)}close(){this.overlay.classList.remove("open"),this.stopPreview(),this.currentFile=null,this.editingAsset=null,this.detectedClipNames=[]}createOverlay(){const e=document.createElement("div");e.className="char-upload-overlay",e.addEventListener("click",i=>{i.target===e&&this.close()});const t=document.createElement("div");return t.className="upload-panel char-upload-panel",e.appendChild(t),e}getPanel(){return this.overlay.querySelector(".char-upload-panel")}async loadAssetAndShowEdit(e){try{const s=((await(await fetch("/custom-assets/_api/list",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"})).json()).assets??[]).find(a=>a.id===e&&a.kind==="character");if(!s){this.close();return}this.editingAsset={id:s.id,name:s.name,scale:s.scale,fileName:s.fileName,thumbnail:s.thumbnail},this.renderStep2Form(s.name,s.scale??1);const n=`/custom-assets/characters/${s.fileName}`;this.loader.loadAsync(n).then(a=>{this.setPreviewModel(a.scene),this.detectedClipNames=a.animations.map(r=>r.name).filter(r=>r&&r!=="default")}).catch(()=>{const a=this.overlay.querySelector(".upload-preview-info");a&&(a.textContent=o()==="en"?"Model load failed":"模型加载失败")})}catch{this.close()}}showStep1(){const e=this.getPanel();e.classList.remove("upload-panel--step2"),e.innerHTML=`
20
+ <div class="upload-header">
21
+ <span class="upload-title">${o()==="en"?"Add Character":"添加角色模型"}</span>
22
+ <button class="upload-close-btn">
23
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
24
+ </button>
25
+ </div>
26
+ <div class="upload-dropzone" id="char-upload-dropzone">
27
+ <div class="dropzone-icon">
28
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
29
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/>
30
+ <polyline points="17 8 12 3 7 8"/>
31
+ <line x1="12" y1="3" x2="12" y2="15"/>
32
+ </svg>
33
+ </div>
34
+ <div class="dropzone-text">${o()==="en"?"Drop .glb / .gltf file here":"拖拽 .glb / .gltf 文件到此处"}</div>
35
+ <div class="dropzone-hint">${o()==="en"?"or click to browse (≤ 50MB)":"或点击选择文件(≤ 50MB)"}</div>
36
+ <input type="file" accept=".glb,.gltf" hidden id="char-upload-file-input">
37
+ </div>
38
+ <div class="upload-error" id="char-upload-error"></div>
39
+ <div class="upload-divider"><span class="upload-divider-line"></span><span class="upload-divider-text">${o()==="en"?"or":"或"}</span><span class="upload-divider-line"></span></div>
40
+ <button class="ai-gen-toggle" id="char-ai-gen-toggle">
41
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l1.09 3.26L16.36 6l-3.27 1.09L12 10.36l-1.09-3.27L7.64 6l3.27-1.09L12 2z"/><path d="M5 15l.55 1.64L7.18 17.2 5.55 17.75 5 19.4l-.55-1.65L2.82 17.2l1.63-.56L5 15z"/><path d="M19 11l.55 1.64 1.63.56-1.63.55L19 15.4l-.55-1.65-1.63-.55 1.63-.56L19 11z"/></svg>
42
+ <span class="ai-gen-label">${o()==="en"?"AI Generate 3D":"AI 生成 3D 角色"}</span>
43
+ <svg class="ai-gen-arrow" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
44
+ </button>
45
+ <div class="ai-gen-tools" id="char-ai-gen-tools">
46
+ <a class="ai-tool-card" data-url="https://3d-models.hunyuan.tencent.com/">
47
+ <span class="ai-tool-logo" style="background:linear-gradient(135deg,#6366f1,#3b82f6)">H</span>
48
+ <span class="ai-tool-name">混元3D</span>
49
+ </a>
50
+ <a class="ai-tool-card" data-url="https://www.meshy.ai">
51
+ <span class="ai-tool-logo" style="background:linear-gradient(135deg,#f97316,#fb923c)">M</span>
52
+ <span class="ai-tool-name">Meshy</span>
53
+ </a>
54
+ <a class="ai-tool-card" data-url="https://hyper3d.ai">
55
+ <span class="ai-tool-logo" style="background:linear-gradient(135deg,#10b981,#34d399)">R</span>
56
+ <span class="ai-tool-name">Rodin</span>
57
+ </a>
58
+ <a class="ai-tool-card" data-url="https://www.tripo3d.ai">
59
+ <span class="ai-tool-logo" style="background:linear-gradient(135deg,#06b6d4,#22d3ee)">T</span>
60
+ <span class="ai-tool-name">Tripo</span>
61
+ </a>
62
+ </div>
63
+ `,e.querySelector(".upload-close-btn").addEventListener("click",()=>this.close());const t=e.querySelector("#char-upload-dropzone"),i=e.querySelector("#char-upload-file-input");t.addEventListener("click",()=>i.click()),t.addEventListener("dragover",a=>{a.preventDefault(),t.classList.add("dragover")}),t.addEventListener("dragleave",()=>t.classList.remove("dragover")),t.addEventListener("drop",a=>{var l;a.preventDefault(),t.classList.remove("dragover");const r=(l=a.dataTransfer)==null?void 0:l.files[0];r&&this.validateAndProceed(r)}),i.addEventListener("change",()=>{var r;const a=(r=i.files)==null?void 0:r[0];a&&this.validateAndProceed(a)});const s=e.querySelector("#char-ai-gen-toggle"),n=e.querySelector("#char-ai-gen-tools");s.addEventListener("click",()=>{const a=n.classList.toggle("expanded");s.classList.toggle("expanded",a)}),n.querySelectorAll(".ai-tool-card").forEach(a=>{a.addEventListener("click",r=>{r.preventDefault();const l=a.dataset.url;l&&window.open(l,"_blank","noopener")})})}validateAndProceed(e){const t=this.overlay.querySelector("#char-upload-error"),i=e.name.substring(e.name.lastIndexOf(".")).toLowerCase();if(!Ke.includes(i)){t.textContent=o()==="en"?"Only .glb / .gltf supported":"仅支持 .glb / .gltf 格式",t.classList.add("visible");return}if(e.size>Ze){t.textContent=o()==="en"?"File exceeds 50MB":"文件超过 50MB 限制",t.classList.add("visible");return}t.classList.remove("visible"),this.currentFile=e,this.showStep2()}showStep2(){const e=this.currentFile.name.replace(/\.(glb|gltf)$/i,"").slice(0,20);this.renderStep2Form(e,1),this.loadPreviewFromFile(this.currentFile)}renderStep2Form(e,t){const i=this.getPanel(),s=!!this.editingAsset;i.classList.add("upload-panel--step2"),i.innerHTML=`
64
+ <div class="upload-header">
65
+ <span class="upload-title">${s?o()==="en"?"Edit Character":"编辑角色模型":o()==="en"?"Add Character":"添加角色模型"}</span>
66
+ <button class="upload-close-btn">
67
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
68
+ </button>
69
+ </div>
70
+ <div class="upload-body">
71
+ <div class="upload-form">
72
+ <label class="upload-label">
73
+ ${o()==="en"?"Name":"名称"}
74
+ <input type="text" class="upload-input" id="char-upload-name" value="${this.escHtml(e)}" maxlength="20" placeholder="${o()==="en"?"Character name":"角色名称"}">
75
+ </label>
76
+ <label class="upload-label">${o()==="en"?"Scale":"缩放"}</label>
77
+ <div class="upload-scale-row">
78
+ <button class="pi-scale-btn" data-action="scale-minus">−</button>
79
+ <input type="range" class="pi-scale-range" id="char-upload-scale" min="0.05" max="5" step="0.05" value="${t}" />
80
+ <button class="pi-scale-btn" data-action="scale-plus">+</button>
81
+ <input type="number" class="pi-scale-num" id="char-upload-scale-num" min="0.05" max="5" step="0.05" value="${t}" />
82
+ </div>
83
+ <div class="upload-error" id="char-upload-error"></div>
84
+ <label class="upload-optimize-label" id="char-optimize-row">
85
+ <input type="checkbox" id="char-upload-optimize" />
86
+ <span class="upload-optimize-check">
87
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
88
+ </span>
89
+ <span class="upload-optimize-text">
90
+ <span class="upload-optimize-name">${o()==="en"?"Optimize":"优化模型"}</span>
91
+ <span class="upload-optimize-hint">${o()==="en"?"Deduplicate & compress":"去重 + 量化压缩,减小文件体积"}</span>
92
+ </span>
93
+ </label>
94
+ <div class="upload-footer">
95
+ <button class="upload-btn upload-btn-cancel" id="char-upload-cancel">${o()==="en"?"Cancel":"取消"}</button>
96
+ <button class="upload-btn upload-btn-save" id="char-upload-save">${s?o()==="en"?"Save":"保存修改":o()==="en"?"Save":"保存"}</button>
97
+ </div>
98
+ </div>
99
+ <div class="upload-preview-area">
100
+ <canvas class="upload-preview-canvas"></canvas>
101
+ <div class="upload-preview-info">${o()==="en"?"Loading...":"加载中..."}</div>
102
+ </div>
103
+ </div>
104
+ `,i.querySelector(".upload-close-btn").addEventListener("click",()=>this.close()),i.querySelector("#char-upload-cancel").addEventListener("click",()=>this.close()),i.querySelector("#char-upload-save").addEventListener("click",()=>this.handleSave()),this.bindScaleControls(),this.initPreview()}bindScaleControls(){const e=this.overlay.querySelector("#char-upload-scale"),t=this.overlay.querySelector("#char-upload-scale-num"),i=this.overlay.querySelector('[data-action="scale-minus"]'),s=this.overlay.querySelector('[data-action="scale-plus"]'),n=a=>{a=Math.max(.05,Math.min(5,Math.round(a*100)/100)),e.value=String(a),t.value=String(a),this.applyPreviewScale()};e.addEventListener("input",()=>n(parseFloat(e.value))),t.addEventListener("input",()=>{const a=parseFloat(t.value);isNaN(a)||(e.value=String(a),this.applyPreviewScale())}),t.addEventListener("blur",()=>n(parseFloat(t.value)||1)),i.addEventListener("click",()=>n((parseFloat(e.value)||1)-.1)),s.addEventListener("click",()=>n((parseFloat(e.value)||1)+.1))}applyPreviewScale(){if(!this.previewPivot)return;const e=this.overlay.querySelector("#char-upload-scale"),t=parseFloat((e==null?void 0:e.value)??"1")||1;this.previewPivot.scale.setScalar(this.basePreviewScale*t)}async handleSave(){var r,l;const e=this.overlay.querySelector("#char-upload-name").value.trim(),t=parseFloat(this.overlay.querySelector("#char-upload-scale").value)||1,i=this.overlay.querySelector("#char-upload-error"),s=!!this.editingAsset;if(!e){i.textContent=o()==="en"?"Enter a name":"请输入角色名称",i.classList.add("visible");return}if(!s&&!this.currentFile)return;const n=this.overlay.querySelector("#char-upload-save");n.disabled=!0,n.textContent=o()==="en"?"Saving...":"保存中...";const a=this.captureThumbnail();try{if(s){const u=await(await fetch("/custom-assets/_api/update",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:this.editingAsset.id,name:e,scale:t,thumbnail:a})})).json();if(u.error){i.textContent=u.error,i.classList.add("visible"),n.disabled=!1,n.textContent=o()==="en"?"Save":"保存修改";return}const h=u.asset;await this.maybeOptimize(h.id,n),this.close(),(r=this.onComplete)==null||r.call(this,{assetId:h.id,meshUrl:`/custom-assets/characters/${h.fileName}`,groupId:`custom-${h.id}`,name:h.name,thumbnail:h.thumbnail,hasEmbeddedAnims:this.detectedClipNames.length>0,detectedClips:this.detectedClipNames.length>0?[...this.detectedClipNames]:void 0})}else{const p=await this.currentFile.arrayBuffer(),u=new Uint8Array(p);let h="";for(let w=0;w<u.byteLength;w++)h+=String.fromCharCode(u[w]);const c=btoa(h),f=await(await fetch("/custom-assets/_api/upload",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({kind:"character",name:e,data:c,scale:t,thumbnail:a})})).json();if(f.error){i.textContent=f.error,i.classList.add("visible"),n.disabled=!1,n.textContent=o()==="en"?"Save":"保存";return}const g=f.asset;await this.maybeOptimize(g.id,n),this.close(),(l=this.onComplete)==null||l.call(this,{assetId:g.id,meshUrl:`/custom-assets/characters/${g.fileName}`,groupId:`custom-${g.id}`,name:g.name,thumbnail:g.thumbnail,hasEmbeddedAnims:this.detectedClipNames.length>0,detectedClips:this.detectedClipNames.length>0?[...this.detectedClipNames]:void 0})}}catch{i.textContent=s?o()==="en"?"Save failed":"保存失败,请重试":o()==="en"?"Upload failed":"上传失败,请重试",i.classList.add("visible"),n.disabled=!1,n.textContent=s?o()==="en"?"Save":"保存修改":o()==="en"?"Save":"保存"}}async maybeOptimize(e,t){const i=this.overlay.querySelector("#char-upload-optimize");if(i!=null&&i.checked){t.textContent=o()==="en"?"Optimizing...":"优化中...";try{const n=await(await fetch("/custom-assets/_api/optimize",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:e})})).json();n.success&&(t.textContent=o()==="en"?`Optimized (-${n.ratio}%)`:`已优化 (-${n.ratio}%)`)}catch{}}}initPreview(){this.stopPreview();const e=this.overlay.querySelector(".upload-preview-canvas");if(!e)return;this.previewCanvas=e,this.previewRenderer=new Y({canvas:e,antialias:!0}),this.previewRenderer.setPixelRatio(Math.min(devicePixelRatio,2)),this.previewRenderer.outputColorSpace=W,this.previewScene=new Q,this.previewScene.background=new ee(1052696),this.previewCamera=new Z(40,1,.01,200),this.previewCamera.position.set(3,2,3),this.previewControls=new ie(this.previewCamera,e),this.previewControls.enableDamping=!0,this.previewControls.autoRotate=!1;const t=new K(16777215,.7);this.previewScene.add(t);const i=new P(16777215,1.2);i.position.set(5,8,5),this.previewScene.add(i);const s=new fe(10,20,2236979,1579044);this.previewScene.add(s),this.resizePreview(),this.startPreviewLoop()}loadPreviewFromFile(e){const t=URL.createObjectURL(e);this.loader.loadAsync(t).then(i=>{URL.revokeObjectURL(t),this.setPreviewModel(i.scene),this.detectedClipNames=i.animations.map(s=>s.name).filter(s=>s&&s!=="default")}).catch(()=>{URL.revokeObjectURL(t);const i=this.overlay.querySelector(".upload-preview-info");i&&(i.textContent=o()==="en"?"Model load failed":"模型加载失败,请检查文件是否完整")})}setPreviewModel(e){if(!this.previewScene||!this.previewCamera||!this.previewControls)return;this.previewPivot&&this.previewScene&&this.previewScene.remove(this.previewPivot),this.previewModel&&this.previewScene&&this.previewScene.remove(this.previewModel);const t=new Ue;t.add(e),this.previewModel=e,this.previewPivot=t,e.traverse(c=>{if(c.isMesh){const v=Array.isArray(c.material)?c.material:[c.material];for(const f of v)f.transparent&&(f.transparent=!1,f.alphaTest=.5,f.opacity=1),f.alphaMap&&(f.alphaTest=Math.max(f.alphaTest,.5)),f.depthWrite=!0,f.side=te}}),this.previewScene.add(t);const i=new B().setFromObject(e),s=i.getSize(new E),n=i.getCenter(new E),a=Math.max(s.x,s.y,s.z);a>0&&(this.basePreviewScale=2.5/a,t.scale.setScalar(this.basePreviewScale),i.setFromObject(t),i.getCenter(n),e.position.sub(new E(n.x/this.basePreviewScale,n.y/this.basePreviewScale,n.z/this.basePreviewScale)),i.setFromObject(t),e.position.y-=i.min.y/this.basePreviewScale);const r=new B().setFromObject(t),l=r.getCenter(new E);this.previewControls.target.copy(l);const p=r.getSize(new E),u=Math.max(p.x,p.y,p.z)*2;this.previewCamera.position.set(l.x,l.y+u*.25,l.z+u);const h=this.overlay.querySelector(".upload-preview-info");h&&(h.textContent=`${s.x.toFixed(1)} × ${s.y.toFixed(1)} × ${s.z.toFixed(1)}`),this.applyPreviewScale()}resizePreview(){if(!this.previewCanvas||!this.previewRenderer||!this.previewCamera)return;const e=this.previewCanvas.clientWidth,t=this.previewCanvas.clientHeight;e>0&&t>0&&(this.previewRenderer.setSize(e,t),this.previewCamera.aspect=e/t,this.previewCamera.updateProjectionMatrix())}startPreviewLoop(){const e=()=>{var t;this.previewRenderer&&(this.animId=requestAnimationFrame(e),this.resizePreview(),(t=this.previewControls)==null||t.update(),this.previewRenderer.render(this.previewScene,this.previewCamera))};this.animId=requestAnimationFrame(e)}stopPreview(){var e,t;cancelAnimationFrame(this.animId),this.previewPivot&&this.previewScene&&this.previewScene.remove(this.previewPivot),(e=this.previewRenderer)==null||e.dispose(),(t=this.previewControls)==null||t.dispose(),this.previewRenderer=null,this.previewScene=null,this.previewCamera=null,this.previewControls=null,this.previewModel=null,this.previewPivot=null,this.previewCanvas=null}captureThumbnail(){if(!(!this.previewRenderer||!this.previewScene||!this.previewCamera)){this.previewRenderer.render(this.previewScene,this.previewCamera);try{return this.previewRenderer.domElement.toDataURL("image/png",.6)}catch{return}}}escHtml(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}}const S=class S{constructor(e,t,i){d(this,"tabsEl");d(this,"gridEl");d(this,"scrollEl");d(this,"allGroups",[]);d(this,"customGroups",[]);d(this,"filter","all");d(this,"listeners",[]);d(this,"onUploadCallback",null);d(this,"previewListener",null);d(this,"deselectedListener",null);d(this,"editListener",null);d(this,"deleteListener",null);d(this,"animListener",null);d(this,"charUpload",null);d(this,"activePopover",null);d(this,"customModelsReadyCallback",null);d(this,"currentGroupId","");d(this,"candidateGroup",null);d(this,"selectedVariant",1);d(this,"selectedColor",1);this.tabsEl=e,this.gridEl=t,this.scrollEl=i,this.allGroups=[...de()],this.probeLibraryAssets(),this.loadCustomModels(),this.initTabs(),this.render()}async probeLibraryAssets(){try{(await fetch("/ext-assets/Characters_1/thumbnails/lib-1.webp",{method:"HEAD"})).ok&&(this.allGroups=[...de(),...Xe()],this.render())}catch{}}onSelect(e){this.listeners.push(e)}onUpload(e){this.onUploadCallback=e}onPreview(e){this.previewListener=e}onDeselected(e){this.deselectedListener=e}onEdit(e){this.editListener=e}onDelete(e){this.deleteListener=e}onAnimMapping(e){this.animListener=e}onCustomModelsReady(e){this.customModelsReadyCallback=e}setCurrentGroupId(e){this.currentGroupId=e,this.render()}setCandidateGroup(e){this.candidateGroup=e,this.render()}async refreshCustomModels(){await this.loadCustomModels(),this.render()}getGroupById(e){return[...this.allGroups,...this.customGroups].find(t=>t.id===e)}getAllAndCustomGroups(){return[...this.allGroups,...this.customGroups]}confirm(){if(!this.candidateGroup)return;const e=N(this.candidateGroup,this.selectedVariant,this.selectedColor);for(const t of this.listeners)t(e,this.candidateGroup.id)}async loadCustomModels(){var e;try{const s=(await(await fetch("/custom-assets/_api/list",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"})).json()).assets??[];this.customGroups=s.filter(n=>n.kind==="character").map(n=>({id:`custom-${n.id}`,displayName:n.name,source:"custom",thumbnailUrl:n.thumbnail,variants:[],colors:[],meshUrlPattern:`/custom-assets/characters/${n.fileName}`,animMapping:n.animMapping,detectedClips:n.detectedClips,animFileUrls:n.animFileUrls,assetId:n.id}))}catch{this.customGroups=[]}(e=this.customModelsReadyCallback)==null||e.call(this)}initTabs(){this.tabsEl.addEventListener("click",e=>{const t=e.target.closest(".cw-picker-tab");t&&(this.filter=t.dataset.filter??"all",this.tabsEl.querySelectorAll(".cw-picker-tab").forEach(i=>i.classList.toggle("active",i===t)),this.render())})}getFilteredGroups(){return this.filter==="custom"?this.customGroups:this.allGroups}render(){var i;this.gridEl.innerHTML="",this.dismissPopover();const e=this.getFilteredGroups(),t=this.filter==="custom";if(t&&e.length===0){this.renderEmptyState();return}t&&this.gridEl.appendChild(this.createAddCard());for(const s of e){const n=document.createElement("div"),a=s.id===this.currentGroupId,r=((i=this.candidateGroup)==null?void 0:i.id)===s.id;let l="cw-model-card";a&&(l+=" current"),r&&!a&&(l+=" candidate"),n.className=l,n.dataset.groupId=s.id;const p=document.createElement("div");if(p.className="cw-model-card-thumb",s.thumbnailUrl){const u=document.createElement("img");u.src=s.thumbnailUrl,u.alt=s.displayName,u.onerror=()=>{u.remove(),p.innerHTML=S.SVG_PERSON},p.appendChild(u)}else p.innerHTML=s.source==="custom"?S.SVG_UPLOAD:S.SVG_PERSON;if(n.appendChild(p),t){const u=document.createElement("div");u.className="cw-model-card-name-row";const h=document.createElement("span");h.className="cw-model-card-name",h.textContent=s.displayName,u.appendChild(h);const c=document.createElement("button");c.className="cw-card-more-btn",c.title=o()==="en"?"More":"更多",c.innerHTML=S.SVG_MORE,c.addEventListener("click",v=>{v.stopPropagation(),this.showCardPopover(c,s)}),u.appendChild(c),n.appendChild(u)}else{const u=document.createElement("div");u.className="cw-model-card-name",u.textContent=s.displayName,n.appendChild(u)}n.addEventListener("click",u=>{var c,v,f;if(u.target.closest(".cw-card-more-btn"))return;if(((c=this.candidateGroup)==null?void 0:c.id)===s.id){this.candidateGroup=null,this.render(),(v=this.deselectedListener)==null||v.call(this);return}this.candidateGroup=s,this.selectedVariant=s.variants[0]??1,this.selectedColor=s.colors[0]??1,this.render();const h=N(s,this.selectedVariant,this.selectedColor);(f=this.previewListener)==null||f.call(this,h,s)}),this.gridEl.appendChild(n)}}createAddCard(){const e=document.createElement("div");return e.className="cw-model-card cw-model-card-upload",e.innerHTML=`
105
+ <div class="cw-model-card-thumb">${S.SVG_PLUS}</div>
106
+ <div class="cw-model-card-name">${o()==="en"?"Add Model":"添加模型"}</div>
107
+ `,e.addEventListener("click",()=>this.handleUpload()),e}renderEmptyState(){const e=document.createElement("div");e.className="cw-picker-empty",e.innerHTML=`
108
+ <div class="cw-picker-empty-icon">
109
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
110
+ <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
111
+ <polyline points="3.29 7 12 12 20.71 7"/>
112
+ <line x1="12" y1="22" x2="12" y2="12"/>
113
+ </svg>
114
+ </div>
115
+ <div class="cw-picker-empty-text">${o()==="en"?"No custom models yet":"还没有自定义角色模型"}</div>
116
+ <div class="cw-picker-empty-hint">${o()==="en"?"Upload .glb / .gltf 3D models":"上传 .glb / .gltf 格式的 3D 角色模型"}</div>
117
+ `;const t=document.createElement("button");t.className="cw-picker-empty-btn",t.textContent=o()==="en"?"+ Add Model":"+ 添加角色模型",t.addEventListener("click",()=>this.handleUpload()),e.appendChild(t),this.gridEl.appendChild(e)}showCardPopover(e,t){this.dismissPopover();const i=document.createElement("div");i.className="custom-popover",i.innerHTML=`
118
+ <button class="popover-item" data-action="edit">${S.SVG_EDIT} ${o()==="en"?"Edit":"编辑"}</button>
119
+ <button class="popover-item" data-action="anim">${S.SVG_ANIM} ${o()==="en"?"Animations":"动画映射"}</button>
120
+ <button class="popover-item popover-danger" data-action="delete">${S.SVG_DELETE} ${o()==="en"?"Delete":"删除"}</button>
121
+ `,e.closest(".cw-model-card");const s=e.getBoundingClientRect(),n=this.gridEl.getBoundingClientRect();i.style.position="absolute",i.style.top=`${s.bottom-n.top+4}px`,i.style.right=`${n.right-s.right}px`,i.style.zIndex="50",i.querySelector('[data-action="edit"]').addEventListener("click",r=>{var l;r.stopPropagation(),this.dismissPopover(),(l=this.editListener)==null||l.call(this,t)}),i.querySelector('[data-action="anim"]').addEventListener("click",r=>{var l;r.stopPropagation(),this.dismissPopover(),(l=this.animListener)==null||l.call(this,t)}),i.querySelector('[data-action="delete"]').addEventListener("click",r=>{var l;r.stopPropagation(),this.dismissPopover(),(l=this.deleteListener)==null||l.call(this,t)}),this.gridEl.style.position="relative",this.gridEl.appendChild(i),this.activePopover=i;const a=r=>{!i.contains(r.target)&&!e.contains(r.target)&&(this.dismissPopover(),document.removeEventListener("click",a))};setTimeout(()=>document.addEventListener("click",a))}dismissPopover(){var e;(e=this.activePopover)==null||e.remove(),this.activePopover=null}handleUpload(){this.charUpload||(this.charUpload=new pe),this.charUpload.open(async e=>{var t;await this.loadCustomModels(),this.filter="custom",this.tabsEl.querySelectorAll(".cw-picker-tab").forEach(i=>i.classList.toggle("active",i.dataset.filter==="custom")),this.render(),(t=this.onUploadCallback)==null||t.call(this)})}openEditForGroup(e){if(e.source!=="custom")return;const t=e.id.replace("custom-","");this.charUpload||(this.charUpload=new pe),this.charUpload.openEdit(t,async()=>{var i;await this.loadCustomModels(),this.render(),(i=this.onUploadCallback)==null||i.call(this)})}};d(S,"SVG_PERSON",'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>'),d(S,"SVG_UPLOAD",'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>'),d(S,"SVG_PLUS",'<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>'),d(S,"SVG_MORE",'<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" stroke="none"><circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/></svg>'),d(S,"SVG_EDIT",'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>'),d(S,"SVG_DELETE",'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>'),d(S,"SVG_ANIM",'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 3l14 9-14 9V3z"/></svg>');let V=S;class et{constructor(){d(this,"overlay");d(this,"loader",new X);d(this,"renderer",null);d(this,"scene",null);d(this,"camera",null);d(this,"controls",null);d(this,"model",null);d(this,"mixer",null);d(this,"currentAction",null);d(this,"clock",new ue);d(this,"animId",0);d(this,"allClips",[]);d(this,"sources",[]);d(this,"mapping",{});d(this,"activeSlot",null);d(this,"meshUrl","");d(this,"resolve",null);this.overlay=document.createElement("div"),this.overlay.className="anim-dialog-overlay",this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.cancel()}),document.getElementById("editor-root").appendChild(this.overlay)}open(e){return new Promise(t=>{this.resolve=t,this.mapping=e.animMapping?{...e.animMapping}:{},this.sources=[],this.allClips=[],this.activeSlot=null,this.meshUrl=e.meshUrl,this.overlay.classList.add("open"),this.buildUI(),this.initPreview(),this.beginLoading(e)})}cancel(){this.finish(null)}confirm(){this.finish({animMapping:{...this.mapping},detectedClips:this.getAllClipNames(),animFileUrls:this.sources.filter(e=>e.removable).map(e=>e.url)})}finish(e){var t;this.overlay.classList.remove("open"),this.stopPreview(),(t=this.resolve)==null||t.call(this,e),this.resolve=null}getAllClipNames(){return[...new Set(this.sources.flatMap(e=>e.clips))]}async beginLoading(e){this.setStatus(o()==="en"?"Loading model...":"正在加载模型...");const t=await this.loadModel(e.meshUrl);if(t.length>0&&(this.sources.push({label:o()==="en"?"Embedded":"模型内嵌",url:e.meshUrl,clips:t.map(i=>i.name),removable:!1}),this.allClips.push(...t)),e.animFileUrls&&e.animFileUrls.length>0)for(const i of e.animFileUrls)this.setStatus(o()==="en"?"Loading animations...":"正在加载动画文件..."),await this.loadAnimFileIntoSources(i,!0);Object.keys(this.mapping).length===0&&this.getAllClipNames().length>0&&(this.mapping=R(this.getAllClipNames())),this.setStatus(null),this.refreshAll(),this.mapping.idle&&this.playSlot("idle")}setStatus(e){const t=this.overlay.querySelector("#amd-loading");t&&(e?(t.style.display="flex",t.textContent=e):t.style.display="none")}async loadAnimFileIntoSources(e,t){try{const s=(await this.loader.loadAsync(e)).animations.filter(r=>r.name&&r.name!=="default");if(s.length===0)return!1;const n=decodeURIComponent(e.split("/").pop()||e),a=n.length>24?n.slice(0,21)+"...":n;this.sources.push({label:a,url:e,clips:s.map(r=>r.name),removable:t});for(const r of s){const l=this.allClips.findIndex(p=>p.name===r.name);l>=0?this.allClips[l]=r:this.allClips.push(r)}return!0}catch{return!1}}buildUI(){this.overlay.innerHTML=`
122
+ <div class="anim-dialog">
123
+ <div class="anim-dialog-header">
124
+ <span class="anim-dialog-title">${o()==="en"?"Animation Mapping":"动画映射配置"}</span>
125
+ <button class="anim-dialog-close" aria-label="${o()==="en"?"Close":"关闭"}">
126
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
127
+ </button>
128
+ </div>
129
+ <div class="anim-dialog-body">
130
+ <div class="anim-dialog-left">
131
+ <div class="amd-section-label">${o()==="en"?"Sources":"动画源"}</div>
132
+ <div class="amd-sources" id="amd-sources"></div>
133
+ <button class="amd-add-btn" id="amd-add-anim">
134
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
135
+ ${o()==="en"?"Add Animation":"追加动画文件"}
136
+ </button>
137
+ <div class="amd-divider"></div>
138
+ <div class="amd-section-label">${o()==="en"?"Slot Mapping":"槽位映射"}</div>
139
+ <div class="amd-map-list" id="amd-mapping"></div>
140
+ </div>
141
+ <div class="anim-dialog-right">
142
+ <div class="amd-preview-wrap" id="amd-preview">
143
+ <canvas class="amd-preview-canvas" id="amd-canvas"></canvas>
144
+ <div class="amd-preview-loading" id="amd-loading">${o()==="en"?"Loading...":"加载中..."}</div>
145
+ <div class="amd-slot-tabs" id="amd-slot-tabs"></div>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ <div class="anim-dialog-footer">
150
+ <button class="amd-btn amd-btn-cancel" id="amd-cancel">${o()==="en"?"Cancel":"取消"}</button>
151
+ <button class="amd-btn amd-btn-confirm" id="amd-confirm">${o()==="en"?"Save":"确认保存"}</button>
152
+ </div>
153
+ </div>
154
+ `,this.overlay.querySelector(".anim-dialog-close").addEventListener("click",()=>this.cancel()),this.overlay.querySelector("#amd-cancel").addEventListener("click",()=>this.cancel()),this.overlay.querySelector("#amd-confirm").addEventListener("click",()=>this.confirm()),this.overlay.querySelector("#amd-add-anim").addEventListener("click",()=>this.handleAddAnimFile())}refreshAll(){this.renderSources(),this.renderMapping(),this.renderSlotTabs()}renderSources(){const e=this.overlay.querySelector("#amd-sources");if(e){if(e.innerHTML="",this.sources.length===0){e.innerHTML=`<div class="amd-source-empty">${o()==="en"?"No animation sources":"未检测到动画源"}</div>`;return}for(const t of this.sources){const i=document.createElement("div");i.className="amd-source-row";const s=document.createElement("div");if(s.className="amd-source-info",s.innerHTML=`<span class="amd-source-name" title="${this.esc(t.label)}">${this.esc(t.label)}</span><span class="amd-source-badge">${t.clips.length}</span>`,i.appendChild(s),t.removable){const n=document.createElement("div");n.className="amd-source-actions";const a=document.createElement("button");a.className="amd-source-act",a.title=o()==="en"?"Replace":"替换",a.innerHTML='<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0115-6.7L21 8"/><path d="M3 22v-6h6"/><path d="M21 12a9 9 0 01-15 6.7L3 16"/></svg>',a.addEventListener("click",()=>this.replaceSource(t)),n.appendChild(a);const r=document.createElement("button");r.className="amd-source-act amd-source-del",r.title=o()==="en"?"Delete":"删除",r.innerHTML='<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>',r.addEventListener("click",()=>this.removeSource(t)),n.appendChild(r),i.appendChild(n)}else{const n=document.createElement("span");n.className="amd-source-tag",n.textContent=o()==="en"?"Built-in":"内嵌",i.appendChild(n)}e.appendChild(i)}}}renderMapping(){const e=this.overlay.querySelector("#amd-mapping");if(!e)return;const t=this.getAllClipNames();e.innerHTML="";for(const i of z){const s=j[i]??i,n=this.mapping[i]??"",a=document.createElement("div");a.className="amd-map-row";const r=document.createElement("span");r.className="amd-map-label",r.textContent=s,a.appendChild(r);const l=document.createElement("div");l.className="amd-map-dd",this.buildCustomSelect(l,t,n,p=>{p?this.mapping[i]=p:delete this.mapping[i],this.renderSlotTabs(),p&&this.playSlot(i)}),a.appendChild(l),e.appendChild(a)}}buildCustomSelect(e,t,i,s){const n=document.createElement("button");n.type="button",n.className="amd-dd-trigger",n.innerHTML=`<span class="amd-dd-text${i?"":" placeholder"}">${i||(o()==="en"?"-- Unmapped --":"-- 未映射 --")}</span>`;const a=document.createElement("div");a.className="amd-dd-menu";const r=()=>{a.innerHTML="";const h=document.createElement("button");h.type="button",h.className=`amd-dd-opt${i?"":" selected"}`,h.textContent=o()==="en"?"-- Unmapped --":"-- 未映射 --",h.addEventListener("click",()=>{l("")}),a.appendChild(h);for(const c of t){const v=document.createElement("button");v.type="button",v.className=`amd-dd-opt${c===i?" selected":""}`,v.textContent=c,v.addEventListener("click",()=>{l(c)}),a.appendChild(v)}},l=h=>{i=h,n.innerHTML=`<span class="amd-dd-text${h?"":" placeholder"}">${h||(o()==="en"?"-- Unmapped --":"-- 未映射 --")}</span>`,p(),s(h)},p=()=>{n.classList.remove("open"),a.classList.remove("open"),document.removeEventListener("click",u)},u=h=>{e.contains(h.target)||p()};n.addEventListener("click",h=>{h.stopPropagation(),a.classList.contains("open")?p():(r(),n.classList.add("open"),a.classList.add("open"),document.addEventListener("click",u))}),e.appendChild(n),e.appendChild(a)}renderSlotTabs(){const e=this.overlay.querySelector("#amd-slot-tabs");if(e){e.innerHTML="";for(const t of z){const i=!!this.mapping[t],s=document.createElement("button");s.className=`amd-slot-tab${i?"":" disabled"}${this.activeSlot===t?" active":""}`,s.textContent=j[t]??t,i&&s.addEventListener("click",()=>this.playSlot(t)),e.appendChild(s)}}}async handleAddAnimFile(){const e=await this.pickFile(".glb,.gltf");if(!e)return;const t=this.overlay.querySelector("#amd-add-anim");t&&(t.disabled=!0,t.textContent=o()==="en"?"Uploading...":"上传中...");try{const i=await this.uploadAnimFile(e);i&&(await this.loadAnimFileIntoSources(i,!0),this.mapping=R(this.getAllClipNames(),this.mapping),this.refreshAll())}catch{}t&&(t.disabled=!1,t.innerHTML=`<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg> ${o()==="en"?"Add Animation":"追加动画文件"}`)}async replaceSource(e){const t=await this.pickFile(".glb,.gltf");if(t)try{const i=await this.uploadAnimFile(t);if(!i)return;await this.removeSource(e),await this.loadAnimFileIntoSources(i,!0),this.mapping=R(this.getAllClipNames(),this.mapping),this.refreshAll()}catch{}}async removeSource(e){try{await fetch("/citizen-workshop/_api/delete-anim",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:e.url})})}catch{}const t=new Set(e.clips);this.sources=this.sources.filter(i=>i!==e),this.allClips=this.allClips.filter(i=>!t.has(i.name));for(const i of z)this.mapping[i]&&t.has(this.mapping[i])&&delete this.mapping[i];this.refreshAll()}pickFile(e){return new Promise(t=>{const i=document.createElement("input");i.type="file",i.accept=e,i.onchange=()=>{var s;return t(((s=i.files)==null?void 0:s[0])??null)},i.click()})}async uploadAnimFile(e){const t=await e.arrayBuffer(),i=new Uint8Array(t);let s="";for(let r=0;r<i.byteLength;r++)s+=String.fromCharCode(i[r]);const a=await(await fetch("/citizen-workshop/_api/upload-anim",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({data:btoa(s),fileName:e.name})})).json();return a.success?a.url:null}initPreview(){const e=this.overlay.querySelector("#amd-canvas");if(!e)return;this.renderer=new Y({canvas:e,antialias:!0}),this.renderer.setPixelRatio(Math.min(devicePixelRatio,2)),this.renderer.outputColorSpace=W,this.scene=new Q,this.scene.background=new ee(1710622),this.camera=new Z(36,1,.1,100),this.camera.position.set(0,1.5,5),this.controls=new ie(this.camera,e),this.controls.enableDamping=!0,this.controls.target.set(0,1,0),this.controls.enablePan=!1,this.scene.add(new K(16774624,1.2));const t=new P(16777215,1.8);t.position.set(5,8,5),this.scene.add(t);const i=new P(16772829,.6);i.position.set(-4,4,-3),this.scene.add(i);const s=new fe(10,20,2763314,2105384);s.position.y=0,this.scene.add(s),this.resizePreview(),this.startLoop()}async loadModel(e){try{const t=await this.loader.loadAsync(e),i=t.scene;i.scale.setScalar(1),i.updateMatrixWorld(!0);const s=new B().setFromObject(i),n=s.getSize(new E),a=s.getCenter(new E),r=Math.max(n.x,n.y,n.z);if(r>0){const c=2.5/r;i.scale.setScalar(c),s.setFromObject(i),s.getCenter(a),i.position.sub(a),s.setFromObject(i),i.position.y-=s.min.y}this.model&&this.scene&&this.scene.remove(this.model),this.model=i,i.traverse(c=>{if(c.isMesh){const v=Array.isArray(c.material)?c.material:[c.material];for(const f of v)f.transparent&&(f.transparent=!1,f.alphaTest=.5,f.opacity=1),f.alphaMap&&(f.alphaTest=Math.max(f.alphaTest,.5)),f.depthWrite=!0,f.side=te}}),this.scene.add(i),this.mixer=new me(i);const l=new B().setFromObject(i),p=l.getCenter(new E),u=l.getSize(new E),h=Math.max(u.x,u.y,u.z)*2;return this.controls.target.copy(p),this.camera.position.set(p.x,p.y+h*.25,p.z+h),t.animations.filter(c=>c.name&&c.name!=="default")}catch{return[]}}playSlot(e){const t=this.mapping[e];if(!t||!this.mixer)return;const i=this.allClips.find(n=>n.name===t);if(!i)return;this.activeSlot=e;const s=this.mixer.clipAction(i);this.currentAction&&this.currentAction!==s?(s.reset().play(),this.currentAction.crossFadeTo(s,.25,!1)):s.reset().play(),this.currentAction=s,this.renderSlotTabs()}resizePreview(){const e=this.overlay.querySelector("#amd-canvas");if(!e||!this.renderer||!this.camera)return;const t=e.clientWidth,i=e.clientHeight;t>0&&i>0&&(this.renderer.setSize(t,i),this.camera.aspect=t/i,this.camera.updateProjectionMatrix())}startLoop(){const e=()=>{var t,i;this.renderer&&(this.animId=requestAnimationFrame(e),this.resizePreview(),(t=this.mixer)==null||t.update(this.clock.getDelta()),(i=this.controls)==null||i.update(),this.renderer.render(this.scene,this.camera))};this.clock.start(),this.animId=requestAnimationFrame(e)}stopPreview(){var e,t,i;cancelAnimationFrame(this.animId),this.model&&this.scene&&this.scene.remove(this.model),(e=this.mixer)==null||e.stopAllAction(),(t=this.renderer)==null||t.dispose(),(i=this.controls)==null||i.dispose(),this.renderer=null,this.scene=null,this.camera=null,this.controls=null,this.model=null,this.mixer=null,this.currentAction=null,this.allClips=[]}esc(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;")}}const $=class ${constructor(){d(this,"container");d(this,"config");d(this,"roster");d(this,"stage");d(this,"picker");d(this,"soulEditor");d(this,"selection",null);d(this,"started",!1);d(this,"undoHistory",[]);d(this,"redoHistory",[]);d(this,"configChangedListeners",[]);d(this,"soulCache",new Map);d(this,"_activeVariant",1);d(this,"_activeColor",1);d(this,"agentList",[]);d(this,"buildingList",[]);d(this,"animDialog",null);d(this,"lastRawHeight",1);d(this,"lastModelSource","builtin");d(this,"pickerOpen",!1);d(this,"prePickerAvatarId","");this.container=document.getElementById("citizen-workshop"),this.config=this.loadDraft()??oe(),this.initRoster(),this.initStage(),this.initPicker(),this.restoreGroupTransformsFromConfig(),this.roster.setAvatarResolver((e,t)=>this.resolveAvatarUrl(e,t)),this.picker.onCustomModelsReady(()=>{this.restoreGroupTransformsFromConfig(),this.roster.render()}),this.initAddButton(),this.initModelChangeBtn(),this.loadRemoteData()}async loadRemoteData(){const[e,t,i]=await Promise.all([this.loadFromFile(),this.fetchAgents(),this.fetchBuildings()]);e&&!this.loadDraft()&&(this.config=e,this.restoreGroupTransformsFromConfig(),this.roster.setConfig(this.config),this.renderInspector(),this.updateModelBar(),this.loadSelectedCharacter()),this.agentList=t,this.buildingList=i,this.selection&&this.renderInspector()}async fetchAgents(){try{return(await(await fetch("/citizen-workshop/_api/agents",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"})).json()).agents??[]}catch{return[]}}async fetchBuildings(){try{return(await(await fetch("/citizen-workshop/_api/buildings",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"})).json()).buildings??[]}catch{return[]}}async fetchSoulContent(e){if(!e)return"";if(this.soulCache.has(e))return this.soulCache.get(e);try{const s=(await(await fetch("/citizen-workshop/_api/load-soul",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e})})).json()).content??"";return this.soulCache.set(e,s),s}catch{return""}}initStage(){const e=document.getElementById("cw-stage-canvas");this.stage=new Qe(e),this.stage.setOnRawHeight((t,i)=>{var s;this.lastRawHeight=t,this.lastModelSource=i,this.getCurrentAvatarId(),((s=this.selection)==null?void 0:s.type)==="citizen"&&this.config.citizens.find(n=>n.id===this.selection.id),this.pickerOpen||(this.ensureTransformForCurrentGroup(t,i),this.renderInspector())})}initPicker(){const e=document.getElementById("cw-picker-tabs"),t=document.getElementById("cw-picker-grid"),i=document.getElementById("cw-picker-scroll");this.picker=new V(e,t,i),this.picker.onSelect((s,n)=>{this.applyModel(n,s)}),this.picker.onPreview((s,n)=>{this.stage.setCharacter(s,void 0,void 0),this.updatePickerFooter(n.displayName)}),this.picker.onDeselected(()=>{this.loadSelectedCharacter(),this.updatePickerFooter(null)}),this.picker.onEdit(s=>{this.picker.openEditForGroup(s)}),this.picker.onAnimMapping(s=>{this.openAnimDialogForGroup(s)}),this.picker.onDelete(async s=>{if(!await this.showConfirm(o()==="en"?"Delete Model":"删除模型",o()==="en"?`Delete "${s.displayName}"? This cannot be undone.`:`确定要删除「${s.displayName}」吗?此操作不可撤销。`))return;const a=s.id.replace("custom-","");try{(await(await fetch("/custom-assets/_api/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:a})})).json()).success&&await this.picker.refreshCustomModels()}catch{}})}applyModel(e,t){if(!this.selection)return;const i=e.startsWith("lib-"),s=e.startsWith("custom-"),n=this.picker.getGroupById(e),a=n!=null&&n.modelTransform?{...n.modelTransform}:void 0;let r;if(i?r={...re}:s&&(r={idle:"",walk:"",typing:"",wave:"",cheer:"",reading:"",frustrated:"",dancing:""}),this.selection.type==="user")this.config.user.avatarId=e,this.config.user.animMapping=r;else if(this.selection.type==="steward")this.config.steward.avatarId=e,this.config.steward.animMapping=r;else if(this.selection.type==="citizen"){const l=this.config.citizens.find(p=>p.id===this.selection.id);l&&(l.avatarId=e,l.animMapping=r)}this.updateModelBar(),this.stage.setCharacter(t,void 0,r,n==null?void 0:n.animFileUrls,a),this.roster.render(),this.renderInspector(),this.saveDraft()}initModelChangeBtn(){const e=document.getElementById("cw-model-change"),t=document.getElementById("cw-picker-close"),i=document.getElementById("cw-picker-cancel"),s=document.getElementById("cw-picker-confirm"),n=document.getElementById("cw-center"),a=()=>{this.pickerOpen=!0,this.prePickerAvatarId=this.getCurrentAvatarId(),n==null||n.classList.add("picker-open"),this.picker.setCurrentGroupId(this.prePickerAvatarId),this.picker.setCandidateGroup(null),this.updatePickerFooter(null),this.scheduleStageResize()},r=l=>{this.pickerOpen=!1,n==null||n.classList.remove("picker-open"),l&&this.picker.candidateGroup?this.picker.confirm():this.loadSelectedCharacter(),this.picker.setCandidateGroup(null),this.scheduleStageResize()};e==null||e.addEventListener("click",()=>a()),t==null||t.addEventListener("click",()=>r(!1)),i==null||i.addEventListener("click",()=>r(!1)),s==null||s.addEventListener("click",()=>r(!0))}scheduleStageResize(){const e=()=>this.stage.resize();e(),setTimeout(e,50),setTimeout(e,150),setTimeout(e,280)}getCurrentAvatarId(){if(!this.selection)return"";if(this.selection.type==="user")return this.config.user.avatarId;if(this.selection.type==="steward")return this.config.steward.avatarId;if(this.selection.type==="citizen"){const e=this.config.citizens.find(t=>t.id===this.selection.id);return(e==null?void 0:e.avatarId)??""}return""}updatePickerFooter(e){const t=document.getElementById("cw-footer-current"),i=document.getElementById("cw-footer-candidate"),s=document.getElementById("cw-picker-confirm"),n=document.querySelector(".cw-picker-footer-arrow"),a=document.querySelector(".cw-picker-footer-actions"),r=this.picker.getGroupById(this.getCurrentAvatarId()),l=(r==null?void 0:r.displayName)||"--";t&&(t.textContent=`${o()==="en"?"Current: ":"已选角色:"}${l}`);const p=!!e;if(n&&(n.style.display=p?"":"none"),i&&(i.style.display=p?"":"none",i.textContent=e?`${o()==="en"?"Change to: ":"更换为:"}${e}`:""),a&&(a.style.display=p?"":"none"),s){const u=this.picker.candidateGroup,h=(u==null?void 0:u.id)===this.getCurrentAvatarId();s.disabled=!u||h,s.textContent=h?o()==="en"?"Already current":"已是当前模型":o()==="en"?"Confirm":"确认更换"}}initRoster(){const e=document.getElementById("cw-roster-list");this.roster=new Ye(e,this.config),this.roster.onChange(t=>{this.selection=t,this.onSelectionChanged()}),this.roster.onDelete(t=>{const i=this.config.citizens.find(n=>n.id===t),s=(i==null?void 0:i.name)||(o()==="en"?"this citizen":"该居民");this.showConfirm(o()==="en"?"Delete Citizen":"删除居民",o()==="en"?`Delete "${s}"?`:`确定要删除「${s}」吗?`).then(n=>{n&&(this.roster.deleteCitizen(t),this.saveDraft())})})}initAddButton(){var e;(e=document.getElementById("cw-add-citizen"))==null||e.addEventListener("click",()=>{this.showAddCitizenDialog()})}showAddCitizenDialog(){const e=document.getElementById("add-citizen-overlay"),t=document.getElementById("add-citizen-name"),i=document.getElementById("add-citizen-ok"),s=document.getElementById("add-citizen-cancel");if(!e||!t)return;t.value="",e.classList.add("open"),setTimeout(()=>t.focus(),50);const n=()=>{e.classList.remove("open"),i.removeEventListener("click",r),s.removeEventListener("click",l),e.removeEventListener("click",p),t.removeEventListener("keydown",u)},a=()=>{const h=t.value.trim();if(!h){t.focus();return}n(),this.roster.addCitizen(h),this.saveDraft()},r=()=>a(),l=()=>n(),p=h=>{h.target===e&&n()},u=h=>{h.key==="Enter"&&a(),h.key==="Escape"&&n()};i.addEventListener("click",r),s.addEventListener("click",l),e.addEventListener("click",p),t.addEventListener("keydown",u)}showConfirm(e,t){return new Promise(i=>{const s=document.getElementById("confirm-overlay");document.getElementById("confirm-title").textContent=e,document.getElementById("confirm-message").textContent=t,s.classList.add("open");const n=h=>{s.classList.remove("open"),p.removeEventListener("click",a),u.removeEventListener("click",r),s.removeEventListener("click",l),i(h)},a=()=>n(!0),r=()=>n(!1),l=h=>{h.target===s&&n(!1)},p=document.getElementById("confirm-ok"),u=document.getElementById("confirm-cancel");p.addEventListener("click",a),u.addEventListener("click",r),s.addEventListener("click",l)})}onSelectionChanged(){this.renderInspector(),this.updateModelBar(),this.loadSelectedCharacter()}loadSelectedCharacter(){if(!this.selection)return;let e="",t;if(this.selection.type==="user")e=this.config.user.avatarId,t=this.config.user.animMapping;else if(this.selection.type==="steward")e=this.config.steward.avatarId,t=this.config.steward.animMapping;else if(this.selection.type==="citizen"){const a=this.config.citizens.find(r=>r.id===this.selection.id);e=(a==null?void 0:a.avatarId)??"",t=a==null?void 0:a.animMapping}if(!e)return;e.startsWith("lib-")&&!t&&(t={...re}),this.picker.setCurrentGroupId(e);const s=this.picker.getGroupById(e),n=s==null?void 0:s.modelTransform;if(s){s.source==="custom"&&s.animMapping&&(t=s.animMapping);const a=N(s);this.stage.setCharacter(a,void 0,t,s.animFileUrls,n)}else{const r=`./assets/models/characters/character-${e.replace("char-","")}.glb`;this.stage.setCharacter(r,void 0,t,void 0,n)}}renderInspector(){const e=document.getElementById("cw-inspector-content");if(!this.selection){e.innerHTML=`<div class="cw-empty-state">${o()==="en"?"Select a character":"选择一个角色"}</div>`;return}if(this.selection.type==="user")this.renderUserInspector(e);else if(this.selection.type==="steward")this.renderStewardInspector(e);else if(this.selection.type==="citizen"){const t=this.selection.id,i=this.config.citizens.find(s=>s.id===t);i&&this.renderCitizenInspector(e,i)}}renderUserInspector(e){var i;const t=this.config.user;e.innerHTML=`
155
+ <div class="cw-identity-row">
156
+ <div class="cw-avatar-clickable" id="cw-user-avatar-btn" title="${o()==="en"?"Change avatar":"点击更换头像"}">
157
+ ${this.resolveAvatarHtml(t.name,t.avatarUrl,t.avatarId)}
158
+ <div class="cw-avatar-hover-overlay">
159
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
160
+ </div>
161
+ </div>
162
+ <div class="cw-identity-fields">
163
+ <input class="cw-input cw-input-name" id="cw-user-name" value="${this.esc(t.name)}" placeholder="${o()==="en"?"Name":"角色名"}" />
164
+ </div>
165
+ </div>
166
+ <div id="cw-user-transform" class="cw-field-conditional"></div>
167
+ `,(i=e.querySelector("#cw-user-name"))==null||i.addEventListener("input",s=>{this.config.user.name=s.target.value,this.roster.setConfig(this.config),this.saveDraft()}),this.bindAvatarClickable("#cw-user-avatar-btn",s=>{this.config.user.avatarUrl=s,this.renderInspector(),this.roster.render(),this.saveDraft()}),this.renderTransformSection(e,"#cw-user-transform")}renderStewardInspector(e){const t=this.config.steward;e.innerHTML=`
168
+ <div class="cw-identity-row">
169
+ <div class="cw-avatar-clickable" id="cw-stew-avatar-btn" title="${o()==="en"?"Change avatar":"点击更换头像"}">
170
+ ${this.resolveAvatarHtml(t.name,t.avatarUrl,t.avatarId)}
171
+ <div class="cw-avatar-hover-overlay">
172
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
173
+ </div>
174
+ </div>
175
+ <div class="cw-identity-fields">
176
+ <input class="cw-input cw-input-name" id="cw-stew-name" value="${this.esc(t.name)}" placeholder="${o()==="en"?"Name":"管家名"}" />
177
+ </div>
178
+ </div>
179
+ <div class="cw-field">
180
+ <div class="cw-field-label">${o()==="en"?"Persona Style":"人设风格"}</div>
181
+ <textarea class="cw-textarea" id="cw-stew-bio" placeholder="${o()==="en"?"Describe personality, style...":"描述管家的性格特征、说话风格…"}">${this.esc(t.bio)}</textarea>
182
+ </div>
183
+ <div id="cw-stew-model-variant" class="cw-field cw-field-conditional"></div>
184
+ <div id="cw-stew-model-color" class="cw-field cw-field-conditional"></div>
185
+ <div class="cw-field">
186
+ <div class="cw-field-label">${o()==="en"?"OpenClaw Binding":"OpenClaw 绑定"}</div>
187
+ <div class="cw-agent-auto-badge">
188
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
189
+ <span>${o()==="en"?"Auto-bound to main Agent":"自动绑定当前 OpenClaw 主 Agent"}</span>
190
+ </div>
191
+ </div>
192
+ <div class="cw-section cw-section-conditional">
193
+ <div class="cw-section-title">${o()==="en"?"Animation Mapping":"动画映射"}</div>
194
+ <div id="cw-stew-anim-container"></div>
195
+ </div>
196
+ <div id="cw-stew-transform" class="cw-field-conditional"></div>
197
+ `,this.bindInput("#cw-stew-name",i=>{this.config.steward.name=i,this.roster.setConfig(this.config)}),this.bindTextarea("#cw-stew-bio",i=>{this.config.steward.bio=i}),this.bindAvatarClickable("#cw-stew-avatar-btn",i=>{this.config.steward.avatarUrl=i,this.renderInspector(),this.roster.render(),this.saveDraft()}),this.renderModelSettingsSection(e,"#cw-stew-model-variant","#cw-stew-model-color",this.config.steward),this.renderAnimMappingSection(e,"#cw-stew-anim-container",this.config.steward),this.renderTransformSection(e,"#cw-stew-transform")}renderCitizenInspector(e,t){var u,h;const i=!!t.persona&&!t.useCustomPersona,s=!!t.useCustomPersona||!t.persona,n=!!t.persona;if(e.innerHTML=`
198
+ <div class="cw-avatar-standalone">
199
+ <div class="cw-avatar-clickable" id="cw-cit-avatar-btn" title="${o()==="en"?"Change avatar":"点击更换头像"}">
200
+ ${this.resolveAvatarHtml(t.name,t.avatarUrl,t.avatarId)}
201
+ <div class="cw-avatar-hover-overlay">
202
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
203
+ </div>
204
+ </div>
205
+ </div>
206
+ <div class="cw-persona-block${s?" cw-persona-custom":" cw-persona-default"}">
207
+ ${n?`
208
+ <div class="cw-persona-header">
209
+ <span class="cw-persona-header-label">${o()==="en"?"Custom Persona":"自定义人设"}</span>
210
+ <div class="pi-toggle${s?" active":""}" id="cw-cit-custom-toggle"></div>
211
+ </div>`:""}
212
+ <div class="cw-field">
213
+ <div class="cw-field-label">${o()==="en"?"Name":"名字"}</div>
214
+ <input class="cw-input cw-input-name" id="cw-cit-name" value="${this.esc(t.name)}" placeholder="${o()==="en"?"Name":"角色名"}" ${i?"readonly":""} />
215
+ </div>
216
+ <div class="cw-field">
217
+ <div class="cw-field-label">${o()==="en"?"Specialty":"专业技能"}</div>
218
+ <input class="cw-input" id="cw-cit-specialty" value="${this.esc(t.specialty)}" placeholder="${o()==="en"?"e.g. Frontend, PM...":"如:前端开发、产品经理…"}" ${i?"readonly":""} />
219
+ </div>
220
+ <div class="cw-field">
221
+ <div class="cw-field-label">${o()==="en"?"Bio":"一句话介绍"}</div>
222
+ <input class="cw-input" id="cw-cit-bio" value="${this.esc(t.bio)}" placeholder="${o()==="en"?"Brief description":"一句话描述角色特点"}" ${i?"readonly":""} />
223
+ </div>
224
+ ${s?`<div class="cw-field">
225
+ <div class="cw-field-label-row">
226
+ <div class="cw-field-label">${o()==="en"?"Full Persona":"完整人设"}</div>
227
+ <button class="cw-ai-gen-btn" id="cw-cit-gen-soul"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l1.09 3.26L16.36 6l-3.27 1.09L12 10.36l-1.09-3.27L7.64 6l3.27-1.09L12 2z"/><path d="M5 15l.55 1.64L7.18 17.2 5.55 17.75 5 19.4l-.55-1.65L2.82 17.2l1.63-.56L5 15z"/><path d="M19 11l.55 1.64 1.63.56-1.63.55L19 15.4l-.55-1.65-1.63-.55 1.63-.56L19 11z"/></svg> ${o()==="en"?"AI Generate":"AI 生成"}</button>
228
+ </div>
229
+ <textarea class="cw-textarea" id="cw-cit-custom-soul" placeholder="${o()==="en"?"Describe personality, style, work...":"详细描述角色的性格、说话风格、工作方式…"}">${this.esc(t.customSoul??"")}</textarea>
230
+ </div>
231
+ <div class="cw-persona-hint">${o()==="en"?"A new Soul file will be generated on publish":"发布时将根据以上内容合成新 Soul 文件"}</div>`:""}
232
+ </div>
233
+ <div id="cw-cit-model-variant" class="cw-field cw-field-conditional"></div>
234
+ <div id="cw-cit-model-color" class="cw-field cw-field-conditional"></div>
235
+ <div class="cw-field">
236
+ <div class="cw-field-label">${o()==="en"?"Agent Mode":"Agent 模式"}</div>
237
+ <div class="cw-agent-toggle-row" id="cw-cit-agent-toggle-row">
238
+ <div class="pi-toggle${t.agentEnabled?" active":""}" id="cw-cit-agent-toggle"></div>
239
+ <span class="cw-agent-toggle-label" id="cw-cit-agent-label">${t.agentEnabled?o()==="en"?"Enabled · Chat after publish":"已开启 · 发布后可对话":o()==="en"?"Disabled":"未开启"}</span>
240
+ </div>
241
+ <div class="cw-agent-hint">${t.agentEnabled?o()==="en"?"A sub-agent will be created for direct chat":"发布后将创建常驻子 Agent,用户可与该居民直接聊天":o()==="en"?"Enable for independent AI personality":"开启后该居民将拥有独立 AI 人格,可与用户对话"}</div>
242
+ </div>
243
+ <div class="cw-field">
244
+ <div class="cw-field-label">${o()==="en"?"Assign Home":"分配场景住宅"}</div>
245
+ <div id="cw-cit-home-dd"></div>
246
+ </div>
247
+ <div class="cw-section cw-section-conditional">
248
+ <div class="cw-section-title">${o()==="en"?"Animation Mapping":"动画映射"}</div>
249
+ <div id="cw-cit-anim-container"></div>
250
+ </div>
251
+ <div id="cw-cit-transform" class="cw-field-conditional"></div>
252
+ `,s&&(this.bindInput("#cw-cit-name",c=>{t.name=c,this.roster.setConfig(this.config)}),this.bindInput("#cw-cit-bio",c=>{t.bio=c}),(u=e.querySelector("#cw-cit-specialty"))==null||u.addEventListener("input",c=>{t.specialty=c.target.value,this.saveDraft()}),this.bindTextarea("#cw-cit-custom-soul",c=>{t.customSoul=c}),(h=e.querySelector("#cw-cit-gen-soul"))==null||h.addEventListener("click",()=>{this.generateSoulForCitizen(t,e)})),n){const c=e.querySelector("#cw-cit-custom-toggle");c==null||c.addEventListener("click",()=>{t.useCustomPersona=!t.useCustomPersona,this.saveDraft(),this.renderInspector()})}const a=e.querySelector("#cw-cit-agent-toggle"),r=e.querySelector("#cw-cit-agent-label"),l=e.querySelector(".cw-agent-hint"),p=()=>{t.agentEnabled=!t.agentEnabled,a.classList.toggle("active",!!t.agentEnabled),r.textContent=t.agentEnabled?o()==="en"?"Enabled · Chat after publish":"已开启 · 发布后可对话":o()==="en"?"Disabled":"未开启",l.textContent=t.agentEnabled?o()==="en"?"A sub-agent will be created for direct chat":"发布后将创建常驻子 Agent,用户可与该居民直接聊天":o()==="en"?"Enable for independent AI personality":"开启后该居民将拥有独立 AI 人格,可与用户对话",this.saveDraft()};a.addEventListener("click",p),r.addEventListener("click",p),this.createDropdown(e,"cw-cit-home-dd",this.buildingList.map(c=>({value:c.id,label:c.name})),t.homeId||"",o()==="en"?"Unassigned":"未分配",c=>{t.homeId=c,this.saveDraft()}),this.bindAvatarClickable("#cw-cit-avatar-btn",c=>{t.avatarUrl=c,this.renderInspector(),this.roster.render(),this.saveDraft()}),this.renderModelSettingsSection(e,"#cw-cit-model-variant","#cw-cit-model-color",t),this.renderAnimMappingSection(e,"#cw-cit-anim-container",t),this.renderTransformSection(e,"#cw-cit-transform")}updateModelBar(){const e=document.getElementById("cw-model-current");if(!e||!this.selection)return;const t=this.getCurrentAvatarId(),i=this.picker.getGroupById(t);e.textContent=(i==null?void 0:i.displayName)||t.replace("char-","").replace(/-/g," ")||"--"}bindInput(e,t){const i=document.querySelector(e);i==null||i.addEventListener("input",()=>{t(i.value),this.saveDraft()})}bindTextarea(e,t){const i=document.querySelector(e);i==null||i.addEventListener("input",()=>{t(i.value),this.saveDraft()})}async generateSoulForCitizen(e,t){const i=t.querySelector("#cw-cit-gen-soul");if(!(!i||!e.name||!e.bio)){i.innerHTML=`<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="cw-spin"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> ${o()==="en"?"Generating...":"生成中..."}`,i.classList.add("disabled");try{const s=new AbortController,n=setTimeout(()=>s.abort(),12e4),a=await fetch("/citizen-workshop/_api/generate-soul",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e.name,bio:e.bio,specialty:e.specialty,industry:e.industry}),signal:s.signal});clearTimeout(n);const r=await a.json();if(r.content){e.customSoul=r.content;const l=t.querySelector("#cw-cit-custom-soul");l&&(l.value=r.content),this.saveDraft()}}catch(s){console.error("[CitizenWorkshop] generate-soul failed:",s)}i.innerHTML=`<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l1.09 3.26L16.36 6l-3.27 1.09L12 10.36l-1.09-3.27L7.64 6l3.27-1.09L12 2z"/><path d="M5 15l.55 1.64L7.18 17.2 5.55 17.75 5 19.4l-.55-1.65L2.82 17.2l1.63-.56L5 15z"/><path d="M19 11l.55 1.64 1.63.56-1.63.55L19 15.4l-.55-1.65-1.63-.55 1.63-.56L19 11z"/></svg> ${o()==="en"?"AI Generate":"AI 生成"}`,i.classList.remove("disabled")}}createDropdown(e,t,i,s,n,a){const r=e.querySelector(`#${t}`);if(!r)return;r.innerHTML="",r.classList.add("cw-dropdown");const l=document.createElement("button");l.type="button",l.className="cw-dropdown-trigger";const p=i.find(g=>g.value===s);l.innerHTML=`<span class="cw-dd-text${p?"":" cw-dd-placeholder"}">${p?this.esc(p.label):n}</span>`,r.appendChild(l);const u=document.createElement("div");u.className="cw-dropdown-menu";const h=document.createElement("button");h.type="button",h.className=`cw-dropdown-option${s?"":" selected"}`,h.textContent=n,h.addEventListener("click",()=>{c(""),v()}),u.appendChild(h);for(const g of i){const w=document.createElement("button");w.type="button",w.className=`cw-dropdown-option${g.value===s?" selected":""}`,w.textContent=g.label,w.addEventListener("click",()=>{c(g.value),v()}),u.appendChild(w)}r.appendChild(u);const c=g=>{const w=i.find(b=>b.value===g);l.innerHTML=`<span class="cw-dd-text${w?"":" cw-dd-placeholder"}">${w?this.esc(w.label):n}</span>`,a(g)},v=()=>{l.classList.remove("open"),u.classList.remove("open"),document.removeEventListener("click",f)},f=g=>{r.contains(g.target)||v()};l.addEventListener("click",g=>{g.stopPropagation(),u.classList.contains("open")?v():(l.classList.add("open"),u.classList.add("open"),document.addEventListener("click",f))})}createCascadeDropdown(e,t,i){const s=e.querySelector(`#${t}`);if(!s)return;s.innerHTML="",s.classList.add("cw-dropdown");const n=i.industry&&i.specialty?`${i.industry} · ${i.specialty}`:i.industry||(o()==="en"?"Select industry":"选择行业 / 专业"),a=document.createElement("button");a.type="button",a.className="cw-dropdown-trigger";const r=!!(i.industry&&i.specialty);a.innerHTML=`<span class="cw-dd-text${r?"":" cw-dd-placeholder"}">${r?this.esc(n):o()==="en"?"Select industry":"选择行业 / 专业"}</span>`,s.appendChild(a);const l=document.createElement("div");l.className="cw-dropdown-menu cw-cascade-menu";for(const h of Fe){const c=O[h]??["通用助手"],v=document.createElement("div");v.className="cw-cascade-group";const f=document.createElement("div");f.className="cw-cascade-label",f.textContent=h,v.appendChild(f);for(const g of c){const w=document.createElement("button");w.type="button";const b=i.industry===h&&i.specialty===g;w.className=`cw-dropdown-option${b?" selected":""}`,w.textContent=g,w.addEventListener("click",()=>{i.industry=h,i.specialty=g,this.saveDraft(),p();const C=`${h} · ${g}`;a.innerHTML=`<span class="cw-dd-text">${this.esc(C)}</span>`}),v.appendChild(w)}l.appendChild(v)}s.appendChild(l);const p=()=>{a.classList.remove("open"),l.classList.remove("open"),document.removeEventListener("click",u)},u=h=>{s.contains(h.target)||p()};a.addEventListener("click",h=>{h.stopPropagation(),l.classList.contains("open")?p():(a.classList.add("open"),l.classList.add("open"),document.addEventListener("click",u))})}bindAvatarClickable(e,t){this.bindAvatarUpload(e,t)}bindAvatarUpload(e,t){const i=document.querySelector(e);i&&i.addEventListener("click",()=>{const s=document.createElement("input");s.type="file",s.accept="image/png,image/jpeg,image/webp",s.onchange=async()=>{var r;const n=(r=s.files)==null?void 0:r[0];if(!n)return;const a=new FileReader;a.onload=async()=>{const l=a.result,p=`avatar_${Date.now()}`;try{const h=await(await fetch("/citizen-workshop/_api/upload-avatar",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fileName:p,imageData:l})})).json();h.success&&h.url&&t(h.url)}catch{}},a.readAsDataURL(n)},s.click()})}renderModelSettingsSection(e,t,i,s){const n=e.querySelector(t),a=e.querySelector(i),r=this.picker.getGroupById(s.avatarId);if(!r){n&&(n.style.display="none"),a&&(a.style.display="none");return}if((!this._activeVariant||!r.variants.includes(this._activeVariant))&&(this._activeVariant=r.variants[0]??1),(!this._activeColor||!r.colors.includes(this._activeColor))&&(this._activeColor=r.colors[0]??1),n)if(r.variants.length>1){n.style.display="",n.innerHTML=`<div class="cw-field-label">${o()==="en"?"3D Variants":"3D模型变体"}</div>`;const l=document.createElement("div");l.className="cw-picker-variant-btns";for(const p of r.variants){const u=document.createElement("button");u.className=`cw-picker-variant-btn${p===this._activeVariant?" active":""}`,u.textContent=String(p),u.addEventListener("click",()=>{this._activeVariant=p;const h=N(r,p,this._activeColor);this.stage.setCharacter(h,void 0,s.animMapping),this.saveDraft(),this.renderModelSettingsSection(e,t,i,s)}),l.appendChild(u)}n.appendChild(l)}else n.style.display="none";if(a)if(r.colors.length>1){a.style.display="",a.innerHTML=`<div class="cw-field-label">${o()==="en"?"3D Colors":"3D模型配色"}</div>`;const l=document.createElement("div");l.className="cw-picker-color-swatches";const p=["#e74c3c","#e67e22","#f1c40f","#2ecc71","#1abc9c","#3498db","#9b59b6","#e91e63","#795548","#607d8b","#34495e","#ecf0f1","#ff6b81","#ffa502","#7bed9f","#70a1ff"];for(let u=0;u<r.colors.length;u++){const h=r.colors[u],c=document.createElement("button");c.className=`cw-picker-color-swatch${h===this._activeColor?" active":""}`,c.style.background=p[u%p.length],c.title=`${o()==="en"?"Color":"配色"} ${h}`,c.addEventListener("click",()=>{this._activeColor=h;const v=N(r,this._activeVariant,h);this.stage.setCharacter(v,void 0,s.animMapping),this.saveDraft(),this.renderModelSettingsSection(e,t,i,s)}),l.appendChild(c)}a.appendChild(l)}else a.style.display="none"}renderAnimMappingSection(e,t,i){var h;const s=e.querySelector(t);if(!s)return;if(!i.avatarId.startsWith("custom-")){const c=s.closest(".cw-section");c&&(c.style.display="none");return}const n=this.picker.getGroupById(i.avatarId),a=(n==null?void 0:n.animMapping)??{},r=Object.values(a).filter(Boolean).length,l=z.length,p=r>0,u=z.filter(c=>a[c]).map(c=>j[c]??c).join(" · ");s.innerHTML=`
253
+ <div class="cw-anim-status ${p?"has-anim":"no-anim"}">
254
+ <div class="cw-anim-status-icon">${p?"✓":"⚠"}</div>
255
+ <div class="cw-anim-status-text">
256
+ <div class="cw-anim-status-title">${p?`${o()==="en"?"Mapped":"已映射"} ${r}/${l}`:o()==="en"?"No animations":"未配置动画"}</div>
257
+ <div class="cw-anim-status-detail">${p?u:o()==="en"?"No animation effects in town":"角色在小镇中将没有动画效果"}</div>
258
+ </div>
259
+ </div>
260
+ <button class="cw-anim-edit-btn" id="cw-anim-edit-btn">${p?o()==="en"?"Edit Mapping":"编辑动画映射":o()==="en"?"Configure Mapping":"配置动画映射"}</button>
261
+ `,(h=s.querySelector("#cw-anim-edit-btn"))==null||h.addEventListener("click",()=>{this.openAnimDialog(i)})}async openAnimDialog(e){const t=this.picker.getGroupById(e.avatarId);!t||t.source!=="custom"||(await this.openAnimDialogForGroup(t),this.renderInspector(),this.loadSelectedCharacter())}async openAnimDialogForGroup(e){if(!e.assetId)return;this.animDialog||(this.animDialog=new et);const t=N(e),i=await this.animDialog.open({meshUrl:t,animMapping:e.animMapping,detectedClips:e.detectedClips,animFileUrls:e.animFileUrls});if(i){e.animMapping=i.animMapping,e.detectedClips=i.detectedClips,e.animFileUrls=i.animFileUrls;try{await fetch("/custom-assets/_api/update",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:e.assetId,animMapping:i.animMapping,detectedClips:i.detectedClips,animFileUrls:i.animFileUrls})})}catch{}}}getCurrentGroup(){const e=this.getCurrentAvatarId();return e?this.picker.getGroupById(e)??null:null}ensureTransformForCurrentGroup(e,t){var n,a,r;const i=this.getCurrentGroup();if(!i)return;i.modelTransform||(i.modelTransform=q(e,t)),this.getCurrentAvatarId();const s=((n=this.selection)==null?void 0:n.type)==="citizen"?this.config.citizens.find(l=>l.id===this.selection.id):null;((a=this.selection)==null?void 0:a.type)==="user"?this.config.user.modelTransform={...i.modelTransform}:((r=this.selection)==null?void 0:r.type)==="steward"?this.config.steward.modelTransform={...i.modelTransform}:s&&(s.modelTransform={...i.modelTransform})}renderTransformSection(e,t){var r,l;const i=e.querySelector(t);if(!i)return;if(this.pickerOpen){i.style.display="none";return}const s=this.getCurrentGroup();if(!s||!s.modelTransform){i.style.display="none";return}i.style.display="";const n=s.modelTransform??ve();this.getCurrentAvatarId(),((r=this.selection)==null?void 0:r.type)==="citizen"&&this.config.citizens.find(p=>p.id===this.selection.id),i.innerHTML=`
262
+ <div class="cw-section">
263
+ <div class="cw-section-title">${o()==="en"?"Transform":"模型调整"}</div>
264
+ <div class="cw-transform-grid">
265
+ <label class="cw-transform-label">${o()==="en"?"Scale":"缩放"}</label>
266
+ <input type="range" class="cw-transform-slider" id="cw-tf-scale" min="0.1" max="5" step="0.01" value="${n.scale}" />
267
+ <input type="number" class="cw-transform-num" id="cw-tf-scale-n" min="0.1" max="5" step="0.01" value="${n.scale}" />
268
+
269
+ <label class="cw-transform-label">${o()==="en"?"Rotate X":"旋转X"}</label>
270
+ <input type="range" class="cw-transform-slider" id="cw-tf-rx" min="-180" max="180" step="1" value="${n.rotationX}" />
271
+ <input type="number" class="cw-transform-num" id="cw-tf-rx-n" min="-180" max="180" step="1" value="${n.rotationX}" />
272
+
273
+ <label class="cw-transform-label">${o()==="en"?"Rotate Y":"旋转Y"}</label>
274
+ <input type="range" class="cw-transform-slider" id="cw-tf-ry" min="-180" max="180" step="1" value="${n.rotationY}" />
275
+ <input type="number" class="cw-transform-num" id="cw-tf-ry-n" min="-180" max="180" step="1" value="${n.rotationY}" />
276
+
277
+ <label class="cw-transform-label">${o()==="en"?"Rotate Z":"旋转Z"}</label>
278
+ <input type="range" class="cw-transform-slider" id="cw-tf-rz" min="-180" max="180" step="1" value="${n.rotationZ}" />
279
+ <input type="number" class="cw-transform-num" id="cw-tf-rz-n" min="-180" max="180" step="1" value="${n.rotationZ}" />
280
+
281
+ <label class="cw-transform-label">${o()==="en"?"Offset X":"偏移X"}</label>
282
+ <input type="range" class="cw-transform-slider" id="cw-tf-ox" min="-2" max="2" step="0.01" value="${n.offsetX}" />
283
+ <input type="number" class="cw-transform-num" id="cw-tf-ox-n" min="-2" max="2" step="0.01" value="${n.offsetX}" />
284
+
285
+ <label class="cw-transform-label">${o()==="en"?"Offset Y":"偏移Y"}</label>
286
+ <input type="range" class="cw-transform-slider" id="cw-tf-oy" min="-2" max="2" step="0.01" value="${n.offsetY}" />
287
+ <input type="number" class="cw-transform-num" id="cw-tf-oy-n" min="-2" max="2" step="0.01" value="${n.offsetY}" />
288
+
289
+ <label class="cw-transform-label">${o()==="en"?"Offset Z":"偏移Z"}</label>
290
+ <input type="range" class="cw-transform-slider" id="cw-tf-oz" min="-2" max="2" step="0.01" value="${n.offsetZ}" />
291
+ <input type="number" class="cw-transform-num" id="cw-tf-oz-n" min="-2" max="2" step="0.01" value="${n.offsetZ}" />
292
+ </div>
293
+ <button class="cw-btn cw-btn-secondary cw-transform-reset" id="cw-tf-reset">${o()==="en"?"Reset":"重置为推荐值"}</button>
294
+ </div>
295
+ `;const a=[["scale","scale"],["rx","rotationX"],["ry","rotationY"],["rz","rotationZ"],["ox","offsetX"],["oy","offsetY"],["oz","offsetZ"]];for(const[p,u]of a){const h=i.querySelector(`#cw-tf-${p}`),c=i.querySelector(`#cw-tf-${p}-n`);if(!h||!c)continue;const v=f=>{const g=parseFloat(f)||0;s.modelTransform||(s.modelTransform={...n}),s.modelTransform[u]=g,this.stage.applyTransform(s.modelTransform)};h.addEventListener("input",()=>{c.value=h.value,v(h.value)}),c.addEventListener("input",()=>{h.value=c.value,v(c.value)}),h.addEventListener("change",()=>this.saveDraft()),c.addEventListener("change",()=>this.saveDraft())}(l=i.querySelector("#cw-tf-reset"))==null||l.addEventListener("click",()=>{s.modelTransform=q(this.lastRawHeight,this.lastModelSource),this.stage.applyTransform(s.modelTransform),this.saveDraft(),this.renderTransformSection(e,t)})}esc(e){return e.replace(/"/g,"&quot;").replace(/</g,"&lt;")}resolveAvatarUrl(e,t){if(e)return e;if(t){const i=this.picker.getGroupById(t)??Ce().find(s=>s.id===t);if(i!=null&&i.thumbnailUrl)return i.thumbnailUrl}return null}resolveAvatarHtml(e,t,i){const s=this.resolveAvatarUrl(t,i);return s?`<img src="${s}" onerror="this.remove();this.parentElement.textContent='${this.esc(e.charAt(0))}'" />`:this.esc(e.charAt(0))}show(){this.container.classList.add("visible"),this.stage.start(),this.started||(this.started=!0,this.roster.select({type:"user"}))}hide(){this.container.classList.remove("visible"),this.stage.stop()}pushUndoSnapshot(){this.undoHistory.push(JSON.stringify(this.config)),this.undoHistory.length>$.MAX_UNDO&&this.undoHistory.shift(),this.redoHistory.length=0}undo(){this.undoHistory.length!==0&&(this.redoHistory.push(JSON.stringify(this.config)),this.config=JSON.parse(this.undoHistory.pop()),this.afterConfigReplaced())}redo(){this.redoHistory.length!==0&&(this.undoHistory.push(JSON.stringify(this.config)),this.config=JSON.parse(this.redoHistory.pop()),this.afterConfigReplaced())}get canUndo(){return this.undoHistory.length>0}get canRedo(){return this.redoHistory.length>0}onConfigChanged(e){this.configChangedListeners.push(e)}notifyConfigChanged(){for(const e of this.configChangedListeners)e()}afterConfigReplaced(){var e;if(this.roster.setConfig(this.config),((e=this.selection)==null?void 0:e.type)==="citizen"){const t=this.selection.id;this.config.citizens.find(i=>i.id===t)||(this.selection={type:"user"})}this.renderInspector(),this.updateModelBar(),this.loadSelectedCharacter(),this.persistDraft(),this.notifyConfigChanged()}exportJSON(){const e=new Blob([JSON.stringify(this.config,null,2)],{type:"application/json"}),t=URL.createObjectURL(e),i=document.createElement("a");i.href=t,i.download="citizen-config.json",i.click(),URL.revokeObjectURL(t)}importJSON(){return new Promise(e=>{const t=document.createElement("input");t.type="file",t.accept=".json",t.onchange=async()=>{var s;const i=(s=t.files)==null?void 0:s[0];if(!i){e(!1);return}try{const n=await i.text(),a=JSON.parse(n);if(!a.user||!a.steward||!Array.isArray(a.citizens)){e(!1);return}this.pushUndoSnapshot(),this.config=a,this.afterConfigReplaced(),e(!0)}catch{e(!1)}},t.click()})}resetToDefault(){this.pushUndoSnapshot(),this.config=oe(),this.undoHistory.length=0,this.redoHistory.length=0,this.afterConfigReplaced()}syncGroupTransformsToConfig(){const e=s=>{const n=this.picker.getGroupById(s);return n!=null&&n.modelTransform?{...n.modelTransform}:void 0},t=e(this.config.user.avatarId);t&&(this.config.user.modelTransform=t);const i=e(this.config.steward.avatarId);i&&(this.config.steward.modelTransform=i);for(const s of this.config.citizens){const n=e(s.avatarId);n&&(s.modelTransform=n)}}restoreGroupTransformsFromConfig(){const e=(t,i)=>{if(!i)return;const s=this.picker.getGroupById(t);s&&!s.modelTransform&&(s.modelTransform=i)};e(this.config.user.avatarId,this.config.user.modelTransform),e(this.config.steward.avatarId,this.config.steward.modelTransform);for(const t of this.config.citizens)e(t.avatarId,t.modelTransform);if(this.config.modelTransforms){for(const[t,i]of Object.entries(this.config.modelTransforms)){const s=this.picker.getGroupById(t);s&&!s.modelTransform&&(s.modelTransform=i)}delete this.config.modelTransforms}}saveDraft(){this.pushUndoSnapshot(),this.persistDraft(),this.notifyConfigChanged()}persistDraft(){this.syncGroupTransformsToConfig();try{localStorage.setItem("cw-draft",JSON.stringify({_v:$.DRAFT_VERSION,...this.config}))}catch{}}loadDraft(){try{const e=localStorage.getItem("cw-draft");if(!e)return null;const t=JSON.parse(e);return t._v!==$.DRAFT_VERSION?(localStorage.removeItem("cw-draft"),null):(delete t._v,t)}catch{}return null}async saveToFile(){this.syncGroupTransformsToConfig();try{const e={};for(const[s,n]of this.soulCache)n&&(e[s]=n);return(await(await fetch("/citizen-workshop/_api/save",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({config:this.config,souls:e})})).json()).success===!0}catch{return!1}}async publish(){if(!await this.saveToFile())return{success:!1,error:o()==="en"?"Draft save failed":"保存草稿失败"};try{const t={};for(const[n,a]of this.soulCache)a&&(t[n]=a);const s=await(await fetch("/citizen-workshop/_api/publish",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({souls:t})})).json();return s.success?{success:!0,changeset:s.changeset}:{success:!1,error:s.error??(o()==="en"?"Publish failed":"发布失败")}}catch{return{success:!1,error:o()==="en"?"Network error":"网络请求失败"}}}async loadFromFile(){try{return(await(await fetch("/citizen-workshop/_api/load",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"})).json()).config??null}catch{return null}}};d($,"MAX_UNDO",50),d($,"DRAFT_VERSION",3);let J=$;Le();async function tt(){var g,w,b,C;it();const m=new J;m.show();const e=document.getElementById("btn-undo"),t=document.getElementById("btn-redo"),i=()=>{e&&(e.disabled=!m.canUndo),t&&(t.disabled=!m.canRedo)};i(),e==null||e.addEventListener("click",()=>{m.undo(),i()}),t==null||t.addEventListener("click",()=>{m.redo(),i()}),m.onConfigChanged(i);const s=document.getElementById("btn-save"),n=y=>{if(!s)return;s.classList.add("save-flash");const x=s.lastChild,A=x.textContent;x.textContent=y,setTimeout(()=>{s.classList.remove("save-flash"),x.textContent=A},1200)},a=async()=>{const y=await m.saveToFile();n(y?o()==="en"?" Saved":" 已保存":o()==="en"?" Failed":" 保存失败")};s==null||s.addEventListener("click",a);const r=document.getElementById("btn-publish"),l=((g=r==null?void 0:r.querySelector("svg"))==null?void 0:g.outerHTML)??"",p='<svg class="cw-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>',u=(y,x=!1)=>{const A=document.querySelector(".cw-toast");A&&A.remove();const k=document.createElement("div");k.className=`cw-toast${x?" cw-toast-error":""}`,k.textContent=y,document.body.appendChild(k),requestAnimationFrame(()=>k.classList.add("visible")),setTimeout(()=>{k.classList.remove("visible"),setTimeout(()=>k.remove(),300)},3e3)};let h=!1;r==null||r.addEventListener("click",async()=>{var x,A,k;if(h||r.disabled)return;h=!0,r.disabled=!0;const y=r.querySelector("svg");y&&(y.outerHTML=p);try{const M=await m.publish();if(M.success){const L=M.changeset,I=[];L&&((x=L.agentToCreate)!=null&&x.length&&I.push(o()==="en"?`Create ${L.agentToCreate.length} Agent(s)`:`创建 ${L.agentToCreate.length} 个 Agent`),(A=L.agentToDisable)!=null&&A.length&&I.push(o()==="en"?`Disable ${L.agentToDisable.length}`:`停用 ${L.agentToDisable.length} 个`),(k=L.agentToUpdateSoul)!=null&&k.length&&I.push(o()==="en"?`Update ${L.agentToUpdateSoul.length}`:`更新 ${L.agentToUpdateSoul.length} 个`));const D=I.length?`(${I.join(",")})`:"";u(o()==="en"?`Published${D}`:`发布成功${D}`)}else u(M.error??(o()==="en"?"Publish failed":"发布失败"),!0)}catch{u(o()==="en"?"Publish request failed":"发布请求失败",!0)}finally{const M=r.querySelector("svg");M&&(M.outerHTML=l),r.disabled=!1,h=!1}}),(w=document.getElementById("btn-export"))==null||w.addEventListener("click",()=>{m.exportJSON()}),(b=document.getElementById("btn-import"))==null||b.addEventListener("click",async()=>{const y=await m.importJSON();n(y?o()==="en"?" Imported":" 已导入":o()==="en"?" Failed":" 导入失败")});const c=(y,x)=>new Promise(A=>{const k=document.getElementById("confirm-overlay");document.getElementById("confirm-title").textContent=y,document.getElementById("confirm-message").textContent=x,k.classList.add("open");const M=_=>{k.classList.remove("open"),ne.removeEventListener("click",L),ae.removeEventListener("click",I),k.removeEventListener("click",D),A(_)},L=()=>M(!0),I=()=>M(!1),D=_=>{_.target===k&&M(!1)},ne=document.getElementById("confirm-ok"),ae=document.getElementById("confirm-cancel");ne.addEventListener("click",L),ae.addEventListener("click",I),k.addEventListener("click",D)});(C=document.getElementById("btn-clear"))==null||C.addEventListener("click",async()=>{await c(o()==="en"?"Clear citizen config":"清空角色配置",o()==="en"?"Clear all citizen configs? This cannot be undone.":"确定要清空所有角色配置吗?此操作不可撤销。")&&m.resetToDefault()}),window.addEventListener("keydown",y=>{if((y.ctrlKey||y.metaKey)&&y.key==="s"&&(y.preventDefault(),a()),(y.ctrlKey||y.metaKey)&&y.key==="z"&&!y.shiftKey){if(y.target instanceof HTMLInputElement||y.target instanceof HTMLTextAreaElement)return;y.preventDefault(),m.undo(),i()}if((y.ctrlKey||y.metaKey)&&y.key==="z"&&y.shiftKey){if(y.target instanceof HTMLInputElement||y.target instanceof HTMLTextAreaElement)return;y.preventDefault(),m.redo(),i()}});const v=document.getElementById("btn-more"),f=document.getElementById("more-menu");v==null||v.addEventListener("click",y=>{y.stopPropagation(),f==null||f.classList.toggle("open")}),document.addEventListener("click",()=>f==null?void 0:f.classList.remove("open"))}tt().catch(console.error);function it(){document.querySelectorAll("[data-i18n]").forEach(m=>{const e=m.getAttribute("data-i18n"),t=F(e);t!==e&&(m.textContent=t)}),document.querySelectorAll("[data-i18n-tip]").forEach(m=>{const e=m.getAttribute("data-i18n-tip"),t=F(e);t!==e&&m.setAttribute("data-tip",t)}),document.querySelectorAll("[data-i18n-title]").forEach(m=>{const e=m.getAttribute("data-i18n-title"),t=F(e);t!==e&&m.setAttribute("title",t)}),document.querySelectorAll("[data-i18n-placeholder]").forEach(m=>{const e=m.getAttribute("data-i18n-placeholder"),t=F(e);t!==e&&(m.placeholder=t)})}
296
+ //# sourceMappingURL=citizenEditor-DubGSJOQ.js.map
@@ -0,0 +1,12 @@
1
+ import{g as p}from"./index-BWfrufil.js";const g="modulepreload",v=function(r,s){return new URL(r,s).href},h={},L=function(s,n,i){let m=Promise.resolve();if(n&&n.length>0){const a=document.getElementsByTagName("link"),e=document.querySelector("meta[property=csp-nonce]"),f=(e==null?void 0:e.nonce)||(e==null?void 0:e.getAttribute("nonce"));m=Promise.allSettled(n.map(t=>{if(t=v(t,i),t in h)return;h[t]=!0;const l=t.endsWith(".css"),w=l?'[rel="stylesheet"]':"";if(!!i)for(let c=a.length-1;c>=0;c--){const u=a[c];if(u.href===t&&(!l||u.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${t}"]${w}`))return;const o=document.createElement("link");if(o.rel=l?"stylesheet":g,l||(o.as="script"),o.crossOrigin="",o.href=t,f&&o.setAttribute("nonce",f),document.head.appendChild(o),l)return new Promise((c,u)=>{o.addEventListener("load",c),o.addEventListener("error",()=>u(new Error(`Unable to preload CSS for ${t}`)))})}))}function d(a){const e=new Event("vite:preloadError",{cancelable:!0});if(e.payload=a,window.dispatchEvent(e),!e.defaultPrevented)throw a}return m.then(a=>{for(const e of a||[])e.status==="rejected"&&d(e.reason);return s().catch(d)})},E=new Set(["new","help"]);function P(r){const s=r.match(/^\/([a-z][\w-]*)\s*([\s\S]*)$/i);if(!s)return null;const n=s[1].toLowerCase(),i=s[2].trim();return n==="reset"?{type:"frontend",command:"new",args:i,raw:r}:E.has(n)?{type:"frontend",command:n,args:i,raw:r}:{type:"gateway",command:n,args:i,raw:r}}const y=["可用指令:",""," /new [model] 创建新会话(可选模型参数)"," /stop 中止当前运行"," /status 查看当前状态"," /model [name] 查看/切换模型"," /think <level> 设置思考深度"," /tools 查看可用工具"," /help 显示此帮助","","更多指令:/fast, /verbose, /reasoning, /btw, /usage, /context, /commands"].join(`
2
+ `),S=`Available commands:
3
+ /new [model] New session (optional model)
4
+ /stop Abort current run
5
+ /status View current status
6
+ /model [name] View/switch model
7
+ /think <level> Set thinking depth
8
+ /tools List tools
9
+ /help Show this help
10
+
11
+ More: /fast, /verbose, /reasoning, /btw, /usage, /context, /commands`;function T(){return p()==="en"?S:y}export{L as _,T as g,P as p};
12
+ //# sourceMappingURL=command-parser-BUd15Bmv.js.map