agentworkshop 0.7.41 → 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 (265) 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/DKySmNPI.js +14 -0
  11. package/.output/public/_nuxt/Di0NY_ht.js +1 -0
  12. package/.output/public/_nuxt/Du9v6qGB.js +1 -0
  13. package/.output/public/_nuxt/{DGbd-UiG.js → EHlEMRii.js} +1 -1
  14. package/.output/public/_nuxt/Eke1fs-U.js +1 -0
  15. package/.output/public/_nuxt/{DeJykBFf.js → H0BcqZtT.js} +1 -1
  16. package/.output/public/_nuxt/{C4oW1HsE.js → HSYEkkce.js} +1 -1
  17. package/.output/public/_nuxt/{D_OTvQza.js → J9oU00D1.js} +1 -1
  18. package/.output/public/_nuxt/OmpTerminalPanel.BxR5-lvi.css +1 -0
  19. package/.output/public/_nuxt/SSyAPaHc.js +1 -0
  20. package/.output/public/_nuxt/TownView.li4EMQxK.css +1 -0
  21. package/.output/public/_nuxt/VdLk4jLE2.js +1 -0
  22. package/.output/public/_nuxt/_id_.BhsXoEuk.css +1 -0
  23. package/.output/public/_nuxt/_id_.DQyj-WKN.css +1 -0
  24. package/.output/public/_nuxt/{_wsId_.B4WiqkIS.css → _wsId_.CTx-BlfF.css} +1 -1
  25. package/.output/public/_nuxt/aShsv_rG.js +19 -0
  26. package/.output/public/_nuxt/{yF0aYMF1.js → aesgXKSr.js} +3 -3
  27. package/.output/public/_nuxt/agents.B21nUEUX.css +1 -0
  28. package/.output/public/_nuxt/aml.ekTjxFkf.css +1 -0
  29. package/.output/public/_nuxt/builds/latest.json +1 -1
  30. package/.output/public/_nuxt/builds/meta/b5506ef0-f48e-4f14-a6c0-d65e505d0b09.json +1 -0
  31. package/.output/public/_nuxt/channel-templates.DplM1pJ3.css +1 -0
  32. package/.output/public/_nuxt/daq.BdVmoIfe.css +1 -0
  33. package/.output/public/_nuxt/dcw.DCgyzpWV.css +1 -0
  34. package/.output/public/_nuxt/{default.J32SgqFY.css → default.Bh3L2HU8.css} +1 -1
  35. package/.output/public/_nuxt/lHAxdSsj.js +1 -0
  36. package/.output/public/_nuxt/pages.j9rzRocr.css +1 -0
  37. package/.output/public/_nuxt/plugins.DhiZbHU8.css +1 -0
  38. package/.output/public/_nuxt/settings.BRx9rIDg.css +1 -0
  39. package/.output/public/_nuxt/teams.BLlOPB_Z.css +1 -0
  40. package/.output/public/_nuxt/tokens.De8xzszn.css +1 -0
  41. package/.output/public/_nuxt/workshop.D1jFNkrn.css +1 -0
  42. package/.output/server/chunks/_/nitro.mjs +748 -733
  43. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs → OmpTerminalPanel-CVvxpsF7.mjs} +15 -15
  44. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs.map → OmpTerminalPanel-CVvxpsF7.mjs.map} +1 -1
  45. package/.output/server/chunks/build/{TownView--otsnr_7.mjs → TownView-CJ6IsDCc.mjs} +226 -225
  46. package/.output/server/chunks/build/{TownView--otsnr_7.mjs.map → TownView-CJ6IsDCc.mjs.map} +1 -1
  47. package/.output/server/chunks/build/{_id_-Brv-JSvr.mjs → _id_-CVD_H0ir.mjs} +143 -142
  48. package/.output/server/chunks/build/_id_-CVD_H0ir.mjs.map +1 -0
  49. package/.output/server/chunks/build/{_id_-D9pl1OZy.mjs → _id_-CtxMb3qp.mjs} +41 -41
  50. package/.output/server/chunks/build/_id_-CtxMb3qp.mjs.map +1 -0
  51. package/.output/server/chunks/build/{_wsId_-BASfDf0s.mjs → _wsId_-DpY3T-md.mjs} +514 -444
  52. package/.output/server/chunks/build/{_wsId_-BASfDf0s.mjs.map → _wsId_-DpY3T-md.mjs.map} +1 -1
  53. package/.output/server/chunks/build/{agents-CLQqJF2y.mjs → agents-DsCkuXDV.mjs} +49 -46
  54. package/.output/server/chunks/build/agents-DsCkuXDV.mjs.map +1 -0
  55. package/.output/server/chunks/build/{aml-_7FdFL2p.mjs → aml-CT0xKGKI.mjs} +122 -105
  56. package/.output/server/chunks/build/aml-CT0xKGKI.mjs.map +1 -0
  57. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs +117 -0
  58. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs.map +1 -0
  59. package/.output/server/chunks/build/{channel-templates-p6SZNjlM.mjs → channel-templates-Dla9FbYa.mjs} +49 -37
  60. package/.output/server/chunks/build/channel-templates-Dla9FbYa.mjs.map +1 -0
  61. package/.output/server/chunks/build/{daq-DkMvD8sg.mjs → daq-DWWSKGAV.mjs} +89 -88
  62. package/.output/server/chunks/build/daq-DWWSKGAV.mjs.map +1 -0
  63. package/.output/server/chunks/build/{dcw-C1jgVJkO.mjs → dcw-CT8k17_E.mjs} +44 -43
  64. package/.output/server/chunks/build/dcw-CT8k17_E.mjs.map +1 -0
  65. package/.output/server/chunks/build/{default-BE4r0FZN.mjs → default-D3525UFX.mjs} +16 -15
  66. package/.output/server/chunks/build/{default-BE4r0FZN.mjs.map → default-D3525UFX.mjs.map} +1 -1
  67. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs → monitor-Dh2Kvaq5.mjs} +2 -2
  68. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs.map → monitor-Dh2Kvaq5.mjs.map} +1 -1
  69. package/.output/server/chunks/build/{pages-B0bUWnC6.mjs → pages-BHcSBM2D.mjs} +24 -18
  70. package/.output/server/chunks/build/pages-BHcSBM2D.mjs.map +1 -0
  71. package/.output/server/chunks/build/{plugins-lGlUr1lj.mjs → plugins-BNgIi61W.mjs} +23 -17
  72. package/.output/server/chunks/build/plugins-BNgIi61W.mjs.map +1 -0
  73. package/.output/server/chunks/build/{settings-Ddd1qx2Z.mjs → settings-ipYBDWdn.mjs} +45 -44
  74. package/.output/server/chunks/build/settings-ipYBDWdn.mjs.map +1 -0
  75. package/.output/server/chunks/build/{teams-MEDLZdMn.mjs → teams-5lx-CwFa.mjs} +47 -44
  76. package/.output/server/chunks/build/teams-5lx-CwFa.mjs.map +1 -0
  77. package/.output/server/chunks/build/{tokens-C7fTM5C2.mjs → tokens-q-IDT8Io.mjs} +73 -53
  78. package/.output/server/chunks/build/tokens-q-IDT8Io.mjs.map +1 -0
  79. package/.output/server/chunks/build/{town-CoQdblbZ.mjs → town-QJWB5wng.mjs} +3 -2
  80. package/.output/server/chunks/build/town-QJWB5wng.mjs.map +1 -0
  81. package/.output/server/chunks/build/{workshop-BL5bhfs0.mjs → workshop-BIlCj_ZP.mjs} +45 -45
  82. package/.output/server/chunks/build/workshop-BIlCj_ZP.mjs.map +1 -0
  83. package/.output/server/chunks/routes/renderer.mjs +1 -1
  84. package/.output/server/chunks/virtual/entry.mjs +637 -86
  85. package/.output/server/chunks/virtual/entry.mjs.map +1 -1
  86. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  87. package/.output/server/package.json +1 -1
  88. package/README-zh.md +13 -3
  89. package/README.md +13 -3
  90. package/app/components/AppHeader.vue +961 -959
  91. package/app/components/workshop/A2aRpcDebugger.vue +237 -237
  92. package/app/components/workshop/AgentInspectorDrawer.vue +424 -424
  93. package/app/components/workshop/AgentLanesView.vue +998 -998
  94. package/app/components/workshop/ArtifactCard.vue +120 -120
  95. package/app/components/workshop/AssetLibrary.vue +452 -452
  96. package/app/components/workshop/ChannelSessionList.vue +812 -804
  97. package/app/components/workshop/CommandPalette.vue +269 -269
  98. package/app/components/workshop/Composer.vue +827 -827
  99. package/app/components/workshop/DeviceTwinPanel.vue +643 -643
  100. package/app/components/workshop/EventBlock.vue +602 -602
  101. package/app/components/workshop/FileSelectorModal.vue +188 -188
  102. package/app/components/workshop/InspectorPanel.vue +301 -301
  103. package/app/components/workshop/MemoryPanel.vue +364 -364
  104. package/app/components/workshop/MultiChannelView.vue +189 -189
  105. package/app/components/workshop/TaskBoardView.vue +686 -686
  106. package/app/components/workshop/TaskInspectorDrawer.vue +390 -390
  107. package/app/components/workshop/TranscriptTimeline.vue +601 -601
  108. package/app/components/workshop/blocks/ClusterArtifact.vue +61 -61
  109. package/app/components/workshop/blocks/ClusterError.vue +85 -85
  110. package/app/components/workshop/blocks/ClusterLife.vue +87 -87
  111. package/app/components/workshop/blocks/ClusterMember.vue +95 -93
  112. package/app/components/workshop/blocks/ClusterMemory.vue +84 -84
  113. package/app/components/workshop/blocks/ClusterOther.vue +67 -67
  114. package/app/components/workshop/blocks/ClusterStatus.vue +149 -147
  115. package/app/components/workshop/blocks/ClusterTool.vue +158 -158
  116. package/app/components/workshop/terminal/OmpTerminalPanel.vue +1011 -1011
  117. package/app/components/workshop/town/TownView.vue +56 -56
  118. package/app/middleware/locale-cookie.global.ts +22 -0
  119. package/app/pages/aml/index.vue +24 -2
  120. package/app/pages/daq/[id].vue +11 -11
  121. package/app/pages/daq/index.vue +2316 -2316
  122. package/app/pages/dcw/[id].vue +18 -19
  123. package/app/pages/dcw/index.vue +3 -3
  124. package/app/pages/index.vue +987 -981
  125. package/app/pages/plugins/index.vue +317 -311
  126. package/app/pages/settings.vue +1564 -1562
  127. package/app/pages/tokens.vue +15 -15
  128. package/app/pages/workshop/agents.vue +612 -609
  129. package/app/pages/workshop/channel-templates.vue +18 -11
  130. package/app/pages/workshop/index.vue +668 -668
  131. package/app/pages/workshop/teams.vue +743 -740
  132. package/app/pages/workshop/w/[wsId].vue +704 -704
  133. package/app/plugins/locale-restore.client.ts +16 -1
  134. package/app/utils/builtin-catalog.ts +104 -0
  135. package/config.yml +170 -170
  136. package/docs/full-test-plan.md +2 -3
  137. package/i18n/locales/en.ts +293 -35
  138. package/i18n/locales/zh-CN.ts +265 -7
  139. package/package.json +2 -2
  140. package/scripts/_dbg-perms-e2e.mjs +132 -109
  141. package/scripts/_demo-agentteam-capture.mjs +278 -0
  142. package/scripts/_e2e-ui-functional.mjs +371 -0
  143. package/scripts/_i18n-en-cjk-scan.mjs +28 -0
  144. package/scripts/_i18n-en-verify.mjs +107 -0
  145. package/scripts/_i18n-extra-checks.mjs +53 -0
  146. package/scripts/_i18n-scan-hardcoded.mjs +180 -0
  147. package/scripts/_i18n-verify-keys.mjs +50 -0
  148. package/scripts/_loop-biax-campaign.mjs +15 -9
  149. package/scripts/_sweep-button-contrast.mjs +87 -0
  150. package/scripts/ui/record-demo.mjs +557 -0
  151. package/server/plugins-builtin/diag-bridge/index.mjs +608 -661
  152. package/server/plugins-builtin/rag-bridge/index.mjs +57 -133
  153. package/server/services/workshop/daq/drivers.ts +11 -0
  154. package/.output/public/_nuxt/88GOowgO2.js +0 -1
  155. package/.output/public/_nuxt/BN16aRdQ.js +0 -1
  156. package/.output/public/_nuxt/BWVhZntO.js +0 -1
  157. package/.output/public/_nuxt/BXxH0UQE.js +0 -1
  158. package/.output/public/_nuxt/BsROmY89.js +0 -1
  159. package/.output/public/_nuxt/C0ZrS1KL.js +0 -19
  160. package/.output/public/_nuxt/C4s_niy-.js +0 -14
  161. package/.output/public/_nuxt/D6mbFA-a.js +0 -1
  162. package/.output/public/_nuxt/DS8qj8EL.js +0 -1
  163. package/.output/public/_nuxt/DUjW5Yq5.js +0 -1
  164. package/.output/public/_nuxt/Dap5PmTL.js +0 -2
  165. package/.output/public/_nuxt/Dqw1lpr-.js +0 -1
  166. package/.output/public/_nuxt/DvLxILQP.js +0 -1
  167. package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +0 -1
  168. package/.output/public/_nuxt/TownView.f-YOdXRs.css +0 -1
  169. package/.output/public/_nuxt/_id_.D9ymtHB5.css +0 -1
  170. package/.output/public/_nuxt/_id_.jf-_8E_c.css +0 -1
  171. package/.output/public/_nuxt/agents.ZD0rVdHE.css +0 -1
  172. package/.output/public/_nuxt/aml.BN8ZEW4h.css +0 -1
  173. package/.output/public/_nuxt/builds/meta/78a295bb-2072-48a4-8b46-6d545fdae743.json +0 -1
  174. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +0 -1
  175. package/.output/public/_nuxt/daq.DjSgY94l.css +0 -1
  176. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +0 -1
  177. package/.output/public/_nuxt/eqq3wWQF.js +0 -18
  178. package/.output/public/_nuxt/pages.D7aSDlUI.css +0 -1
  179. package/.output/public/_nuxt/plugins.sb4nDGqr.css +0 -1
  180. package/.output/public/_nuxt/qr6R6Ha1.js +0 -1
  181. package/.output/public/_nuxt/settings.BTgR-NiE.css +0 -1
  182. package/.output/public/_nuxt/teams.CxnxKVVs.css +0 -1
  183. package/.output/public/_nuxt/tokens.BFY7qv4f.css +0 -1
  184. package/.output/public/_nuxt/workshop.CR_QzeAN.css +0 -1
  185. package/.output/server/chunks/build/_id_-Brv-JSvr.mjs.map +0 -1
  186. package/.output/server/chunks/build/_id_-D9pl1OZy.mjs.map +0 -1
  187. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +0 -1
  188. package/.output/server/chunks/build/aml-_7FdFL2p.mjs.map +0 -1
  189. package/.output/server/chunks/build/channel-templates-p6SZNjlM.mjs.map +0 -1
  190. package/.output/server/chunks/build/daq-DkMvD8sg.mjs.map +0 -1
  191. package/.output/server/chunks/build/dcw-C1jgVJkO.mjs.map +0 -1
  192. package/.output/server/chunks/build/pages-B0bUWnC6.mjs.map +0 -1
  193. package/.output/server/chunks/build/plugins-lGlUr1lj.mjs.map +0 -1
  194. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +0 -1
  195. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +0 -1
  196. package/.output/server/chunks/build/tokens-C7fTM5C2.mjs.map +0 -1
  197. package/.output/server/chunks/build/town-CoQdblbZ.mjs.map +0 -1
  198. package/.output/server/chunks/build/workshop-BL5bhfs0.mjs.map +0 -1
  199. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.continue.json +0 -1
  200. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.json +0 -1
  201. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/054bb4bc4e4217a2ab0b0c6b663747214a9af464f5568ea60e784704e340bb0f.source +0 -153
  202. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/0554197c3d2bac203a1f5c48a5d52495d1336db1bd7aa4f4c19875810363e684.source +0 -153
  203. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/05c203d12a954fffba942e1d626e3f5fec05b18a5aef2bac003b7884a4b77406.source +0 -431
  204. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/145a6e6ba811af35aed31e0cc3211f53706a14980fa069ee9e3c1591139f96f0.source +0 -180
  205. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/1ae0fb281f97899c025e368ca73f41349b01fb5f85b19e08d7ebccff49095ed5.source +0 -112
  206. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/20ee9967aa0541c516db7575bdd4a1a745939bc7016bd10d7635903113edb8ec.source +0 -227
  207. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/233751843aeba9d4c5263f275b8102de00443c06f286918cbd23e0b6729e01aa.source +0 -131
  208. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/263c2e05b425e5c322e60c6475279f963bab3a873eb521b21df7fec935a46597.source +0 -160
  209. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/2ac5eaac28eaaee2178c4b32014df8e732f9dd432a285a872f5b7f31c1395fa4.source +0 -157
  210. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/33a0db0d2d95d2c11d0720090458e5fba1425376f5ceb5b422d1d3471be3226b.source +0 -348
  211. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/359e474ae83db35e4615d3728de6bf694b64d4dce9b536730a3b6518f9c0494a.source +0 -296
  212. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/37a4c3c31c4b70f6b719c53d1e9c4fcba603f31adfdd11bb6ef9f0d094923c56.source +0 -285
  213. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/461feca1c14a695eb8b0f17a6f95fe97eeceabc0510faaea4ba6d25bde147d8d.source +0 -217
  214. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/4cfadbda8591f29d5e1e230dc000e701b756e850441b3900fb596a4d2f6e98c2.source +0 -301
  215. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/50ca2810cf0b248c96948994c0c404531bbad6e553166b6816b3b03bfa0f75d2.source +0 -151
  216. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/52d9e8cb591a9ae86e238c3620a610cd386717bee222c0f37b25b2ac7e523dc4.source +0 -135
  217. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/6c87daafa4c006560a954636a97333f6123153c9d34d96203f7ee3a14e556357.source +0 -156
  218. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/77bc47854b577900621cacf1697f81fa2bc005af129d37614a97a3441c08f99f.source +0 -72
  219. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/7bbb789d3a527817118cf8399ed1861d6a962ade10878fd07223435d333a7089.source +0 -469
  220. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/86b7c1daafc8b60ba9982dde5bf1bcc74265709483c176e71f23ec6fc267451f.source +0 -259
  221. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/8df66d62bd41984d0970e8236882080c5085ac937d07a29779463d6a03b37c76.source +0 -97
  222. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/9962f2d1b3a85137bd40cc835725600b39f89ee02a27c06b2e0402e1eafde115.source +0 -277
  223. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a1090ea066b792cb201739f4994e37bf7c12f52e805239a1eab87f18a537f266.source +0 -171
  224. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a5241091650134d0919897e086a606a3846c8b2b54f8b4c84a8696c145ac3e9e.source +0 -425
  225. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/af19fa4c6e34d5540028be4b17600161283af3a2a2dfc61a3fdc0834436a0c14.source +0 -99
  226. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3713d78e2b2f93905ff6be8d58a8d0a2f42dea322eb67c987fda22ff6a084d5.source +0 -325
  227. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3849943c3aff102d1af815631fc843a2a52c17646a4b04696ece323e483bbec.source +0 -332
  228. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/bac735a3803a6650b7f1eb8f587e6dc5c68ea055000c3c99a11789438b78e837.source +0 -43
  229. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d32c24d61eb8b33c73e5a8fc7e4dc7ef437aed238ee4371c2b00617e6104090a.source +0 -154
  230. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d5170dc9a9879fda04fdf91d8a110d4efb6787ee38dda596d4da899883d36a72.source +0 -531
  231. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/dc59bcd7bb397be63ba2f2c3fec23f3782dcb17d2d75a9981eeced761f33eb32.source +0 -439
  232. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/eb5ff8cf66b92aec7bf6726781d8df74faa2b030c3aaac17d6b00c1fc04c9b32.source +0 -323
  233. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f10615a6fae18436e70d1201b2d44fdae3174a3aa7bb4db97dea1b3ed291d721.source +0 -433
  234. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f36d3e271668bc1260c2823542d22dbf1d0f7041ff16b1ac175bce2256e582ca.source +0 -280
  235. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f94449380af09f31ac352152007146a75cc80bae8d6411ffe57b2c56f933b0ff.source +0 -117
  236. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/fe4dfd81178fd6b648335f6405d9ede71319aebde796008e50db21f8adf44c99.source +0 -160
  237. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/ffb4e3aa548cfdadabaa44ab599f9b6a3ca6288176a2cc7a4b2fc2a12b5fb848.source +0 -101
  238. package/scripts/.mimosa/hook-status/sess_aeaecb58-8833-4b83-b93c-050467d21e03-d944d71bb3.json +0 -14
  239. package/scripts/_dbg-audit-all-pages.mjs +0 -43
  240. package/scripts/_dbg-daq-frames-e2e.mjs +0 -118
  241. package/scripts/_dbg-daq-rowstyle-verify.mjs +0 -112
  242. package/scripts/_dbg-hitl-timeout-e2e.mjs +0 -93
  243. package/scripts/_dbg-live-line-e2e.mjs +0 -279
  244. package/scripts/_dbg-minimal-omp.mjs +0 -77
  245. package/scripts/_dbg-mode-nav.mjs +0 -173
  246. package/scripts/_dbg-real-scenario-timeline.log +0 -55
  247. package/scripts/_dbg-team-repro.mjs +0 -76
  248. package/scripts/_dbg-town-agents-probe.mjs +0 -60
  249. package/scripts/_dbg-visual-acceptance.mjs +0 -63
  250. package/scripts/_townview_style.css +0 -1185
  251. package/scripts/_townview_template.html +0 -1215
  252. package/server/data/agent-node-bindings.json +0 -554
  253. package/server/data/daq-templates.json +0 -16
  254. package/server/data/daqs.json +0 -7464
  255. package/server/data/dcw-lines.json +0 -1787
  256. package/server/data/dcw-products.json +0 -1626
  257. package/server/data/dcw-recipes.json +0 -4672
  258. package/server/data/dcw-rollback.json +0 -20754
  259. package/server/data/dcw-runs.json +0 -6354
  260. package/server/data/dcw-templates.json +0 -146
  261. package/server/data/dcw-writes.json +0 -15650
  262. package/server/data/dcws.json +0 -9033
  263. package/server/data/device-twins.json +0 -133
  264. package/server/data/line-runs.json +0 -322
  265. package/server/data/scene-layouts.json +0 -277
