agentworkshop 0.7.40 → 0.7.44

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 (327) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/B8uy9iRW.js +1 -0
  3. package/.output/public/_nuxt/BGTYYQH-.js +1 -0
  4. package/.output/public/_nuxt/BUmHEJ77.js +2 -0
  5. package/.output/public/_nuxt/BY7YrV_D.js +1 -0
  6. package/.output/public/_nuxt/Bqcgz95s.js +18 -0
  7. package/.output/public/_nuxt/C7UlqPeD.js +1 -0
  8. package/.output/public/_nuxt/CTmDn-YP.js +1 -0
  9. package/.output/public/_nuxt/Cs8pVeVG.js +1 -0
  10. package/.output/public/_nuxt/{DcsCcxp4.js → D98NH8kG.js} +1 -1
  11. package/.output/public/_nuxt/DKySmNPI.js +14 -0
  12. package/.output/public/_nuxt/Di0NY_ht.js +1 -0
  13. package/.output/public/_nuxt/Du9v6qGB.js +1 -0
  14. package/.output/public/_nuxt/{C_iYD-EB.js → EHlEMRii.js} +1 -1
  15. package/.output/public/_nuxt/Eke1fs-U.js +1 -0
  16. package/.output/public/_nuxt/{DeJykBFf.js → H0BcqZtT.js} +1 -1
  17. package/.output/public/_nuxt/{C4oW1HsE.js → HSYEkkce.js} +1 -1
  18. package/.output/public/_nuxt/{DZshyNLa.js → IREZ1eC2.js} +1 -1
  19. package/.output/public/_nuxt/{D_OTvQza.js → J9oU00D1.js} +1 -1
  20. package/.output/public/_nuxt/OmpTerminalPanel.BxR5-lvi.css +1 -0
  21. package/.output/public/_nuxt/SSyAPaHc.js +1 -0
  22. package/.output/public/_nuxt/TownView.li4EMQxK.css +1 -0
  23. package/.output/public/_nuxt/VdLk4jLE2.js +1 -0
  24. package/.output/public/_nuxt/_id_.BhsXoEuk.css +1 -0
  25. package/.output/public/_nuxt/_id_.DQyj-WKN.css +1 -0
  26. package/.output/public/_nuxt/{_wsId_.B4WiqkIS.css → _wsId_.CTx-BlfF.css} +1 -1
  27. package/.output/public/_nuxt/aShsv_rG.js +19 -0
  28. package/.output/public/_nuxt/{yF0aYMF1.js → aesgXKSr.js} +3 -3
  29. package/.output/public/_nuxt/agents.B21nUEUX.css +1 -0
  30. package/.output/public/_nuxt/aml.ekTjxFkf.css +1 -0
  31. package/.output/public/_nuxt/builds/latest.json +1 -1
  32. package/.output/public/_nuxt/builds/meta/b5506ef0-f48e-4f14-a6c0-d65e505d0b09.json +1 -0
  33. package/.output/public/_nuxt/ca4TJ3Op.js +1 -0
  34. package/.output/public/_nuxt/channel-templates.DplM1pJ3.css +1 -0
  35. package/.output/public/_nuxt/daq.BdVmoIfe.css +1 -0
  36. package/.output/public/_nuxt/dcw.DCgyzpWV.css +1 -0
  37. package/.output/public/_nuxt/{default.J32SgqFY.css → default.Bh3L2HU8.css} +1 -1
  38. package/.output/public/_nuxt/lHAxdSsj.js +1 -0
  39. package/.output/public/_nuxt/pages.j9rzRocr.css +1 -0
  40. package/.output/public/_nuxt/plugins.DhiZbHU8.css +1 -0
  41. package/.output/public/_nuxt/settings.BRx9rIDg.css +1 -0
  42. package/.output/public/_nuxt/teams.BLlOPB_Z.css +1 -0
  43. package/.output/public/_nuxt/tokens.De8xzszn.css +1 -0
  44. package/.output/public/_nuxt/workshop.D1jFNkrn.css +1 -0
  45. package/.output/server/chunks/_/nitro.mjs +1058 -830
  46. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  47. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs → OmpTerminalPanel-CVvxpsF7.mjs} +15 -15
  48. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs.map → OmpTerminalPanel-CVvxpsF7.mjs.map} +1 -1
  49. package/.output/server/chunks/build/{TownView-BuLAsmpR.mjs → TownView-CJ6IsDCc.mjs} +229 -228
  50. package/.output/server/chunks/build/{TownView-BuLAsmpR.mjs.map → TownView-CJ6IsDCc.mjs.map} +1 -1
  51. package/.output/server/chunks/build/{_id_-BU1wfOID.mjs → _id_-CVD_H0ir.mjs} +149 -147
  52. package/.output/server/chunks/build/_id_-CVD_H0ir.mjs.map +1 -0
  53. package/.output/server/chunks/build/{_id_-BYsEua57.mjs → _id_-CtxMb3qp.mjs} +47 -46
  54. package/.output/server/chunks/build/_id_-CtxMb3qp.mjs.map +1 -0
  55. package/.output/server/chunks/build/{_wsId_-BxlDcJHg.mjs → _wsId_-DpY3T-md.mjs} +516 -446
  56. package/.output/server/chunks/build/{_wsId_-BxlDcJHg.mjs.map → _wsId_-DpY3T-md.mjs.map} +1 -1
  57. package/.output/server/chunks/build/{agents-CLQqJF2y.mjs → agents-DsCkuXDV.mjs} +49 -46
  58. package/.output/server/chunks/build/agents-DsCkuXDV.mjs.map +1 -0
  59. package/.output/server/chunks/build/{aml-_7FdFL2p.mjs → aml-CT0xKGKI.mjs} +122 -105
  60. package/.output/server/chunks/build/aml-CT0xKGKI.mjs.map +1 -0
  61. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs +117 -0
  62. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs.map +1 -0
  63. package/.output/server/chunks/build/{channel-templates-p6SZNjlM.mjs → channel-templates-Dla9FbYa.mjs} +49 -37
  64. package/.output/server/chunks/build/channel-templates-Dla9FbYa.mjs.map +1 -0
  65. package/.output/server/chunks/build/{daq-cgyjKmJu.mjs → daq-DWWSKGAV.mjs} +95 -93
  66. package/.output/server/chunks/build/daq-DWWSKGAV.mjs.map +1 -0
  67. package/.output/server/chunks/build/{dcw-CoTxBlAZ.mjs → dcw-CT8k17_E.mjs} +46 -45
  68. package/.output/server/chunks/build/dcw-CT8k17_E.mjs.map +1 -0
  69. package/.output/server/chunks/build/{default-BE4r0FZN.mjs → default-D3525UFX.mjs} +16 -15
  70. package/.output/server/chunks/build/{default-BE4r0FZN.mjs.map → default-D3525UFX.mjs.map} +1 -1
  71. package/.output/server/chunks/build/{logs-BSOeZwwI.mjs → logs--dByNyhw.mjs} +2 -2
  72. package/.output/server/chunks/build/{logs-BSOeZwwI.mjs.map → logs--dByNyhw.mjs.map} +1 -1
  73. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs → monitor-Dh2Kvaq5.mjs} +2 -2
  74. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs.map → monitor-Dh2Kvaq5.mjs.map} +1 -1
  75. package/.output/server/chunks/build/{pages-DFxjn9Xa.mjs → pages-BHcSBM2D.mjs} +26 -20
  76. package/.output/server/chunks/build/pages-BHcSBM2D.mjs.map +1 -0
  77. package/.output/server/chunks/build/{plugins-lGlUr1lj.mjs → plugins-BNgIi61W.mjs} +23 -17
  78. package/.output/server/chunks/build/plugins-BNgIi61W.mjs.map +1 -0
  79. package/.output/server/chunks/build/{settings-Ddd1qx2Z.mjs → settings-ipYBDWdn.mjs} +45 -44
  80. package/.output/server/chunks/build/settings-ipYBDWdn.mjs.map +1 -0
  81. package/.output/server/chunks/build/{teams-MEDLZdMn.mjs → teams-5lx-CwFa.mjs} +47 -44
  82. package/.output/server/chunks/build/teams-5lx-CwFa.mjs.map +1 -0
  83. package/.output/server/chunks/build/{tokens-C7fTM5C2.mjs → tokens-q-IDT8Io.mjs} +73 -53
  84. package/.output/server/chunks/build/tokens-q-IDT8Io.mjs.map +1 -0
  85. package/.output/server/chunks/build/{town-qMPql9Se.mjs → town-QJWB5wng.mjs} +5 -4
  86. package/.output/server/chunks/build/town-QJWB5wng.mjs.map +1 -0
  87. package/.output/server/chunks/build/{useDaqStream-CIBaiGAl.mjs → useDaqStream-DfCFpb3p.mjs} +9 -3
  88. package/.output/server/chunks/build/useDaqStream-DfCFpb3p.mjs.map +1 -0
  89. package/.output/server/chunks/build/{useDcwStream-CBbiYnWe.mjs → useDcwStream-UPiX-3iO.mjs} +11 -2
  90. package/.output/server/chunks/build/useDcwStream-UPiX-3iO.mjs.map +1 -0
  91. package/.output/server/chunks/build/{workshop-BL5bhfs0.mjs → workshop-BIlCj_ZP.mjs} +45 -45
  92. package/.output/server/chunks/build/workshop-BIlCj_ZP.mjs.map +1 -0
  93. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  94. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  95. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
  96. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
  97. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
  98. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
  99. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
  100. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  101. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  102. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  103. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  104. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  105. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  106. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  107. package/.output/server/chunks/routes/api/workshop/index5.get.mjs +3 -1
  108. package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
  109. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +3 -1
  110. package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
  111. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  112. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  113. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  114. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  115. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  116. package/.output/server/chunks/routes/renderer.mjs +2 -2
  117. package/.output/server/chunks/virtual/entry.mjs +639 -88
  118. package/.output/server/chunks/virtual/entry.mjs.map +1 -1
  119. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  120. package/.output/server/package.json +1 -1
  121. package/README-zh.md +16 -6
  122. package/README.md +17 -7
  123. package/app/components/AppHeader.vue +961 -959
  124. package/app/components/workshop/A2aRpcDebugger.vue +237 -237
  125. package/app/components/workshop/AgentInspectorDrawer.vue +424 -424
  126. package/app/components/workshop/AgentLanesView.vue +998 -998
  127. package/app/components/workshop/ArtifactCard.vue +120 -120
  128. package/app/components/workshop/AssetLibrary.vue +452 -452
  129. package/app/components/workshop/ChannelSessionList.vue +812 -804
  130. package/app/components/workshop/CommandPalette.vue +269 -269
  131. package/app/components/workshop/Composer.vue +827 -827
  132. package/app/components/workshop/DeviceTwinPanel.vue +643 -643
  133. package/app/components/workshop/EventBlock.vue +602 -602
  134. package/app/components/workshop/FileSelectorModal.vue +188 -188
  135. package/app/components/workshop/InspectorPanel.vue +301 -301
  136. package/app/components/workshop/MemoryPanel.vue +364 -364
  137. package/app/components/workshop/MultiChannelView.vue +189 -189
  138. package/app/components/workshop/TaskBoardView.vue +686 -686
  139. package/app/components/workshop/TaskInspectorDrawer.vue +390 -390
  140. package/app/components/workshop/TranscriptTimeline.vue +601 -601
  141. package/app/components/workshop/blocks/ClusterArtifact.vue +61 -61
  142. package/app/components/workshop/blocks/ClusterError.vue +85 -85
  143. package/app/components/workshop/blocks/ClusterLife.vue +87 -87
  144. package/app/components/workshop/blocks/ClusterMember.vue +95 -93
  145. package/app/components/workshop/blocks/ClusterMemory.vue +84 -84
  146. package/app/components/workshop/blocks/ClusterOther.vue +67 -67
  147. package/app/components/workshop/blocks/ClusterStatus.vue +149 -147
  148. package/app/components/workshop/blocks/ClusterTool.vue +158 -158
  149. package/app/components/workshop/terminal/OmpTerminalPanel.vue +1011 -1011
  150. package/app/components/workshop/town/TownView.vue +56 -56
  151. package/app/composables/workshop/useDaqStream.ts +7 -4
  152. package/app/composables/workshop/useDcwStream.ts +8 -2
  153. package/app/middleware/locale-cookie.global.ts +22 -0
  154. package/app/pages/aml/index.vue +24 -2
  155. package/app/pages/daq/[id].vue +20 -16
  156. package/app/pages/daq/index.vue +2310 -2307
  157. package/app/pages/dcw/[id].vue +26 -23
  158. package/app/pages/dcw/index.vue +3 -3
  159. package/app/pages/index.vue +987 -981
  160. package/app/pages/plugins/index.vue +317 -311
  161. package/app/pages/settings.vue +1564 -1562
  162. package/app/pages/tokens.vue +15 -15
  163. package/app/pages/workshop/agents.vue +612 -609
  164. package/app/pages/workshop/channel-templates.vue +18 -11
  165. package/app/pages/workshop/index.vue +668 -668
  166. package/app/pages/workshop/teams.vue +743 -740
  167. package/app/pages/workshop/w/[wsId].vue +704 -704
  168. package/app/plugins/locale-restore.client.ts +16 -1
  169. package/app/utils/builtin-catalog.ts +104 -0
  170. package/config.yml +170 -170
  171. package/docs/cli.en.md +2 -2
  172. package/docs/cli.md +2 -2
  173. package/docs/full-test-plan.md +2 -3
  174. package/docs/plugins.en.md +33 -1
  175. package/docs/plugins.md +29 -1
  176. package/docs/sdk.en.md +1 -1
  177. package/docs/sdk.md +1 -1
  178. package/i18n/locales/en.ts +293 -35
  179. package/i18n/locales/zh-CN.ts +265 -7
  180. package/package.json +4 -3
  181. package/scripts/_dbg-perms-e2e.mjs +132 -109
  182. package/scripts/_demo-agentteam-capture.mjs +278 -0
  183. package/scripts/_e2e-ui-functional.mjs +371 -0
  184. package/scripts/_i18n-en-cjk-scan.mjs +28 -0
  185. package/scripts/_i18n-en-verify.mjs +107 -0
  186. package/scripts/_i18n-extra-checks.mjs +53 -0
  187. package/scripts/_i18n-scan-hardcoded.mjs +180 -0
  188. package/scripts/_i18n-verify-keys.mjs +50 -0
  189. package/scripts/_loop-biax-campaign.mjs +130 -0
  190. package/scripts/_sweep-button-contrast.mjs +87 -0
  191. package/scripts/test-serial-bridge.mjs +418 -0
  192. package/scripts/ui/e2e-interactive.mjs +328 -0
  193. package/scripts/ui/record-demo.mjs +557 -0
  194. package/scripts/ui/seed-industrial-demo.mjs +171 -0
  195. package/server/api/workshop/daq/index.get.ts +3 -1
  196. package/server/api/workshop/dcw/index.get.ts +3 -0
  197. package/server/plugins/aw-plugins.ts +9 -7
  198. package/server/plugins-builtin/diag-bridge/index.mjs +608 -661
  199. package/server/plugins-builtin/rag-bridge/index.mjs +57 -133
  200. package/server/plugins-builtin/serial-bridge/client.mjs +210 -0
  201. package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -0
  202. package/server/plugins-builtin/serial-bridge/i18n.json +62 -0
  203. package/server/plugins-builtin/serial-bridge/index.mjs +247 -0
  204. package/server/plugins-builtin/serial-bridge/lib.mjs +208 -0
  205. package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -0
  206. package/server/services/workshop/daq/daq-controller.ts +7 -2
  207. package/server/services/workshop/daq/drivers.ts +89 -3
  208. package/server/services/workshop/dcw/dcw-controller.ts +5 -1
  209. package/server/services/workshop/dcw/drivers.ts +85 -2
  210. package/server/services/workshop/dcw/plugin-bridge.ts +45 -0
  211. package/server/services/workshop/plugins/host.mjs +30 -0
  212. package/shared/daq-protocol.ts +15 -0
  213. package/.output/public/_nuxt/88GOowgO2.js +0 -1
  214. package/.output/public/_nuxt/BOt2SP4z.js +0 -1
  215. package/.output/public/_nuxt/BP-NyKVG.js +0 -18
  216. package/.output/public/_nuxt/BXxH0UQE.js +0 -1
  217. package/.output/public/_nuxt/BsROmY89.js +0 -1
  218. package/.output/public/_nuxt/ClILjvWH.js +0 -1
  219. package/.output/public/_nuxt/CzOCSGLe.js +0 -1
  220. package/.output/public/_nuxt/D8qvkfyY.js +0 -1
  221. package/.output/public/_nuxt/DCn7QZ5N.js +0 -2
  222. package/.output/public/_nuxt/DS8qj8EL.js +0 -1
  223. package/.output/public/_nuxt/DUjW5Yq5.js +0 -1
  224. package/.output/public/_nuxt/Dihnimlk.js +0 -19
  225. package/.output/public/_nuxt/Dqw1lpr-.js +0 -1
  226. package/.output/public/_nuxt/Dr9v6C0f.js +0 -14
  227. package/.output/public/_nuxt/Du9ZJtnj.js +0 -1
  228. package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +0 -1
  229. package/.output/public/_nuxt/TownView.f-YOdXRs.css +0 -1
  230. package/.output/public/_nuxt/_id_.CM4bIgKY.css +0 -1
  231. package/.output/public/_nuxt/_id_.bNV69Dtf.css +0 -1
  232. package/.output/public/_nuxt/agents.ZD0rVdHE.css +0 -1
  233. package/.output/public/_nuxt/aml.BN8ZEW4h.css +0 -1
  234. package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +0 -1
  235. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +0 -1
  236. package/.output/public/_nuxt/daq.B0D3dPAc.css +0 -1
  237. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +0 -1
  238. package/.output/public/_nuxt/pages.D7aSDlUI.css +0 -1
  239. package/.output/public/_nuxt/plugins.sb4nDGqr.css +0 -1
  240. package/.output/public/_nuxt/qr6R6Ha1.js +0 -1
  241. package/.output/public/_nuxt/settings.BTgR-NiE.css +0 -1
  242. package/.output/public/_nuxt/teams.CxnxKVVs.css +0 -1
  243. package/.output/public/_nuxt/tokens.BFY7qv4f.css +0 -1
  244. package/.output/public/_nuxt/workshop.CR_QzeAN.css +0 -1
  245. package/.output/server/chunks/build/_id_-BU1wfOID.mjs.map +0 -1
  246. package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +0 -1
  247. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +0 -1
  248. package/.output/server/chunks/build/aml-_7FdFL2p.mjs.map +0 -1
  249. package/.output/server/chunks/build/channel-templates-p6SZNjlM.mjs.map +0 -1
  250. package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +0 -1
  251. package/.output/server/chunks/build/dcw-CoTxBlAZ.mjs.map +0 -1
  252. package/.output/server/chunks/build/pages-DFxjn9Xa.mjs.map +0 -1
  253. package/.output/server/chunks/build/plugins-lGlUr1lj.mjs.map +0 -1
  254. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +0 -1
  255. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +0 -1
  256. package/.output/server/chunks/build/tokens-C7fTM5C2.mjs.map +0 -1
  257. package/.output/server/chunks/build/town-qMPql9Se.mjs.map +0 -1
  258. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs.map +0 -1
  259. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs.map +0 -1
  260. package/.output/server/chunks/build/workshop-BL5bhfs0.mjs.map +0 -1
  261. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.continue.json +0 -1
  262. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.json +0 -1
  263. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/054bb4bc4e4217a2ab0b0c6b663747214a9af464f5568ea60e784704e340bb0f.source +0 -153
  264. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/0554197c3d2bac203a1f5c48a5d52495d1336db1bd7aa4f4c19875810363e684.source +0 -153
  265. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/05c203d12a954fffba942e1d626e3f5fec05b18a5aef2bac003b7884a4b77406.source +0 -431
  266. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/145a6e6ba811af35aed31e0cc3211f53706a14980fa069ee9e3c1591139f96f0.source +0 -180
  267. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/1ae0fb281f97899c025e368ca73f41349b01fb5f85b19e08d7ebccff49095ed5.source +0 -112
  268. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/20ee9967aa0541c516db7575bdd4a1a745939bc7016bd10d7635903113edb8ec.source +0 -227
  269. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/233751843aeba9d4c5263f275b8102de00443c06f286918cbd23e0b6729e01aa.source +0 -131
  270. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/263c2e05b425e5c322e60c6475279f963bab3a873eb521b21df7fec935a46597.source +0 -160
  271. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/2ac5eaac28eaaee2178c4b32014df8e732f9dd432a285a872f5b7f31c1395fa4.source +0 -157
  272. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/33a0db0d2d95d2c11d0720090458e5fba1425376f5ceb5b422d1d3471be3226b.source +0 -348
  273. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/359e474ae83db35e4615d3728de6bf694b64d4dce9b536730a3b6518f9c0494a.source +0 -296
  274. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/37a4c3c31c4b70f6b719c53d1e9c4fcba603f31adfdd11bb6ef9f0d094923c56.source +0 -285
  275. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/461feca1c14a695eb8b0f17a6f95fe97eeceabc0510faaea4ba6d25bde147d8d.source +0 -217
  276. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/4cfadbda8591f29d5e1e230dc000e701b756e850441b3900fb596a4d2f6e98c2.source +0 -301
  277. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/50ca2810cf0b248c96948994c0c404531bbad6e553166b6816b3b03bfa0f75d2.source +0 -151
  278. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/52d9e8cb591a9ae86e238c3620a610cd386717bee222c0f37b25b2ac7e523dc4.source +0 -135
  279. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/6c87daafa4c006560a954636a97333f6123153c9d34d96203f7ee3a14e556357.source +0 -156
  280. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/77bc47854b577900621cacf1697f81fa2bc005af129d37614a97a3441c08f99f.source +0 -72
  281. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/7bbb789d3a527817118cf8399ed1861d6a962ade10878fd07223435d333a7089.source +0 -469
  282. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/86b7c1daafc8b60ba9982dde5bf1bcc74265709483c176e71f23ec6fc267451f.source +0 -259
  283. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/8df66d62bd41984d0970e8236882080c5085ac937d07a29779463d6a03b37c76.source +0 -97
  284. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/9962f2d1b3a85137bd40cc835725600b39f89ee02a27c06b2e0402e1eafde115.source +0 -277
  285. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a1090ea066b792cb201739f4994e37bf7c12f52e805239a1eab87f18a537f266.source +0 -171
  286. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a5241091650134d0919897e086a606a3846c8b2b54f8b4c84a8696c145ac3e9e.source +0 -425
  287. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/af19fa4c6e34d5540028be4b17600161283af3a2a2dfc61a3fdc0834436a0c14.source +0 -99
  288. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3713d78e2b2f93905ff6be8d58a8d0a2f42dea322eb67c987fda22ff6a084d5.source +0 -325
  289. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3849943c3aff102d1af815631fc843a2a52c17646a4b04696ece323e483bbec.source +0 -332
  290. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/bac735a3803a6650b7f1eb8f587e6dc5c68ea055000c3c99a11789438b78e837.source +0 -43
  291. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d32c24d61eb8b33c73e5a8fc7e4dc7ef437aed238ee4371c2b00617e6104090a.source +0 -154
  292. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d5170dc9a9879fda04fdf91d8a110d4efb6787ee38dda596d4da899883d36a72.source +0 -531
  293. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/dc59bcd7bb397be63ba2f2c3fec23f3782dcb17d2d75a9981eeced761f33eb32.source +0 -439
  294. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/eb5ff8cf66b92aec7bf6726781d8df74faa2b030c3aaac17d6b00c1fc04c9b32.source +0 -323
  295. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f10615a6fae18436e70d1201b2d44fdae3174a3aa7bb4db97dea1b3ed291d721.source +0 -433
  296. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f36d3e271668bc1260c2823542d22dbf1d0f7041ff16b1ac175bce2256e582ca.source +0 -280
  297. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f94449380af09f31ac352152007146a75cc80bae8d6411ffe57b2c56f933b0ff.source +0 -117
  298. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/fe4dfd81178fd6b648335f6405d9ede71319aebde796008e50db21f8adf44c99.source +0 -160
  299. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/ffb4e3aa548cfdadabaa44ab599f9b6a3ca6288176a2cc7a4b2fc2a12b5fb848.source +0 -101
  300. package/scripts/.mimosa/hook-status/sess_aeaecb58-8833-4b83-b93c-050467d21e03-d944d71bb3.json +0 -14
  301. package/scripts/_dbg-audit-all-pages.mjs +0 -43
  302. package/scripts/_dbg-daq-frames-e2e.mjs +0 -118
  303. package/scripts/_dbg-daq-rowstyle-verify.mjs +0 -112
  304. package/scripts/_dbg-hitl-timeout-e2e.mjs +0 -93
  305. package/scripts/_dbg-live-line-e2e.mjs +0 -279
  306. package/scripts/_dbg-minimal-omp.mjs +0 -77
  307. package/scripts/_dbg-mode-nav.mjs +0 -173
  308. package/scripts/_dbg-real-scenario-timeline.log +0 -55
  309. package/scripts/_dbg-team-repro.mjs +0 -76
  310. package/scripts/_dbg-town-agents-probe.mjs +0 -60
  311. package/scripts/_dbg-visual-acceptance.mjs +0 -63
  312. package/scripts/_townview_style.css +0 -1185
  313. package/scripts/_townview_template.html +0 -1215
  314. package/server/data/agent-node-bindings.json +0 -554
  315. package/server/data/daq-templates.json +0 -16
  316. package/server/data/daqs.json +0 -7464
  317. package/server/data/dcw-lines.json +0 -1787
  318. package/server/data/dcw-products.json +0 -1626
  319. package/server/data/dcw-recipes.json +0 -4672
  320. package/server/data/dcw-rollback.json +0 -20754
  321. package/server/data/dcw-runs.json +0 -6354
  322. package/server/data/dcw-templates.json +0 -146
  323. package/server/data/dcw-writes.json +0 -15650
  324. package/server/data/dcws.json +0 -9033
  325. package/server/data/device-twins.json +0 -133
  326. package/server/data/line-runs.json +0 -322
  327. package/server/data/scene-layouts.json +0 -277
