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,704 +1,704 @@
1
- <script setup lang="ts">
2
- /**
3
- * Workspace 主控台(Zcode 风格 Harness):
4
- * 顶栏(WS 状态 + seq + 视图切换)+ 左栏 Channel 会话 + 中部三视图
5
- * (时间线 / Agent lanes / 任务板)+ 右侧 Inspector + 底部 Composer +
6
- * Agent/Task 双抽屉(执行详情)。
7
- * 挂载 workspace 全部 channel 的 WS 订阅;聚焦 channel 驱动中部/右侧上下文。
8
- */
9
- import { useWorkspacesStore } from '../../../stores/workshop/workspaces'
10
- import { useEntitiesStore } from '../../../stores/workshop/entities'
11
- import { useWorkshopWs } from '../../../composables/workshop/useWorkshopWs'
12
- import { useUserStore } from '../../../stores/workshop/user'
13
- import { useStorage } from '@vueuse/core'
14
- import { useResponsive } from '../../../composables/useResponsive'
15
-
16
- const { t } = useI18n()
17
-
18
- definePageMeta({ layout: 'default' })
19
-
20
- const route = useRoute()
21
- const wsId = computed(() => String(route.params.wsId))
22
- const userStore = useUserStore()
23
- const wsStore = useWorkspacesStore()
24
-
25
- // 用户守卫 + workspace 服务端加载
26
- // SSR 阶段不判登录(会话恢复是客户端插件,服务端无登录态——同步踢回会把
27
- // 刷新/直达 URL 的已登录用户误弹回总览);客户端挂载后校验并按需加载
28
- const authReady = ref(false)
29
- onMounted(() => {
30
- if (!userStore.isLoggedIn) {
31
- navigateTo('/workshop')
32
- return
33
- }
34
- authReady.value = true
35
- if (!wsStore.loaded) wsStore.load().catch(() => {})
36
- })
37
- const entities = useEntitiesStore()
38
- const { subscribe, unsubscribe, conn } = useWorkshopWs()
39
-
40
- // 视口档位(唯一判据;SSR 期返回桌面档)——
41
- // 窄屏(≤1023)三栏仪表台只剩 ~460px 画布,必须换成「单通道示波器」形态
42
- const { isDesktop } = useResponsive()
43
-
44
- const workspace = computed(() => wsStore.workspaces.find(w => w.id === wsId.value))
45
- // 聚焦 channel:activeChannelId 须在挂载清单内(陈旧持久化/竞态下回退首频道),
46
- // 避免订阅死频道 → 快照永不到达 → 右栏/时间线长时间"空数据"假象
47
- const channelId = computed(() => {
48
- const ws = workspace.value
49
- if (!ws) return undefined
50
- const ids = ws.channelIds
51
- const active = ws.activeChannelId
52
- return active && ids.includes(active) ? active : ids[0]
53
- })
54
-
55
- // 订阅生命周期:workspace 挂载的 channel 变化 → 增量 sub/unsub
56
- watch(
57
- () => [wsId.value, workspace.value?.channelIds.join(',') ?? ''],
58
- () => {
59
- const mounted = new Set(workspace.value?.channelIds ?? [])
60
- for (const id of mounted) subscribe(id)
61
- },
62
- { immediate: true },
63
- )
64
- watch(
65
- () => workspace.value?.channelIds.join(',') ?? '',
66
- (_next, prev) => {
67
- if (prev === undefined) return
68
- const mounted = new Set(workspace.value?.channelIds ?? [])
69
- for (const prevId of prev.split(',').filter(Boolean)) {
70
- if (!mounted.has(prevId)) unsubscribe(prevId)
71
- }
72
- },
73
- )
74
- onBeforeUnmount(() => {
75
- for (const id of workspace.value?.channelIds ?? []) unsubscribe(id)
76
- })
77
-
78
- const stateColor = computed(() =>
79
- conn.state === 'open' ? 'var(--tone-success-dot)' : conn.state === 'connecting' ? 'var(--tone-warning-dot)' : 'var(--tone-danger-dot)',
80
- )
81
- const lastSeq = computed(() => (channelId.value ? conn.cursors[channelId.value] ?? 0 : 0))
82
-
83
- // 视图切换(P1 三视图 + P2 多通道同屏 + P5 RPG 小镇)
84
- // 深链:?view=lanes/board/split/town 直达指定视图(可分享/收藏)
85
- // inspector 只在窄屏出现(桌面它是右侧常驻栏),故不进深链白名单
86
- type CenterView = 'timeline' | 'lanes' | 'board' | 'split' | 'town' | 'inspector'
87
- const VIEW_KEYS: Record<string, CenterView> = {
88
- 1: 'timeline',
89
- 2: 'lanes',
90
- 3: 'board',
91
- 4: 'split',
92
- 5: 'town',
93
- }
94
- const VIEW_VALUES = new Set(['timeline', 'lanes', 'board', 'split', 'town'])
95
- const initView = route.query.view
96
- const view = ref<CenterView>(
97
- typeof initView === 'string' && VIEW_VALUES.has(initView) ? initView as CenterView : 'timeline',
98
- )
99
- const viewOptions = computed(() => {
100
- const base = [
101
- { value: 'timeline', label: t('wsView.k3otu32010') },
102
- { value: 'lanes', label: 'Agent lanes' },
103
- { value: 'board', label: t('wsView.k3ko7a8011') },
104
- { value: 'split', label: t('wsView.k3xbmo012') },
105
- { value: 'town', label: t('wsView.k1cz0pbw013') },
106
- ]
107
- // 窄屏「一次一区」:检查器不占侧栏,并入切换条(四区都由同一条承载)
108
- return narrowUI.value ? [...base, { value: 'inspector', label: '检查器' }] : base
109
- })
110
- // 数字快捷键 1-5 直切视图(非输入焦点时;控制台型键盘操作与 ⌘K 面板同一取向)
111
- const onViewKey = (ev: KeyboardEvent): void => {
112
- // 窄屏抽屉:Esc 收起(与全站侧栏抽屉一致)
113
- if (ev.key === 'Escape' && narrowUI.value && leftOpen.value) {
114
- leftOpen.value = false
115
- return
116
- }
117
- if (ev.metaKey || ev.ctrlKey || ev.altKey) return
118
- const t = ev.target as HTMLElement | null
119
- if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return
120
- const next = VIEW_KEYS[ev.key]
121
- if (!next || !channelId.value) return
122
- ev.preventDefault()
123
- view.value = next
124
- }
125
- onMounted(() => window.addEventListener('keydown', onViewKey))
126
- onBeforeUnmount(() => window.removeEventListener('keydown', onViewKey))
127
-
128
- // 侧栏折叠(现代 harness 布局:左会话栏 / 右检查器可按需收起)
129
- // 形态差异只在挂载后生效:SSR/首帧一律按桌面结构渲染,客户端接管后才翻档,
130
- // 否则服务端 5 项切换条 vs 客户端 6 项会触发水合告警
131
- const mounted = ref(false)
132
- onMounted(() => {
133
- mounted.value = true
134
- })
135
- const narrowUI = computed(() => mounted.value && !isDesktop.value)
136
-
137
- const leftOpen = ref(true)
138
- const rightOpen = ref(true)
139
- /** 窄屏:左右侧栏都不再占位(收成覆盖式抽屉 / 并入切换条) */
140
- const applyTier = (desktop: boolean): void => {
141
- leftOpen.value = desktop
142
- rightOpen.value = desktop
143
- // 检查器只是窄屏的第 4 区:回桌面后它回到右栏,中部得有自己的区(否则中部空白)
144
- if (desktop && view.value === 'inspector') view.value = 'timeline'
145
- }
146
- onMounted(() => {
147
- if (!isDesktop.value) applyTier(false)
148
- })
149
- watch(isDesktop, d => applyTier(d))
150
- // 抽屉里选中频道后自动收起(窄屏少一次手动关闭)
151
- watch(channelId, (next, prev) => {
152
- if (prev !== undefined && next !== prev && narrowUI.value) leftOpen.value = false
153
- })
154
-
155
- // 侧栏宽度拖拽调节(PaneSplitter;localStorage 持久化,双击复位到默认值)
156
- const LEFT_W_DEFAULT = 248
157
- const RIGHT_W_DEFAULT = 300
158
- const leftWidth = useStorage('aw.harness.leftW', LEFT_W_DEFAULT)
159
- const rightWidth = useStorage('aw.harness.rightW', RIGHT_W_DEFAULT)
160
- const resizeLeft = (d: number): void => {
161
- leftWidth.value = Math.min(460, Math.max(220, leftWidth.value + d))
162
- }
163
- const resizeRight = (d: number): void => {
164
- rightWidth.value = Math.min(560, Math.max(240, rightWidth.value - d))
165
- }
166
- // 初始化消毒:陈旧持久化值(超出合法范围/异常类型)夹取回默认邻域,防布局被历史脏数据撑坏
167
- resizeLeft(0)
168
- resizeRight(0)
169
-
170
- // 抽屉状态(P1)
171
- const agentDrawerOpen = ref(false)
172
- const agentDrawerId = ref<string | null>(null)
173
- const openAgent = (id: string): void => {
174
- agentDrawerId.value = id
175
- agentDrawerOpen.value = true
176
- }
177
- /** 活动条入口:跨 channel 的 busy 成员 → 先聚焦其 channel 再开抽屉 */
178
- const openAgentInChannel = (target: { channelId: string, agentId: string }): void => {
179
- wsStore.setActiveChannel(wsId.value, target.channelId)
180
- openAgent(target.agentId)
181
- }
182
- /** @提及 pill 点击入口(ClusterRoute/ClusterStream inject;时间线与 lanes 全树可用) */
183
- provide('aw:open-agent', openAgentInChannel)
184
- const taskDrawerOpen = ref(false)
185
- const taskDrawerId = ref<string | null>(null)
186
- const openTask = (id: string): void => {
187
- taskDrawerId.value = id
188
- taskDrawerOpen.value = true
189
- }
190
-
191
- // ⌘K 命令面板 + A2A 调试器(P2)
192
- const paletteOpen = ref(false)
193
- const a2aDebugOpen = ref(false)
194
- const composerBox = ref<HTMLElement | null>(null)
195
- const focusComposer = (): void => {
196
- composerBox.value?.querySelector('textarea')?.focus()
197
- }
198
-
199
- useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop` })
200
- </script>
201
-
202
- <template>
203
- <div class="harness">
204
- <!-- @提及悬停信息卡(文档级委托;时间线/lanes 全树生效) -->
205
- <workshop-mention-hover-card />
206
- <!-- 顶栏 -->
207
- <div class="topbar">
208
- <div
209
- class="left"
210
- tabindex="-1"
211
- @keydown="onViewKey"
212
- >
213
- <span class="topbar-mark i-tabler-box" />
214
- <span class="ws-name">{{ workspace?.name ?? '未知 Workspace' }}</span>
215
- <span
216
- v-if="channelId"
217
- class="chan-chip"
218
- :title="channelId"
219
- >
220
- <span class="chan-hash">#</span>{{ entities.channels[channelId]?.name ?? channelId.slice(0, 8) }}
221
- </span>
222
- <a-segmented
223
- v-if="channelId"
224
- v-model:value="view"
225
- size="small"
226
- :options="viewOptions"
227
- class="view-switch"
228
- :title="$t('wsView.kqwckjr001')"
229
- @keydown="onViewKey"
230
- />
231
- </div>
232
- <div class="right">
233
- <!-- 窄屏:频道会话列表入口(覆盖式抽屉;该页一次只看一区) -->
234
- <button
235
- v-if="narrowUI"
236
- class="pane-toggle im toggle-left-narrow"
237
- :class="{ off: !leftOpen }"
238
- title="频道会话列表"
239
- @click="leftOpen = !leftOpen"
240
- >
241
- <span class="i-tabler-list-details im-pop" />
242
- </button>
243
- <button
244
- class="pane-toggle im toggle-left-desk"
245
- :class="{ off: !leftOpen }"
246
- :title="$t('wsView.k1tsy2e4002')"
247
- @click="leftOpen = !leftOpen"
248
- >
249
- <span class="i-tabler-layout-sidebar-left-collapse im-pop" />
250
- </button>
251
- <button
252
- class="pane-toggle im toggle-right-desk"
253
- :class="{ off: !rightOpen }"
254
- :title="$t('wsView.k1tx0ppf003')"
255
- @click="rightOpen = !rightOpen"
256
- >
257
- <span class="i-tabler-layout-sidebar-right-collapse im-pop" />
258
- </button>
259
- <button
260
- class="pane-toggle im"
261
- title="A2A RPC/SSE 调试器"
262
- @click="a2aDebugOpen = true"
263
- >
264
- <span class="i-tabler-terminal-2 im-pop" />
265
- </button>
266
- <button
267
- class="pane-toggle im"
268
- :title="$t('wsView.k1cvg8sb004')"
269
- @click="paletteOpen = true"
270
- >
271
- <span class="i-tabler-command im-pop" />
272
- </button>
273
- <span
274
- class="dot"
275
- :style="{ background: stateColor }"
276
- />
277
- <span
278
- class="ws-state"
279
- :data-state="conn.state"
280
- >{{ conn.state }}</span>
281
- <span class="seq">seq {{ lastSeq }}</span>
282
- </div>
283
- </div>
284
-
285
- <!-- 主体三栏(左/右侧栏可折叠 + 拖拽调宽;分隔条 hairline 即面板边界) -->
286
- <div class="main">
287
- <!-- 窄屏抽屉遮罩:点空白收起(不盖住底部 Composer,输入不被"锁在"抽屉后) -->
288
- <div
289
- v-if="narrowUI && leftOpen"
290
- class="drawer-scrim"
291
- @click="leftOpen = false"
292
- />
293
- <div
294
- v-if="leftOpen"
295
- class="left-pane"
296
- :style="{ flexBasis: narrowUI ? 'auto' : `${leftWidth}px` }"
297
- >
298
- <div class="left-scroll">
299
- <workshop-channel-session-list :ws-id="wsId" />
300
- </div>
301
- <workshop-live-agent-bar
302
- :ws-id="wsId"
303
- @open-agent="openAgentInChannel"
304
- />
305
- </div>
306
- <workshop-pane-splitter
307
- v-if="leftOpen && !narrowUI"
308
- :label="$t('wsView.k1fkfvra005')"
309
- @resize="resizeLeft"
310
- @reset="leftWidth = LEFT_W_DEFAULT"
311
- />
312
- <div class="center-pane">
313
- <template v-if="channelId">
314
- <workshop-transcript-timeline
315
- v-if="view === 'timeline'"
316
- :channel-id="channelId"
317
- />
318
- <workshop-agent-lanes-view
319
- v-else-if="view === 'lanes'"
320
- :channel-id="channelId"
321
- />
322
- <workshop-task-board-view
323
- v-else-if="view === 'board'"
324
- :channel-id="channelId"
325
- @open-task="openTask"
326
- />
327
- <workshop-multi-channel-view
328
- v-else-if="view === 'split'"
329
- :ws-id="wsId"
330
- @open-task="openTask"
331
- />
332
- <workshop-town-view
333
- v-else-if="view === 'town'"
334
- :channel-id="channelId"
335
- />
336
- <!-- 窄屏第 4 区:检查器并入切换条(桌面仍是右侧常驻栏) -->
337
- <workshop-inspector-panel
338
- v-else-if="view === 'inspector' && narrowUI"
339
- :channel-id="channelId"
340
- @open-agent="openAgent"
341
- @open-task="openTask"
342
- />
343
- </template>
344
- <div
345
- v-else
346
- class="pane-empty"
347
- >
348
- <span class="pe-icon i-tabler-messages" />
349
- <div class="pe-title">
350
- {{ $t('wsView.klzbtyp007') }} <span class="aw-serif-accent-italic">Channel</span> {{ $t('wsView.k3zaj4008') }}
351
- </div>
352
- <div class="pe-sub">
353
- {{ $t('wsView.k1us0cxy009') }}
354
- </div>
355
- </div>
356
- </div>
357
- <workshop-pane-splitter
358
- v-if="rightOpen && !narrowUI"
359
- :label="$t('wsView.kwqb0st006')"
360
- @resize="resizeRight"
361
- @reset="rightWidth = RIGHT_W_DEFAULT"
362
- />
363
- <div
364
- v-if="rightOpen && !narrowUI"
365
- class="right-pane"
366
- :class="{ empty: wsStore.loaded && !channelId }"
367
- :style="{ flexBasis: `${rightWidth}px` }"
368
- >
369
- <workshop-inspector-panel
370
- v-if="channelId"
371
- :channel-id="channelId"
372
- @open-agent="openAgent"
373
- @open-task="openTask"
374
- />
375
- <!-- 加载中/无频道的诚实降级态(workspace 列表未返回前不误判为"空") -->
376
- <div
377
- v-else-if="!wsStore.loaded"
378
- class="pane-loading"
379
- >
380
- 正在加载 Workspace…
381
- </div>
382
- </div>
383
- </div>
384
-
385
- <!-- Composer -->
386
- <div
387
- v-if="channelId"
388
- ref="composerBox"
389
- class="composer-pane"
390
- >
391
- <workshop-composer :channel-id="channelId" />
392
-
393
- <!-- 抽屉 -->
394
- <workshop-agent-inspector-drawer
395
- v-model:open="agentDrawerOpen"
396
- :channel-id="channelId ?? ''"
397
- :agent-id="agentDrawerId"
398
- />
399
- <workshop-task-inspector-drawer
400
- v-model:open="taskDrawerOpen"
401
- :channel-id="channelId ?? ''"
402
- :task-id="taskDrawerId"
403
- />
404
- <workshop-a2a-rpc-debugger
405
- v-model:open="a2aDebugOpen"
406
- :channel-id="channelId ?? ''"
407
- />
408
-
409
- <!-- ⌘K 命令面板 -->
410
- <workshop-command-palette
411
- v-model:open="paletteOpen"
412
- :ws-id="wsId"
413
- @set-view="view = $event"
414
- @open-a2a-debug="a2aDebugOpen = true"
415
- @compose="focusComposer"
416
- />
417
- </div>
418
- </div>
419
- </template>
420
-
421
- <style scoped>
422
- .harness {
423
- display: flex;
424
- flex-direction: column;
425
- height: calc(100dvh - var(--app-header-h, 56px) - var(--app-footer-h, 46px) - 16px);
426
- min-height: 0;
427
- margin: 0;
428
- border: 1px solid var(--line);
429
- border-radius: var(--radius-shell);
430
- overflow: hidden;
431
- background: var(--paper-raised);
432
- box-shadow: var(--shadow-card);
433
- }
434
- .topbar {
435
- display: flex;
436
- align-items: center;
437
- justify-content: space-between;
438
- padding: 8px 14px;
439
- font-size: 13px;
440
- background: var(--paper-raised);
441
- border-bottom: 1px solid var(--line);
442
- }
443
- .left,
444
- .right {
445
- display: flex;
446
- gap: 8px;
447
- align-items: center;
448
- min-width: 0;
449
- }
450
- .topbar-mark {
451
- font-size: 15px;
452
- color: var(--ink-faint);
453
- }
454
- .ws-name {
455
- max-width: 260px;
456
- overflow: hidden;
457
- font-family: var(--font-display);
458
- font-weight: 400;
459
- font-size: 17px;
460
- letter-spacing: -0.01em;
461
- text-overflow: ellipsis;
462
- white-space: nowrap;
463
- }
464
- /* channel 胶囊:hairline chip + serif # */
465
- .chan-chip {
466
- display: inline-flex;
467
- gap: 3px;
468
- align-items: center;
469
- max-width: 200px;
470
- padding: 1px 10px;
471
- overflow: hidden;
472
- font-size: 12px;
473
- color: var(--ink-soft);
474
- text-overflow: ellipsis;
475
- white-space: nowrap;
476
- border: 1px solid var(--line-strong);
477
- border-radius: var(--radius-pill);
478
- }
479
- .chan-hash {
480
- font-family: var(--font-display);
481
- color: var(--ink-faint);
482
- }
483
- .view-switch { margin-left: 8px; }
484
- .dot { width: 8px; height: 8px; border-radius: 50%; }
485
- .ws-state {
486
- font-family: var(--font-mono);
487
- font-size: 10px;
488
- letter-spacing: 0.1em;
489
- text-transform: uppercase;
490
- padding: 1px 6px;
491
- border: 1px solid var(--line);
492
- border-radius: var(--radius-chip);
493
- }
494
- .ws-state[data-state='open'] { color: var(--tone-success-dot); border-color: color-mix(in srgb, var(--tone-success-dot) 45%, transparent); }
495
- .ws-state[data-state='connecting'] { color: var(--tone-warning-dot); border-color: color-mix(in srgb, var(--tone-warning-dot) 45%, transparent); }
496
- .ws-state[data-state='closed'] { color: var(--tone-danger-dot); border-color: color-mix(in srgb, var(--tone-danger-dot) 45%, transparent); }
497
- .seq {
498
- font-family: var(--font-mono);
499
- font-variant-numeric: tabular-nums;
500
- font-size: 10px;
501
- color: var(--ink-faint);
502
- }
503
-
504
- /* 侧栏折叠开关:幽灵图标钮 */
505
- .pane-toggle {
506
- display: inline-flex;
507
- align-items: center;
508
- justify-content: center;
509
- width: 26px;
510
- height: 26px;
511
- font-size: 14px;
512
- color: var(--ink-soft);
513
- cursor: pointer;
514
- background: transparent;
515
- border: 0;
516
- border-radius: var(--radius-panel-sm);
517
- transition: color var(--transition-fast), background var(--transition-fast), opacity var(--transition-fast);
518
- }
519
- .pane-toggle:hover {
520
- color: var(--ink);
521
- background: var(--paper-deep);
522
- }
523
- .pane-toggle.off {
524
- opacity: 0.4;
525
- }
526
- .pane-toggle.off:hover {
527
- opacity: 1;
528
- }
529
- .main {
530
- display: flex;
531
- flex: 1 1 auto;
532
- min-height: 0;
533
- }
534
- .left-pane {
535
- display: flex;
536
- flex: 0 0 auto; /* 宽度由拖拽分隔条驱动(inline flexBasis) */
537
- flex-direction: column;
538
- min-height: 0;
539
- overflow: hidden;
540
- background: var(--paper);
541
- }
542
- .left-scroll {
543
- flex: 1 1 auto;
544
- min-height: 0;
545
- overflow: hidden auto;
546
- }
547
- .center-pane {
548
- display: flex;
549
- flex: 1 1 auto;
550
- min-width: 0;
551
- min-height: 0;
552
- max-width: 100%;
553
- overflow: hidden;
554
- background: var(--paper); /* 灰画布:消息气泡/白色面板在此浮出(Slack 式分层) */
555
- }
556
- .center-pane > * {
557
- min-width: 0;
558
- max-width: 100%;
559
- }
560
- .right-pane {
561
- flex: 0 0 auto; /* 宽度由拖拽分隔条驱动(inline flexBasis) */
562
- min-height: 0;
563
- overflow: hidden;
564
- background: var(--paper);
565
- }
566
- .right-pane.empty { opacity: 0.35; }
567
- /* workspace 列表加载中的诚实降级态(不算"空",不淡化整栏) */
568
- .pane-loading {
569
- display: flex;
570
- align-items: center;
571
- justify-content: center;
572
- height: 100%;
573
- font-size: 12px;
574
- color: var(--ink-faint);
575
- }
576
- .composer-pane { flex: 0 0 auto; }
577
-
578
- /* ══════════════════════════════════════════════════════════════════════════
579
- 窄屏形态 · 单通道示波器(≤1023px) —— 一次只看一路信号
580
- ──────────────────────────────────────────────────────────────────────────
581
- 桌面是「三栏仪表台」:会话栏 248 + 画布 + 检查器 300 + 两条分隔条。
582
- 到了 ≤1023,画布只剩 ~460px(900–1023 还要再让 64px 图标轨),
583
- 硬塞的结果是三栏互相压扁。所以窄屏换一台仪器,而不是把仪表台缩小:
584
-
585
- · 中部视图(时间线 / lanes / 任务板 / 检查器)→ 由顶栏切换条承载,
586
- 一次只挂载一区,顶部切换条独占一行、可横扫;
587
- · 频道会话列表 → 覆盖式抽屉(点遮罩/Esc/选中频道收起),不再占位;
588
- · 拖拽分隔条在窄屏没有意义(没有第二栏可分配宽度)→ 隐藏;
589
- · 触摸目标 ≥40px、标签 ≥11.5px(手持 30cm 距离下 10px 只剩 6px 有效字号)。
590
-
591
- ⚠️ 断点数值与 main.css v5 / useResponsive.ts 一致(1024 = 三栏仪表台下限),
592
- 此处不再引入新魔数。桌面(≥1024)样式完全不受影响。
593
- ══════════════════════════════════════════════════════════════════════════ */
594
- @media (max-width: 1023.98px) {
595
- .harness {
596
- /* 窄屏收掉壳体外留白,把纵向像素让给内容与底部 Composer */
597
- height: calc(100dvh - var(--app-header-h, 56px) - var(--app-footer-h, 46px) - 8px);
598
- }
599
-
600
- .topbar {
601
- flex-wrap: wrap;
602
- gap: 6px 8px;
603
- padding: 8px 10px;
604
- }
605
-
606
- .topbar .left {
607
- row-gap: 8px;
608
- }
609
-
610
- .ws-name {
611
- max-width: 40vw;
612
- font-size: 15px;
613
- }
614
-
615
- .chan-chip {
616
- max-width: 34vw;
617
- font-size: 12px;
618
- }
619
-
620
- /* 切换条独占一行:五个视图名不再挤掉工作区名(channel 名仍可见) */
621
- .view-switch {
622
- flex: 1 1 100%;
623
- margin-left: 0;
624
- }
625
-
626
- .ws-state,
627
- .seq {
628
- font-size: 11.5px;
629
- }
630
-
631
- /* 触摸目标:26px 的幽灵图标钮在手持设备上点不中 */
632
- .pane-toggle {
633
- width: 40px;
634
- height: 40px;
635
- font-size: 18px;
636
- }
637
-
638
- .toggle-left-desk,
639
- .toggle-right-desk {
640
- display: none;
641
- }
642
-
643
- .main {
644
- position: relative;
645
- }
646
-
647
- .left-pane {
648
- position: absolute;
649
- top: 0;
650
- bottom: 0;
651
- left: 0;
652
- z-index: 30;
653
- width: min(86vw, 320px);
654
- border-right: 1px solid var(--line);
655
- box-shadow: var(--shadow-float);
656
- animation: pane-in 0.18s cubic-bezier(0.22, 1, 0.36, 1);
657
- }
658
-
659
- .drawer-scrim {
660
- position: absolute;
661
- inset: 0;
662
- z-index: 25;
663
- background: color-mix(in srgb, var(--ink) 34%, transparent);
664
- }
665
-
666
- /* 窄屏没有第二栏可分宽度,拖拽条只会白占 9px */
667
- .pane-splitter {
668
- display: none;
669
- }
670
-
671
- .pane-loading {
672
- font-size: 13px;
673
- }
674
- }
675
-
676
- /* 窄屏专属入口在桌面不出现(响应式形态差异不用 JS 表达,避免水合抖动) */
677
- .toggle-left-narrow {
678
- display: none;
679
- }
680
-
681
- @media (max-width: 1023.98px) {
682
- .toggle-left-narrow {
683
- display: inline-flex;
684
- }
685
- }
686
-
687
- @media (prefers-reduced-motion: reduce) {
688
- .left-pane {
689
- animation: none;
690
- }
691
- }
692
-
693
- @keyframes pane-in {
694
- from {
695
- opacity: 0.5;
696
- transform: translateX(-16px);
697
- }
698
-
699
- to {
700
- opacity: 1;
701
- transform: none;
702
- }
703
- }
704
- </style>
1
+ <script setup lang="ts">
2
+ /**
3
+ * Workspace 主控台(Zcode 风格 Harness):
4
+ * 顶栏(WS 状态 + seq + 视图切换)+ 左栏 Channel 会话 + 中部三视图
5
+ * (时间线 / Agent lanes / 任务板)+ 右侧 Inspector + 底部 Composer +
6
+ * Agent/Task 双抽屉(执行详情)。
7
+ * 挂载 workspace 全部 channel 的 WS 订阅;聚焦 channel 驱动中部/右侧上下文。
8
+ */
9
+ import { useWorkspacesStore } from '../../../stores/workshop/workspaces'
10
+ import { useEntitiesStore } from '../../../stores/workshop/entities'
11
+ import { useWorkshopWs } from '../../../composables/workshop/useWorkshopWs'
12
+ import { useUserStore } from '../../../stores/workshop/user'
13
+ import { useStorage } from '@vueuse/core'
14
+ import { useResponsive } from '../../../composables/useResponsive'
15
+
16
+ const { t } = useI18n()
17
+
18
+ definePageMeta({ layout: 'default' })
19
+
20
+ const route = useRoute()
21
+ const wsId = computed(() => String(route.params.wsId))
22
+ const userStore = useUserStore()
23
+ const wsStore = useWorkspacesStore()
24
+
25
+ // 用户守卫 + workspace 服务端加载
26
+ // SSR 阶段不判登录(会话恢复是客户端插件,服务端无登录态——同步踢回会把
27
+ // 刷新/直达 URL 的已登录用户误弹回总览);客户端挂载后校验并按需加载
28
+ const authReady = ref(false)
29
+ onMounted(() => {
30
+ if (!userStore.isLoggedIn) {
31
+ navigateTo('/workshop')
32
+ return
33
+ }
34
+ authReady.value = true
35
+ if (!wsStore.loaded) wsStore.load().catch(() => {})
36
+ })
37
+ const entities = useEntitiesStore()
38
+ const { subscribe, unsubscribe, conn } = useWorkshopWs()
39
+
40
+ // 视口档位(唯一判据;SSR 期返回桌面档)——
41
+ // 窄屏(≤1023)三栏仪表台只剩 ~460px 画布,必须换成「单通道示波器」形态
42
+ const { isDesktop } = useResponsive()
43
+
44
+ const workspace = computed(() => wsStore.workspaces.find(w => w.id === wsId.value))
45
+ // 聚焦 channel:activeChannelId 须在挂载清单内(陈旧持久化/竞态下回退首频道),
46
+ // 避免订阅死频道 → 快照永不到达 → 右栏/时间线长时间"空数据"假象
47
+ const channelId = computed(() => {
48
+ const ws = workspace.value
49
+ if (!ws) return undefined
50
+ const ids = ws.channelIds
51
+ const active = ws.activeChannelId
52
+ return active && ids.includes(active) ? active : ids[0]
53
+ })
54
+
55
+ // 订阅生命周期:workspace 挂载的 channel 变化 → 增量 sub/unsub
56
+ watch(
57
+ () => [wsId.value, workspace.value?.channelIds.join(',') ?? ''],
58
+ () => {
59
+ const mounted = new Set(workspace.value?.channelIds ?? [])
60
+ for (const id of mounted) subscribe(id)
61
+ },
62
+ { immediate: true },
63
+ )
64
+ watch(
65
+ () => workspace.value?.channelIds.join(',') ?? '',
66
+ (_next, prev) => {
67
+ if (prev === undefined) return
68
+ const mounted = new Set(workspace.value?.channelIds ?? [])
69
+ for (const prevId of prev.split(',').filter(Boolean)) {
70
+ if (!mounted.has(prevId)) unsubscribe(prevId)
71
+ }
72
+ },
73
+ )
74
+ onBeforeUnmount(() => {
75
+ for (const id of workspace.value?.channelIds ?? []) unsubscribe(id)
76
+ })
77
+
78
+ const stateColor = computed(() =>
79
+ conn.state === 'open' ? 'var(--tone-success-dot)' : conn.state === 'connecting' ? 'var(--tone-warning-dot)' : 'var(--tone-danger-dot)',
80
+ )
81
+ const lastSeq = computed(() => (channelId.value ? conn.cursors[channelId.value] ?? 0 : 0))
82
+
83
+ // 视图切换(P1 三视图 + P2 多通道同屏 + P5 RPG 小镇)
84
+ // 深链:?view=lanes/board/split/town 直达指定视图(可分享/收藏)
85
+ // inspector 只在窄屏出现(桌面它是右侧常驻栏),故不进深链白名单
86
+ type CenterView = 'timeline' | 'lanes' | 'board' | 'split' | 'town' | 'inspector'
87
+ const VIEW_KEYS: Record<string, CenterView> = {
88
+ 1: 'timeline',
89
+ 2: 'lanes',
90
+ 3: 'board',
91
+ 4: 'split',
92
+ 5: 'town',
93
+ }
94
+ const VIEW_VALUES = new Set(['timeline', 'lanes', 'board', 'split', 'town'])
95
+ const initView = route.query.view
96
+ const view = ref<CenterView>(
97
+ typeof initView === 'string' && VIEW_VALUES.has(initView) ? initView as CenterView : 'timeline',
98
+ )
99
+ const viewOptions = computed(() => {
100
+ const base = [
101
+ { value: 'timeline', label: t('wsView.k3otu32010') },
102
+ { value: 'lanes', label: 'Agent lanes' },
103
+ { value: 'board', label: t('wsView.k3ko7a8011') },
104
+ { value: 'split', label: t('wsView.k3xbmo012') },
105
+ { value: 'town', label: t('wsView.k1cz0pbw013') },
106
+ ]
107
+ // 窄屏「一次一区」:检查器不占侧栏,并入切换条(四区都由同一条承载)
108
+ return narrowUI.value ? [...base, { value: 'inspector', label: t('wsView.inspector') }] : base
109
+ })
110
+ // 数字快捷键 1-5 直切视图(非输入焦点时;控制台型键盘操作与 ⌘K 面板同一取向)
111
+ const onViewKey = (ev: KeyboardEvent): void => {
112
+ // 窄屏抽屉:Esc 收起(与全站侧栏抽屉一致)
113
+ if (ev.key === 'Escape' && narrowUI.value && leftOpen.value) {
114
+ leftOpen.value = false
115
+ return
116
+ }
117
+ if (ev.metaKey || ev.ctrlKey || ev.altKey) return
118
+ const t = ev.target as HTMLElement | null
119
+ if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return
120
+ const next = VIEW_KEYS[ev.key]
121
+ if (!next || !channelId.value) return
122
+ ev.preventDefault()
123
+ view.value = next
124
+ }
125
+ onMounted(() => window.addEventListener('keydown', onViewKey))
126
+ onBeforeUnmount(() => window.removeEventListener('keydown', onViewKey))
127
+
128
+ // 侧栏折叠(现代 harness 布局:左会话栏 / 右检查器可按需收起)
129
+ // 形态差异只在挂载后生效:SSR/首帧一律按桌面结构渲染,客户端接管后才翻档,
130
+ // 否则服务端 5 项切换条 vs 客户端 6 项会触发水合告警
131
+ const mounted = ref(false)
132
+ onMounted(() => {
133
+ mounted.value = true
134
+ })
135
+ const narrowUI = computed(() => mounted.value && !isDesktop.value)
136
+
137
+ const leftOpen = ref(true)
138
+ const rightOpen = ref(true)
139
+ /** 窄屏:左右侧栏都不再占位(收成覆盖式抽屉 / 并入切换条) */
140
+ const applyTier = (desktop: boolean): void => {
141
+ leftOpen.value = desktop
142
+ rightOpen.value = desktop
143
+ // 检查器只是窄屏的第 4 区:回桌面后它回到右栏,中部得有自己的区(否则中部空白)
144
+ if (desktop && view.value === 'inspector') view.value = 'timeline'
145
+ }
146
+ onMounted(() => {
147
+ if (!isDesktop.value) applyTier(false)
148
+ })
149
+ watch(isDesktop, d => applyTier(d))
150
+ // 抽屉里选中频道后自动收起(窄屏少一次手动关闭)
151
+ watch(channelId, (next, prev) => {
152
+ if (prev !== undefined && next !== prev && narrowUI.value) leftOpen.value = false
153
+ })
154
+
155
+ // 侧栏宽度拖拽调节(PaneSplitter;localStorage 持久化,双击复位到默认值)
156
+ const LEFT_W_DEFAULT = 248
157
+ const RIGHT_W_DEFAULT = 300
158
+ const leftWidth = useStorage('aw.harness.leftW', LEFT_W_DEFAULT)
159
+ const rightWidth = useStorage('aw.harness.rightW', RIGHT_W_DEFAULT)
160
+ const resizeLeft = (d: number): void => {
161
+ leftWidth.value = Math.min(460, Math.max(220, leftWidth.value + d))
162
+ }
163
+ const resizeRight = (d: number): void => {
164
+ rightWidth.value = Math.min(560, Math.max(240, rightWidth.value - d))
165
+ }
166
+ // 初始化消毒:陈旧持久化值(超出合法范围/异常类型)夹取回默认邻域,防布局被历史脏数据撑坏
167
+ resizeLeft(0)
168
+ resizeRight(0)
169
+
170
+ // 抽屉状态(P1)
171
+ const agentDrawerOpen = ref(false)
172
+ const agentDrawerId = ref<string | null>(null)
173
+ const openAgent = (id: string): void => {
174
+ agentDrawerId.value = id
175
+ agentDrawerOpen.value = true
176
+ }
177
+ /** 活动条入口:跨 channel 的 busy 成员 → 先聚焦其 channel 再开抽屉 */
178
+ const openAgentInChannel = (target: { channelId: string, agentId: string }): void => {
179
+ wsStore.setActiveChannel(wsId.value, target.channelId)
180
+ openAgent(target.agentId)
181
+ }
182
+ /** @提及 pill 点击入口(ClusterRoute/ClusterStream inject;时间线与 lanes 全树可用) */
183
+ provide('aw:open-agent', openAgentInChannel)
184
+ const taskDrawerOpen = ref(false)
185
+ const taskDrawerId = ref<string | null>(null)
186
+ const openTask = (id: string): void => {
187
+ taskDrawerId.value = id
188
+ taskDrawerOpen.value = true
189
+ }
190
+
191
+ // ⌘K 命令面板 + A2A 调试器(P2)
192
+ const paletteOpen = ref(false)
193
+ const a2aDebugOpen = ref(false)
194
+ const composerBox = ref<HTMLElement | null>(null)
195
+ const focusComposer = (): void => {
196
+ composerBox.value?.querySelector('textarea')?.focus()
197
+ }
198
+
199
+ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop` })
200
+ </script>
201
+
202
+ <template>
203
+ <div class="harness">
204
+ <!-- @提及悬停信息卡(文档级委托;时间线/lanes 全树生效) -->
205
+ <workshop-mention-hover-card />
206
+ <!-- 顶栏 -->
207
+ <div class="topbar">
208
+ <div
209
+ class="left"
210
+ tabindex="-1"
211
+ @keydown="onViewKey"
212
+ >
213
+ <span class="topbar-mark i-tabler-box" />
214
+ <span class="ws-name">{{ workspace?.name ?? $t('wsView.unknownWs') }}</span>
215
+ <span
216
+ v-if="channelId"
217
+ class="chan-chip"
218
+ :title="channelId"
219
+ >
220
+ <span class="chan-hash">#</span>{{ entities.channels[channelId]?.name ?? channelId.slice(0, 8) }}
221
+ </span>
222
+ <a-segmented
223
+ v-if="channelId"
224
+ v-model:value="view"
225
+ size="small"
226
+ :options="viewOptions"
227
+ class="view-switch"
228
+ :title="$t('wsView.kqwckjr001')"
229
+ @keydown="onViewKey"
230
+ />
231
+ </div>
232
+ <div class="right">
233
+ <!-- 窄屏:频道会话列表入口(覆盖式抽屉;该页一次只看一区) -->
234
+ <button
235
+ v-if="narrowUI"
236
+ class="pane-toggle im toggle-left-narrow"
237
+ :class="{ off: !leftOpen }"
238
+ :title="$t('wsView.channelListTitle')"
239
+ @click="leftOpen = !leftOpen"
240
+ >
241
+ <span class="i-tabler-list-details im-pop" />
242
+ </button>
243
+ <button
244
+ class="pane-toggle im toggle-left-desk"
245
+ :class="{ off: !leftOpen }"
246
+ :title="$t('wsView.k1tsy2e4002')"
247
+ @click="leftOpen = !leftOpen"
248
+ >
249
+ <span class="i-tabler-layout-sidebar-left-collapse im-pop" />
250
+ </button>
251
+ <button
252
+ class="pane-toggle im toggle-right-desk"
253
+ :class="{ off: !rightOpen }"
254
+ :title="$t('wsView.k1tx0ppf003')"
255
+ @click="rightOpen = !rightOpen"
256
+ >
257
+ <span class="i-tabler-layout-sidebar-right-collapse im-pop" />
258
+ </button>
259
+ <button
260
+ class="pane-toggle im"
261
+ :title="$t('wsView.a2aTitle')"
262
+ @click="a2aDebugOpen = true"
263
+ >
264
+ <span class="i-tabler-terminal-2 im-pop" />
265
+ </button>
266
+ <button
267
+ class="pane-toggle im"
268
+ :title="$t('wsView.k1cvg8sb004')"
269
+ @click="paletteOpen = true"
270
+ >
271
+ <span class="i-tabler-command im-pop" />
272
+ </button>
273
+ <span
274
+ class="dot"
275
+ :style="{ background: stateColor }"
276
+ />
277
+ <span
278
+ class="ws-state"
279
+ :data-state="conn.state"
280
+ >{{ conn.state }}</span>
281
+ <span class="seq">seq {{ lastSeq }}</span>
282
+ </div>
283
+ </div>
284
+
285
+ <!-- 主体三栏(左/右侧栏可折叠 + 拖拽调宽;分隔条 hairline 即面板边界) -->
286
+ <div class="main">
287
+ <!-- 窄屏抽屉遮罩:点空白收起(不盖住底部 Composer,输入不被"锁在"抽屉后) -->
288
+ <div
289
+ v-if="narrowUI && leftOpen"
290
+ class="drawer-scrim"
291
+ @click="leftOpen = false"
292
+ />
293
+ <div
294
+ v-if="leftOpen"
295
+ class="left-pane"
296
+ :style="{ flexBasis: narrowUI ? 'auto' : `${leftWidth}px` }"
297
+ >
298
+ <div class="left-scroll">
299
+ <workshop-channel-session-list :ws-id="wsId" />
300
+ </div>
301
+ <workshop-live-agent-bar
302
+ :ws-id="wsId"
303
+ @open-agent="openAgentInChannel"
304
+ />
305
+ </div>
306
+ <workshop-pane-splitter
307
+ v-if="leftOpen && !narrowUI"
308
+ :label="$t('wsView.k1fkfvra005')"
309
+ @resize="resizeLeft"
310
+ @reset="leftWidth = LEFT_W_DEFAULT"
311
+ />
312
+ <div class="center-pane">
313
+ <template v-if="channelId">
314
+ <workshop-transcript-timeline
315
+ v-if="view === 'timeline'"
316
+ :channel-id="channelId"
317
+ />
318
+ <workshop-agent-lanes-view
319
+ v-else-if="view === 'lanes'"
320
+ :channel-id="channelId"
321
+ />
322
+ <workshop-task-board-view
323
+ v-else-if="view === 'board'"
324
+ :channel-id="channelId"
325
+ @open-task="openTask"
326
+ />
327
+ <workshop-multi-channel-view
328
+ v-else-if="view === 'split'"
329
+ :ws-id="wsId"
330
+ @open-task="openTask"
331
+ />
332
+ <workshop-town-view
333
+ v-else-if="view === 'town'"
334
+ :channel-id="channelId"
335
+ />
336
+ <!-- 窄屏第 4 区:检查器并入切换条(桌面仍是右侧常驻栏) -->
337
+ <workshop-inspector-panel
338
+ v-else-if="view === 'inspector' && narrowUI"
339
+ :channel-id="channelId"
340
+ @open-agent="openAgent"
341
+ @open-task="openTask"
342
+ />
343
+ </template>
344
+ <div
345
+ v-else
346
+ class="pane-empty"
347
+ >
348
+ <span class="pe-icon i-tabler-messages" />
349
+ <div class="pe-title">
350
+ {{ $t('wsView.klzbtyp007') }} <span class="aw-serif-accent-italic">Channel</span> {{ $t('wsView.k3zaj4008') }}
351
+ </div>
352
+ <div class="pe-sub">
353
+ {{ $t('wsView.k1us0cxy009') }}
354
+ </div>
355
+ </div>
356
+ </div>
357
+ <workshop-pane-splitter
358
+ v-if="rightOpen && !narrowUI"
359
+ :label="$t('wsView.kwqb0st006')"
360
+ @resize="resizeRight"
361
+ @reset="rightWidth = RIGHT_W_DEFAULT"
362
+ />
363
+ <div
364
+ v-if="rightOpen && !narrowUI"
365
+ class="right-pane"
366
+ :class="{ empty: wsStore.loaded && !channelId }"
367
+ :style="{ flexBasis: `${rightWidth}px` }"
368
+ >
369
+ <workshop-inspector-panel
370
+ v-if="channelId"
371
+ :channel-id="channelId"
372
+ @open-agent="openAgent"
373
+ @open-task="openTask"
374
+ />
375
+ <!-- 加载中/无频道的诚实降级态(workspace 列表未返回前不误判为"空") -->
376
+ <div
377
+ v-else-if="!wsStore.loaded"
378
+ class="pane-loading"
379
+ >
380
+ {{ $t('wsView.loadingWs') }}
381
+ </div>
382
+ </div>
383
+ </div>
384
+
385
+ <!-- Composer -->
386
+ <div
387
+ v-if="channelId"
388
+ ref="composerBox"
389
+ class="composer-pane"
390
+ >
391
+ <workshop-composer :channel-id="channelId" />
392
+
393
+ <!-- 抽屉 -->
394
+ <workshop-agent-inspector-drawer
395
+ v-model:open="agentDrawerOpen"
396
+ :channel-id="channelId ?? ''"
397
+ :agent-id="agentDrawerId"
398
+ />
399
+ <workshop-task-inspector-drawer
400
+ v-model:open="taskDrawerOpen"
401
+ :channel-id="channelId ?? ''"
402
+ :task-id="taskDrawerId"
403
+ />
404
+ <workshop-a2a-rpc-debugger
405
+ v-model:open="a2aDebugOpen"
406
+ :channel-id="channelId ?? ''"
407
+ />
408
+
409
+ <!-- ⌘K 命令面板 -->
410
+ <workshop-command-palette
411
+ v-model:open="paletteOpen"
412
+ :ws-id="wsId"
413
+ @set-view="view = $event"
414
+ @open-a2a-debug="a2aDebugOpen = true"
415
+ @compose="focusComposer"
416
+ />
417
+ </div>
418
+ </div>
419
+ </template>
420
+
421
+ <style scoped>
422
+ .harness {
423
+ display: flex;
424
+ flex-direction: column;
425
+ height: calc(100dvh - var(--app-header-h, 56px) - var(--app-footer-h, 46px) - 16px);
426
+ min-height: 0;
427
+ margin: 0;
428
+ border: 1px solid var(--line);
429
+ border-radius: var(--radius-shell);
430
+ overflow: hidden;
431
+ background: var(--paper-raised);
432
+ box-shadow: var(--shadow-card);
433
+ }
434
+ .topbar {
435
+ display: flex;
436
+ align-items: center;
437
+ justify-content: space-between;
438
+ padding: 8px 14px;
439
+ font-size: 13px;
440
+ background: var(--paper-raised);
441
+ border-bottom: 1px solid var(--line);
442
+ }
443
+ .left,
444
+ .right {
445
+ display: flex;
446
+ gap: 8px;
447
+ align-items: center;
448
+ min-width: 0;
449
+ }
450
+ .topbar-mark {
451
+ font-size: 15px;
452
+ color: var(--ink-faint);
453
+ }
454
+ .ws-name {
455
+ max-width: 260px;
456
+ overflow: hidden;
457
+ font-family: var(--font-display);
458
+ font-weight: 400;
459
+ font-size: 17px;
460
+ letter-spacing: -0.01em;
461
+ text-overflow: ellipsis;
462
+ white-space: nowrap;
463
+ }
464
+ /* channel 胶囊:hairline chip + serif # */
465
+ .chan-chip {
466
+ display: inline-flex;
467
+ gap: 3px;
468
+ align-items: center;
469
+ max-width: 200px;
470
+ padding: 1px 10px;
471
+ overflow: hidden;
472
+ font-size: 12px;
473
+ color: var(--ink-soft);
474
+ text-overflow: ellipsis;
475
+ white-space: nowrap;
476
+ border: 1px solid var(--line-strong);
477
+ border-radius: var(--radius-pill);
478
+ }
479
+ .chan-hash {
480
+ font-family: var(--font-display);
481
+ color: var(--ink-faint);
482
+ }
483
+ .view-switch { margin-left: 8px; }
484
+ .dot { width: 8px; height: 8px; border-radius: 50%; }
485
+ .ws-state {
486
+ font-family: var(--font-mono);
487
+ font-size: 10px;
488
+ letter-spacing: 0.1em;
489
+ text-transform: uppercase;
490
+ padding: 1px 6px;
491
+ border: 1px solid var(--line);
492
+ border-radius: var(--radius-chip);
493
+ }
494
+ .ws-state[data-state='open'] { color: var(--tone-success-dot); border-color: color-mix(in srgb, var(--tone-success-dot) 45%, transparent); }
495
+ .ws-state[data-state='connecting'] { color: var(--tone-warning-dot); border-color: color-mix(in srgb, var(--tone-warning-dot) 45%, transparent); }
496
+ .ws-state[data-state='closed'] { color: var(--tone-danger-dot); border-color: color-mix(in srgb, var(--tone-danger-dot) 45%, transparent); }
497
+ .seq {
498
+ font-family: var(--font-mono);
499
+ font-variant-numeric: tabular-nums;
500
+ font-size: 10px;
501
+ color: var(--ink-faint);
502
+ }
503
+
504
+ /* 侧栏折叠开关:幽灵图标钮 */
505
+ .pane-toggle {
506
+ display: inline-flex;
507
+ align-items: center;
508
+ justify-content: center;
509
+ width: 26px;
510
+ height: 26px;
511
+ font-size: 14px;
512
+ color: var(--ink-soft);
513
+ cursor: pointer;
514
+ background: transparent;
515
+ border: 0;
516
+ border-radius: var(--radius-panel-sm);
517
+ transition: color var(--transition-fast), background var(--transition-fast), opacity var(--transition-fast);
518
+ }
519
+ .pane-toggle:hover {
520
+ color: var(--ink);
521
+ background: var(--paper-deep);
522
+ }
523
+ .pane-toggle.off {
524
+ opacity: 0.4;
525
+ }
526
+ .pane-toggle.off:hover {
527
+ opacity: 1;
528
+ }
529
+ .main {
530
+ display: flex;
531
+ flex: 1 1 auto;
532
+ min-height: 0;
533
+ }
534
+ .left-pane {
535
+ display: flex;
536
+ flex: 0 0 auto; /* 宽度由拖拽分隔条驱动(inline flexBasis) */
537
+ flex-direction: column;
538
+ min-height: 0;
539
+ overflow: hidden;
540
+ background: var(--paper);
541
+ }
542
+ .left-scroll {
543
+ flex: 1 1 auto;
544
+ min-height: 0;
545
+ overflow: hidden auto;
546
+ }
547
+ .center-pane {
548
+ display: flex;
549
+ flex: 1 1 auto;
550
+ min-width: 0;
551
+ min-height: 0;
552
+ max-width: 100%;
553
+ overflow: hidden;
554
+ background: var(--paper); /* 灰画布:消息气泡/白色面板在此浮出(Slack 式分层) */
555
+ }
556
+ .center-pane > * {
557
+ min-width: 0;
558
+ max-width: 100%;
559
+ }
560
+ .right-pane {
561
+ flex: 0 0 auto; /* 宽度由拖拽分隔条驱动(inline flexBasis) */
562
+ min-height: 0;
563
+ overflow: hidden;
564
+ background: var(--paper);
565
+ }
566
+ .right-pane.empty { opacity: 0.35; }
567
+ /* workspace 列表加载中的诚实降级态(不算"空",不淡化整栏) */
568
+ .pane-loading {
569
+ display: flex;
570
+ align-items: center;
571
+ justify-content: center;
572
+ height: 100%;
573
+ font-size: 12px;
574
+ color: var(--ink-faint);
575
+ }
576
+ .composer-pane { flex: 0 0 auto; }
577
+
578
+ /* ══════════════════════════════════════════════════════════════════════════
579
+ 窄屏形态 · 单通道示波器(≤1023px) —— 一次只看一路信号
580
+ ──────────────────────────────────────────────────────────────────────────
581
+ 桌面是「三栏仪表台」:会话栏 248 + 画布 + 检查器 300 + 两条分隔条。
582
+ 到了 ≤1023,画布只剩 ~460px(900–1023 还要再让 64px 图标轨),
583
+ 硬塞的结果是三栏互相压扁。所以窄屏换一台仪器,而不是把仪表台缩小:
584
+
585
+ · 中部视图(时间线 / lanes / 任务板 / 检查器)→ 由顶栏切换条承载,
586
+ 一次只挂载一区,顶部切换条独占一行、可横扫;
587
+ · 频道会话列表 → 覆盖式抽屉(点遮罩/Esc/选中频道收起),不再占位;
588
+ · 拖拽分隔条在窄屏没有意义(没有第二栏可分配宽度)→ 隐藏;
589
+ · 触摸目标 ≥40px、标签 ≥11.5px(手持 30cm 距离下 10px 只剩 6px 有效字号)。
590
+
591
+ ⚠️ 断点数值与 main.css v5 / useResponsive.ts 一致(1024 = 三栏仪表台下限),
592
+ 此处不再引入新魔数。桌面(≥1024)样式完全不受影响。
593
+ ══════════════════════════════════════════════════════════════════════════ */
594
+ @media (max-width: 1023.98px) {
595
+ .harness {
596
+ /* 窄屏收掉壳体外留白,把纵向像素让给内容与底部 Composer */
597
+ height: calc(100dvh - var(--app-header-h, 56px) - var(--app-footer-h, 46px) - 8px);
598
+ }
599
+
600
+ .topbar {
601
+ flex-wrap: wrap;
602
+ gap: 6px 8px;
603
+ padding: 8px 10px;
604
+ }
605
+
606
+ .topbar .left {
607
+ row-gap: 8px;
608
+ }
609
+
610
+ .ws-name {
611
+ max-width: 40vw;
612
+ font-size: 15px;
613
+ }
614
+
615
+ .chan-chip {
616
+ max-width: 34vw;
617
+ font-size: 12px;
618
+ }
619
+
620
+ /* 切换条独占一行:五个视图名不再挤掉工作区名(channel 名仍可见) */
621
+ .view-switch {
622
+ flex: 1 1 100%;
623
+ margin-left: 0;
624
+ }
625
+
626
+ .ws-state,
627
+ .seq {
628
+ font-size: 11.5px;
629
+ }
630
+
631
+ /* 触摸目标:26px 的幽灵图标钮在手持设备上点不中 */
632
+ .pane-toggle {
633
+ width: 40px;
634
+ height: 40px;
635
+ font-size: 18px;
636
+ }
637
+
638
+ .toggle-left-desk,
639
+ .toggle-right-desk {
640
+ display: none;
641
+ }
642
+
643
+ .main {
644
+ position: relative;
645
+ }
646
+
647
+ .left-pane {
648
+ position: absolute;
649
+ top: 0;
650
+ bottom: 0;
651
+ left: 0;
652
+ z-index: 30;
653
+ width: min(86vw, 320px);
654
+ border-right: 1px solid var(--line);
655
+ box-shadow: var(--shadow-float);
656
+ animation: pane-in 0.18s cubic-bezier(0.22, 1, 0.36, 1);
657
+ }
658
+
659
+ .drawer-scrim {
660
+ position: absolute;
661
+ inset: 0;
662
+ z-index: 25;
663
+ background: color-mix(in srgb, var(--ink) 34%, transparent);
664
+ }
665
+
666
+ /* 窄屏没有第二栏可分宽度,拖拽条只会白占 9px */
667
+ .pane-splitter {
668
+ display: none;
669
+ }
670
+
671
+ .pane-loading {
672
+ font-size: 13px;
673
+ }
674
+ }
675
+
676
+ /* 窄屏专属入口在桌面不出现(响应式形态差异不用 JS 表达,避免水合抖动) */
677
+ .toggle-left-narrow {
678
+ display: none;
679
+ }
680
+
681
+ @media (max-width: 1023.98px) {
682
+ .toggle-left-narrow {
683
+ display: inline-flex;
684
+ }
685
+ }
686
+
687
+ @media (prefers-reduced-motion: reduce) {
688
+ .left-pane {
689
+ animation: none;
690
+ }
691
+ }
692
+
693
+ @keyframes pane-in {
694
+ from {
695
+ opacity: 0.5;
696
+ transform: translateX(-16px);
697
+ }
698
+
699
+ to {
700
+ opacity: 1;
701
+ transform: none;
702
+ }
703
+ }
704
+ </style>