@@ -1,1215 +0,0 @@
1
- <template>
2
- <div class="town-view">
3
- <!-- ================= 顶部导航 ================= -->
4
- <header class="topnav">
5
- <div class="brand">
6
- <svg
7
- class="brand-glyph"
8
- viewBox="0 0 32 32"
9
- fill="none"
10
- aria-hidden="true"
11
- >
12
- <path
13
- d="M16 2.5 27.5 9v14L16 29.5 4.5 23V9L16 2.5Z"
14
- stroke="#35e0a0"
15
- stroke-width="1.6"
16
- />
17
- <path
18
- d="M16 8.2 22.5 12v8L16 23.8 9.5 20v-8L16 8.2Z"
19
- stroke="#41c8f4"
20
- stroke-width="1.3"
21
- opacity=".85"
22
- />
23
- <circle
24
- cx="16"
25
- cy="16"
26
- r="2.4"
27
- fill="#35e0a0"
28
- />
29
- <circle
30
- cx="25.5"
31
- cy="7.5"
32
- r="1.6"
33
- fill="#41c8f4"
34
- />
35
- </svg>
36
- <div>
37
- <div class="brand-name">
38
- DIGITAL <em>TWIN</em>
39
- </div>
40
- <div class="brand-sub">
41
- AGENT 数字孪生空间
42
- </div>
43
- </div>
44
- </div>
45
- <nav
46
- class="nav-tabs"
47
- aria-label="场景模式"
48
- >
49
- <div class="seg">
50
- <button
51
- :class="{ on: mode === 'browse' }"
52
- @click="mode === 'edit' && toggleMode()"
53
- >
54
- 运行
55
- </button>
56
- <button
57
- :class="{ on: mode === 'edit' }"
58
- @click="mode === 'browse' && toggleMode()"
59
- >
60
- 编辑
61
- </button>
62
- </div>
63
- <button
64
- class="nav-action"
65
- title="把全部设备的位置/朝向/缩放写入数据库"
66
- @click="saveLayout"
67
- >
68
- 保存布局
69
- </button>
70
- <span
71
- v-if="saveState && saveState.state !== 'idle'"
72
- class="save-chip"
73
- :class="`s-${saveState.state}`"
74
- >{{ saveStateLabel }}</span>
75
- </nav>
76
- <div class="nav-right">
77
- <span class="nav-chip mono">{{ fps }} FPS</span>
78
- <div
79
- class="avatar-chip"
80
- title="当前用户"
81
- >
82
- <div class="avatar-fallback">
83
- {{ (userStore.name ?? 'OP').slice(0, 2).toUpperCase() }}
84
- </div>
85
- <span>{{ userStore.name || '产线管理员' }}</span>
86
- </div>
87
- </div>
88
- </header>
89
-
90
- <!-- ================= 三栏应用区 ================= -->
91
- <div class="app">
92
- <!-- 左轨:设备资源 / 数采节点 / 场景管理 -->
93
- <aside class="rail rail-left">
94
- <section class="panel">
95
- <div class="panel-hd">
96
- <h3>设备资源</h3>
97
- <span class="panel-tag">{{ deviceModels.length }}</span>
98
- </div>
99
- <WorkshopAssetLibrary class="lib-embed" />
100
- </section>
101
-
102
- <section class="panel">
103
- <div class="panel-hd">
104
- <h3>数采节点 · DAQ</h3>
105
- <span class="panel-tag">{{ daqTwins.length }}</span>
106
- </div>
107
- <div class="daq-list">
108
- <div
109
- v-for="tpl in daqTemplates"
110
- :key="tpl.id"
111
- class="daq-card"
112
- draggable="true"
113
- :title="`${tpl.name} · 拖到场景实例化,量程 ${tpl.min} ~ ${tpl.max} ${tpl.unit}`"
114
- @dragstart="onDaqDragStart($event, tpl)"
115
- >
116
- <span class="daq-ico" />
117
- <div class="daq-meta">
118
- <span class="daq-name">{{ tpl.name }}</span>
119
- <span class="daq-code">{{ tpl.code }}</span>
120
- </div>
121
- <span class="daq-count">×{{ daqCount(tpl.id) }}</span>
122
- </div>
123
- </div>
124
- </section>
125
-
126
- <section class="panel">
127
- <div class="panel-hd">
128
- <h3>场景管理</h3>
129
- <span class="panel-tag">{{ dockChannels.length }}</span>
130
- </div>
131
- <div class="scene-list">
132
- <div
133
- v-for="ch in dockChannels"
134
- :key="ch.channelId"
135
- class="scene-row"
136
- :class="{ active: ch.placed }"
137
- :draggable="!ch.placed"
138
- :data-channel-id="ch.channelId"
139
- :title="ch.placed ? '已在场景中,点击定位' : '拖拽到场景放置'"
140
- @dragstart="ch.placed ? undefined : onChannelDragStart($event, ch.channelId)"
141
- @click="onDockCardClick(ch)"
142
- >
143
- <span
144
- class="scene-ico"
145
- :style="{ '--ch': ch.color }"
146
- />
147
- <div class="scene-meta-wrap">
148
- <span class="scene-name">{{ ch.name }}</span>
149
- <span class="scene-meta">{{ ch.agentCount }} 精魂 · {{ ch.placed ? '已放置' : '未放置' }}</span>
150
- </div>
151
- <span
152
- v-if="ch.placed"
153
- class="scene-cur"
154
- >✓</span>
155
- <span
156
- v-else
157
- class="scene-add"
158
- >+</span>
159
- </div>
160
- <div
161
- v-if="dockHint"
162
- class="scene-hint"
163
- >
164
- {{ dockHint }}
165
- </div>
166
- </div>
167
- </section>
168
- </aside>
169
-
170
- <!-- 中栏:舞台 + 底部坞 -->
171
- <main class="stage-col">
172
- <div
173
- ref="stageRef"
174
- class="stage"
175
- >
176
- <div
177
- id="town-host"
178
- ref="hostRef"
179
- class="town-host"
180
- />
181
- <div class="stage-vignette" />
182
-
183
- <div class="stage-top">
184
- <div class="vp-title">
185
- <h2>产线孪生总览</h2>
186
- <span class="vp-id">CH · {{ activeChannelName || '加载中' }}</span>
187
- </div>
188
- <div class="vp-tools">
189
- <button
190
- class="vp-tool"
191
- :class="{ on: showCallouts }"
192
- title="数据标注显隐"
193
- @click="showCallouts = !showCallouts"
194
- >
195
- 标
196
- </button>
197
- <button
198
- class="vp-tool"
199
- title="定位选中实体"
200
- @click="locateSelected"
201
- >
202
- 定
203
- </button>
204
- <button
205
- class="vp-tool"
206
- title="全屏舞台"
207
- @click="fullscreenStage"
208
- >
209
- 全
210
- </button>
211
- </div>
212
- </div>
213
-
214
- <div class="angle-chip">
215
- <select
216
- :value="viewPreset"
217
- aria-label="视角预设"
218
- @change="onViewPreset($event)"
219
- >
220
- <option value="std">
221
- 3D 视角 · 标准
222
- </option>
223
- <option value="top">
224
- 俯视 · TOP
225
- </option>
226
- <option value="front">
227
- 前视 · FRONT
228
- </option>
229
- <option value="side">
230
- 侧视 · SIDE
231
- </option>
232
- </select>
233
- </div>
234
-
235
- <!-- 数据标注层 -->
236
- <div
237
- v-if="showCallouts"
238
- class="callout-layer"
239
- >
240
- <div
241
- v-for="c in callouts"
242
- :key="c.id"
243
- class="callout"
244
- :class="{ warn: c.warn }"
245
- :style="{ left: c.x + 'px', top: c.y + 'px' }"
246
- >
247
- <div class="co-label">
248
- <span class="co-dot" />{{ c.label }}
249
- </div>
250
- <div class="co-val">
251
- {{ c.value }}<small>{{ c.unit }}</small>
252
- </div>
253
- <div class="co-range">
254
- 正常范围 {{ c.min }} ~ {{ c.max }}
255
- </div>
256
- </div>
257
- </div>
258
-
259
- <!-- KPI 条 -->
260
- <div class="kpi-strip">
261
- <div class="kpi">
262
- <span class="kpi-ico c1">设</span>
263
- <div class="kpi-meta">
264
- <div class="kpi-label">
265
- 设备总数
266
- </div>
267
- <div class="kpi-val">
268
- {{ deviceCount }}<small>台</small>
269
- </div>
270
- </div>
271
- </div>
272
- <div class="kpi">
273
- <span class="kpi-ico c2">运</span>
274
- <div class="kpi-meta">
275
- <div class="kpi-label">
276
- 运行设备
277
- </div>
278
- <div class="kpi-val">
279
- {{ runningCount }}<small>台</small>
280
- </div>
281
- </div>
282
- </div>
283
- <div class="kpi">
284
- <span class="kpi-ico c3">领</span>
285
- <div class="kpi-meta">
286
- <div class="kpi-label">
287
- 领地
288
- </div>
289
- <div class="kpi-val">
290
- {{ blockCount }}
291
- </div>
292
- </div>
293
- </div>
294
- <div class="kpi">
295
- <span class="kpi-ico c4">魂</span>
296
- <div class="kpi-meta">
297
- <div class="kpi-label">
298
- 精魂
299
- </div>
300
- <div class="kpi-val">
301
- {{ agentCount }}
302
- </div>
303
- </div>
304
- </div>
305
- <div class="kpi">
306
- <span class="kpi-ico c5">帧</span>
307
- <div class="kpi-meta">
308
- <div class="kpi-label">
309
- FPS
310
- </div>
311
- <div class="kpi-val">
312
- {{ fps }}
313
- </div>
314
- </div>
315
- </div>
316
- </div>
317
-
318
- <div
319
- v-if="blockCount === 0 && ready"
320
- class="empty-hint"
321
- >
322
- <b>空场景</b>
323
- <span>从左侧「设备资源 / 数采节点」拖入实体,开始搭建数字孪生空间</span>
324
- </div>
325
-
326
- <!-- 精魂会话台(选中角色;浮动) -->
327
- <div
328
- v-if="selected?.kind === 'agent'"
329
- class="agent-chat drag-panel"
330
- :data-agent-id="selected.id"
331
- :style="panelPos['agent-chat'] ? { left: panelPos['agent-chat'].x + 'px', top: panelPos['agent-chat'].y + 'px', bottom: 'auto', transform: 'none' } : undefined"
332
- >
333
- <div
334
- class="agent-chat-head drag-grip"
335
- title="拖动移动面板"
336
- @pointerdown="onPanelGripDown($event, 'agent-chat')"
337
- >
338
- <span
339
- class="chat-badge"
340
- :style="{ '--p-acc': agentChatColor }"
341
- >{{ agentChatTitle.slice(0, 1).toUpperCase() }}</span>
342
- <div class="chat-nameplate">
343
- <span class="chat-name">{{ agentChatTitle }}</span>
344
- <span class="chat-meta">
345
- <span
346
- class="chat-state"
347
- :class="`s-${selectedAgentMeta?.state ?? 'idle'}`"
348
- >{{ agentChatStateLabel }}</span>
349
- <span
350
- v-if="selectedAgentMeta?.harness"
351
- class="chat-harness"
352
- >{{ selectedAgentMeta.harness.toUpperCase() }}</span>
353
- <span class="chat-count">{{ agentHistory.length + agentChatRows.length }} 条</span>
354
- </span>
355
- </div>
356
- <button
357
- class="chat-refresh"
358
- title="重新拉取该角色的历史对话"
359
- @click="onRefreshHistory"
360
- >
361
- {{ historyLoading ? '…' : '↻' }}
362
- </button>
363
- </div>
364
- <div
365
- ref="chatScroll"
366
- class="rpg-lines"
367
- >
368
- <div
369
- v-if="historyLoading"
370
- class="rpg-note"
371
- >
372
- 正在读取历史对话…
373
- </div>
374
- <template v-if="agentHistory.length">
375
- <div class="rpg-divider">
376
- 历史记录 · {{ agentHistory.length }}
377
- </div>
378
- <div
379
- v-for="r in agentHistory"
380
- :key="r.id"
381
- class="rpg-line hist"
382
- >
383
- <span class="rpg-time">{{ fmtTime(r.at) }}</span>
384
- <div class="rpg-bubble">
385
- <span
386
- v-if="chatKindLabel(r.kind)"
387
- class="rpg-kind"
388
- :class="`k-${r.kind}`"
389
- >{{ chatKindLabel(r.kind) }}</span>
390
- <span class="rpg-text">{{ r.text }}</span>
391
- </div>
392
- </div>
393
- </template>
394
- <div
395
- v-if="agentChatRows.length"
396
- class="rpg-divider live"
397
- >
398
- 实时 · {{ agentChatRows.length }}
399
- </div>
400
- <div
401
- v-for="r in agentChatRows"
402
- :key="r.id"
403
- class="rpg-line live"
404
- >
405
- <span class="rpg-time">{{ fmtTime(r.at) }}</span>
406
- <div class="rpg-bubble">
407
- <span class="rpg-text">{{ r.text }}</span>
408
- </div>
409
- </div>
410
- <div
411
- v-if="selectedAgentMeta?.state === 'busy'"
412
- class="rpg-typing"
413
- >
414
- <span class="ty-dot" /><span class="ty-dot" /><span class="ty-dot" />
415
- <span class="ty-label">执行中</span>
416
- </div>
417
- <div
418
- v-if="!historyLoading && !agentHistory.length && !agentChatRows.length"
419
- class="rpg-note"
420
- >
421
- 暂无对话 · 该精魂尚未开口
422
- </div>
423
- </div>
424
- </div>
425
-
426
- <!-- 频道边界编辑(浮动) -->
427
- <div
428
- v-if="selectedChannel && boundaryDraft"
429
- class="boundary-panel drag-panel"
430
- :style="panelPos.boundary ? { left: panelPos.boundary.x + 'px', top: panelPos.boundary.y + 'px', bottom: 'auto', transform: 'none' } : undefined"
431
- >
432
- <div
433
- class="bp-title drag-grip"
434
- title="拖动移动面板"
435
- @pointerdown="onPanelGripDown($event, 'boundary')"
436
- >
437
- <span class="bp-name">{{ entities.channels[selectedChannel]?.name ?? selectedChannel.slice(0, 8) }}</span>
438
- <span class="bp-sub">频道管理</span>
439
- <div class="bp-tabs">
440
- <button
441
- class="bp-tab"
442
- :class="{ on: channelPanelTab === 'boundary' }"
443
- @click="openChannelTab('boundary')"
444
- >
445
- 边界
446
- </button>
447
- <button
448
- class="bp-tab"
449
- :class="{ on: channelPanelTab === 'members' }"
450
- @click="openChannelTab('members')"
451
- >
452
- 成员 {{ channelMembers.length }}
453
- </button>
454
- </div>
455
- </div>
456
- <template v-if="channelPanelTab === 'boundary'">
457
- <div class="bp-row">
458
- <span class="bp-label">形状</span>
459
- <div class="bp-seg">
460
- <button
461
- class="seg-btn"
462
- :class="{ on: boundaryDraft.shape === 'ellipse' }"
463
- @click="boundaryDraft.shape = 'ellipse'; applyBoundaryDraft()"
464
- >
465
- 椭圆
466
- </button>
467
- <button
468
- class="seg-btn"
469
- :class="{ on: boundaryDraft.shape === 'rect' }"
470
- @click="boundaryDraft.shape = 'rect'; applyBoundaryDraft()"
471
- >
472
- 矩形
473
- </button>
474
- </div>
475
- </div>
476
- <div class="bp-row">
477
- <span class="bp-label">横轴半径</span>
478
- <input
479
- v-model.number="boundaryDraft.radiusX"
480
- class="bp-range"
481
- type="range"
482
- min="80"
483
- max="4000"
484
- step="8"
485
- @change="applyBoundaryDraft"
486
- >
487
- <span class="bp-val">{{ Math.round(boundaryDraft.radiusX) }}</span>
488
- </div>
489
- <div class="bp-row">
490
- <span class="bp-label">纵轴半径</span>
491
- <input
492
- v-model.number="boundaryDraft.radiusZ"
493
- class="bp-range"
494
- type="range"
495
- min="60"
496
- max="4000"
497
- step="8"
498
- @change="applyBoundaryDraft"
499
- >
500
- <span class="bp-val">{{ Math.round(boundaryDraft.radiusZ) }}</span>
501
- </div>
502
- <div class="bp-row">
503
- <span class="bp-label">朝向</span>
504
- <input
505
- v-model.number="boundaryDraft.rotationY"
506
- class="bp-range"
507
- type="range"
508
- min="0"
509
- max="360"
510
- step="5"
511
- @change="applyBoundaryDraft"
512
- >
513
- <span class="bp-val">{{ Math.round(boundaryDraft.rotationY ?? 0) }}°</span>
514
- </div>
515
- </template>
516
- <template v-else>
517
- <div class="bp-hint">
518
- 为频道成员选择 3D 角色模型(实时换装并持久化)
519
- </div>
520
- <div class="member-list">
521
- <div
522
- v-for="m in channelMembers"
523
- :key="m.agentId"
524
- class="member-row"
525
- >
526
- <span
527
- class="member-ava"
528
- :style="{ color: hashColor(selectedChannel) }"
529
- >{{ m.name.charAt(0).toUpperCase() }}</span>
530
- <div class="member-info">
531
- <span class="member-name">{{ m.name }}</span>
532
- <span class="member-role">{{ m.role === 'lead' ? '领队' : '成员' }}</span>
533
- </div>
534
- <select
535
- class="member-select"
536
- :value="(m.modelRef ?? '') || 'hero-3d'"
537
- @change="bindMemberModel(m.agentId, ($event.target as HTMLSelectElement).value)"
538
- >
539
- <option
540
- v-for="model in agentModels"
541
- :key="model.id"
542
- :value="model.id"
543
- >
544
- {{ model.name }}
545
- </option>
546
- </select>
547
- </div>
548
- <div
549
- v-if="channelMembers.length === 0"
550
- class="bp-hint"
551
- >
552
- 该频道暂无成员
553
- </div>
554
- </div>
555
- </template>
556
- <div class="bp-actions">
557
- <button
558
- v-if="channelPanelTab === 'boundary'"
559
- class="bp-btn save"
560
- @click="saveChannelLayout"
561
- >
562
- 保存边界
563
- </button>
564
- <button
565
- class="bp-btn danger"
566
- @click="removeChannelFromScene"
567
- >
568
- 移除频道
569
- </button>
570
- <button
571
- class="bp-btn"
572
- @click="onSelectChannel(null)"
573
- >
574
- 关闭
575
- </button>
576
- </div>
577
- </div>
578
-
579
- <!-- 反馈芯片 -->
580
- <div
581
- v-if="lastDropText"
582
- class="drop-chip"
583
- >
584
- {{ lastDropText }}
585
- </div>
586
- <div
587
- v-if="errorText"
588
- class="error-chip"
589
- >
590
- {{ errorText }}
591
- </div>
592
-
593
- <!-- 加载遮罩 -->
594
- <div
595
- v-if="!ready"
596
- data-hud="town-loading"
597
- class="loading-mask"
598
- >
599
- <div class="loading-spinner" />
600
- <span class="loading-text">正在铺设数字孪生空间…</span>
601
- </div>
602
- </div>
603
-
604
- <!-- 底部坞:场景控制 + 趋势分析 -->
605
- <div class="dock">
606
- <section class="dock-card">
607
- <div class="dock-hd">
608
- <h3>场景控制</h3>
609
- <span class="dock-mode">{{ mode === 'edit' ? '编辑模式' : '运行模式' }}</span>
610
- </div>
611
- <div class="ctl-row">
612
- <span class="ctl-name">环境光照</span>
613
- <input
614
- class="ctl-range"
615
- type="range"
616
- min="30"
617
- max="220"
618
- :value="Math.round(exposure * 100)"
619
- @input="onExposureInput($event)"
620
- >
621
- <span class="ctl-val">{{ Math.round(exposure * 100) }}%</span>
622
- </div>
623
- <div class="ctl-row">
624
- <span class="ctl-name">领地染色</span>
625
- <input
626
- class="ctl-range"
627
- type="range"
628
- min="10"
629
- max="100"
630
- :value="Math.round(tintOpacity * 100)"
631
- @input="onTintInput($event)"
632
- >
633
- <span class="ctl-val">{{ Math.round(tintOpacity * 100) }}%</span>
634
- </div>
635
- <div class="ctl-row">
636
- <span class="ctl-name">网格吸附</span>
637
- <button
638
- class="snap-toggle"
639
- :class="{ on: snap }"
640
- @click="toggleSnap"
641
- >
642
- {{ snap ? '开' : '关' }}
643
- </button>
644
- <span class="ctl-val" />
645
- </div>
646
- <div class="ctl-btns">
647
- <button
648
- class="btn btn-primary"
649
- @click="onResetView"
650
- >
651
- 重置视角
652
- </button>
653
- <button
654
- class="btn btn-ghost"
655
- @click="saveLayout"
656
- >
657
- 保存布局
658
- </button>
659
- </div>
660
- </section>
661
-
662
- <section class="dock-card">
663
- <div class="dock-hd">
664
- <h3>趋势分析</h3>
665
- <div class="trend-legend">
666
- <button
667
- v-for="t in daqTwins"
668
- :key="t.id"
669
- class="lg-chip"
670
- :class="{ off: hiddenTrends[t.id] }"
671
- @click="toggleTrend(t.id)"
672
- >
673
- <span
674
- class="lg-dot"
675
- :style="{ background: trendColor(t.id) }"
676
- />{{ t.name }}
677
- </button>
678
- </div>
679
- </div>
680
- <div class="trend-wrap">
681
- <canvas
682
- ref="trendCanvas"
683
- class="trend-cv"
684
- />
685
- <span
686
- v-if="!daqTwins.length"
687
- class="trend-empty"
688
- >从左侧拖入数采节点,实时趋势将在此绘制</span>
689
- </div>
690
- </section>
691
- </div>
692
- </main>
693
-
694
- <!-- 右轨:Inspector / 设备运行状态 / 关键设备 / 实时事件 / 导航 -->
695
- <aside class="rail rail-right">
696
- <section
697
- v-if="selected"
698
- class="panel inspector"
699
- >
700
- <div class="panel-hd">
701
- <h3>{{ selected.kind === 'device' ? (selectedIsDaq ? '数采节点' : '设备实例') : '精魂' }}</h3>
702
- <button
703
- class="mini-btn"
704
- title="取消选中"
705
- @click="closeScale"
706
- >
707
- ✕
708
- </button>
709
- </div>
710
-
711
- <div class="ins-chip-row">
712
- <span class="ins-chip mono">{{ selected.id.slice(0, 8) }}</span>
713
- <span
714
- v-if="selected.kind === 'device' && selectedIsDaq"
715
- class="ins-chip accent"
716
- >DAQ</span>
717
- </div>
718
-
719
- <template v-if="selected.kind === 'device'">
720
- <div
721
- v-if="selectedIsDaq"
722
- class="daq-info"
723
- >
724
- <div class="daq-info-row">
725
- <span>实时值</span>
726
- <b class="cy">{{ selectedDaqSim ? fmtDaq(selectedDaqSim) : '--' }} {{ selectedDaqSim?.tpl.unit }}</b>
727
- </div>
728
- <div class="daq-info-row">
729
- <span>正常范围</span>
730
- <b>{{ selectedDaqSim?.tpl.min }} ~ {{ selectedDaqSim?.tpl.max }}</b>
731
- </div>
732
- <div class="daq-info-row">
733
- <span>绑定设备</span>
734
- <b>{{ daqBoundDeviceName || '未绑定' }}</b>
735
- </div>
736
- <div class="bind-add">
737
- <select
738
- v-model="bindPick"
739
- class="bind-select"
740
- >
741
- <option value="">
742
- 选择设备实例…
743
- </option>
744
- <option
745
- v-for="dv in deviceTwins.twins.filter(x => x.kind !== 'daq' && x.id !== selected.id)"
746
- :key="dv.id"
747
- :value="dv.id"
748
- >
749
- {{ dv.name }}
750
- </option>
751
- </select>
752
- <button
753
- class="bind-add-btn"
754
- :disabled="!bindPick"
755
- @click="daqBoundDeviceName ? unbindDaq(selected.id) : bindDaq(selected.id, bindPick); bindPick = ''"
756
- >
757
- {{ daqBoundDeviceName ? '解绑' : '绑定' }}
758
- </button>
759
- </div>
760
- </div>
761
- <template v-else>
762
- <div class="obj-row">
763
- <span class="obj-label">名称</span>
764
- <input
765
- v-model="objNameDraft"
766
- class="obj-input"
767
- placeholder="设备名称"
768
- @change="onObjNameCommit"
769
- @keydown.enter="onObjNameCommit"
770
- >
771
- </div>
772
- <div class="obj-row">
773
- <span class="obj-label">模型</span>
774
- <select
775
- class="obj-select"
776
- :value="scene3dRef?.getDeviceModelRef?.(selected.id) ?? ''"
777
- @change="bindDeviceModel(($event.target as HTMLSelectElement).value)"
778
- >
779
- <option
780
- v-for="m in deviceModels"
781
- :key="m.id"
782
- :value="m.id"
783
- >
784
- {{ m.name }}
785
- </option>
786
- </select>
787
- </div>
788
-
789
- <!-- 数采绑定 -->
790
- <div class="sect-hd">
791
- 数采绑定
792
- </div>
793
- <div
794
- v-for="r in boundDaqRows"
795
- :key="r.daqId"
796
- class="bind-row-item"
797
- >
798
- <span
799
- class="bind-dot"
800
- :style="{ background: r.color }"
801
- />
802
- <span class="bind-name">{{ r.name }}</span>
803
- <span class="bind-val">{{ r.value }} {{ r.unit }}</span>
804
- <button
805
- class="bind-x"
806
- title="解除绑定"
807
- @click="unbindDaq(r.daqId)"
808
- >
809
- ✕
810
- </button>
811
- </div>
812
- <div
813
- v-if="!boundDaqRows.length"
814
- class="ins-empty"
815
- >
816
- 暂无绑定 · 可拖入数采节点后绑定
817
- </div>
818
- <div class="bind-add">
819
- <select
820
- v-model="bindPick"
821
- class="bind-select"
822
- >
823
- <option value="">
824
- 选择数采节点…
825
- </option>
826
- <option
827
- v-for="dq in daqTwins"
828
- :key="dq.id"
829
- :value="dq.id"
830
- >
831
- {{ dq.name }}
832
- </option>
833
- </select>
834
- <button
835
- class="bind-add-btn"
836
- :disabled="!bindPick"
837
- @click="bindDaq(bindPick, selected.id); bindPick = ''"
838
- >
839
- 绑定
840
- </button>
841
- </div>
842
-
843
- <!-- 变换模式(Blender G/R/S) -->
844
- <div class="sect-hd">
845
- 变换 · BLENDER
846
- </div>
847
- <div class="xz-seg">
848
- <button
849
- class="seg-btn"
850
- :class="{ on: tMode === 'translate' }"
851
- @click="setTMode('translate')"
852
- >
853
- 移动 G
854
- </button>
855
- <button
856
- class="seg-btn"
857
- :class="{ on: tMode === 'rotate' }"
858
- @click="setTMode('rotate')"
859
- >
860
- 旋转 R
861
- </button>
862
- <button
863
- class="seg-btn"
864
- :class="{ on: tMode === 'scale' }"
865
- @click="setTMode('scale')"
866
- >
867
- 缩放 S
868
- </button>
869
- </div>
870
- <div class="scale-row">
871
- <span class="scale-min">0.2×</span>
872
- <input
873
- class="scale-range"
874
- type="range"
875
- min="0.2"
876
- max="5"
877
- step="0.05"
878
- :value="selected.scale"
879
- @input="onScaleInput(Number(($event.target as HTMLInputElement).value))"
880
- @change="onScaleCommit(Number(($event.target as HTMLInputElement).value))"
881
- >
882
- <span class="scale-max">5×</span>
883
- </div>
884
- <div class="scale-val">
885
- {{ Math.round(selected.scale * 100) }}%
886
- </div>
887
-
888
- <div class="ins-foot">
889
- <button
890
- class="btn btn-danger ins-del"
891
- :class="{ armed: deviceDeleteArmed === selected.id }"
892
- @click="removeSelectedDevice"
893
- >
894
- {{ deviceDeleteArmed === selected.id ? '再次点击确认删除' : '删除设备实例' }}
895
- </button>
896
- </div>
897
- </template>
898
- </template>
899
-
900
- <template v-else-if="selected.kind === 'agent'">
901
- <div class="obj-row">
902
- <span class="obj-label">模型</span>
903
- <select
904
- class="obj-select"
905
- :value="(scene3dRef?.getAgentModel?.(selected.id) ?? '') || 'hero-3d'"
906
- @change="bindAgentModel(($event.target as HTMLSelectElement).value)"
907
- >
908
- <option
909
- v-for="m in agentModels"
910
- :key="m.id"
911
- :value="m.id"
912
- >
913
- {{ m.name }}
914
- </option>
915
- </select>
916
- </div>
917
- <div class="obj-sep" />
918
- <div class="obj-row">
919
- <span class="obj-label">活动范围</span>
920
- <span class="range-status">{{ agentRangeStatusText }}</span>
921
- <button
922
- class="obj-mini"
923
- :class="{ on: agentDrawingRange }"
924
- title="在场景中拖动框选,确定该角色的移动范围"
925
- @click="onToggleRangeDraw"
926
- >
927
- {{ agentDrawingRange ? '绘制中' : '框选绘制' }}
928
- </button>
929
- </div>
930
- <template v-if="agentRangeDraft">
931
- <div class="obj-row">
932
- <span class="obj-label">形状</span>
933
- <div class="bp-seg">
934
- <button
935
- class="seg-btn"
936
- :class="{ on: agentRangeDraft.shape === 'ellipse' }"
937
- @click="agentRangeDraft.shape = 'ellipse'; applyAgentRangeDraft()"
938
- >
939
- 椭圆
940
- </button>
941
- <button
942
- class="seg-btn"
943
- :class="{ on: agentRangeDraft.shape === 'rect' }"
944
- @click="agentRangeDraft.shape = 'rect'; applyAgentRangeDraft()"
945
- >
946
- 矩形
947
- </button>
948
- </div>
949
- </div>
950
- <div class="obj-row">
951
- <span class="obj-label">横轴</span>
952
- <input
953
- v-model.number="agentRangeDraft.radiusX"
954
- class="obj-range"
955
- type="range"
956
- min="40"
957
- max="4000"
958
- step="8"
959
- @input="applyAgentRangeDraft"
960
- @change="onAgentRangeCommit"
961
- >
962
- <span class="bp-val">{{ Math.round(agentRangeDraft.radiusX) }}</span>
963
- </div>
964
- <div class="obj-row">
965
- <span class="obj-label">纵轴</span>
966
- <input
967
- v-model.number="agentRangeDraft.radiusZ"
968
- class="obj-range"
969
- type="range"
970
- min="40"
971
- max="4000"
972
- step="8"
973
- @input="applyAgentRangeDraft"
974
- @change="onAgentRangeCommit"
975
- >
976
- <span class="bp-val">{{ Math.round(agentRangeDraft.radiusZ) }}</span>
977
- </div>
978
- <button
979
- class="obj-mini danger"
980
- @click="onClearAgentRange"
981
- >
982
- 清除范围(回退频道)
983
- </button>
984
- </template>
985
- <div
986
- v-else
987
- class="ins-empty"
988
- >
989
- 未设置:沿用频道领地活动;点「框选绘制」定制
990
- </div>
991
- </template>
992
- </section>
993
-
994
- <section class="panel">
995
- <div class="panel-hd">
996
- <h3>设备运行状态</h3>
997
- </div>
998
- <div class="health-body">
999
- <div class="donut-wrap">
1000
- <canvas
1001
- ref="donutCanvas"
1002
- width="118"
1003
- height="118"
1004
- />
1005
- <div class="donut-center">
1006
- <b>{{ healthPct }}%</b>
1007
- <span>设备健康度</span>
1008
- </div>
1009
- </div>
1010
- <div class="health-legend">
1011
- <div class="hl-row">
1012
- <span
1013
- class="hl-dot"
1014
- :style="{ background: 'var(--hud-accent)' }"
1015
- />
1016
- 运行中
1017
- <span class="n">{{ runningCount }}</span>
1018
- </div>
1019
- <div class="hl-row">
1020
- <span
1021
- class="hl-dot"
1022
- :style="{ background: 'var(--hud-amber)' }"
1023
- />
1024
- 待命
1025
- <span class="n">{{ idleCount }}</span>
1026
- </div>
1027
- <div class="hl-row">
1028
- <span
1029
- class="hl-dot"
1030
- :style="{ background: 'var(--hud-danger)' }"
1031
- />
1032
- 告警
1033
- <span class="n">{{ alarmCount }}</span>
1034
- </div>
1035
- </div>
1036
- </div>
1037
- </section>
1038
-
1039
- <section class="panel">
1040
- <div class="panel-hd">
1041
- <h3>关键设备监控</h3>
1042
- <span class="panel-tag">{{ deviceTwins.twins.length }}</span>
1043
- </div>
1044
- <WorkshopDeviceTwinPanel
1045
- class="kd-embed"
1046
- @focus-device="onFocusDevice"
1047
- />
1048
- </section>
1049
-
1050
- <section class="panel">
1051
- <div class="panel-hd">
1052
- <h3>实时事件</h3>
1053
- <span class="panel-tag">{{ ticker.length }}</span>
1054
- </div>
1055
- <div class="event-list">
1056
- <div
1057
- v-for="(t, i) in [...ticker].reverse()"
1058
- :key="`${t.at}-${i}`"
1059
- class="event-row"
1060
- >
1061
- <span class="ev-time">{{ fmtTime(t.at) }}</span>
1062
- <span
1063
- class="ev-name"
1064
- :title="t.agentName"
1065
- >{{ t.agentName }}</span>
1066
- <span
1067
- class="ev-text"
1068
- :title="t.text"
1069
- >{{ t.text }}</span>
1070
- </div>
1071
- <div
1072
- v-if="!ticker.length"
1073
- class="event-empty"
1074
- >
1075
- 事件总线待命
1076
- </div>
1077
- </div>
1078
- </section>
1079
-
1080
- <section class="panel">
1081
- <div class="panel-hd">
1082
- <h3>3D 视图导航</h3>
1083
- </div>
1084
- <div
1085
- v-if="minimap && miniView"
1086
- class="mm-body"
1087
- >
1088
- <div class="mm-wrap">
1089
- <svg
1090
- :viewBox="`${miniView.ox} ${miniView.oy} ${miniView.winW} ${miniView.winH}`"
1091
- class="mini-svg"
1092
- role="img"
1093
- aria-label="镜头居中导航图,点击跳转,拖动平移镜头"
1094
- @click="onMinimapClick"
1095
- @pointerdown="onMinimapDown"
1096
- @pointermove="onMinimapMove"
1097
- @pointerup="onMinimapUp"
1098
- @pointercancel="onMinimapUp"
1099
- @pointerleave="onMinimapUp"
1100
- >
1101
- <rect
1102
- :x="miniView.ox"
1103
- :y="miniView.oy"
1104
- :width="miniView.winW"
1105
- :height="miniView.winH"
1106
- fill="#060b13"
1107
- />
1108
- <circle
1109
- v-for="b in minimap.blocks"
1110
- :key="`b-${b.name}`"
1111
- :cx="b.x * minimap.world.w"
1112
- :cy="b.y * minimap.world.h"
1113
- :rx="(b.rx ?? 0.04) * minimap.world.w"
1114
- :ry="(b.rz ?? 0.03) * minimap.world.h"
1115
- :fill="toHex(b.color)"
1116
- fill-opacity="0.12"
1117
- :stroke="toHex(b.color)"
1118
- stroke-opacity="0.6"
1119
- stroke-width="3"
1120
- />
1121
- <rect
1122
- v-for="d in minimap.devices"
1123
- :key="`d-${d.x.toFixed(4)}-${d.y.toFixed(4)}`"
1124
- :x="d.x * minimap.world.w - 23"
1125
- :y="d.y * minimap.world.h - 23"
1126
- width="46"
1127
- height="46"
1128
- rx="6"
1129
- :fill="toHex(d.color)"
1130
- opacity="0.95"
1131
- />
1132
- <circle
1133
- v-for="a in minimap.agents"
1134
- :key="`a-${a.x.toFixed(4)}-${a.y.toFixed(4)}`"
1135
- :cx="a.x * minimap.world.w"
1136
- :cy="a.y * minimap.world.h"
1137
- r="27"
1138
- :fill="toHex(a.color)"
1139
- stroke="rgba(6,10,16,0.7)"
1140
- stroke-width="6"
1141
- />
1142
- <g class="mini-reticle">
1143
- <rect
1144
- :x="miniView.ox + miniView.winW / 2 - 8"
1145
- :y="miniView.oy + miniView.winH / 2 - 8"
1146
- width="16"
1147
- height="16"
1148
- fill="none"
1149
- stroke="#35e0a0"
1150
- stroke-width="4"
1151
- />
1152
- </g>
1153
- </svg>
1154
- </div>
1155
- <div class="mm-col">
1156
- <button
1157
- class="vp-tool"
1158
- title="重置视角"
1159
- @click="onResetView"
1160
- >
1161
- 重
1162
- </button>
1163
- <button
1164
- class="vp-tool"
1165
- title="缩小"
1166
- @click="scene3dRef?.zoomBy(0.25)"
1167
- >
1168
- −
1169
- </button>
1170
- <button
1171
- class="vp-tool"
1172
- title="放大"
1173
- @click="scene3dRef?.zoomBy(-0.25)"
1174
- >
1175
- +
1176
- </button>
1177
- </div>
1178
- </div>
1179
- <div class="mm-meta mono">
1180
- {{ blockCount }} 领地 · {{ minimap.devices?.length ?? 0 }} 设备
1181
- </div>
1182
- </section>
1183
- </aside>
1184
- </div>
1185
-
1186
- <!-- ================= 状态栏 ================= -->
1187
- <footer class="statusbar">
1188
- <span>
1189
- <span
1190
- class="sb-dot"
1191
- :class="{ red: conn.state !== 'open' }"
1192
- />系统状态 <b>{{ conn.state === 'open' ? '正常' : syncing ? '同步中' : '离线' }}</b>
1193
- </span>
1194
- <span>
1195
- 领地 {{ blockCount }} · 精魂 {{ agentCount }} · 设备 {{ deviceCount }}
1196
- </span>
1197
- <span class="sb-lat mono">
1198
- {{ fps }} FPS
1199
- </span>
1200
- <span class="copy">
1201
- © 2026 ABO · DIGITAL TWIN · 演示数据为模拟信号
1202
- </span>
1203
- </footer>
1204
-
1205
- <!-- 加载遮罩(全页) -->
1206
- <div
1207
- v-if="!ready"
1208
- data-hud="town-loading"
1209
- class="loading-mask"
1210
- >
1211
- <div class="loading-spinner" />
1212
- <span class="loading-text">正在铺设数字孪生空间…</span>
1213
- </div>
1214
- </div>
1215
- </template>