@@ -1,452 +1,452 @@
1
- <script setup lang="ts">
2
- /**
3
- * 设备模型库(Asset Library)面板 —— 只列举「设备/实体」3D 模型,支持上传/删除。
4
- *
5
- * 交互:
6
- * - 仅展示 kind === 'dev' 的模型(扫描 public/assets/game/devices + 用户上传);
7
- * character 角色模型不出现在模型库 —— 在频道成员管理中为成员设置。
8
- * - 每张模型卡可拖拽:拖起把 assetId 写入 dataTransfer,落到孪生场景 →
9
- * TownScene3D.dropModelOnWorld() 在落点生成一个设备实例(数字孪生)。
10
- * - 上传:选择 .glb/.gltf/.obj/.fbx → POST /api/workshop/assets/devices,写入 devices 目录。
11
- * - 删除:删除对应模型文件(仅限 devices 目录内)。
12
- */
13
- import { useCharacterAssets } from '@/app/composables/workshop/useCharacterAssets'
14
- import ModelPreview3D from './town/ModelPreview3D.vue'
15
-
16
- const { t } = useI18n()
17
-
18
- const assets = useCharacterAssets()
19
-
20
- /** 侧边模型库:展示所有设备实体模型(含内置模型、设备目录扫描与用户上传)。 */
21
- const deviceModels = computed(() =>
22
- assets.models.filter(m => m.kind === 'dev'),
23
- )
24
-
25
- /** 可 3D 预览的格式(glb/gltf);obj/fbx 保持占位图标 */
26
- const isPreviewable = (name: string): boolean => /\.(glb|gltf)$/i.test(name)
27
-
28
- // 拖起:把模型 id 交给 dataTransfer(HTML5 DnD)
29
- function onDragStart(e: DragEvent, id: string): void {
30
- if (!e.dataTransfer) return
31
- e.dataTransfer.setData('application/x-aw-model', id)
32
- e.dataTransfer.setData('text/plain', id)
33
- e.dataTransfer.effectAllowed = 'copy'
34
- }
35
-
36
- // ---------- 上传(设备 3D 模型:拖放/选择 → 本地 3D 预览 → 确认上传) ----------
37
- const uploading = ref(false)
38
- const uploadError = ref('')
39
- const uploadFile = ref<File | null>(null)
40
- const dropActive = ref(false)
41
- const onPickFile = (e: Event): void => {
42
- const input = e.target as HTMLInputElement
43
- setUploadFile(input.files?.[0] ?? null)
44
- }
45
- const onDropFile = (e: DragEvent): void => {
46
- dropActive.value = false
47
- setUploadFile(e.dataTransfer?.files?.[0] ?? null)
48
- }
49
- const setUploadFile = (f: File | null): void => {
50
- if (f && !/\.(glb|gltf|obj|fbx)$/i.test(f.name)) {
51
- uploadError.value = '仅支持 .glb/.gltf/.obj/.fbx 模型文件'
52
- return
53
- }
54
- uploadFile.value = f
55
- uploadError.value = ''
56
- }
57
- const clearUpload = (): void => {
58
- uploadFile.value = null
59
- }
60
- async function doUpload(): Promise<void> {
61
- const f = uploadFile.value
62
- if (!f) {
63
- uploadError.value = t('assetLibrary.kcjqcaw012')
64
- return
65
- }
66
- uploading.value = true
67
- uploadError.value = ''
68
- try {
69
- const model = await assets.uploadDevice(f)
70
- uploadFile.value = null
71
- uploadError.value = t('assetLibrary.k8nv3or016', { p0: model.name })
72
- }
73
- catch (err) {
74
- uploadError.value = apiErrorMessage(err)
75
- }
76
- finally {
77
- uploading.value = false
78
- }
79
- }
80
-
81
- // ---------- 删除(设备模型文件;已生成实例不受影响,刷新后节点因缺模型被跳过) ----------
82
- const deletingId = ref('')
83
- const deleteMsg = ref('')
84
- async function doRemove(id: string): Promise<void> {
85
- deletingId.value = id
86
- deleteMsg.value = ''
87
- try {
88
- await assets.removeDevice(id)
89
- deleteMsg.value = t('assetLibrary.k8n918k013')
90
- }
91
- catch (err) {
92
- deleteMsg.value = apiErrorMessage(err)
93
- }
94
- finally {
95
- deletingId.value = ''
96
- }
97
- }
98
- </script>
99
-
100
- <template>
101
- <aside class="asset-lib">
102
- <div class="lib-head">
103
- <span class="head-title">{{ $t('assetLibrary.k7la4x5001') }}</span>
104
- <span class="head-hint">{{ $t('assetLibrary.kulif5b002') }}</span>
105
- </div>
106
-
107
- <!-- 上传区(设备 3D 模型:拖放/选择 → 本地 3D 预览 → 确认上传) -->
108
- <div
109
- class="upload-box"
110
- :class="{ drop: dropActive }"
111
- @dragover.prevent="dropActive = true"
112
- @dragleave="dropActive = false"
113
- @drop.prevent="onDropFile"
114
- >
115
- <label class="upload-row">
116
- <input
117
- type="file"
118
- accept=".glb,.gltf,.obj,.fbx"
119
- class="file-input"
120
- @change="onPickFile"
121
- >
122
- <span class="upload-name">
123
- <span class="i-tabler-plus upload-ico" />
124
- {{ uploadFile?.name || $t('assetLibrary.k1iiort6010') }}
125
- </span>
126
- </label>
127
- <span
128
- v-if="dropActive"
129
- class="drop-hint"
130
- >
131
- <span class="i-tabler-download" />
132
- {{ $t('assetLibrary.k5hg6jq003') }}
133
- </span>
134
-
135
- <!-- 上传前本地 3D 预览(glb/gltf 实时渲染真实形状) -->
136
- <template v-if="uploadFile">
137
- <ModelPreview3D
138
- v-if="isPreviewable(uploadFile.name)"
139
- :local-file="uploadFile"
140
- :height="92"
141
- />
142
- <span
143
- v-else
144
- class="upload-file-tag"
145
- >{{ uploadFile.name }} ({{ $t('assetLibrary.k1xz5wvk009') }}</span>
146
- <div class="upload-actions">
147
- <button
148
- class="upload-btn go"
149
- :disabled="uploading"
150
- @click="doUpload"
151
- >
152
- {{ uploading ? $t('assetLibrary.k1ar7n8i005') : $t('assetLibrary.k1hhga4h014') }}
153
- </button>
154
- <button
155
- class="upload-btn"
156
- :disabled="uploading"
157
- @click="clearUpload"
158
- >
159
- {{ $t('assetLibrary.k3xdnn004') }}
160
- </button>
161
- </div>
162
- </template>
163
- <span
164
- v-else-if="uploading"
165
- class="mini-err"
166
- >{{ $t('assetLibrary.k1ar7n8i005') }}</span>
167
- <span
168
- v-if="uploadError && !uploadFile"
169
- class="mini-err"
170
- >{{ uploadError }}</span>
171
- </div>
172
-
173
- <div class="lib-grid">
174
- <div
175
- v-for="m in deviceModels"
176
- :key="m.id"
177
- class="model-card"
178
- :data-model-id="m.id"
179
- draggable="true"
180
- :title="m.hint || $t('assetLibrary.katn7lo015', { p0: m.name })"
181
- @dragstart="onDragStart($event, m.id)"
182
- >
183
- <!-- GLB/GLTF:实时 3D 模型预览(真实几何形状);其余格式占位图标 -->
184
- <ModelPreview3D
185
- v-if="isPreviewable(m.file)"
186
- :file="m.file"
187
- :height="76"
188
- />
189
- <span
190
- v-else
191
- class="model-img glb dev"
192
- >
193
- <span
194
- class="i-tabler-cpu glb-cube"
195
- aria-hidden="true"
196
- />
197
- </span>
198
- <span class="model-name">{{ m.name }}</span>
199
- <span class="model-badge">{{ $t('assetLibrary.k1n32zs0006') }}</span>
200
- <div class="card-actions">
201
- <button
202
- v-if="m.file.includes('/assets/game/devices/')"
203
- class="card-btn danger"
204
- :disabled="deletingId === m.id"
205
- @click="doRemove(m.id)"
206
- >
207
- {{ deletingId === m.id ? '…' : $t('assetLibrary.k498l011') }}
208
- </button>
209
- </div>
210
- </div>
211
- <div
212
- v-if="!assets.loaded"
213
- class="lib-note"
214
- >
215
- <span class="loading-dot" />
216
- {{ $t('assetLibrary.k1kq2tmi007') }}
217
- </div>
218
- <div
219
- v-else-if="deviceModels.length === 0"
220
- class="lib-note"
221
- >
222
- {{ $t('assetLibrary.k15527e1008') }}
223
- </div>
224
- </div>
225
-
226
- <span
227
- v-if="deleteMsg"
228
- class="mini-msg"
229
- >{{ deleteMsg }}</span>
230
- </aside>
231
- </template>
232
-
233
- <style scoped>
234
- /* ============================================================
235
- * 设备模型库 —— 工业 HMI 单层样式(消费 .town-view 的 --hud-* 令牌,
236
- * 带本地兜底;不再依赖宿主 :deep 覆盖)
237
- * ============================================================ */
238
- .asset-lib {
239
- display: flex;
240
- flex-direction: column;
241
- gap: 8px;
242
- width: 184px;
243
- flex: none;
244
- padding: 0 0 10px;
245
- font-family: var(--font-body);
246
- }
247
- .lib-head {
248
- display: flex;
249
- gap: 7px;
250
- align-items: center;
251
- padding: 10px 12px 6px;
252
- border-bottom: 1px solid rgba(38, 51, 64, 0.45);
253
- }
254
- .head-title {
255
- font-family: var(--font-mono);
256
- font-size: 10px;
257
- font-weight: 600;
258
- letter-spacing: 0.16em;
259
- color: var(--hud-text, #d9e4ee);
260
- white-space: nowrap;
261
- }
262
- .head-hint {
263
- margin-left: auto;
264
- font-family: var(--font-mono);
265
- font-size: 10px;
266
- color: var(--hud-faint, #5b6c7b);
267
- white-space: nowrap;
268
- }
269
-
270
- /* 上传区:虚线收件格(拖放/选择 → 本地 3D 预览 → 确认) */
271
- .upload-box {
272
- display: flex;
273
- flex-direction: column;
274
- gap: 7px;
275
- margin: 8px 10px 0;
276
- padding: 7px;
277
- border: 1px dashed rgba(38, 51, 64, 0.8);
278
- border-radius: 2px;
279
- background: rgba(11, 16, 24, 0.4);
280
- transition: border-color 0.16s var(--hud-ease, ease), background 0.16s var(--hud-ease, ease);
281
- }
282
- .upload-box.drop {
283
- border-color: var(--hud-accent, #4da3ff);
284
- background: rgba(77, 163, 255, 0.08);
285
- }
286
- .upload-row { display: flex; align-items: center; gap: 6px; min-height: 26px; cursor: pointer; }
287
- .file-input { display: none; }
288
- .upload-name {
289
- display: inline-flex;
290
- align-items: center;
291
- gap: 5px;
292
- font-family: var(--font-mono);
293
- font-size: 10px;
294
- color: var(--hud-dim, #8496a5);
295
- overflow: hidden;
296
- text-overflow: ellipsis;
297
- white-space: nowrap;
298
- }
299
- .upload-ico { color: var(--hud-accent, #4da3ff); font-size: 13px; }
300
- .drop-hint {
301
- display: inline-flex;
302
- align-items: center;
303
- gap: 5px;
304
- font-family: var(--font-mono);
305
- font-size: 10px;
306
- letter-spacing: 0.08em;
307
- color: var(--hud-accent, #4da3ff);
308
- padding: 2px 0 4px;
309
- }
310
- .upload-file-tag {
311
- font-family: var(--font-mono);
312
- font-size: 9.5px;
313
- color: var(--hud-dim, #8496a5);
314
- padding: 3px 6px;
315
- border: 1px solid var(--hud-line, #263340);
316
- border-radius: 2px;
317
- }
318
- .upload-actions { display: flex; gap: 6px; align-items: center; }
319
- .upload-btn {
320
- padding: 5px 10px;
321
- font-family: var(--font-mono);
322
- font-size: 10px;
323
- font-weight: 600;
324
- letter-spacing: 0.06em;
325
- color: var(--hud-text, #d9e4ee);
326
- background: var(--hud-panel-raised, #141b26);
327
- border: 1px solid var(--hud-line, #263340);
328
- border-radius: 2px;
329
- cursor: pointer;
330
- transition: border-color 0.14s var(--hud-ease, ease), color 0.14s var(--hud-ease, ease);
331
- }
332
- .upload-btn:hover:not(:disabled) { border-color: var(--hud-accent, #4da3ff); color: var(--hud-accent, #4da3ff); }
333
- .upload-btn:disabled { opacity: 0.45; cursor: default; }
334
- .upload-btn.go {
335
- color: var(--hud-amber, #f5a742);
336
- border-color: rgba(245, 167, 66, 0.55);
337
- background: rgba(245, 167, 66, 0.08);
338
- }
339
- .upload-btn.go:hover:not(:disabled) {
340
- background: rgba(245, 167, 66, 0.16);
341
- border-color: var(--hud-amber, #f5a742);
342
- color: var(--hud-amber, #f5a742);
343
- }
344
- .mini-err { font-family: var(--font-mono); font-size: 9.5px; color: var(--hud-danger, #ff6b5c); }
345
- .mini-err, .mini-msg { font-size: 10px; }
346
- .mini-msg { font-family: var(--font-mono); color: var(--hud-dim, #8496a5); padding: 0 10px; }
347
-
348
- /* 模型清单:方角装备行 */
349
- .lib-grid {
350
- display: flex;
351
- flex-direction: column;
352
- gap: 4px;
353
- max-height: 42vh;
354
- overflow: hidden auto;
355
- padding: 6px 10px 2px;
356
- }
357
- .model-card {
358
- position: relative;
359
- display: flex;
360
- flex-direction: column;
361
- align-items: stretch;
362
- gap: 4px;
363
- padding: 6px;
364
- cursor: grab;
365
- background: rgba(20, 27, 38, 0.65);
366
- border: 0;
367
- border-radius: 2px;
368
- transition: background 0.14s var(--hud-ease, ease);
369
- }
370
- .model-card:hover {
371
- background: var(--hud-panel-hover, #1a2432);
372
- }
373
- .model-card:active { cursor: grabbing; }
374
- .model-card .model-preview-3d { margin-bottom: 2px; }
375
- .model-name {
376
- font-size: 11px;
377
- font-weight: 600;
378
- color: var(--hud-text, #d9e4ee);
379
- text-align: left;
380
- padding: 0 2px;
381
- overflow: hidden;
382
- text-overflow: ellipsis;
383
- white-space: nowrap;
384
- }
385
- .model-badge {
386
- font-family: var(--font-mono);
387
- /* 8.5px 在这套 HUD 里是"看得见读不了"的下限之外(实测被审计稳定判为不可读);
388
- 10px 是同类铭牌(--hud 微字)在 1440 下的地板。 */
389
- font-size: 10px;
390
- letter-spacing: 0.08em;
391
- color: var(--hud-faint, #5b6c7b);
392
- text-align: left;
393
- padding: 0 2px;
394
- }
395
- .card-actions { display: flex; justify-content: flex-end; gap: 4px; }
396
- .card-btn {
397
- padding: 2px 8px;
398
- font-family: var(--font-mono);
399
- font-size: 10px;
400
- font-weight: 600;
401
- letter-spacing: 0.06em;
402
- color: var(--hud-text, #d9e4ee);
403
- background: transparent;
404
- border: 1px solid var(--hud-line, #263340);
405
- border-radius: 2px;
406
- cursor: pointer;
407
- transition: border-color 0.14s var(--hud-ease, ease), color 0.14s var(--hud-ease, ease);
408
- }
409
- .card-btn:hover:not(:disabled) { border-color: var(--hud-accent, #4da3ff); color: var(--hud-accent, #4da3ff); }
410
- .card-btn.danger { color: var(--hud-danger, #ff6b5c); }
411
- .card-btn.danger:hover:not(:disabled) { border-color: var(--hud-danger, #ff6b5c); color: var(--hud-danger, #ff6b5c); }
412
- .card-btn:disabled { opacity: 0.45; cursor: default; }
413
-
414
- /* 非 GLB 格式占位图标(obj/fbx 暂不支持实时预览) */
415
- .model-img {
416
- width: 46px;
417
- height: 68px;
418
- object-fit: contain;
419
- pointer-events: none;
420
- }
421
- .model-img.glb {
422
- display: inline-flex;
423
- align-items: center;
424
- justify-content: center;
425
- width: 100%;
426
- height: 44px;
427
- background: var(--hud-input, #0b1018);
428
- border: 1px dashed var(--hud-line, #263340);
429
- border-radius: 2px;
430
- }
431
- .glb-cube { font-size: 24px; line-height: 1; color: var(--hud-faint, #5b6c7b); }
432
-
433
- .lib-note {
434
- display: flex;
435
- gap: 7px;
436
- align-items: center;
437
- padding: 8px 2px;
438
- font-family: var(--font-mono);
439
- font-size: 9.5px;
440
- color: var(--hud-faint, #5b6c7b);
441
- }
442
- .loading-dot {
443
- width: 12px;
444
- height: 12px;
445
- border: 2px solid var(--hud-line, #263340);
446
- border-top-color: var(--hud-accent, #4da3ff);
447
- border-radius: 50%;
448
- animation: spin 0.8s linear infinite;
449
- }
450
- @keyframes spin { to { transform: rotate(360deg); } }
451
- @media (prefers-reduced-motion: reduce) { .loading-dot { animation: none; } }
452
- </style>
1
+ <script setup lang="ts">
2
+ /**
3
+ * 设备模型库(Asset Library)面板 —— 只列举「设备/实体」3D 模型,支持上传/删除。
4
+ *
5
+ * 交互:
6
+ * - 仅展示 kind === 'dev' 的模型(扫描 public/assets/game/devices + 用户上传);
7
+ * character 角色模型不出现在模型库 —— 在频道成员管理中为成员设置。
8
+ * - 每张模型卡可拖拽:拖起把 assetId 写入 dataTransfer,落到孪生场景 →
9
+ * TownScene3D.dropModelOnWorld() 在落点生成一个设备实例(数字孪生)。
10
+ * - 上传:选择 .glb/.gltf/.obj/.fbx → POST /api/workshop/assets/devices,写入 devices 目录。
11
+ * - 删除:删除对应模型文件(仅限 devices 目录内)。
12
+ */
13
+ import { useCharacterAssets } from '@/app/composables/workshop/useCharacterAssets'
14
+ import ModelPreview3D from './town/ModelPreview3D.vue'
15
+
16
+ const { t } = useI18n()
17
+
18
+ const assets = useCharacterAssets()
19
+
20
+ /** 侧边模型库:展示所有设备实体模型(含内置模型、设备目录扫描与用户上传)。 */
21
+ const deviceModels = computed(() =>
22
+ assets.models.filter(m => m.kind === 'dev'),
23
+ )
24
+
25
+ /** 可 3D 预览的格式(glb/gltf);obj/fbx 保持占位图标 */
26
+ const isPreviewable = (name: string): boolean => /\.(glb|gltf)$/i.test(name)
27
+
28
+ // 拖起:把模型 id 交给 dataTransfer(HTML5 DnD)
29
+ function onDragStart(e: DragEvent, id: string): void {
30
+ if (!e.dataTransfer) return
31
+ e.dataTransfer.setData('application/x-aw-model', id)
32
+ e.dataTransfer.setData('text/plain', id)
33
+ e.dataTransfer.effectAllowed = 'copy'
34
+ }
35
+
36
+ // ---------- 上传(设备 3D 模型:拖放/选择 → 本地 3D 预览 → 确认上传) ----------
37
+ const uploading = ref(false)
38
+ const uploadError = ref('')
39
+ const uploadFile = ref<File | null>(null)
40
+ const dropActive = ref(false)
41
+ const onPickFile = (e: Event): void => {
42
+ const input = e.target as HTMLInputElement
43
+ setUploadFile(input.files?.[0] ?? null)
44
+ }
45
+ const onDropFile = (e: DragEvent): void => {
46
+ dropActive.value = false
47
+ setUploadFile(e.dataTransfer?.files?.[0] ?? null)
48
+ }
49
+ const setUploadFile = (f: File | null): void => {
50
+ if (f && !/\.(glb|gltf|obj|fbx)$/i.test(f.name)) {
51
+ uploadError.value = t('assetLibrary.badModelFile')
52
+ return
53
+ }
54
+ uploadFile.value = f
55
+ uploadError.value = ''
56
+ }
57
+ const clearUpload = (): void => {
58
+ uploadFile.value = null
59
+ }
60
+ async function doUpload(): Promise<void> {
61
+ const f = uploadFile.value
62
+ if (!f) {
63
+ uploadError.value = t('assetLibrary.kcjqcaw012')
64
+ return
65
+ }
66
+ uploading.value = true
67
+ uploadError.value = ''
68
+ try {
69
+ const model = await assets.uploadDevice(f)
70
+ uploadFile.value = null
71
+ uploadError.value = t('assetLibrary.k8nv3or016', { p0: model.name })
72
+ }
73
+ catch (err) {
74
+ uploadError.value = apiErrorMessage(err)
75
+ }
76
+ finally {
77
+ uploading.value = false
78
+ }
79
+ }
80
+
81
+ // ---------- 删除(设备模型文件;已生成实例不受影响,刷新后节点因缺模型被跳过) ----------
82
+ const deletingId = ref('')
83
+ const deleteMsg = ref('')
84
+ async function doRemove(id: string): Promise<void> {
85
+ deletingId.value = id
86
+ deleteMsg.value = ''
87
+ try {
88
+ await assets.removeDevice(id)
89
+ deleteMsg.value = t('assetLibrary.k8n918k013')
90
+ }
91
+ catch (err) {
92
+ deleteMsg.value = apiErrorMessage(err)
93
+ }
94
+ finally {
95
+ deletingId.value = ''
96
+ }
97
+ }
98
+ </script>
99
+
100
+ <template>
101
+ <aside class="asset-lib">
102
+ <div class="lib-head">
103
+ <span class="head-title">{{ $t('assetLibrary.k7la4x5001') }}</span>
104
+ <span class="head-hint">{{ $t('assetLibrary.kulif5b002') }}</span>
105
+ </div>
106
+
107
+ <!-- 上传区(设备 3D 模型:拖放/选择 → 本地 3D 预览 → 确认上传) -->
108
+ <div
109
+ class="upload-box"
110
+ :class="{ drop: dropActive }"
111
+ @dragover.prevent="dropActive = true"
112
+ @dragleave="dropActive = false"
113
+ @drop.prevent="onDropFile"
114
+ >
115
+ <label class="upload-row">
116
+ <input
117
+ type="file"
118
+ accept=".glb,.gltf,.obj,.fbx"
119
+ class="file-input"
120
+ @change="onPickFile"
121
+ >
122
+ <span class="upload-name">
123
+ <span class="i-tabler-plus upload-ico" />
124
+ {{ uploadFile?.name || $t('assetLibrary.k1iiort6010') }}
125
+ </span>
126
+ </label>
127
+ <span
128
+ v-if="dropActive"
129
+ class="drop-hint"
130
+ >
131
+ <span class="i-tabler-download" />
132
+ {{ $t('assetLibrary.k5hg6jq003') }}
133
+ </span>
134
+
135
+ <!-- 上传前本地 3D 预览(glb/gltf 实时渲染真实形状) -->
136
+ <template v-if="uploadFile">
137
+ <ModelPreview3D
138
+ v-if="isPreviewable(uploadFile.name)"
139
+ :local-file="uploadFile"
140
+ :height="92"
141
+ />
142
+ <span
143
+ v-else
144
+ class="upload-file-tag"
145
+ >{{ uploadFile.name }} ({{ $t('assetLibrary.k1xz5wvk009') }}</span>
146
+ <div class="upload-actions">
147
+ <button
148
+ class="upload-btn go"
149
+ :disabled="uploading"
150
+ @click="doUpload"
151
+ >
152
+ {{ uploading ? $t('assetLibrary.k1ar7n8i005') : $t('assetLibrary.k1hhga4h014') }}
153
+ </button>
154
+ <button
155
+ class="upload-btn"
156
+ :disabled="uploading"
157
+ @click="clearUpload"
158
+ >
159
+ {{ $t('assetLibrary.k3xdnn004') }}
160
+ </button>
161
+ </div>
162
+ </template>
163
+ <span
164
+ v-else-if="uploading"
165
+ class="mini-err"
166
+ >{{ $t('assetLibrary.k1ar7n8i005') }}</span>
167
+ <span
168
+ v-if="uploadError && !uploadFile"
169
+ class="mini-err"
170
+ >{{ uploadError }}</span>
171
+ </div>
172
+
173
+ <div class="lib-grid">
174
+ <div
175
+ v-for="m in deviceModels"
176
+ :key="m.id"
177
+ class="model-card"
178
+ :data-model-id="m.id"
179
+ draggable="true"
180
+ :title="townModelHint(t, m) || $t('assetLibrary.katn7lo015', { p0: townModelName(t, m) })"
181
+ @dragstart="onDragStart($event, m.id)"
182
+ >
183
+ <!-- GLB/GLTF:实时 3D 模型预览(真实几何形状);其余格式占位图标 -->
184
+ <ModelPreview3D
185
+ v-if="isPreviewable(m.file)"
186
+ :file="m.file"
187
+ :height="76"
188
+ />
189
+ <span
190
+ v-else
191
+ class="model-img glb dev"
192
+ >
193
+ <span
194
+ class="i-tabler-cpu glb-cube"
195
+ aria-hidden="true"
196
+ />
197
+ </span>
198
+ <span class="model-name">{{ townModelName(t, m) }}</span>
199
+ <span class="model-badge">{{ $t('assetLibrary.k1n32zs0006') }}</span>
200
+ <div class="card-actions">
201
+ <button
202
+ v-if="m.file.includes('/assets/game/devices/')"
203
+ class="card-btn danger"
204
+ :disabled="deletingId === m.id"
205
+ @click="doRemove(m.id)"
206
+ >
207
+ {{ deletingId === m.id ? '…' : $t('assetLibrary.k498l011') }}
208
+ </button>
209
+ </div>
210
+ </div>
211
+ <div
212
+ v-if="!assets.loaded"
213
+ class="lib-note"
214
+ >
215
+ <span class="loading-dot" />
216
+ {{ $t('assetLibrary.k1kq2tmi007') }}
217
+ </div>
218
+ <div
219
+ v-else-if="deviceModels.length === 0"
220
+ class="lib-note"
221
+ >
222
+ {{ $t('assetLibrary.k15527e1008') }}
223
+ </div>
224
+ </div>
225
+
226
+ <span
227
+ v-if="deleteMsg"
228
+ class="mini-msg"
229
+ >{{ deleteMsg }}</span>
230
+ </aside>
231
+ </template>
232
+
233
+ <style scoped>
234
+ /* ============================================================
235
+ * 设备模型库 —— 工业 HMI 单层样式(消费 .town-view 的 --hud-* 令牌,
236
+ * 带本地兜底;不再依赖宿主 :deep 覆盖)
237
+ * ============================================================ */
238
+ .asset-lib {
239
+ display: flex;
240
+ flex-direction: column;
241
+ gap: 8px;
242
+ width: 184px;
243
+ flex: none;
244
+ padding: 0 0 10px;
245
+ font-family: var(--font-body);
246
+ }
247
+ .lib-head {
248
+ display: flex;
249
+ gap: 7px;
250
+ align-items: center;
251
+ padding: 10px 12px 6px;
252
+ border-bottom: 1px solid rgba(38, 51, 64, 0.45);
253
+ }
254
+ .head-title {
255
+ font-family: var(--font-mono);
256
+ font-size: 10px;
257
+ font-weight: 600;
258
+ letter-spacing: 0.16em;
259
+ color: var(--hud-text, #d9e4ee);
260
+ white-space: nowrap;
261
+ }
262
+ .head-hint {
263
+ margin-left: auto;
264
+ font-family: var(--font-mono);
265
+ font-size: 10px;
266
+ color: var(--hud-faint, #5b6c7b);
267
+ white-space: nowrap;
268
+ }
269
+
270
+ /* 上传区:虚线收件格(拖放/选择 → 本地 3D 预览 → 确认) */
271
+ .upload-box {
272
+ display: flex;
273
+ flex-direction: column;
274
+ gap: 7px;
275
+ margin: 8px 10px 0;
276
+ padding: 7px;
277
+ border: 1px dashed rgba(38, 51, 64, 0.8);
278
+ border-radius: 2px;
279
+ background: rgba(11, 16, 24, 0.4);
280
+ transition: border-color 0.16s var(--hud-ease, ease), background 0.16s var(--hud-ease, ease);
281
+ }
282
+ .upload-box.drop {
283
+ border-color: var(--hud-accent, #4da3ff);
284
+ background: rgba(77, 163, 255, 0.08);
285
+ }
286
+ .upload-row { display: flex; align-items: center; gap: 6px; min-height: 26px; cursor: pointer; }
287
+ .file-input { display: none; }
288
+ .upload-name {
289
+ display: inline-flex;
290
+ align-items: center;
291
+ gap: 5px;
292
+ font-family: var(--font-mono);
293
+ font-size: 10px;
294
+ color: var(--hud-dim, #8496a5);
295
+ overflow: hidden;
296
+ text-overflow: ellipsis;
297
+ white-space: nowrap;
298
+ }
299
+ .upload-ico { color: var(--hud-accent, #4da3ff); font-size: 13px; }
300
+ .drop-hint {
301
+ display: inline-flex;
302
+ align-items: center;
303
+ gap: 5px;
304
+ font-family: var(--font-mono);
305
+ font-size: 10px;
306
+ letter-spacing: 0.08em;
307
+ color: var(--hud-accent, #4da3ff);
308
+ padding: 2px 0 4px;
309
+ }
310
+ .upload-file-tag {
311
+ font-family: var(--font-mono);
312
+ font-size: 9.5px;
313
+ color: var(--hud-dim, #8496a5);
314
+ padding: 3px 6px;
315
+ border: 1px solid var(--hud-line, #263340);
316
+ border-radius: 2px;
317
+ }
318
+ .upload-actions { display: flex; gap: 6px; align-items: center; }
319
+ .upload-btn {
320
+ padding: 5px 10px;
321
+ font-family: var(--font-mono);
322
+ font-size: 10px;
323
+ font-weight: 600;
324
+ letter-spacing: 0.06em;
325
+ color: var(--hud-text, #d9e4ee);
326
+ background: var(--hud-panel-raised, #141b26);
327
+ border: 1px solid var(--hud-line, #263340);
328
+ border-radius: 2px;
329
+ cursor: pointer;
330
+ transition: border-color 0.14s var(--hud-ease, ease), color 0.14s var(--hud-ease, ease);
331
+ }
332
+ .upload-btn:hover:not(:disabled) { border-color: var(--hud-accent, #4da3ff); color: var(--hud-accent, #4da3ff); }
333
+ .upload-btn:disabled { opacity: 0.45; cursor: default; }
334
+ .upload-btn.go {
335
+ color: var(--hud-amber, #f5a742);
336
+ border-color: rgba(245, 167, 66, 0.55);
337
+ background: rgba(245, 167, 66, 0.08);
338
+ }
339
+ .upload-btn.go:hover:not(:disabled) {
340
+ background: rgba(245, 167, 66, 0.16);
341
+ border-color: var(--hud-amber, #f5a742);
342
+ color: var(--hud-amber, #f5a742);
343
+ }
344
+ .mini-err { font-family: var(--font-mono); font-size: 9.5px; color: var(--hud-danger, #ff6b5c); }
345
+ .mini-err, .mini-msg { font-size: 10px; }
346
+ .mini-msg { font-family: var(--font-mono); color: var(--hud-dim, #8496a5); padding: 0 10px; }
347
+
348
+ /* 模型清单:方角装备行 */
349
+ .lib-grid {
350
+ display: flex;
351
+ flex-direction: column;
352
+ gap: 4px;
353
+ max-height: 42vh;
354
+ overflow: hidden auto;
355
+ padding: 6px 10px 2px;
356
+ }
357
+ .model-card {
358
+ position: relative;
359
+ display: flex;
360
+ flex-direction: column;
361
+ align-items: stretch;
362
+ gap: 4px;
363
+ padding: 6px;
364
+ cursor: grab;
365
+ background: rgba(20, 27, 38, 0.65);
366
+ border: 0;
367
+ border-radius: 2px;
368
+ transition: background 0.14s var(--hud-ease, ease);
369
+ }
370
+ .model-card:hover {
371
+ background: var(--hud-panel-hover, #1a2432);
372
+ }
373
+ .model-card:active { cursor: grabbing; }
374
+ .model-card .model-preview-3d { margin-bottom: 2px; }
375
+ .model-name {
376
+ font-size: 11px;
377
+ font-weight: 600;
378
+ color: var(--hud-text, #d9e4ee);
379
+ text-align: left;
380
+ padding: 0 2px;
381
+ overflow: hidden;
382
+ text-overflow: ellipsis;
383
+ white-space: nowrap;
384
+ }
385
+ .model-badge {
386
+ font-family: var(--font-mono);
387
+ /* 8.5px 在这套 HUD 里是"看得见读不了"的下限之外(实测被审计稳定判为不可读);
388
+ 10px 是同类铭牌(--hud 微字)在 1440 下的地板。 */
389
+ font-size: 10px;
390
+ letter-spacing: 0.08em;
391
+ color: var(--hud-faint, #5b6c7b);
392
+ text-align: left;
393
+ padding: 0 2px;
394
+ }
395
+ .card-actions { display: flex; justify-content: flex-end; gap: 4px; }
396
+ .card-btn {
397
+ padding: 2px 8px;
398
+ font-family: var(--font-mono);
399
+ font-size: 10px;
400
+ font-weight: 600;
401
+ letter-spacing: 0.06em;
402
+ color: var(--hud-text, #d9e4ee);
403
+ background: transparent;
404
+ border: 1px solid var(--hud-line, #263340);
405
+ border-radius: 2px;
406
+ cursor: pointer;
407
+ transition: border-color 0.14s var(--hud-ease, ease), color 0.14s var(--hud-ease, ease);
408
+ }
409
+ .card-btn:hover:not(:disabled) { border-color: var(--hud-accent, #4da3ff); color: var(--hud-accent, #4da3ff); }
410
+ .card-btn.danger { color: var(--hud-danger, #ff6b5c); }
411
+ .card-btn.danger:hover:not(:disabled) { border-color: var(--hud-danger, #ff6b5c); color: var(--hud-danger, #ff6b5c); }
412
+ .card-btn:disabled { opacity: 0.45; cursor: default; }
413
+
414
+ /* 非 GLB 格式占位图标(obj/fbx 暂不支持实时预览) */
415
+ .model-img {
416
+ width: 46px;
417
+ height: 68px;
418
+ object-fit: contain;
419
+ pointer-events: none;
420
+ }
421
+ .model-img.glb {
422
+ display: inline-flex;
423
+ align-items: center;
424
+ justify-content: center;
425
+ width: 100%;
426
+ height: 44px;
427
+ background: var(--hud-input, #0b1018);
428
+ border: 1px dashed var(--hud-line, #263340);
429
+ border-radius: 2px;
430
+ }
431
+ .glb-cube { font-size: 24px; line-height: 1; color: var(--hud-faint, #5b6c7b); }
432
+
433
+ .lib-note {
434
+ display: flex;
435
+ gap: 7px;
436
+ align-items: center;
437
+ padding: 8px 2px;
438
+ font-family: var(--font-mono);
439
+ font-size: 9.5px;
440
+ color: var(--hud-faint, #5b6c7b);
441
+ }
442
+ .loading-dot {
443
+ width: 12px;
444
+ height: 12px;
445
+ border: 2px solid var(--hud-line, #263340);
446
+ border-top-color: var(--hud-accent, #4da3ff);
447
+ border-radius: 50%;
448
+ animation: spin 0.8s linear infinite;
449
+ }
450
+ @keyframes spin { to { transform: rotate(360deg); } }
451
+ @media (prefers-reduced-motion: reduce) { .loading-dot { animation: none; } }
452
+ </style>