agentworkshop 0.7.9 → 0.7.12

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 (534) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{BUPj10t4.js → 6vYobj4F.js} +1 -1
  3. package/.output/public/_nuxt/{BCJFMBQ6.js → B-xskWta.js} +1 -1
  4. package/.output/public/_nuxt/{BC8s1Guv.js → B3Psw_sL.js} +1 -1
  5. package/.output/public/_nuxt/B53Mp7lD.js +1 -0
  6. package/.output/public/_nuxt/{BDASpD_i.js → BBwMH_hG.js} +1 -1
  7. package/.output/public/_nuxt/BNXfwXpE.js +1 -0
  8. package/.output/public/_nuxt/BbZAzXjZ2.js +1 -0
  9. package/.output/public/_nuxt/{CFn4vHwD.js → Bc3PiGGr.js} +1 -1
  10. package/.output/public/_nuxt/BcrN7QEl2.js +1 -0
  11. package/.output/public/_nuxt/BgOhoDTI.js +1 -0
  12. package/.output/public/_nuxt/Bmpvki7M.js +1 -0
  13. package/.output/public/_nuxt/BxPPAkGA.js +3 -0
  14. package/.output/public/_nuxt/{u7KWCGSb.js → BxtpDtzC.js} +1 -1
  15. package/.output/public/_nuxt/{DxrgFxIr2.js → C4OAdbVK2.js} +1 -1
  16. package/.output/public/_nuxt/C6m-kenn.js +1 -0
  17. package/.output/public/_nuxt/CcwJ2mrD2.js +1 -0
  18. package/.output/public/_nuxt/Cj6139IM.js +1 -0
  19. package/.output/public/_nuxt/{6PZPfFcO.js → Cor9siSf.js} +1 -1
  20. package/.output/public/_nuxt/CvYfzHYz.js +1 -0
  21. package/.output/public/_nuxt/D2JyEL28.js +14 -0
  22. package/.output/public/_nuxt/{DDdEnkdw.js → D8xY-Yjc.js} +2 -2
  23. package/.output/public/_nuxt/{Ct68Z0232.js → DA4lxCWC2.js} +1 -1
  24. package/.output/public/_nuxt/{MyA0Oeqd.js → DNxVTfhe.js} +1 -1
  25. package/.output/public/_nuxt/DT-Wg8r1.js +1 -0
  26. package/.output/public/_nuxt/DTYBfjRY.js +1 -0
  27. package/.output/public/_nuxt/{DvgKXPgu2.js → DWcEk1ur2.js} +1 -1
  28. package/.output/public/_nuxt/DaE5saZg.js +1 -0
  29. package/.output/public/_nuxt/Dhqra7NB.js +1 -0
  30. package/.output/public/_nuxt/DlRO_q5L.js +1 -0
  31. package/.output/public/_nuxt/{D8EfUxO2.js → Ds8JbqGB.js} +1 -1
  32. package/.output/public/_nuxt/Dsdy4Ua-.js +1 -0
  33. package/.output/public/_nuxt/{DN5RuZ8k.js → DyisHHzD.js} +1 -1
  34. package/.output/public/_nuxt/{91DZTUMJ.js → E88a-JCG.js} +1 -1
  35. package/.output/public/_nuxt/ORt9oDIy2.js +1 -0
  36. package/.output/public/_nuxt/{By8BpbKH.js → QoCzfpo2.js} +1 -1
  37. package/.output/public/_nuxt/RzXYI9FM.js +23 -0
  38. package/.output/public/_nuxt/{BuktKVqK2.js → arUY5dyq2.js} +2 -2
  39. package/.output/public/_nuxt/bAFueD6a.js +1 -0
  40. package/.output/public/_nuxt/builds/latest.json +1 -1
  41. package/.output/public/_nuxt/builds/meta/54e22640-4fd0-4fee-9077-31d4d4bb7969.json +1 -0
  42. package/.output/public/_nuxt/dcw.rpqpOYQ8.css +1 -0
  43. package/.output/public/_nuxt/{default.BR3e5fPY.css → default.BbgdTDCk.css} +1 -1
  44. package/.output/public/_nuxt/{C9cU_Ujd.js → eHYnGew0.js} +1 -1
  45. package/.output/public/_nuxt/{DM4CNkXz.js → eUGyJdiT.js} +1 -1
  46. package/.output/public/_nuxt/{entry.5qf6EqfN.css → entry.Bv_GCmPP.css} +1 -1
  47. package/.output/public/_nuxt/hb_ceMJP.js +1 -0
  48. package/.output/public/_nuxt/{B1g8pJmr.js → kkEthL84.js} +1 -1
  49. package/.output/public/_nuxt/logs.BS1sb41K.css +1 -0
  50. package/.output/public/_nuxt/pages.BkE-AfIb.css +1 -0
  51. package/.output/public/_nuxt/permissions.DTRtZeTw.css +1 -0
  52. package/.output/public/_nuxt/plugins.BcSEwwGb.css +1 -0
  53. package/.output/public/_nuxt/settings.BqMF2nfj.css +1 -0
  54. package/.output/public/_nuxt/tokens.rW1xHxRW.css +1 -0
  55. package/.output/public/_nuxt/workshop.CbymC2LY.css +1 -0
  56. package/.output/public/_nuxt/zVkqCZ78.js +1 -0
  57. package/.output/server/chunks/_/LeftOutlined.mjs +1 -1
  58. package/.output/server/chunks/_/SearchOutlined.mjs +1 -1
  59. package/.output/server/chunks/_/character-asset.repo.mjs +1 -1
  60. package/.output/server/chunks/_/host-bindings.mjs +1 -1
  61. package/.output/server/chunks/_/nitro.mjs +1549 -1228
  62. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  63. package/.output/server/chunks/_/scene-layout.repo.mjs +1 -1
  64. package/.output/server/chunks/build/Col-biaNnEPf.mjs +1 -1
  65. package/.output/server/chunks/build/{OmpTerminalPanel-COphA1br.mjs → OmpTerminalPanel-Ban3yJZ4.mjs} +4 -5
  66. package/.output/server/chunks/build/OmpTerminalPanel-Ban3yJZ4.mjs.map +1 -0
  67. package/.output/server/chunks/build/{TownView-D8DSdO3H.mjs → TownView-CunmEMzS.mjs} +8 -9
  68. package/.output/server/chunks/build/{TownView-D8DSdO3H.mjs.map → TownView-CunmEMzS.mjs.map} +1 -1
  69. package/.output/server/chunks/build/{_id_-BV4FfiYd.mjs → _id_-CVnMdSeG.mjs} +7 -7
  70. package/.output/server/chunks/build/{_id_-BV4FfiYd.mjs.map → _id_-CVnMdSeG.mjs.map} +1 -1
  71. package/.output/server/chunks/build/{_id_-TP9hvnBM.mjs → _id_-D265Wh_k.mjs} +7 -7
  72. package/.output/server/chunks/build/{_id_-TP9hvnBM.mjs.map → _id_-D265Wh_k.mjs.map} +1 -1
  73. package/.output/server/chunks/build/{_wsId_-Mm9sQSz2.mjs → _wsId_-eNJmBp05.mjs} +17 -17
  74. package/.output/server/chunks/build/{_wsId_-Mm9sQSz2.mjs.map → _wsId_-eNJmBp05.mjs.map} +1 -1
  75. package/.output/server/chunks/build/{agents-To3a5xcZ.mjs → agents-B10wmFri.mjs} +9 -9
  76. package/.output/server/chunks/build/{agents-To3a5xcZ.mjs.map → agents-B10wmFri.mjs.map} +1 -1
  77. package/.output/server/chunks/build/alert-qJnjyRcP.mjs +1 -1
  78. package/.output/server/chunks/build/api-error-dul30BYQ.mjs +1 -1
  79. package/.output/server/chunks/build/button-DEcZFcqN.mjs +2 -2
  80. package/.output/server/chunks/build/{card-Dipvaykh.mjs → card-CCPQoKlh.mjs} +4 -4
  81. package/.output/server/chunks/build/{card-Dipvaykh.mjs.map → card-CCPQoKlh.mjs.map} +1 -1
  82. package/.output/server/chunks/build/{channel-templates-DRsP-qxl.mjs → channel-templates-CpWxVk5A.mjs} +7 -7
  83. package/.output/server/chunks/build/{channel-templates-DRsP-qxl.mjs.map → channel-templates-CpWxVk5A.mjs.map} +1 -1
  84. package/.output/server/chunks/build/{checkbox-dzcpU_Cd.mjs → checkbox-Dv1kjaUZ.mjs} +3 -3
  85. package/.output/server/chunks/build/{checkbox-dzcpU_Cd.mjs.map → checkbox-Dv1kjaUZ.mjs.map} +1 -1
  86. package/.output/server/chunks/build/client-only-D5BzSQc6.mjs +1 -1
  87. package/.output/server/chunks/build/{daq-_hCEaYLd.mjs → daq-DxaXk_8N.mjs} +9 -10
  88. package/.output/server/chunks/build/daq-DxaXk_8N.mjs.map +1 -0
  89. package/.output/server/chunks/build/{dcw-C7pJ4Y0p.mjs → dcw-B0dtiHFG.mjs} +47 -47
  90. package/.output/server/chunks/build/dcw-B0dtiHFG.mjs.map +1 -0
  91. package/.output/server/chunks/build/{default-Dnjs36e8.mjs → default-DdV642BY.mjs} +41 -13
  92. package/.output/server/chunks/build/{default-Dnjs36e8.mjs.map → default-DdV642BY.mjs.map} +1 -1
  93. package/.output/server/chunks/build/drawer-Cmh90erI.mjs +1 -1
  94. package/.output/server/chunks/build/dropdown-vhn1NPFs.mjs +2 -2
  95. package/.output/server/chunks/build/hasIn-vQ9LWt-J.mjs +554 -0
  96. package/.output/server/chunks/build/hasIn-vQ9LWt-J.mjs.map +1 -0
  97. package/.output/server/chunks/build/initDefaultProps-DIbWwP4R.mjs +1 -1
  98. package/.output/server/chunks/build/{input-sf_BT6o6.mjs → input-DxDqeyQW.mjs} +5 -553
  99. package/.output/server/chunks/build/input-DxDqeyQW.mjs.map +1 -0
  100. package/.output/server/chunks/build/{input-number-Bd55elNN.mjs → input-number-CZ3ZrCsy.mjs} +3 -3
  101. package/.output/server/chunks/build/{input-number-Bd55elNN.mjs.map → input-number-CZ3ZrCsy.mjs.map} +1 -1
  102. package/.output/server/chunks/build/{logs-C039TFW1.mjs → logs-Crf6f1IC.mjs} +39 -40
  103. package/.output/server/chunks/build/logs-Crf6f1IC.mjs.map +1 -0
  104. package/.output/server/chunks/build/menu-WCrlpYVh.mjs +2 -2
  105. package/.output/server/chunks/build/modal-yvUquPj5.mjs +2 -2
  106. package/.output/server/chunks/build/{monitor-MIOvsyLa.mjs → monitor-miqq9xHB.mjs} +11 -11
  107. package/.output/server/chunks/build/{monitor-MIOvsyLa.mjs.map → monitor-miqq9xHB.mjs.map} +1 -1
  108. package/.output/server/chunks/build/{pages-C_VSGSGo.mjs → pages-BmRUbGUi.mjs} +32 -22
  109. package/.output/server/chunks/build/pages-BmRUbGUi.mjs.map +1 -0
  110. package/.output/server/chunks/build/permissions-CxEgXic0.mjs +534 -0
  111. package/.output/server/chunks/build/permissions-CxEgXic0.mjs.map +1 -0
  112. package/.output/server/chunks/build/{plugins-4FMWkN8d.mjs → plugins-wLIKHcIE.mjs} +19 -20
  113. package/.output/server/chunks/build/{plugins-4FMWkN8d.mjs.map → plugins-wLIKHcIE.mjs.map} +1 -1
  114. package/.output/server/chunks/build/popconfirm-CRaHcz5O.mjs +2 -2
  115. package/.output/server/chunks/build/responsiveObserve-CS0UB6wQ.mjs +1 -1
  116. package/.output/server/chunks/build/{segmented-DPe9ZzLO.mjs → segmented-ChmQZwz6.mjs} +5 -4
  117. package/.output/server/chunks/build/{segmented-DPe9ZzLO.mjs.map → segmented-ChmQZwz6.mjs.map} +1 -1
  118. package/.output/server/chunks/build/select-D-CeE5vY.mjs +1 -1
  119. package/.output/server/chunks/build/{settings-DA8IkGLo.mjs → settings-BPBll_7K.mjs} +28 -26
  120. package/.output/server/chunks/build/settings-BPBll_7K.mjs.map +1 -0
  121. package/.output/server/chunks/build/space-CCBYAkXt.mjs +2 -2
  122. package/.output/server/chunks/build/spin-HVY81X_L.mjs +1 -1
  123. package/.output/server/chunks/build/switch-CelRXpcy.mjs +2 -2
  124. package/.output/server/chunks/build/{table-Cswq-l2r.mjs → table-BtksyDlQ.mjs} +5 -5
  125. package/.output/server/chunks/build/{table-Cswq-l2r.mjs.map → table-BtksyDlQ.mjs.map} +1 -1
  126. package/.output/server/chunks/build/{tabs-BM3tAh_J.mjs → tabs-DQv-_-R6.mjs} +3 -3
  127. package/.output/server/chunks/build/{tabs-BM3tAh_J.mjs.map → tabs-DQv-_-R6.mjs.map} +1 -1
  128. package/.output/server/chunks/build/{tag-NO9jEqKy.mjs → tag-A0tZ2Ouq.mjs} +4 -4
  129. package/.output/server/chunks/build/{tag-NO9jEqKy.mjs.map → tag-A0tZ2Ouq.mjs.map} +1 -1
  130. package/.output/server/chunks/build/{teams-DA1lkf1n.mjs → teams-CNflUtPh.mjs} +7 -7
  131. package/.output/server/chunks/build/{teams-DA1lkf1n.mjs.map → teams-CNflUtPh.mjs.map} +1 -1
  132. package/.output/server/chunks/build/{tokens-DKMvvk3e.mjs → tokens-BKLzcdMg.mjs} +53 -84
  133. package/.output/server/chunks/build/tokens-BKLzcdMg.mjs.map +1 -0
  134. package/.output/server/chunks/build/town-BUqRobze.mjs +1 -1
  135. package/.output/server/chunks/build/{town-D0thZ7PK.mjs → town-BbaR-8io.mjs} +9 -10
  136. package/.output/server/chunks/build/town-BbaR-8io.mjs.map +1 -0
  137. package/.output/server/chunks/build/{useDaqStream-BVTRw8_H.mjs → useDaqStream-BkmyQT06.mjs} +34 -23
  138. package/.output/server/chunks/build/useDaqStream-BkmyQT06.mjs.map +1 -0
  139. package/.output/server/chunks/build/{useDcwStream-DNsivqI_.mjs → useDcwStream-DWVoU96J.mjs} +60 -46
  140. package/.output/server/chunks/build/useDcwStream-DWVoU96J.mjs.map +1 -0
  141. package/.output/server/chunks/build/{useDeviceTwins-WeIom7A6.mjs → useDeviceTwins-34WGk9aN.mjs} +2 -2
  142. package/.output/server/chunks/build/{useDeviceTwins-WeIom7A6.mjs.map → useDeviceTwins-34WGk9aN.mjs.map} +1 -1
  143. package/.output/server/chunks/build/useLocalTime-9a4B-aHv.mjs +1 -1
  144. package/.output/server/chunks/build/{useOpsLog-DcKnPO43.mjs → useOpsLog-C9zTzYof.mjs} +27 -15
  145. package/.output/server/chunks/build/useOpsLog-C9zTzYof.mjs.map +1 -0
  146. package/.output/server/chunks/build/useSiteConfig-DJe_KMVr.mjs +1 -1
  147. package/.output/server/chunks/build/useWorkshopApi-D2gq06N2.mjs +1 -1
  148. package/.output/server/chunks/build/{useWorkshopWs-B3j5wIAK.mjs → useWorkshopWs-CprkZEdq.mjs} +2 -3
  149. package/.output/server/chunks/build/useWorkshopWs-CprkZEdq.mjs.map +1 -0
  150. package/.output/server/chunks/build/{users-CmVvb1oq.mjs → users-D6RNylrW.mjs} +10 -10
  151. package/.output/server/chunks/build/{users-CmVvb1oq.mjs.map → users-D6RNylrW.mjs.map} +1 -1
  152. package/.output/server/chunks/build/{workshop-BhWnJMWs.mjs → workshop-CTcYDq2i.mjs} +29 -24
  153. package/.output/server/chunks/build/{workshop-BhWnJMWs.mjs.map → workshop-CTcYDq2i.mjs.map} +1 -1
  154. package/.output/server/chunks/build/{workspaces-IfORCDJE.mjs → workspaces-BuydxM-3.mjs} +2 -3
  155. package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +1 -0
  156. package/.output/server/chunks/routes/api/health.get.mjs +1 -1
  157. package/.output/server/chunks/routes/api/index.get.mjs +1 -1
  158. package/.output/server/chunks/routes/api/index.post.mjs +1 -1
  159. package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
  160. package/.output/server/chunks/routes/api/metrics.mjs +1 -1
  161. package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs +1 -1
  162. package/.output/server/chunks/routes/api/plugins/client/_name_.get.mjs +1 -1
  163. package/.output/server/chunks/routes/api/plugins/manifest.get.mjs +1 -1
  164. package/.output/server/chunks/routes/api/system/config.get.mjs +1 -1
  165. package/.output/server/chunks/routes/api/system/index.get.mjs +1 -1
  166. package/.output/server/chunks/routes/api/system/index.patch.mjs +1 -1
  167. package/.output/server/chunks/routes/api/system/monitor/terminal/ws.mjs +1 -1
  168. package/.output/server/chunks/routes/api/system/monitor/terminate.post.mjs +1 -1
  169. package/.output/server/chunks/routes/api/system/monitor.get.mjs +1 -1
  170. package/.output/server/chunks/routes/api/system/settings/events.get.mjs +1 -1
  171. package/.output/server/chunks/routes/api/system/settings/reset.post.mjs +1 -1
  172. package/.output/server/chunks/routes/api/users/_id_.delete.mjs +1 -1
  173. package/.output/server/chunks/routes/api/users/_id_.get.mjs +1 -1
  174. package/.output/server/chunks/routes/api/users/_id_.put.mjs +1 -1
  175. package/.output/server/chunks/routes/api/users/index.get.mjs +1 -1
  176. package/.output/server/chunks/routes/api/users/index.post.mjs +1 -1
  177. package/.output/server/chunks/routes/api/users/login.post.mjs +1 -1
  178. package/.output/server/chunks/routes/api/users/logout.post.mjs +1 -1
  179. package/.output/server/chunks/routes/api/users/me.get.mjs +1 -1
  180. package/.output/server/chunks/routes/api/users/register.post.mjs +1 -1
  181. package/.output/server/chunks/routes/api/users/setup-status.get.mjs +1 -1
  182. package/.output/server/chunks/routes/api/users/tokens/_id/token.get.mjs +1 -1
  183. package/.output/server/chunks/routes/api/users/tokens/_id_.delete.mjs +1 -1
  184. package/.output/server/chunks/routes/api/users/tokens/_id_.patch.mjs +1 -1
  185. package/.output/server/chunks/routes/api/workshop/_...path_.mjs +1 -1
  186. package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.get.mjs +1 -1
  187. package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.post.mjs +1 -1
  188. package/.output/server/chunks/routes/api/workshop/a2a/send.post.mjs +1 -1
  189. package/.output/server/chunks/routes/api/workshop/agent-tools/approvals/_id/decide.post.mjs +1 -1
  190. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.delete.mjs +1 -1
  191. package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.patch.mjs +1 -1
  192. package/.output/server/chunks/routes/api/workshop/agent-tools/index.get.mjs +1 -1
  193. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +19 -8
  194. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs.map +1 -1
  195. package/.output/server/chunks/routes/api/workshop/agent-tools/index2.get.mjs +1 -1
  196. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +2 -2
  197. package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs +2 -2
  198. package/.output/server/chunks/routes/api/workshop/agents/_id_.delete.mjs +1 -1
  199. package/.output/server/chunks/routes/api/workshop/agents/_id_.get.mjs +1 -1
  200. package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +1 -1
  201. package/.output/server/chunks/routes/api/workshop/agents/subscribe.post.mjs +1 -1
  202. package/.output/server/chunks/routes/api/workshop/approvals/_id/decide.post.mjs +1 -1
  203. package/.output/server/chunks/routes/api/workshop/assets/character/_id_.delete.mjs +1 -1
  204. package/.output/server/chunks/routes/api/workshop/assets/devices/_id_.delete.mjs +1 -1
  205. package/.output/server/chunks/routes/api/workshop/assets/index.get.mjs +1 -1
  206. package/.output/server/chunks/routes/api/workshop/assets/index.post.mjs +2 -2
  207. package/.output/server/chunks/routes/api/workshop/assets/index2.get.mjs +1 -1
  208. package/.output/server/chunks/routes/api/workshop/assets/index2.post.mjs +2 -2
  209. package/.output/server/chunks/routes/api/workshop/audit.get.mjs +1 -1
  210. package/.output/server/chunks/routes/api/workshop/caller.mjs +1 -1
  211. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +3 -3
  212. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.delete.mjs +1 -1
  213. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.get.mjs +1 -1
  214. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs +1 -1
  215. package/.output/server/chunks/routes/api/workshop/channel-templates/from-channel.post.mjs +1 -1
  216. package/.output/server/chunks/routes/api/workshop/channels/_id/activate.post.mjs +2 -2
  217. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.get.mjs +1 -1
  218. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.post.mjs +1 -1
  219. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/_memoryId_.delete.mjs +1 -1
  220. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/search.post.mjs +1 -1
  221. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/model.patch.mjs +1 -1
  222. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/position.patch.mjs +1 -1
  223. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/range.patch.mjs +1 -1
  224. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/stop.post.mjs +1 -1
  225. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.delete.mjs +1 -1
  226. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs +1 -1
  227. package/.output/server/chunks/routes/api/workshop/channels/_id/events.get.mjs +1 -1
  228. package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs +1 -1
  229. package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +2 -2
  230. package/.output/server/chunks/routes/api/workshop/channels/_id/index2.get.mjs +1 -1
  231. package/.output/server/chunks/routes/api/workshop/channels/_id/index2.post.mjs +1 -1
  232. package/.output/server/chunks/routes/api/workshop/channels/_id/index3.get.mjs +1 -1
  233. package/.output/server/chunks/routes/api/workshop/channels/_id/index3.post.mjs +1 -1
  234. package/.output/server/chunks/routes/api/workshop/channels/_id/index4.get.mjs +1 -1
  235. package/.output/server/chunks/routes/api/workshop/channels/_id/index4.post.mjs +1 -1
  236. package/.output/server/chunks/routes/api/workshop/channels/_id/memories/_memoryId_.delete.mjs +1 -1
  237. package/.output/server/chunks/routes/api/workshop/channels/_id/queue.get.mjs +1 -1
  238. package/.output/server/chunks/routes/api/workshop/channels/_id/terminals.get.mjs +2 -2
  239. package/.output/server/chunks/routes/api/workshop/channels/_id_.delete.mjs +1 -1
  240. package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +2 -2
  241. package/.output/server/chunks/routes/api/workshop/channels/index.patch.mjs +1 -1
  242. package/.output/server/chunks/routes/api/workshop/channels.get.mjs +1 -1
  243. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  244. package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +7 -6
  245. package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs.map +1 -1
  246. package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs +5 -4
  247. package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs.map +1 -1
  248. package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs +5 -4
  249. package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs.map +1 -1
  250. package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +5 -4
  251. package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs.map +1 -1
  252. package/.output/server/chunks/routes/api/workshop/daq/_id/test.post.mjs +2 -2
  253. package/.output/server/chunks/routes/api/workshop/daq/_id_.delete.mjs +2 -2
  254. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +5 -4
  255. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs.map +1 -1
  256. package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs +5 -2
  257. package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs.map +1 -1
  258. package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +2 -2
  259. package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs +3 -2
  260. package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs.map +1 -1
  261. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs +2 -2
  262. package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +2 -2
  263. package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +2 -2
  264. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +2 -2
  265. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +2 -2
  266. package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +2 -2
  267. package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +7 -6
  268. package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs.map +1 -1
  269. package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +2 -2
  270. package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +2 -2
  271. package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +5 -4
  272. package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs.map +1 -1
  273. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +4 -3
  274. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
  275. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -2
  276. package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +2 -2
  277. package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +2 -2
  278. package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +2 -2
  279. package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +4 -4
  280. package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs.map +1 -1
  281. package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +8 -4
  282. package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs.map +1 -1
  283. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +4 -4
  284. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
  285. package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +2 -2
  286. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +2 -2
  287. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +2 -2
  288. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +2 -2
  289. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -4
  290. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
  291. package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +2 -2
  292. package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +7 -3
  293. package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs.map +1 -1
  294. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +3 -2
  295. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs.map +1 -1
  296. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +3 -2
  297. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs.map +1 -1
  298. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +2 -2
  299. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs +2 -2
  300. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +2 -2
  301. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +2 -2
  302. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +2 -2
  303. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +2 -2
  304. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +2 -2
  305. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +2 -2
  306. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +2 -2
  307. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/mark-good.post.mjs +2 -2
  308. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +2 -2
  309. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +2 -2
  310. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.patch.mjs +2 -2
  311. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +2 -2
  312. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +5 -3
  313. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs.map +1 -1
  314. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +2 -2
  315. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +2 -2
  316. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +2 -2
  317. package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +2 -2
  318. package/.output/server/chunks/routes/api/workshop/device-twins/_id/control.post.mjs +1 -1
  319. package/.output/server/chunks/routes/api/workshop/device-twins/_id/telemetry.post.mjs +1 -1
  320. package/.output/server/chunks/routes/api/workshop/device-twins/_id_.delete.mjs +2 -2
  321. package/.output/server/chunks/routes/api/workshop/device-twins/_id_.patch.mjs +2 -2
  322. package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs +1 -1
  323. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +2 -2
  324. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +2 -2
  325. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +2 -2
  326. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +2 -2
  327. package/.output/server/chunks/routes/api/workshop/index.get.mjs +1 -1
  328. package/.output/server/chunks/routes/api/workshop/index.post.mjs +1 -1
  329. package/.output/server/chunks/routes/api/workshop/index.put.mjs +41 -0
  330. package/.output/server/chunks/routes/api/workshop/index.put.mjs.map +1 -0
  331. package/.output/server/chunks/routes/api/workshop/index10.get.mjs +4 -3
  332. package/.output/server/chunks/routes/api/workshop/index10.get.mjs.map +1 -1
  333. package/.output/server/chunks/routes/api/workshop/index11.get.mjs +27 -0
  334. package/.output/server/chunks/routes/api/workshop/index11.get.mjs.map +1 -0
  335. package/.output/server/chunks/routes/api/workshop/index2.get.mjs +1 -1
  336. package/.output/server/chunks/routes/api/workshop/index2.post.mjs +1 -1
  337. package/.output/server/chunks/routes/api/workshop/index3.get.mjs +1 -1
  338. package/.output/server/chunks/routes/api/workshop/index3.post.mjs +4 -4
  339. package/.output/server/chunks/routes/api/workshop/index3.post.mjs.map +1 -1
  340. package/.output/server/chunks/routes/api/workshop/index4.get.mjs +5 -4
  341. package/.output/server/chunks/routes/api/workshop/index4.get.mjs.map +1 -1
  342. package/.output/server/chunks/routes/api/workshop/index4.post.mjs +2 -2
  343. package/.output/server/chunks/routes/api/workshop/index5.get.mjs +18 -11
  344. package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
  345. package/.output/server/chunks/routes/api/workshop/index5.post.mjs +2 -2
  346. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
  347. package/.output/server/chunks/routes/api/workshop/index6.post.mjs +2 -2
  348. package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
  349. package/.output/server/chunks/routes/api/workshop/index7.post.mjs +1 -1
  350. package/.output/server/chunks/routes/api/workshop/index8.get.mjs +24 -12
  351. package/.output/server/chunks/routes/api/workshop/index8.get.mjs.map +1 -1
  352. package/.output/server/chunks/routes/api/workshop/index8.post.mjs +1 -1
  353. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +12 -7
  354. package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
  355. package/.output/server/chunks/routes/api/workshop/mailbox/all.get.mjs +1 -1
  356. package/.output/server/chunks/routes/api/workshop/mailbox.get.mjs +1 -1
  357. package/.output/server/chunks/routes/api/workshop/memories/maintenance.post.mjs +1 -1
  358. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +2 -2
  359. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +2 -2
  360. package/.output/server/chunks/routes/api/workshop/plugins/index.get.mjs +1 -1
  361. package/.output/server/chunks/routes/api/workshop/runtime.get.mjs +1 -1
  362. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +2 -2
  363. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +2 -2
  364. package/.output/server/chunks/routes/api/workshop/scene/layouts.get.mjs +1 -1
  365. package/.output/server/chunks/routes/api/workshop/tasks/_id/complete.post.mjs +1 -1
  366. package/.output/server/chunks/routes/api/workshop/tasks/_id/dispatch.post.mjs +1 -1
  367. package/.output/server/chunks/routes/api/workshop/tasks/_id/index.post.mjs +1 -1
  368. package/.output/server/chunks/routes/api/workshop/tasks/_id/report.post.mjs +1 -1
  369. package/.output/server/chunks/routes/api/workshop/tasks/_id/retry.post.mjs +1 -1
  370. package/.output/server/chunks/routes/api/workshop/tasks/_id_.get.mjs +1 -1
  371. package/.output/server/chunks/routes/api/workshop/tasks.get.mjs +1 -1
  372. package/.output/server/chunks/routes/api/workshop/teams/_id/deploy.post.mjs +2 -2
  373. package/.output/server/chunks/routes/api/workshop/teams/_id/index.get.mjs +1 -1
  374. package/.output/server/chunks/routes/api/workshop/teams/_id/index.post.mjs +1 -1
  375. package/.output/server/chunks/routes/api/workshop/teams/_id/members/_agentId_.delete.mjs +1 -1
  376. package/.output/server/chunks/routes/api/workshop/teams/_id_.delete.mjs +1 -1
  377. package/.output/server/chunks/routes/api/workshop/teams/_id_.get.mjs +1 -1
  378. package/.output/server/chunks/routes/api/workshop/teams/_id_.patch.mjs +1 -1
  379. package/.output/server/chunks/routes/api/workshop/users/me.get.mjs +1 -1
  380. package/.output/server/chunks/routes/api/workshop/users/register.post.mjs +1 -1
  381. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +3 -3
  382. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.delete.mjs +1 -1
  383. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channels/_channelId_.post.mjs +1 -1
  384. package/.output/server/chunks/routes/api/workshop/workspaces/index.delete.mjs +1 -1
  385. package/.output/server/chunks/routes/renderer.mjs +3 -3
  386. package/.output/server/chunks/virtual/entry.mjs +275 -33
  387. package/.output/server/chunks/virtual/entry.mjs.map +1 -1
  388. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  389. package/.output/server/index.mjs +1 -1
  390. package/.output/server/package.json +1 -1
  391. package/README-zh.md +15 -0
  392. package/README.md +20 -0
  393. package/app/assets/css/main.css +161 -0
  394. package/app/components/AppSidebar.vue +7 -1
  395. package/app/components/AwChart.vue +12 -2
  396. package/app/composables/useRouteMeta.ts +8 -1
  397. package/app/composables/workshop/apiClient.ts +103 -95
  398. package/app/composables/workshop/useDaqStream.ts +35 -21
  399. package/app/composables/workshop/useDcwStream.ts +22 -5
  400. package/app/composables/workshop/useOpsLog.ts +26 -13
  401. package/app/composables/workshop/useWorkshopWs.ts +11 -2
  402. package/app/pages/dcw/index.vue +1079 -1079
  403. package/app/pages/index.vue +7 -2
  404. package/app/pages/logs/index.vue +621 -621
  405. package/app/pages/permissions/index.vue +330 -0
  406. package/app/pages/plugins/index.vue +6 -3
  407. package/app/pages/settings.vue +4 -2
  408. package/app/pages/tokens.vue +9 -13
  409. package/app/pages/workshop/index.vue +3 -1
  410. package/app/plugins/http.ts +18 -2
  411. package/app/plugins/session-restore.client.ts +7 -2
  412. package/app/stores/workshop/user.ts +14 -1
  413. package/i18n/locales/en.ts +1944 -1906
  414. package/i18n/locales/zh-CN.ts +1943 -1905
  415. package/package.json +1 -1
  416. package/scripts/_dbg-acc-1-setup.mjs +57 -0
  417. package/scripts/_dbg-acc-2-diag.mjs +14 -0
  418. package/scripts/_dbg-acc-2-intervals.mjs +81 -0
  419. package/scripts/_dbg-acc-3-daq.mjs +48 -0
  420. package/scripts/_dbg-acc-4-team.mjs +92 -0
  421. package/scripts/_dbg-acc-5-visual.mjs +63 -0
  422. package/scripts/_dbg-acc-5b-ws-town.mjs +18 -0
  423. package/scripts/_dbg-acc-5c-town.mjs +12 -0
  424. package/scripts/_dbg-acc-6-i18n.mjs +19 -0
  425. package/scripts/_dbg-audit-neg-e2e.mjs +64 -0
  426. package/scripts/_dbg-final-features.mjs +155 -0
  427. package/scripts/_dbg-final-fixups.mjs +62 -0
  428. package/scripts/_dbg-final-visual.mjs +29 -0
  429. package/scripts/_dbg-kicker-check.mjs +11 -0
  430. package/scripts/_dbg-perms-e2e.mjs +102 -0
  431. package/scripts/_dbg-perms-visual.mjs +36 -0
  432. package/scripts/_dbg-plugins-e2e.mjs +1 -1
  433. package/scripts/_dbg-protocol-matrix.mjs +16 -0
  434. package/scripts/_dbg-swatch-check.mjs +10 -0
  435. package/scripts/_dbg-ui-shots.mjs +28 -0
  436. package/sdk/api.mjs +117 -101
  437. package/sdk/context.mjs +240 -219
  438. package/sdk/hooks.mjs +105 -103
  439. package/sdk/index.d.mts +142 -135
  440. package/sdk/lifecycle.mjs +26 -25
  441. package/server/api/workshop/agent-tools/bindings/index.post.ts +40 -20
  442. package/server/api/workshop/daq/[id]/bind.post.ts +5 -2
  443. package/server/api/workshop/daq/[id]/frames/content.get.ts +4 -1
  444. package/server/api/workshop/daq/[id]/frames.get.ts +4 -1
  445. package/server/api/workshop/daq/[id]/samples.get.ts +4 -1
  446. package/server/api/workshop/daq/[id].patch.ts +3 -0
  447. package/server/api/workshop/daq/alarms/[id]/ack.post.ts +20 -15
  448. package/server/api/workshop/daq/controller.post.ts +2 -0
  449. package/server/api/workshop/daq/index.get.ts +5 -3
  450. package/server/api/workshop/daq/index.post.ts +2 -2
  451. package/server/api/workshop/dcw/[id]/bind.post.ts +20 -17
  452. package/server/api/workshop/dcw/[id]/test.post.ts +18 -15
  453. package/server/api/workshop/dcw/[id]/write.post.ts +3 -0
  454. package/server/api/workshop/dcw/index.get.ts +22 -9
  455. package/server/api/workshop/dcw/line/query.get.ts +38 -31
  456. package/server/api/workshop/dcw/lines/[id]/start.post.ts +3 -0
  457. package/server/api/workshop/dcw/lines/[id]/stop.post.ts +3 -0
  458. package/server/api/workshop/dcw/lines/index.get.ts +8 -2
  459. package/server/api/workshop/dcw/lines/index.post.ts +2 -2
  460. package/server/api/workshop/dcw/products/index.post.ts +16 -16
  461. package/server/api/workshop/dcw/runs/[id]/data.get.ts +20 -15
  462. package/server/api/workshop/dcw/runs/index.get.ts +13 -11
  463. package/server/api/workshop/permissions/index.get.ts +30 -0
  464. package/server/api/workshop/permissions/index.put.ts +29 -0
  465. package/server/api/workshop/ws.ts +18 -6
  466. package/server/data/daqs.json +48 -48
  467. package/server/data/dcw-products.json +21 -0
  468. package/server/data/dcw-recipes.json +84 -0
  469. package/server/data/dcw-rollback.json +94 -6
  470. package/server/data/dcw-runs.json +81 -75
  471. package/server/data/dcw-writes.json +108 -0
  472. package/server/data/dcws.json +791 -528
  473. package/server/data/line-runs.json +8 -8
  474. package/server/plugins/backup.ts +119 -116
  475. package/server/repositories/user.repository.ts +86 -6
  476. package/server/services/user.service.ts +5 -3
  477. package/server/services/workshop/daq/bus/index.ts +5 -3
  478. package/server/services/workshop/daq/bus/inproc.adapter.ts +12 -3
  479. package/server/services/workshop/daq/bus/mqtt.adapter.ts +13 -0
  480. package/server/services/workshop/daq/bus/queue-port.ts +2 -0
  481. package/server/services/workshop/daq/daq-controller.ts +18 -1
  482. package/server/services/workshop/db/ops.repo.ts +6 -0
  483. package/server/services/workshop/dcw/dcw-controller.ts +10 -2
  484. package/server/services/workshop/permissions.ts +79 -0
  485. package/server/services/workshop/plugins/host.mjs +38 -1
  486. package/server/services/workshop/scene-events.ts +25 -1
  487. package/shared/config/single-instance.mjs +24 -0
  488. package/shared/daq-protocol.ts +2 -0
  489. package/.output/public/_nuxt/B0HpdCKS.js +0 -1
  490. package/.output/public/_nuxt/B1kA8LhC.js +0 -14
  491. package/.output/public/_nuxt/B9MUiDFl.js +0 -1
  492. package/.output/public/_nuxt/B9dLFSYI.js +0 -1
  493. package/.output/public/_nuxt/BaMkWl5i2.js +0 -1
  494. package/.output/public/_nuxt/BmHvC_OU.js +0 -1
  495. package/.output/public/_nuxt/CBciOiTQ.js +0 -1
  496. package/.output/public/_nuxt/CUMdhxg9.js +0 -1
  497. package/.output/public/_nuxt/CgvGu2IO.js +0 -1
  498. package/.output/public/_nuxt/CjDQPXfv.js +0 -1
  499. package/.output/public/_nuxt/CmHzfVc5.js +0 -3
  500. package/.output/public/_nuxt/Cn3J3NXR.js +0 -23
  501. package/.output/public/_nuxt/Cst47G4-.js +0 -1
  502. package/.output/public/_nuxt/D0Szyn_D.js +0 -1
  503. package/.output/public/_nuxt/D3VJ3lex.js +0 -1
  504. package/.output/public/_nuxt/D8wyHvcz2.js +0 -1
  505. package/.output/public/_nuxt/Dqj2h40U.js +0 -1
  506. package/.output/public/_nuxt/OV6PBW2z.js +0 -1
  507. package/.output/public/_nuxt/builds/meta/92d6bd39-dbc5-46d5-90d8-8387f36715fa.json +0 -1
  508. package/.output/public/_nuxt/dcw.DcDjwvqv.css +0 -1
  509. package/.output/public/_nuxt/logs.BO-BSyjm.css +0 -1
  510. package/.output/public/_nuxt/pKv_VWR4.js +0 -1
  511. package/.output/public/_nuxt/pages.CytEOfUs.css +0 -1
  512. package/.output/public/_nuxt/plugins.Clsky_kl.css +0 -1
  513. package/.output/public/_nuxt/settings.DeRNNUcP.css +0 -1
  514. package/.output/public/_nuxt/tokens.BPLe9ioQ.css +0 -1
  515. package/.output/public/_nuxt/wirVyscc.js +0 -1
  516. package/.output/public/_nuxt/workshop.BIdoGfoC.css +0 -1
  517. package/.output/server/chunks/build/OmpTerminalPanel-COphA1br.mjs.map +0 -1
  518. package/.output/server/chunks/build/daq-_hCEaYLd.mjs.map +0 -1
  519. package/.output/server/chunks/build/dcw-C7pJ4Y0p.mjs.map +0 -1
  520. package/.output/server/chunks/build/input-sf_BT6o6.mjs.map +0 -1
  521. package/.output/server/chunks/build/logs-C039TFW1.mjs.map +0 -1
  522. package/.output/server/chunks/build/pages-C_VSGSGo.mjs.map +0 -1
  523. package/.output/server/chunks/build/settings-DA8IkGLo.mjs.map +0 -1
  524. package/.output/server/chunks/build/tokens-DKMvvk3e.mjs.map +0 -1
  525. package/.output/server/chunks/build/town-D0thZ7PK.mjs.map +0 -1
  526. package/.output/server/chunks/build/useDaqStream-BVTRw8_H.mjs.map +0 -1
  527. package/.output/server/chunks/build/useDcwStream-DNsivqI_.mjs.map +0 -1
  528. package/.output/server/chunks/build/useOpsLog-DcKnPO43.mjs.map +0 -1
  529. package/.output/server/chunks/build/useWorkshopWs-B3j5wIAK.mjs.map +0 -1
  530. package/.output/server/chunks/build/user-Dk-ytEUV.mjs +0 -140
  531. package/.output/server/chunks/build/user-Dk-ytEUV.mjs.map +0 -1
  532. package/.output/server/chunks/build/workspaces-IfORCDJE.mjs.map +0 -1
  533. /package/.output/public/_nuxt/{CNs_Ozdc2.js → CNs_Ozdc.js} +0 -0
  534. /package/.output/public/_nuxt/{Cno6kYiO2.js → Cno6kYiO.js} +0 -0
@@ -1,1079 +1,1079 @@
1
- <script setup lang="ts">
2
- import { computed, reactive, ref } from 'vue'
3
- import { DCW_LINE_COLORS, type DcwTemplateIcon, type LineView } from '#shared/dcw-protocol'
4
- import { useDcwStream } from '~/composables/workshop/useDcwStream'
5
-
6
- const { t } = useI18n()
7
-
8
- const dcw = useDcwStream()
9
- void dcw.load()
10
-
11
- // ---------- 产线总览 ----------
12
- interface LineCard {
13
- line: LineView
14
- nodes: number
15
- products: number
16
- recipes: number
17
- }
18
-
19
- const cards = computed<LineCard[]>(() =>
20
- dcw.lines.map(line => ({
21
- line,
22
- nodes: dcw.nodes.filter(n => n.lineId === line.id).length,
23
- products: dcw.products.filter(p => p.lineId === line.id).length,
24
- recipes: dcw.recipes.filter(r => r.lineId === line.id).length,
25
- })),
26
- )
27
- const unassignedCount = computed(() =>
28
- dcw.nodes.filter(n => !n.lineId).length + dcw.products.filter(p => !p.lineId).length,
29
- )
30
-
31
- // ---------- 清单筛选(94 条历史线的管理页:导航先于遍历) ----------
32
- const filterState = ref<'all' | 'running' | 'idle'>('all')
33
- const searchText = ref('')
34
- const runningCount = computed(() => cards.value.filter(c => dcw.lineStateOf(c.line.id).active).length)
35
- const shownCards = computed(() => cards.value.filter((c) => {
36
- const active = dcw.lineStateOf(c.line.id).active
37
- if (filterState.value === 'running' && !active) return false
38
- if (filterState.value === 'idle' && active) return false
39
- const q = searchText.value.trim().toLowerCase()
40
- if (q && !c.line.name.toLowerCase().includes(q) && !(c.line.description ?? '').toLowerCase().includes(q)) return false
41
- return true
42
- }))
43
-
44
- // ---------- 新建产线 ----------
45
- const createOpen = ref(false)
46
- const createSaving = ref(false)
47
- const createError = ref('')
48
- const createForm = reactive({ name: '', description: '', color: '' })
49
-
50
- const nextColor = computed(() => DCW_LINE_COLORS[dcw.lines.length % DCW_LINE_COLORS.length] ?? '#3aa0ff')
51
-
52
- function openCreate(): void {
53
- createForm.name = ''
54
- createForm.description = ''
55
- createForm.color = ''
56
- createError.value = ''
57
- createOpen.value = true
58
- }
59
-
60
- async function doCreateLine(): Promise<void> {
61
- createSaving.value = true
62
- createError.value = ''
63
- try {
64
- await dcw.createLine({
65
- name: createForm.name.trim(),
66
- description: createForm.description.trim(),
67
- color: createForm.color || undefined,
68
- })
69
- createOpen.value = false
70
- }
71
- catch (err) {
72
- createError.value = apiErrorMessage(err)
73
- }
74
- finally {
75
- createSaving.value = false
76
- }
77
- }
78
-
79
- // ---------- 快捷启停(卡片上) ----------
80
- const quickBusy = ref('')
81
- const quickErr = ref('')
82
- const quickPick = reactive<Record<string, string>>({})
83
-
84
- async function quickStart(card: LineCard): Promise<void> {
85
- quickBusy.value = card.line.id
86
- quickErr.value = ''
87
- try {
88
- const rid = quickPick[card.line.id] ?? ''
89
- if (!rid) throw new Error(t('dcw.k188cswn039'))
90
- await dcw.startLine(card.line.id, rid)
91
- }
92
- catch (err) {
93
- quickErr.value = apiErrorMessage(err)
94
- }
95
- finally {
96
- quickBusy.value = ''
97
- }
98
- }
99
-
100
- async function quickStop(card: LineCard): Promise<void> {
101
- quickBusy.value = card.line.id
102
- quickErr.value = ''
103
- try {
104
- await dcw.stopLine(card.line.id)
105
- }
106
- catch (err) {
107
- quickErr.value = apiErrorMessage(err)
108
- }
109
- finally {
110
- quickBusy.value = ''
111
- }
112
- }
113
-
114
- /** 卡片配方下拉:本产线配方(继承自产品) */
115
- function recipesOf(lineId: string) {
116
- return dcw.recipes.filter(r => r.lineId === lineId)
117
- }
118
-
119
- /** 删除产线(弹窗两步确认;purge 勾选 = 连同旗下节点/产品/配方一并清理) */
120
- const delOpen = ref(false)
121
- const delBusy = ref(false)
122
- const delErr = ref('')
123
- const delPurge = ref(true)
124
- const delCard = ref<LineCard | null>(null)
125
-
126
- function openDelete(card: LineCard): void {
127
- delCard.value = card
128
- delPurge.value = true
129
- delErr.value = ''
130
- delOpen.value = true
131
- }
132
-
133
- async function doDeleteLine(): Promise<void> {
134
- if (!delCard.value)
135
- return
136
- delBusy.value = true
137
- delErr.value = ''
138
- try {
139
- await dcw.removeLine(delCard.value.line.id, delPurge.value)
140
- delOpen.value = false
141
- }
142
- catch (err) {
143
- delErr.value = apiErrorMessage(err)
144
- }
145
- finally {
146
- delBusy.value = false
147
- }
148
- }
149
-
150
- /** 编辑产线(名称/描述/光晕色) */
151
- const editOpen = ref(false)
152
- const editSaving = ref(false)
153
- const editError = ref('')
154
- const editForm = reactive({ id: '', name: '', description: '', color: '' })
155
-
156
- function openEdit(card: LineCard): void {
157
- editForm.id = card.line.id
158
- editForm.name = card.line.name
159
- editForm.description = card.line.description
160
- editForm.color = card.line.color
161
- editError.value = ''
162
- editOpen.value = true
163
- }
164
-
165
- async function doEditLine(): Promise<void> {
166
- editSaving.value = true
167
- editError.value = ''
168
- try {
169
- await dcw.updateLine(editForm.id, {
170
- name: editForm.name.trim(),
171
- description: editForm.description.trim(),
172
- color: editForm.color || undefined,
173
- })
174
- editOpen.value = false
175
- }
176
- catch (err) {
177
- editError.value = apiErrorMessage(err)
178
- }
179
- finally {
180
- editSaving.value = false
181
- }
182
- }
183
-
184
- // ---------- 控制模板管理(自定义分类) ----------
185
- const tplOpen = ref(false)
186
- const tplError = ref('')
187
- const tplForm = reactive({ name: '', ch: '', code: '', unit: '', min: '' as number | '', max: '' as number | '', decimals: 1, icon: 'gateway' as DcwTemplateIcon, semantics: '' })
188
- const tplIcons: Array<{ key: DcwTemplateIcon, label: string }> = [
189
- { key: 'thermo', label: t('dcw.k422b8040') },
190
- { key: 'pressure', label: t('dcw.k3x6ff041') },
191
- { key: 'tension', label: t('dcw.k3z9xc042') },
192
- { key: 'encoder', label: t('dcw.kjb3vhs043') },
193
- { key: 'camera', label: t('dcw.k47atw044') },
194
- { key: 'gateway', label: t('dcw.k48c07045') },
195
- ]
196
-
197
- async function doCreateTemplate(): Promise<void> {
198
- tplError.value = ''
199
- try {
200
- if (!tplForm.name.trim()) throw new Error(t('dcw.k6ugbw2046'))
201
- if (tplForm.min === '' || tplForm.max === '') throw new Error(t('dcw.k13awowo047'))
202
- await dcw.createTemplate({
203
- name: tplForm.name.trim(),
204
- ch: tplForm.ch.trim() || tplForm.name.trim(),
205
- code: tplForm.code.trim() || 'CUSTOM',
206
- unit: tplForm.unit.trim() || '-',
207
- min: Number(tplForm.min),
208
- max: Number(tplForm.max),
209
- decimals: Number(tplForm.decimals) || 0,
210
- icon: tplForm.icon,
211
- semantics: tplForm.semantics.trim() || undefined,
212
- })
213
- tplForm.name = ''
214
- tplForm.ch = ''
215
- tplForm.code = ''
216
- tplForm.unit = ''
217
- tplForm.min = ''
218
- tplForm.max = ''
219
- }
220
- catch (err) {
221
- tplError.value = apiErrorMessage(err)
222
- }
223
- }
224
-
225
- async function doRemoveTemplate(key: string): Promise<void> {
226
- tplError.value = ''
227
- try {
228
- await fetch(`/api/workshop/dcw/templates/${key}`, {
229
- method: 'DELETE',
230
- headers: { authorization: `Bearer ${document.cookie.match(/(?:^|;\s*)token=([^;]+)/)?.[1] ?? ''}` },
231
- }).then(async (r) => {
232
- const json = await r.json().catch(() => ({}))
233
- if (json?.code !== 0) throw new Error(json?.message ?? t('dcw.k1bphrb3048'))
234
- })
235
- const i = dcw.templates.findIndex(t => t.key === key)
236
- if (i >= 0) dcw.templates.splice(i, 1)
237
- }
238
- catch (err) {
239
- tplError.value = apiErrorMessage(err)
240
- }
241
- }
242
-
243
- const builtinCount = computed(() => dcw.templates.filter(t => t.builtin).length)
244
- </script>
245
-
246
- <template>
247
- <div class="page">
248
- <div class="aw-page-head">
249
- <div>
250
- <p class="aw-kicker">
251
- AGENTWORKSHOP / LINE OPERATIONS
252
- </p>
253
- <h1>{{ $t('dcw.k1b2tk5c009') }}</h1>
254
- <p class="sub">
255
- {{ $t('dcw.k5q0aqi010') }}
256
- </p>
257
- </div>
258
- <div class="badges mono">
259
- <span
260
- v-if="unassignedCount"
261
- class="badge warn-badge"
262
- :title="$t('dcw.k1i5a9vw001')"
263
- >{{ $t('dcw.k3ootr6049') }} {{ unassignedCount }}</span>
264
- <span class="badge">{{ $t('dcw.k3wj9n050') }} {{ dcw.lines.length }}</span>
265
- <button
266
- class="badge tpl-btn"
267
- @click="tplOpen = true"
268
- >
269
- {{ $t('dcw.k11nndsp051') }} {{ dcw.templates.length }}
270
- </button>
271
- </div>
272
- </div>
273
-
274
- <p
275
- v-if="quickErr"
276
- class="banner bad"
277
- >
278
- {{ quickErr }}
279
- </p>
280
-
281
- <!-- 清单筛选:状态分段 + 名称搜索(复用 aw-seg / 页内 inp 语言) -->
282
- <div class="fleet-filter">
283
- <div class="aw-seg">
284
- <button
285
- :class="{ on: filterState === 'all' }"
286
- @click="filterState = 'all'"
287
- >
288
- {{ $t('common.all') }} {{ cards.length }}
289
- </button>
290
- <button
291
- :class="{ on: filterState === 'running' }"
292
- @click="filterState = 'running'"
293
- >
294
- {{ $t('dcw.k1eox1el055') }} {{ runningCount }}
295
- </button>
296
- <button
297
- :class="{ on: filterState === 'idle' }"
298
- @click="filterState = 'idle'"
299
- >
300
- {{ $t('dcw.k149r6y7059') }} {{ cards.length - runningCount }}
301
- </button>
302
- </div>
303
- <input
304
- v-model="searchText"
305
- class="inp fleet-search"
306
- type="search"
307
- :placeholder="$t('dcw.filterSearchPh')"
308
- >
309
- </div>
310
-
311
- <!-- 产线卡片栅格 -->
312
- <div class="line-grid">
313
- <div
314
- v-for="c in shownCards"
315
- :key="c.line.id"
316
- class="line-card"
317
- :class="{ idle: !dcw.lineStateOf(c.line.id).active }"
318
- :style="{ '--lc': c.line.color }"
319
- >
320
- <div class="lc-head">
321
- <span class="lc-dot" />
322
- <b class="lc-name">{{ c.line.name }}</b>
323
- <span
324
- class="lc-state"
325
- :class="{ on: dcw.lineStateOf(c.line.id).active }"
326
- >{{ dcw.lineStateOf(c.line.id).active ? $t('dcw.k1eox1el055') : $t('dcw.k149r6y7059') }}</span>
327
- <button
328
- class="lc-act"
329
- :title="$t('common.edit')"
330
- @click="openEdit(c)"
331
- >
332
- <span class="i-tabler-pencil" />
333
- </button>
334
- <button
335
- class="lc-act danger"
336
- :title="$t('dcw.k3xakp026')"
337
- @click="openDelete(c)"
338
- >
339
- <span class="i-tabler-trash" />
340
- </button>
341
- </div>
342
- <small
343
- v-if="dcw.lineStateOf(c.line.id).active"
344
- class="lc-run mono"
345
- >{{ dcw.lineStateOf(c.line.id).productName }} · {{ dcw.lineStateOf(c.line.id).recipeName }} · {{ $t('dcw.k3zz0f052') }} {{ dcw.lineStateOf(c.line.id).taggedSamples }}</small>
346
- <small
347
- v-else
348
- class="lc-run dim"
349
- :class="{ ph: !c.line.description }"
350
- >{{ c.line.description || $t('dcw.k18moyq1056') }}</small>
351
- <div class="lc-stats mono">
352
- <span>{{ $t('dcw.k45uio011') }} <b>{{ c.nodes }}</b></span>
353
- <span>{{ $t('dcw.k3waz1012') }} <b>{{ c.products }}</b></span>
354
- <span>{{ $t('dcw.k48grv013') }} <b>{{ c.recipes }}</b></span>
355
- </div>
356
- <div class="lc-ctl">
357
- <template v-if="!dcw.lineStateOf(c.line.id).active">
358
- <select
359
- v-model="quickPick[c.line.id]"
360
- class="inp"
361
- :disabled="recipesOf(c.line.id).length === 0"
362
- :title="$t('dcw.k1dhaby4003')"
363
- >
364
- <option value="">
365
- {{ recipesOf(c.line.id).length ? $t('dcw.kutxzsz057') : $t('dcw.k1elczt9060') }}
366
- </option>
367
- <option
368
- v-for="r in recipesOf(c.line.id)"
369
- :key="r.id"
370
- :value="r.id"
371
- :disabled="r.params.length === 0"
372
- >
373
- {{ r.name }}({{ r.params.length }})
374
- </option>
375
- </select>
376
- <button
377
- class="pill-btn"
378
- :disabled="quickBusy === c.line.id || !quickPick[c.line.id]"
379
- @click="quickStart(c)"
380
- >
381
- {{ $t('dcw.k149b4vg014') }}
382
- </button>
383
- </template>
384
- <button
385
- v-else
386
- class="pill-btn stop"
387
- :disabled="quickBusy === c.line.id"
388
- @click="quickStop(c)"
389
- >
390
- {{ $t('dcw.k148rclf015') }}
391
- </button>
392
- </div>
393
- <NuxtLink
394
- class="lc-manage"
395
- :to="`/dcw/${c.line.id}`"
396
- >
397
- {{ $t('dcw.k1fwqw5g016') }}
398
- </NuxtLink>
399
- </div>
400
-
401
- <!-- 新建产线卡 -->
402
- <button
403
- class="line-card new-card"
404
- @click="openCreate"
405
- >
406
- <span class="i-tabler-plus" />
407
- {{ $t('dcw.k1efe391017') }}
408
- <small>{{ $t('dcw.k19dlbli053') }}{{ nextColor }})</small>
409
- </button>
410
- </div>
411
-
412
- <p
413
- v-if="dcw.loaded && dcw.lines.length === 0"
414
- class="banner"
415
- >
416
- {{ $t('dcw.k1sk85vs018') }}
417
- </p>
418
- <p
419
- v-else-if="dcw.loaded && shownCards.length === 0"
420
- class="banner"
421
- >
422
- {{ $t('dcw.filterEmpty') }}
423
- </p>
424
-
425
- <!-- 新建产线弹窗 -->
426
- <Transition name="modal">
427
- <div
428
- v-if="createOpen"
429
- class="modal-mask"
430
- @click.self="createOpen = false"
431
- >
432
- <div class="modal">
433
- <h3 class="m-title">
434
- {{ $t('dcw.k1efe391017') }}
435
- </h3>
436
- <label class="f">
437
- <span>{{ $t('dcw.k1b2ioko019') }}<em>*</em></span>
438
- <input
439
- v-model="createForm.name"
440
- class="inp"
441
- :placeholder="$t('dcw.kru37i3004')"
442
- >
443
- </label>
444
- <label class="f">
445
- <span>{{ $t('dcw.k24dxcd020') }}</span>
446
- <input
447
- v-model="createForm.description"
448
- class="inp"
449
- :placeholder="$t('dcw.k1f2nwsp005')"
450
- >
451
- </label>
452
- <div class="f">
453
- <span>{{ $t('dcw.k1x7nubr021') }}</span>
454
- <div class="color-row">
455
- <button
456
- v-for="c in DCW_LINE_COLORS"
457
- :key="c"
458
- class="color-dot"
459
- :class="{ on: createForm.color === c }"
460
- :style="{ background: c }"
461
- @click="createForm.color = createForm.color === c ? '' : c"
462
- />
463
- <small class="dim">{{ createForm.color || $t('dcw.k1qidbpy061', { p0: nextColor }) }}</small>
464
- </div>
465
- </div>
466
- <p
467
- v-if="createError"
468
- class="m-err"
469
- >
470
- {{ createError }}
471
- </p>
472
- <div class="m-actions">
473
- <button
474
- class="mini-btn"
475
- @click="createOpen = false"
476
- >
477
- {{ $t('dcw.k3xdnn022') }}
478
- </button>
479
- <button
480
- class="pill-btn"
481
- :disabled="createSaving || !createForm.name.trim()"
482
- @click="doCreateLine"
483
- >
484
- {{ $t('dcw.k3wzi2023') }}
485
- </button>
486
- </div>
487
- </div>
488
- </div>
489
- </Transition>
490
-
491
- <!-- 编辑产线弹窗 -->
492
- <Transition name="modal">
493
- <div
494
- v-if="editOpen"
495
- class="modal-mask"
496
- @click.self="editOpen = false"
497
- >
498
- <div class="modal">
499
- <h3 class="m-title">
500
- {{ $t('dcw.k5tq8wc071') }}
501
- </h3>
502
- <label class="f">
503
- <span>{{ $t('dcw.k1b2ioko019') }}<em>*</em></span>
504
- <input
505
- v-model="editForm.name"
506
- class="inp"
507
- :placeholder="$t('dcw.kru37i3004')"
508
- >
509
- </label>
510
- <label class="f">
511
- <span>{{ $t('dcw.k24dxcd020') }}</span>
512
- <input
513
- v-model="editForm.description"
514
- class="inp"
515
- :placeholder="$t('dcw.k1f2nwsp005')"
516
- >
517
- </label>
518
- <div class="f">
519
- <span>{{ $t('dcw.k1x7nubr021') }}</span>
520
- <div class="color-row">
521
- <button
522
- v-for="c in DCW_LINE_COLORS"
523
- :key="c"
524
- class="color-dot"
525
- :class="{ on: editForm.color === c }"
526
- :style="{ background: c }"
527
- @click="editForm.color = editForm.color === c ? '' : c"
528
- />
529
- <small class="dim">{{ editForm.color || $t('dcw.k1qidbpy061', { p0: nextColor }) }}</small>
530
- </div>
531
- </div>
532
- <p
533
- v-if="editError"
534
- class="m-err"
535
- >
536
- {{ editError }}
537
- </p>
538
- <div class="m-actions">
539
- <button
540
- class="mini-btn"
541
- @click="editOpen = false"
542
- >
543
- {{ $t('dcw.k3xdnn022') }}
544
- </button>
545
- <button
546
- class="pill-btn"
547
- :disabled="editSaving || !editForm.name.trim()"
548
- @click="doEditLine"
549
- >
550
- {{ $t('common.save') }}
551
- </button>
552
- </div>
553
- </div>
554
- </div>
555
- </Transition>
556
-
557
- <!-- 删除产线弹窗(两步确认;purge 勾选决定是否级联清理) -->
558
- <Transition name="modal">
559
- <div
560
- v-if="delOpen && delCard"
561
- class="modal-mask"
562
- @click.self="delOpen = false"
563
- >
564
- <div class="modal">
565
- <h3 class="m-title danger-title">
566
- <span class="i-tabler-alert-triangle" />
567
- {{ $t('dcw.k7xq2mfd063', { p0: delCard.line.name }) }}
568
- </h3>
569
- <p class="del-summary">
570
- {{ delPurge
571
- ? $t('dcw.k4r7nbd073', { p0: delCard.line.name, p1: delCard.nodes, p2: delCard.products, p3: delCard.recipes })
572
- : $t('dcw.k1gp649b062', { p0: delCard.line.name, p1: delCard.nodes, p2: delCard.products, p3: delCard.recipes }) }}
573
- </p>
574
- <label class="del-purge">
575
- <input
576
- v-model="delPurge"
577
- type="checkbox"
578
- >
579
- <span>{{ $t('dcw.k9m2vxa072') }}</span>
580
- </label>
581
- <p
582
- v-if="delErr"
583
- class="m-err"
584
- >
585
- {{ delErr }}
586
- </p>
587
- <div class="m-actions">
588
- <button
589
- class="mini-btn"
590
- @click="delOpen = false"
591
- >
592
- {{ $t('dcw.k3xdnn022') }}
593
- </button>
594
- <button
595
- class="pill-btn danger"
596
- :disabled="delBusy"
597
- @click="doDeleteLine"
598
- >
599
- {{ $t('dcw.k3xakp026') }}
600
- </button>
601
- </div>
602
- </div>
603
- </div>
604
- </Transition>
605
-
606
- <!-- 控制模板管理弹窗 -->
607
- <Transition name="modal">
608
- <div
609
- v-if="tplOpen"
610
- class="modal-mask"
611
- @click.self="tplOpen = false"
612
- >
613
- <div class="modal wide">
614
- <h3 class="m-title">
615
- {{ $t('dcw.k11oadmx024') }} <small class="dim mono">{{ $t('dcw.k3x23c054') }} {{ builtinCount }} · {{ $t('dcw.k3t616a058') }} {{ dcw.templates.length - builtinCount }}</small>
616
- </h3>
617
- <p class="dim tpl-hint">
618
- {{ $t('dcw.k2hlav5025') }}
619
- </p>
620
- <div class="tpl-list">
621
- <div
622
- v-for="t in dcw.templates"
623
- :key="t.key"
624
- class="tpl-row"
625
- :title="t.semantics ?? ''"
626
- >
627
- <b>{{ t.name }}</b>
628
- <small class="mono dim">{{ t.code }} · {{ t.min }}~{{ t.max }} {{ t.unit }}</small>
629
- <small
630
- v-if="t.semantics"
631
- class="tpl-sem"
632
- >{{ t.semantics.slice(0, 40) }}{{ t.semantics.length > 40 ? '…' : '' }}</small>
633
- <span
634
- class="tpl-tag"
635
- :class="{ builtin: t.builtin }"
636
- >{{ t.builtin ? $t('dcw.k3x23c054') : $t('dcw.k3t616a058') }}</span>
637
- <button
638
- v-if="!t.builtin"
639
- class="mini-btn danger"
640
- @click="doRemoveTemplate(t.key)"
641
- >
642
- {{ $t('dcw.k3xakp026') }}
643
- </button>
644
- </div>
645
- </div>
646
- <p class="sec-label">
647
- {{ $t('dcw.k169eb4s027') }}
648
- </p>
649
- <div class="tpl-form">
650
- <label class="f">
651
- <span>{{ $t('dcw.k3xhia028') }}<em>*</em></span>
652
- <input
653
- v-model="tplForm.name"
654
- class="inp"
655
- :placeholder="$t('dcw.k1tooi3o006')"
656
- >
657
- </label>
658
- <label class="f">
659
- <span>{{ $t('dcw.k1bqk219029') }}</span>
660
- <input
661
- v-model="tplForm.ch"
662
- class="inp"
663
- :placeholder="$t('dcw.k698qz0007')"
664
- >
665
- </label>
666
- <label class="f">
667
- <span>{{ $t('dcw.k1ayxrqb030') }}</span>
668
- <input
669
- v-model="tplForm.code"
670
- class="inp"
671
- placeholder="如 MOTOR · I"
672
- >
673
- </label>
674
- <label class="f">
675
- <span>{{ $t('dcw.k3x4ef031') }}</span>
676
- <input
677
- v-model="tplForm.unit"
678
- class="inp"
679
- placeholder="如 A"
680
- >
681
- </label>
682
- <label class="f">
683
- <span>{{ $t('dcw.k1l9jv5m032') }}<em>*</em></span>
684
- <input
685
- v-model.number="tplForm.min"
686
- type="number"
687
- class="inp"
688
- >
689
- </label>
690
- <label class="f">
691
- <span>{{ $t('dcw.k1l9jv4p033') }}<em>*</em></span>
692
- <input
693
- v-model.number="tplForm.max"
694
- type="number"
695
- class="inp"
696
- >
697
- </label>
698
- <label class="f">
699
- <span>{{ $t('dcw.k3mxmcx034') }}</span>
700
- <input
701
- v-model.number="tplForm.decimals"
702
- type="number"
703
- class="inp"
704
- >
705
- </label>
706
- <label class="f">
707
- <span>{{ $t('dcw.k3xx56035') }}</span>
708
- <select
709
- v-model="tplForm.icon"
710
- class="inp"
711
- >
712
- <option
713
- v-for="ic in tplIcons"
714
- :key="ic.key"
715
- :value="ic.key"
716
- >
717
- {{ ic.label }}
718
- </option>
719
- </select>
720
- </label>
721
- </div>
722
- <label class="f">
723
- <span>{{ $t('dcw.k1b6qorg036') }}</span>
724
- <textarea
725
- v-model="tplForm.semantics"
726
- class="inp"
727
- rows="3"
728
- :placeholder="$t('dcw.k1qdnfzc008')"
729
- />
730
- </label>
731
- <p
732
- v-if="tplError"
733
- class="m-err"
734
- >
735
- {{ tplError }}
736
- </p>
737
- <div class="m-actions">
738
- <button
739
- class="mini-btn"
740
- @click="tplOpen = false"
741
- >
742
- {{ $t('dcw.k3x62t037') }}
743
- </button>
744
- <button
745
- class="pill-btn"
746
- :disabled="!tplForm.name.trim() || tplForm.min === '' || tplForm.max === ''"
747
- @click="doCreateTemplate"
748
- >
749
- {{ $t('dcw.k1bg9nga038') }}
750
- </button>
751
- </div>
752
- </div>
753
- </div>
754
- </Transition>
755
-
756
- <p
757
- v-if="dcw.error"
758
- class="banner bad"
759
- >
760
- {{ dcw.error }}
761
- </p>
762
- </div>
763
- </template>
764
-
765
- <style scoped>
766
- .page { display: flex; flex-direction: column; gap: 14px; }
767
- .badges { display: flex; gap: 8px; }
768
- /* 状态徽章 = 玻璃芯片:双主题令牌化,浅色不再发白发黄 */
769
- .badge {
770
- padding: 3px 10px;
771
- font-size: 11px;
772
- color: var(--ink-faint);
773
- background: var(--glass-bg);
774
- border: 1px solid var(--glass-line);
775
- border-radius: 999px;
776
- backdrop-filter: var(--frost-blur);
777
- -webkit-backdrop-filter: var(--frost-blur);
778
- }
779
- .warn-badge { color: var(--tone-warning-dot); background: var(--tone-warning-bg); border-color: color-mix(in srgb, var(--tone-warning-dot) 32%, transparent); }
780
- .tpl-btn { color: var(--tone-info-dot); background: var(--tone-info-bg); border-color: color-mix(in srgb, var(--tone-info-dot) 34%, transparent); cursor: pointer; }
781
- .tpl-btn:hover { border-color: color-mix(in srgb, var(--tone-info-dot) 60%, transparent); }
782
-
783
- /* 清单筛选条:状态分段 + 搜索(与页内控件同一语言) */
784
- .fleet-filter {
785
- display: flex;
786
- flex-wrap: wrap;
787
- gap: 12px;
788
- align-items: center;
789
- margin-bottom: 14px;
790
- }
791
- .fleet-filter .aw-seg button {
792
- font-family: var(--font-mono);
793
- font-size: 11.5px;
794
- font-variant-numeric: tabular-nums;
795
- }
796
- .fleet-search {
797
- width: 220px;
798
- margin-left: auto;
799
- height: 30px;
800
- padding: 0 10px;
801
- font-size: 11.5px;
802
- color: var(--ink);
803
- background: var(--paper-deep);
804
- border: 1px solid var(--line-strong);
805
- border-radius: var(--radius-panel-sm);
806
- }
807
- .fleet-search:focus { outline: none; border-color: var(--tone-success-dot); box-shadow: 0 0 0 3px var(--accent-soft); }
808
- .line-grid {
809
- display: grid;
810
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
811
- gap: 14px;
812
- }
813
- /* 产线卡 = Aurora Glass 仪表砖:半透 surface + 深模糊 + 内缘折射;
814
- 运行卡在玻璃顶缘叠一线产线色洗染做「图」,待机卡收敛为磨砂做「底」 */
815
- .line-card {
816
- position: relative;
817
- display: flex;
818
- flex-direction: column;
819
- gap: 9px;
820
- padding: 14px 16px;
821
- text-align: left;
822
- background: var(--surface-glass);
823
- backdrop-filter: var(--aurora-blur);
824
- -webkit-backdrop-filter: var(--aurora-blur);
825
- border: 1px solid var(--glass-line);
826
- border-radius: var(--radius-panel);
827
- box-shadow: var(--glass-edge);
828
- transition: border-color var(--transition-base), transform var(--transition-base), box-shadow var(--transition-base);
829
- }
830
- @media (hover: hover) and (prefers-reduced-motion: no-preference) {
831
- .line-card:hover {
832
- border-color: color-mix(in srgb, var(--lc, #3aa0ff) 55%, var(--line-strong));
833
- transform: translateY(-2px);
834
- box-shadow: var(--glass-edge), var(--shadow-float);
835
- }
836
- }
837
- .line-card:not(.idle) {
838
- border-color: color-mix(in srgb, var(--lc, #3aa0ff) 38%, var(--line-strong));
839
- background:
840
- linear-gradient(180deg, color-mix(in srgb, var(--lc, #3aa0ff) 9%, transparent), transparent 46%),
841
- var(--surface-glass);
842
- }
843
- .line-card.idle {
844
- background: var(--frost-bg);
845
- }
846
- .line-card.idle .lc-dot {
847
- box-shadow: none;
848
- opacity: 0.72;
849
- }
850
- .line-card.idle .lc-name {
851
- color: var(--ink-faint);
852
- }
853
- .lc-head { display: flex; gap: 8px; align-items: center; }
854
- .lc-dot {
855
- width: 10px;
856
- height: 10px;
857
- flex: none;
858
- background: var(--lc);
859
- border-radius: 4px;
860
- box-shadow: 0 0 10px color-mix(in srgb, var(--lc) 70%, transparent);
861
- }
862
- .lc-name {
863
- overflow: hidden;
864
- font-size: 14.5px;
865
- color: var(--ink);
866
- text-overflow: ellipsis;
867
- white-space: nowrap;
868
- }
869
- .lc-state {
870
- margin-left: auto;
871
- flex: none;
872
- font-family: var(--font-mono);
873
- font-size: 10px;
874
- color: var(--ink-fainter);
875
- }
876
- .lc-state.on { color: var(--tone-success-dot); }
877
- /* 头部图标钮:编辑/删除,悬停语义色染色 */
878
- .lc-act {
879
- display: inline-flex;
880
- flex: none;
881
- align-items: center;
882
- justify-content: center;
883
- width: 22px;
884
- height: 22px;
885
- padding: 0;
886
- font-size: 13px;
887
- color: var(--ink-fainter);
888
- background: transparent;
889
- border: 0;
890
- border-radius: 6px;
891
- cursor: pointer;
892
- opacity: 0;
893
- transition:
894
- color 0.15s ease,
895
- background 0.15s ease,
896
- opacity 0.15s ease,
897
- transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
898
- }
899
- .line-card:hover .lc-act,
900
- .line-card:focus-within .lc-act { opacity: 1; }
901
- .lc-act:hover { color: var(--tone-info-dot); background: var(--tone-info-bg); }
902
- .lc-act.danger:hover { color: var(--tone-danger-dot); background: var(--tone-danger-bg); }
903
- .lc-act:active { transform: scale(0.9); }
904
- .lc-run { font-size: 10.5px; color: var(--tone-info-dot); }
905
- .lc-run.dim { color: var(--ink-fainter); }
906
- /* 无描述时的兜底提示进一步退后(77 张卡同文反复出现即是噪音;不压到 0.55 以下,保浅色可读) */
907
- .lc-run.dim.ph { opacity: 0.75; }
908
- .lc-stats {
909
- display: flex;
910
- gap: 14px;
911
- font-size: 10.5px;
912
- color: var(--ink-faint);
913
- }
914
- .lc-stats b { color: var(--ink); font-weight: 600; }
915
- .lc-ctl { display: flex; gap: 8px; }
916
- .lc-ctl .inp {
917
- flex: 1;
918
- min-width: 0;
919
- height: 30px;
920
- padding: 0 9px;
921
- font-size: 11.5px;
922
- color: var(--ink);
923
- background: var(--paper-deep);
924
- border: 1px solid var(--line-strong);
925
- border-radius: var(--radius-panel-sm);
926
- }
927
- .lc-ctl .inp:focus { outline: none; border-color: var(--tone-success-dot); box-shadow: 0 0 0 3px var(--accent-soft); }
928
- .pill-btn {
929
- flex: none;
930
- height: 30px;
931
- padding: 0 14px;
932
- font-size: 11.5px;
933
- font-weight: 600;
934
- color: var(--on-accent);
935
- background: var(--accent);
936
- border: 0;
937
- border-radius: var(--radius-panel-sm);
938
- cursor: pointer;
939
- transition: background 0.15s, box-shadow 0.15s;
940
- }
941
- .pill-btn:hover:not(:disabled) { background: var(--accent-strong); box-shadow: 0 0 14px var(--accent-soft); }
942
- .pill-btn:disabled { opacity: 0.4; cursor: default; }
943
- .pill-btn.danger,
944
- .pill-btn.stop { background: var(--tone-danger-bg); color: var(--tone-danger-dot); }
945
- .pill-btn.danger:hover:not(:disabled),
946
- .pill-btn.stop:hover:not(:disabled) { background: color-mix(in srgb, var(--tone-danger-dot) 26%, transparent); box-shadow: none; }
947
- .lc-manage {
948
- font-size: 11.5px;
949
- font-weight: 600;
950
- color: var(--tone-info-dot);
951
- text-decoration: none;
952
- }
953
- .lc-manage:hover { text-decoration: underline; }
954
- .new-card {
955
- align-items: center;
956
- justify-content: center;
957
- gap: 6px;
958
- font-size: 13px;
959
- font-weight: 600;
960
- color: var(--ink-faint);
961
- background: transparent;
962
- border: 1px dashed var(--line-strong);
963
- box-shadow: none;
964
- backdrop-filter: none;
965
- -webkit-backdrop-filter: none;
966
- cursor: pointer;
967
- }
968
- .new-card:hover { color: var(--tone-success-dot); border-color: var(--tone-success-dot); transform: none; }
969
- .new-card small { font-weight: 400; font-size: 10px; color: var(--ink-fainter); }
970
-
971
- .banner { padding: 8px 12px; font-size: 12px; border-radius: 10px; }
972
- .banner.bad { color: var(--tone-danger-dot); background: var(--tone-danger-bg); border: 1px solid color-mix(in srgb, var(--tone-danger-dot) 30%, transparent); }
973
-
974
- .modal-mask {
975
- position: fixed;
976
- inset: 0;
977
- z-index: 80;
978
- display: grid;
979
- place-items: center;
980
- background: var(--scrim);
981
- backdrop-filter: blur(3px);
982
- }
983
- /* 弹窗入退场(occasional 频次;防跳变):遮罩淡入淡出,卡片 fade+上浮 8px 入场;
984
- * modal 不锚定触发器,transform-origin 居中豁免;退场对称快出 */
985
- .modal-enter-active { transition: opacity 180ms cubic-bezier(0.22, 1, 0.36, 1); }
986
- .modal-leave-active { transition: opacity 130ms ease; }
987
- .modal-enter-from,
988
- .modal-leave-to { opacity: 0; }
989
- .modal-enter-active .modal { transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1); }
990
- .modal-enter-from .modal { transform: translateY(8px) scale(0.96); }
991
- .modal {
992
- display: flex;
993
- flex-direction: column;
994
- gap: 12px;
995
- width: min(440px, calc(100vw - 40px));
996
- max-height: 82vh;
997
- padding: 18px;
998
- overflow: auto;
999
- background: var(--surface-glass-strong);
1000
- backdrop-filter: var(--aurora-blur);
1001
- -webkit-backdrop-filter: var(--aurora-blur);
1002
- border: 1px solid var(--glass-line);
1003
- border-radius: var(--radius-panel);
1004
- box-shadow: var(--glass-edge), var(--shadow-float);
1005
- }
1006
- .modal.wide { width: min(640px, calc(100vw - 40px)); }
1007
- .m-title { display: flex; gap: 10px; align-items: baseline; font-size: 15px; color: var(--ink); }
1008
- .m-title.danger-title { align-items: center; color: var(--tone-danger-dot); }
1009
- .f { display: flex; flex-direction: column; gap: 5px; font-size: 11px; color: var(--ink-faint); }
1010
- .f em { color: var(--tone-danger-dot); font-style: normal; }
1011
- .f .inp {
1012
- height: 30px;
1013
- padding: 0 9px;
1014
- font-size: 12px;
1015
- color: var(--ink);
1016
- background: var(--paper-deep);
1017
- border: 1px solid var(--line-strong);
1018
- border-radius: var(--radius-panel-sm);
1019
- }
1020
- .f .inp:focus { outline: none; border-color: var(--tone-success-dot); box-shadow: 0 0 0 3px var(--accent-soft); }
1021
- .color-row { display: flex; gap: 7px; align-items: center; }
1022
- .color-dot {
1023
- width: 20px;
1024
- height: 20px;
1025
- border: 2px solid transparent;
1026
- border-radius: 6px;
1027
- cursor: pointer;
1028
- }
1029
- .color-dot.on { border-color: var(--ink); box-shadow: 0 0 10px currentColor; }
1030
- .m-actions { display: flex; gap: 8px; justify-content: flex-end; }
1031
- .m-err { font-size: 11px; color: var(--tone-danger-dot); }
1032
- .dim { color: var(--ink-fainter); }
1033
- .sec-label { font-size: 10px; font-weight: 700; color: var(--ink-fainter); letter-spacing: 0.16em; }
1034
- /* 删除确认:摘要行 + 级联勾选 */
1035
- .del-summary { font-size: 12px; line-height: 1.6; color: var(--ink-soft); }
1036
- .del-purge { display: flex; gap: 8px; align-items: center; font-size: 12px; color: var(--ink); cursor: pointer; }
1037
- .del-purge input {
1038
- width: 14px;
1039
- height: 14px;
1040
- accent-color: var(--tone-danger-dot);
1041
- }
1042
- .tpl-hint { font-size: 11px; }
1043
- .tpl-list { display: flex; flex-direction: column; gap: 6px; }
1044
- .tpl-row {
1045
- display: flex;
1046
- gap: 10px;
1047
- align-items: center;
1048
- padding: 7px 10px;
1049
- font-size: 12px;
1050
- color: var(--ink);
1051
- background: var(--frost-bg);
1052
- border: 1px solid var(--glass-line);
1053
- border-radius: 9px;
1054
- }
1055
- .tpl-row small { flex: 1; }
1056
- .tpl-tag {
1057
- padding: 1px 8px;
1058
- font-family: var(--font-mono);
1059
- font-size: 9px;
1060
- color: var(--tone-info-dot);
1061
- border: 1px solid color-mix(in srgb, var(--tone-info-dot) 40%, transparent);
1062
- border-radius: 5px;
1063
- }
1064
- .tpl-tag.builtin { color: var(--ink-fainter); border-color: var(--line-strong); }
1065
- .tpl-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
1066
- .tpl-sem { flex: 1; color: var(--ink-faint); }
1067
- textarea.inp { height: auto; padding: 6px 9px; font-size: 11.5px; resize: vertical; }
1068
- .mini-btn {
1069
- padding: 4px 10px;
1070
- font-size: 10.5px;
1071
- color: var(--ink-faint);
1072
- background: var(--paper-deep);
1073
- border: 1px solid var(--line-strong);
1074
- border-radius: 7px;
1075
- cursor: pointer;
1076
- }
1077
- .mini-btn:hover { border-color: var(--ink-faint); color: var(--ink); }
1078
- .mini-btn.danger { color: var(--tone-danger-dot); }
1079
- </style>
1
+ <script setup lang="ts">
2
+ import { computed, reactive, ref } from 'vue'
3
+ import { DCW_LINE_COLORS, type DcwTemplateIcon, type LineView } from '#shared/dcw-protocol'
4
+ import { useDcwStream } from '~/composables/workshop/useDcwStream'
5
+
6
+ const { t } = useI18n()
7
+
8
+ const dcw = useDcwStream()
9
+ void dcw.load()
10
+
11
+ // ---------- 产线总览 ----------
12
+ interface LineCard {
13
+ line: LineView
14
+ nodes: number
15
+ products: number
16
+ recipes: number
17
+ }
18
+
19
+ const cards = computed<LineCard[]>(() =>
20
+ dcw.lines.map(line => ({
21
+ line,
22
+ nodes: dcw.nodes.filter(n => n.lineId === line.id).length,
23
+ products: dcw.products.filter(p => p.lineId === line.id).length,
24
+ recipes: dcw.recipes.filter(r => r.lineId === line.id).length,
25
+ })),
26
+ )
27
+ const unassignedCount = computed(() =>
28
+ dcw.nodes.filter(n => !n.lineId).length + dcw.products.filter(p => !p.lineId).length,
29
+ )
30
+
31
+ // ---------- 清单筛选(94 条历史线的管理页:导航先于遍历) ----------
32
+ const filterState = ref<'all' | 'running' | 'idle'>('all')
33
+ const searchText = ref('')
34
+ const runningCount = computed(() => cards.value.filter(c => dcw.lineStateOf(c.line.id).active).length)
35
+ const shownCards = computed(() => cards.value.filter((c) => {
36
+ const active = dcw.lineStateOf(c.line.id).active
37
+ if (filterState.value === 'running' && !active) return false
38
+ if (filterState.value === 'idle' && active) return false
39
+ const q = searchText.value.trim().toLowerCase()
40
+ if (q && !c.line.name.toLowerCase().includes(q) && !(c.line.description ?? '').toLowerCase().includes(q)) return false
41
+ return true
42
+ }))
43
+
44
+ // ---------- 新建产线 ----------
45
+ const createOpen = ref(false)
46
+ const createSaving = ref(false)
47
+ const createError = ref('')
48
+ const createForm = reactive({ name: '', description: '', color: '' })
49
+
50
+ const nextColor = computed(() => DCW_LINE_COLORS[dcw.lines.length % DCW_LINE_COLORS.length] ?? '#3aa0ff')
51
+
52
+ function openCreate(): void {
53
+ createForm.name = ''
54
+ createForm.description = ''
55
+ createForm.color = ''
56
+ createError.value = ''
57
+ createOpen.value = true
58
+ }
59
+
60
+ async function doCreateLine(): Promise<void> {
61
+ createSaving.value = true
62
+ createError.value = ''
63
+ try {
64
+ await dcw.createLine({
65
+ name: createForm.name.trim(),
66
+ description: createForm.description.trim(),
67
+ color: createForm.color || undefined,
68
+ })
69
+ createOpen.value = false
70
+ }
71
+ catch (err) {
72
+ createError.value = apiErrorMessage(err)
73
+ }
74
+ finally {
75
+ createSaving.value = false
76
+ }
77
+ }
78
+
79
+ // ---------- 快捷启停(卡片上) ----------
80
+ const quickBusy = ref('')
81
+ const quickErr = ref('')
82
+ const quickPick = reactive<Record<string, string>>({})
83
+
84
+ async function quickStart(card: LineCard): Promise<void> {
85
+ quickBusy.value = card.line.id
86
+ quickErr.value = ''
87
+ try {
88
+ const rid = quickPick[card.line.id] ?? ''
89
+ if (!rid) throw new Error(t('dcw.k188cswn039'))
90
+ await dcw.startLine(card.line.id, rid)
91
+ }
92
+ catch (err) {
93
+ quickErr.value = apiErrorMessage(err)
94
+ }
95
+ finally {
96
+ quickBusy.value = ''
97
+ }
98
+ }
99
+
100
+ async function quickStop(card: LineCard): Promise<void> {
101
+ quickBusy.value = card.line.id
102
+ quickErr.value = ''
103
+ try {
104
+ await dcw.stopLine(card.line.id)
105
+ }
106
+ catch (err) {
107
+ quickErr.value = apiErrorMessage(err)
108
+ }
109
+ finally {
110
+ quickBusy.value = ''
111
+ }
112
+ }
113
+
114
+ /** 卡片配方下拉:本产线配方(继承自产品) */
115
+ function recipesOf(lineId: string) {
116
+ return dcw.recipes.filter(r => r.lineId === lineId)
117
+ }
118
+
119
+ /** 删除产线(弹窗两步确认;purge 勾选 = 连同旗下节点/产品/配方一并清理) */
120
+ const delOpen = ref(false)
121
+ const delBusy = ref(false)
122
+ const delErr = ref('')
123
+ const delPurge = ref(true)
124
+ const delCard = ref<LineCard | null>(null)
125
+
126
+ function openDelete(card: LineCard): void {
127
+ delCard.value = card
128
+ delPurge.value = true
129
+ delErr.value = ''
130
+ delOpen.value = true
131
+ }
132
+
133
+ async function doDeleteLine(): Promise<void> {
134
+ if (!delCard.value)
135
+ return
136
+ delBusy.value = true
137
+ delErr.value = ''
138
+ try {
139
+ await dcw.removeLine(delCard.value.line.id, delPurge.value)
140
+ delOpen.value = false
141
+ }
142
+ catch (err) {
143
+ delErr.value = apiErrorMessage(err)
144
+ }
145
+ finally {
146
+ delBusy.value = false
147
+ }
148
+ }
149
+
150
+ /** 编辑产线(名称/描述/光晕色) */
151
+ const editOpen = ref(false)
152
+ const editSaving = ref(false)
153
+ const editError = ref('')
154
+ const editForm = reactive({ id: '', name: '', description: '', color: '' })
155
+
156
+ function openEdit(card: LineCard): void {
157
+ editForm.id = card.line.id
158
+ editForm.name = card.line.name
159
+ editForm.description = card.line.description
160
+ editForm.color = card.line.color
161
+ editError.value = ''
162
+ editOpen.value = true
163
+ }
164
+
165
+ async function doEditLine(): Promise<void> {
166
+ editSaving.value = true
167
+ editError.value = ''
168
+ try {
169
+ await dcw.updateLine(editForm.id, {
170
+ name: editForm.name.trim(),
171
+ description: editForm.description.trim(),
172
+ color: editForm.color || undefined,
173
+ })
174
+ editOpen.value = false
175
+ }
176
+ catch (err) {
177
+ editError.value = apiErrorMessage(err)
178
+ }
179
+ finally {
180
+ editSaving.value = false
181
+ }
182
+ }
183
+
184
+ // ---------- 控制模板管理(自定义分类) ----------
185
+ const tplOpen = ref(false)
186
+ const tplError = ref('')
187
+ const tplForm = reactive({ name: '', ch: '', code: '', unit: '', min: '' as number | '', max: '' as number | '', decimals: 1, icon: 'gateway' as DcwTemplateIcon, semantics: '' })
188
+ const tplIcons: Array<{ key: DcwTemplateIcon, label: string }> = [
189
+ { key: 'thermo', label: t('dcw.k422b8040') },
190
+ { key: 'pressure', label: t('dcw.k3x6ff041') },
191
+ { key: 'tension', label: t('dcw.k3z9xc042') },
192
+ { key: 'encoder', label: t('dcw.kjb3vhs043') },
193
+ { key: 'camera', label: t('dcw.k47atw044') },
194
+ { key: 'gateway', label: t('dcw.k48c07045') },
195
+ ]
196
+
197
+ async function doCreateTemplate(): Promise<void> {
198
+ tplError.value = ''
199
+ try {
200
+ if (!tplForm.name.trim()) throw new Error(t('dcw.k6ugbw2046'))
201
+ if (tplForm.min === '' || tplForm.max === '') throw new Error(t('dcw.k13awowo047'))
202
+ await dcw.createTemplate({
203
+ name: tplForm.name.trim(),
204
+ ch: tplForm.ch.trim() || tplForm.name.trim(),
205
+ code: tplForm.code.trim() || 'CUSTOM',
206
+ unit: tplForm.unit.trim() || '-',
207
+ min: Number(tplForm.min),
208
+ max: Number(tplForm.max),
209
+ decimals: Number(tplForm.decimals) || 0,
210
+ icon: tplForm.icon,
211
+ semantics: tplForm.semantics.trim() || undefined,
212
+ })
213
+ tplForm.name = ''
214
+ tplForm.ch = ''
215
+ tplForm.code = ''
216
+ tplForm.unit = ''
217
+ tplForm.min = ''
218
+ tplForm.max = ''
219
+ }
220
+ catch (err) {
221
+ tplError.value = apiErrorMessage(err)
222
+ }
223
+ }
224
+
225
+ async function doRemoveTemplate(key: string): Promise<void> {
226
+ tplError.value = ''
227
+ try {
228
+ await fetch(`/api/workshop/dcw/templates/${key}`, {
229
+ method: 'DELETE',
230
+ headers: { authorization: `Bearer ${document.cookie.match(/(?:^|;\s*)token=([^;]+)/)?.[1] ?? ''}` },
231
+ }).then(async (r) => {
232
+ const json = await r.json().catch(() => ({}))
233
+ if (json?.code !== 0) throw new Error(json?.message ?? t('dcw.k1bphrb3048'))
234
+ })
235
+ const i = dcw.templates.findIndex(t => t.key === key)
236
+ if (i >= 0) dcw.templates.splice(i, 1)
237
+ }
238
+ catch (err) {
239
+ tplError.value = apiErrorMessage(err)
240
+ }
241
+ }
242
+
243
+ const builtinCount = computed(() => dcw.templates.filter(t => t.builtin).length)
244
+ </script>
245
+
246
+ <template>
247
+ <div class="page">
248
+ <div class="aw-page-head">
249
+ <div>
250
+ <p class="aw-kicker">
251
+ AGENTWORKSHOP / LINE OPERATIONS
252
+ </p>
253
+ <h1>{{ $t('dcw.k1b2tk5c009') }}</h1>
254
+ <p class="sub">
255
+ {{ $t('dcw.k5q0aqi010') }}
256
+ </p>
257
+ </div>
258
+ <div class="badges mono">
259
+ <span
260
+ v-if="unassignedCount"
261
+ class="badge warn-badge"
262
+ :title="$t('dcw.k1i5a9vw001')"
263
+ >{{ $t('dcw.k3ootr6049') }} {{ unassignedCount }}</span>
264
+ <span class="badge">{{ $t('dcw.k3wj9n050') }} {{ dcw.lines.length }}</span>
265
+ <button
266
+ class="badge tpl-btn"
267
+ @click="tplOpen = true"
268
+ >
269
+ {{ $t('dcw.k11nndsp051') }} {{ dcw.templates.length }}
270
+ </button>
271
+ </div>
272
+ </div>
273
+
274
+ <p
275
+ v-if="quickErr"
276
+ class="banner bad"
277
+ >
278
+ {{ quickErr }}
279
+ </p>
280
+
281
+ <!-- 清单筛选:状态分段 + 名称搜索(复用 aw-seg / 页内 inp 语言) -->
282
+ <div class="fleet-filter">
283
+ <div class="aw-seg">
284
+ <button
285
+ :class="{ on: filterState === 'all' }"
286
+ @click="filterState = 'all'"
287
+ >
288
+ {{ $t('common.all') }} {{ cards.length }}
289
+ </button>
290
+ <button
291
+ :class="{ on: filterState === 'running' }"
292
+ @click="filterState = 'running'"
293
+ >
294
+ {{ $t('dcw.k1eox1el055') }} {{ runningCount }}
295
+ </button>
296
+ <button
297
+ :class="{ on: filterState === 'idle' }"
298
+ @click="filterState = 'idle'"
299
+ >
300
+ {{ $t('dcw.k149r6y7059') }} {{ cards.length - runningCount }}
301
+ </button>
302
+ </div>
303
+ <input
304
+ v-model="searchText"
305
+ class="inp fleet-search"
306
+ type="search"
307
+ :placeholder="$t('dcw.filterSearchPh')"
308
+ >
309
+ </div>
310
+
311
+ <!-- 产线卡片栅格 -->
312
+ <div class="line-grid aw-stagger">
313
+ <div
314
+ v-for="c in shownCards"
315
+ :key="c.line.id"
316
+ class="line-card aw-liftable"
317
+ :class="{ idle: !dcw.lineStateOf(c.line.id).active }"
318
+ :style="{ '--lc': c.line.color }"
319
+ >
320
+ <div class="lc-head">
321
+ <span class="lc-dot" />
322
+ <b class="lc-name">{{ c.line.name }}</b>
323
+ <span
324
+ class="lc-state"
325
+ :class="{ on: dcw.lineStateOf(c.line.id).active }"
326
+ >{{ dcw.lineStateOf(c.line.id).active ? $t('dcw.k1eox1el055') : $t('dcw.k149r6y7059') }}</span>
327
+ <button
328
+ class="lc-act"
329
+ :title="$t('common.edit')"
330
+ @click="openEdit(c)"
331
+ >
332
+ <span class="i-tabler-pencil" />
333
+ </button>
334
+ <button
335
+ class="lc-act danger"
336
+ :title="$t('dcw.k3xakp026')"
337
+ @click="openDelete(c)"
338
+ >
339
+ <span class="i-tabler-trash" />
340
+ </button>
341
+ </div>
342
+ <small
343
+ v-if="dcw.lineStateOf(c.line.id).active"
344
+ class="lc-run mono"
345
+ >{{ dcw.lineStateOf(c.line.id).productName }} · {{ dcw.lineStateOf(c.line.id).recipeName }} · {{ $t('dcw.k3zz0f052') }} {{ dcw.lineStateOf(c.line.id).taggedSamples }}</small>
346
+ <small
347
+ v-else
348
+ class="lc-run dim"
349
+ :class="{ ph: !c.line.description }"
350
+ >{{ c.line.description || $t('dcw.k18moyq1056') }}</small>
351
+ <div class="lc-stats mono">
352
+ <span>{{ $t('dcw.k45uio011') }} <b>{{ c.nodes }}</b></span>
353
+ <span>{{ $t('dcw.k3waz1012') }} <b>{{ c.products }}</b></span>
354
+ <span>{{ $t('dcw.k48grv013') }} <b>{{ c.recipes }}</b></span>
355
+ </div>
356
+ <div class="lc-ctl">
357
+ <template v-if="!dcw.lineStateOf(c.line.id).active">
358
+ <select
359
+ v-model="quickPick[c.line.id]"
360
+ class="inp"
361
+ :disabled="recipesOf(c.line.id).length === 0"
362
+ :title="$t('dcw.k1dhaby4003')"
363
+ >
364
+ <option value="">
365
+ {{ recipesOf(c.line.id).length ? $t('dcw.kutxzsz057') : $t('dcw.k1elczt9060') }}
366
+ </option>
367
+ <option
368
+ v-for="r in recipesOf(c.line.id)"
369
+ :key="r.id"
370
+ :value="r.id"
371
+ :disabled="r.params.length === 0"
372
+ >
373
+ {{ r.name }}({{ r.params.length }})
374
+ </option>
375
+ </select>
376
+ <button
377
+ class="pill-btn"
378
+ :disabled="quickBusy === c.line.id || !quickPick[c.line.id]"
379
+ @click="quickStart(c)"
380
+ >
381
+ {{ $t('dcw.k149b4vg014') }}
382
+ </button>
383
+ </template>
384
+ <button
385
+ v-else
386
+ class="pill-btn stop"
387
+ :disabled="quickBusy === c.line.id"
388
+ @click="quickStop(c)"
389
+ >
390
+ {{ $t('dcw.k148rclf015') }}
391
+ </button>
392
+ </div>
393
+ <NuxtLink
394
+ class="lc-manage"
395
+ :to="`/dcw/${c.line.id}`"
396
+ >
397
+ {{ $t('dcw.k1fwqw5g016') }}
398
+ </NuxtLink>
399
+ </div>
400
+
401
+ <!-- 新建产线卡 -->
402
+ <button
403
+ class="line-card new-card"
404
+ @click="openCreate"
405
+ >
406
+ <span class="i-tabler-plus" />
407
+ {{ $t('dcw.k1efe391017') }}
408
+ <small>{{ $t('dcw.k19dlbli053') }}</small>
409
+ </button>
410
+ </div>
411
+
412
+ <p
413
+ v-if="dcw.loaded && dcw.lines.length === 0"
414
+ class="banner"
415
+ >
416
+ {{ $t('dcw.k1sk85vs018') }}
417
+ </p>
418
+ <p
419
+ v-else-if="dcw.loaded && shownCards.length === 0"
420
+ class="banner"
421
+ >
422
+ {{ $t('dcw.filterEmpty') }}
423
+ </p>
424
+
425
+ <!-- 新建产线弹窗 -->
426
+ <Transition name="modal">
427
+ <div
428
+ v-if="createOpen"
429
+ class="modal-mask"
430
+ @click.self="createOpen = false"
431
+ >
432
+ <div class="modal">
433
+ <h3 class="m-title">
434
+ {{ $t('dcw.k1efe391017') }}
435
+ </h3>
436
+ <label class="f">
437
+ <span>{{ $t('dcw.k1b2ioko019') }}<em>*</em></span>
438
+ <input
439
+ v-model="createForm.name"
440
+ class="inp"
441
+ :placeholder="$t('dcw.kru37i3004')"
442
+ >
443
+ </label>
444
+ <label class="f">
445
+ <span>{{ $t('dcw.k24dxcd020') }}</span>
446
+ <input
447
+ v-model="createForm.description"
448
+ class="inp"
449
+ :placeholder="$t('dcw.k1f2nwsp005')"
450
+ >
451
+ </label>
452
+ <div class="f">
453
+ <span>{{ $t('dcw.k1x7nubr021') }}</span>
454
+ <div class="color-row">
455
+ <button
456
+ v-for="c in DCW_LINE_COLORS"
457
+ :key="c"
458
+ class="color-dot"
459
+ :class="{ on: createForm.color === c }"
460
+ :style="{ background: c }"
461
+ @click="createForm.color = createForm.color === c ? '' : c"
462
+ />
463
+ <small class="dim">{{ createForm.color || $t('dcw.k1qidbpy061', { p0: nextColor }) }}</small>
464
+ </div>
465
+ </div>
466
+ <p
467
+ v-if="createError"
468
+ class="m-err"
469
+ >
470
+ {{ createError }}
471
+ </p>
472
+ <div class="m-actions">
473
+ <button
474
+ class="mini-btn"
475
+ @click="createOpen = false"
476
+ >
477
+ {{ $t('dcw.k3xdnn022') }}
478
+ </button>
479
+ <button
480
+ class="pill-btn"
481
+ :disabled="createSaving || !createForm.name.trim()"
482
+ @click="doCreateLine"
483
+ >
484
+ {{ $t('dcw.k3wzi2023') }}
485
+ </button>
486
+ </div>
487
+ </div>
488
+ </div>
489
+ </Transition>
490
+
491
+ <!-- 编辑产线弹窗 -->
492
+ <Transition name="modal">
493
+ <div
494
+ v-if="editOpen"
495
+ class="modal-mask"
496
+ @click.self="editOpen = false"
497
+ >
498
+ <div class="modal">
499
+ <h3 class="m-title">
500
+ {{ $t('dcw.k5tq8wc071') }}
501
+ </h3>
502
+ <label class="f">
503
+ <span>{{ $t('dcw.k1b2ioko019') }}<em>*</em></span>
504
+ <input
505
+ v-model="editForm.name"
506
+ class="inp"
507
+ :placeholder="$t('dcw.kru37i3004')"
508
+ >
509
+ </label>
510
+ <label class="f">
511
+ <span>{{ $t('dcw.k24dxcd020') }}</span>
512
+ <input
513
+ v-model="editForm.description"
514
+ class="inp"
515
+ :placeholder="$t('dcw.k1f2nwsp005')"
516
+ >
517
+ </label>
518
+ <div class="f">
519
+ <span>{{ $t('dcw.k1x7nubr021') }}</span>
520
+ <div class="color-row">
521
+ <button
522
+ v-for="c in DCW_LINE_COLORS"
523
+ :key="c"
524
+ class="color-dot"
525
+ :class="{ on: editForm.color === c }"
526
+ :style="{ background: c }"
527
+ @click="editForm.color = editForm.color === c ? '' : c"
528
+ />
529
+ <small class="dim">{{ editForm.color || $t('dcw.k1qidbpy061', { p0: nextColor }) }}</small>
530
+ </div>
531
+ </div>
532
+ <p
533
+ v-if="editError"
534
+ class="m-err"
535
+ >
536
+ {{ editError }}
537
+ </p>
538
+ <div class="m-actions">
539
+ <button
540
+ class="mini-btn"
541
+ @click="editOpen = false"
542
+ >
543
+ {{ $t('dcw.k3xdnn022') }}
544
+ </button>
545
+ <button
546
+ class="pill-btn"
547
+ :disabled="editSaving || !editForm.name.trim()"
548
+ @click="doEditLine"
549
+ >
550
+ {{ $t('common.save') }}
551
+ </button>
552
+ </div>
553
+ </div>
554
+ </div>
555
+ </Transition>
556
+
557
+ <!-- 删除产线弹窗(两步确认;purge 勾选决定是否级联清理) -->
558
+ <Transition name="modal">
559
+ <div
560
+ v-if="delOpen && delCard"
561
+ class="modal-mask"
562
+ @click.self="delOpen = false"
563
+ >
564
+ <div class="modal">
565
+ <h3 class="m-title danger-title">
566
+ <span class="i-tabler-alert-triangle" />
567
+ {{ $t('dcw.k7xq2mfd063', { p0: delCard.line.name }) }}
568
+ </h3>
569
+ <p class="del-summary">
570
+ {{ delPurge
571
+ ? $t('dcw.k4r7nbd073', { p0: delCard.line.name, p1: delCard.nodes, p2: delCard.products, p3: delCard.recipes })
572
+ : $t('dcw.k1gp649b062', { p0: delCard.line.name, p1: delCard.nodes, p2: delCard.products, p3: delCard.recipes }) }}
573
+ </p>
574
+ <label class="del-purge">
575
+ <input
576
+ v-model="delPurge"
577
+ type="checkbox"
578
+ >
579
+ <span>{{ $t('dcw.k9m2vxa072') }}</span>
580
+ </label>
581
+ <p
582
+ v-if="delErr"
583
+ class="m-err"
584
+ >
585
+ {{ delErr }}
586
+ </p>
587
+ <div class="m-actions">
588
+ <button
589
+ class="mini-btn"
590
+ @click="delOpen = false"
591
+ >
592
+ {{ $t('dcw.k3xdnn022') }}
593
+ </button>
594
+ <button
595
+ class="pill-btn danger"
596
+ :disabled="delBusy"
597
+ @click="doDeleteLine"
598
+ >
599
+ {{ $t('dcw.k3xakp026') }}
600
+ </button>
601
+ </div>
602
+ </div>
603
+ </div>
604
+ </Transition>
605
+
606
+ <!-- 控制模板管理弹窗 -->
607
+ <Transition name="modal">
608
+ <div
609
+ v-if="tplOpen"
610
+ class="modal-mask"
611
+ @click.self="tplOpen = false"
612
+ >
613
+ <div class="modal wide">
614
+ <h3 class="m-title">
615
+ {{ $t('dcw.k11oadmx024') }} <small class="dim mono">{{ $t('dcw.k3x23c054') }} {{ builtinCount }} · {{ $t('dcw.k3t616a058') }} {{ dcw.templates.length - builtinCount }}</small>
616
+ </h3>
617
+ <p class="dim tpl-hint">
618
+ {{ $t('dcw.k2hlav5025') }}
619
+ </p>
620
+ <div class="tpl-list">
621
+ <div
622
+ v-for="t in dcw.templates"
623
+ :key="t.key"
624
+ class="tpl-row"
625
+ :title="t.semantics ?? ''"
626
+ >
627
+ <b>{{ t.name }}</b>
628
+ <small class="mono dim">{{ t.code }} · {{ t.min }}~{{ t.max }} {{ t.unit }}</small>
629
+ <small
630
+ v-if="t.semantics"
631
+ class="tpl-sem"
632
+ >{{ t.semantics.slice(0, 40) }}{{ t.semantics.length > 40 ? '…' : '' }}</small>
633
+ <span
634
+ class="tpl-tag"
635
+ :class="{ builtin: t.builtin }"
636
+ >{{ t.builtin ? $t('dcw.k3x23c054') : $t('dcw.k3t616a058') }}</span>
637
+ <button
638
+ v-if="!t.builtin"
639
+ class="mini-btn danger"
640
+ @click="doRemoveTemplate(t.key)"
641
+ >
642
+ {{ $t('dcw.k3xakp026') }}
643
+ </button>
644
+ </div>
645
+ </div>
646
+ <p class="sec-label">
647
+ {{ $t('dcw.k169eb4s027') }}
648
+ </p>
649
+ <div class="tpl-form">
650
+ <label class="f">
651
+ <span>{{ $t('dcw.k3xhia028') }}<em>*</em></span>
652
+ <input
653
+ v-model="tplForm.name"
654
+ class="inp"
655
+ :placeholder="$t('dcw.k1tooi3o006')"
656
+ >
657
+ </label>
658
+ <label class="f">
659
+ <span>{{ $t('dcw.k1bqk219029') }}</span>
660
+ <input
661
+ v-model="tplForm.ch"
662
+ class="inp"
663
+ :placeholder="$t('dcw.k698qz0007')"
664
+ >
665
+ </label>
666
+ <label class="f">
667
+ <span>{{ $t('dcw.k1ayxrqb030') }}</span>
668
+ <input
669
+ v-model="tplForm.code"
670
+ class="inp"
671
+ placeholder="如 MOTOR · I"
672
+ >
673
+ </label>
674
+ <label class="f">
675
+ <span>{{ $t('dcw.k3x4ef031') }}</span>
676
+ <input
677
+ v-model="tplForm.unit"
678
+ class="inp"
679
+ placeholder="如 A"
680
+ >
681
+ </label>
682
+ <label class="f">
683
+ <span>{{ $t('dcw.k1l9jv5m032') }}<em>*</em></span>
684
+ <input
685
+ v-model.number="tplForm.min"
686
+ type="number"
687
+ class="inp"
688
+ >
689
+ </label>
690
+ <label class="f">
691
+ <span>{{ $t('dcw.k1l9jv4p033') }}<em>*</em></span>
692
+ <input
693
+ v-model.number="tplForm.max"
694
+ type="number"
695
+ class="inp"
696
+ >
697
+ </label>
698
+ <label class="f">
699
+ <span>{{ $t('dcw.k3mxmcx034') }}</span>
700
+ <input
701
+ v-model.number="tplForm.decimals"
702
+ type="number"
703
+ class="inp"
704
+ >
705
+ </label>
706
+ <label class="f">
707
+ <span>{{ $t('dcw.k3xx56035') }}</span>
708
+ <select
709
+ v-model="tplForm.icon"
710
+ class="inp"
711
+ >
712
+ <option
713
+ v-for="ic in tplIcons"
714
+ :key="ic.key"
715
+ :value="ic.key"
716
+ >
717
+ {{ ic.label }}
718
+ </option>
719
+ </select>
720
+ </label>
721
+ </div>
722
+ <label class="f">
723
+ <span>{{ $t('dcw.k1b6qorg036') }}</span>
724
+ <textarea
725
+ v-model="tplForm.semantics"
726
+ class="inp"
727
+ rows="3"
728
+ :placeholder="$t('dcw.k1qdnfzc008')"
729
+ />
730
+ </label>
731
+ <p
732
+ v-if="tplError"
733
+ class="m-err"
734
+ >
735
+ {{ tplError }}
736
+ </p>
737
+ <div class="m-actions">
738
+ <button
739
+ class="mini-btn"
740
+ @click="tplOpen = false"
741
+ >
742
+ {{ $t('dcw.k3x62t037') }}
743
+ </button>
744
+ <button
745
+ class="pill-btn"
746
+ :disabled="!tplForm.name.trim() || tplForm.min === '' || tplForm.max === ''"
747
+ @click="doCreateTemplate"
748
+ >
749
+ {{ $t('dcw.k1bg9nga038') }}
750
+ </button>
751
+ </div>
752
+ </div>
753
+ </div>
754
+ </Transition>
755
+
756
+ <p
757
+ v-if="dcw.error"
758
+ class="banner bad"
759
+ >
760
+ {{ dcw.error }}
761
+ </p>
762
+ </div>
763
+ </template>
764
+
765
+ <style scoped>
766
+ .page { display: flex; flex-direction: column; gap: 14px; }
767
+ .badges { display: flex; gap: 8px; }
768
+ /* 状态徽章 = 玻璃芯片:双主题令牌化,浅色不再发白发黄 */
769
+ .badge {
770
+ padding: 3px 10px;
771
+ font-size: 11px;
772
+ color: var(--ink-faint);
773
+ background: var(--glass-bg);
774
+ border: 1px solid var(--glass-line);
775
+ border-radius: 999px;
776
+ backdrop-filter: var(--frost-blur);
777
+ -webkit-backdrop-filter: var(--frost-blur);
778
+ }
779
+ .warn-badge { color: var(--tone-warning-dot); background: var(--tone-warning-bg); border-color: color-mix(in srgb, var(--tone-warning-dot) 32%, transparent); }
780
+ .tpl-btn { color: var(--tone-info-dot); background: var(--tone-info-bg); border-color: color-mix(in srgb, var(--tone-info-dot) 34%, transparent); cursor: pointer; }
781
+ .tpl-btn:hover { border-color: color-mix(in srgb, var(--tone-info-dot) 60%, transparent); }
782
+
783
+ /* 清单筛选条:状态分段 + 搜索(与页内控件同一语言) */
784
+ .fleet-filter {
785
+ display: flex;
786
+ flex-wrap: wrap;
787
+ gap: 12px;
788
+ align-items: center;
789
+ margin-bottom: 14px;
790
+ }
791
+ .fleet-filter .aw-seg button {
792
+ font-family: var(--font-mono);
793
+ font-size: 11.5px;
794
+ font-variant-numeric: tabular-nums;
795
+ }
796
+ .fleet-search {
797
+ width: 220px;
798
+ margin-left: auto;
799
+ height: 30px;
800
+ padding: 0 10px;
801
+ font-size: 11.5px;
802
+ color: var(--ink);
803
+ background: var(--paper-deep);
804
+ border: 1px solid var(--line-strong);
805
+ border-radius: var(--radius-panel-sm);
806
+ }
807
+ .fleet-search:focus { outline: none; border-color: var(--tone-success-dot); box-shadow: 0 0 0 3px var(--accent-soft); }
808
+ .line-grid {
809
+ display: grid;
810
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
811
+ gap: 14px;
812
+ }
813
+ /* 产线卡 = Aurora Glass 仪表砖:半透 surface + 深模糊 + 内缘折射;
814
+ 运行卡在玻璃顶缘叠一线产线色洗染做「图」,待机卡收敛为磨砂做「底」 */
815
+ .line-card {
816
+ position: relative;
817
+ display: flex;
818
+ flex-direction: column;
819
+ gap: 9px;
820
+ padding: 14px 16px;
821
+ text-align: left;
822
+ background: var(--surface-glass);
823
+ backdrop-filter: var(--aurora-blur);
824
+ -webkit-backdrop-filter: var(--aurora-blur);
825
+ border: 1px solid var(--glass-line);
826
+ border-radius: var(--radius-panel);
827
+ box-shadow: var(--glass-edge);
828
+ transition: border-color var(--transition-base), transform var(--transition-base), box-shadow var(--transition-base);
829
+ }
830
+ @media (hover: hover) and (prefers-reduced-motion: no-preference) {
831
+ .line-card:hover {
832
+ border-color: color-mix(in srgb, var(--lc, #3aa0ff) 55%, var(--line-strong));
833
+ transform: translateY(-2px);
834
+ box-shadow: var(--glass-edge), var(--shadow-float);
835
+ }
836
+ }
837
+ .line-card:not(.idle) {
838
+ border-color: color-mix(in srgb, var(--lc, #3aa0ff) 38%, var(--line-strong));
839
+ background:
840
+ linear-gradient(180deg, color-mix(in srgb, var(--lc, #3aa0ff) 9%, transparent), transparent 46%),
841
+ var(--surface-glass);
842
+ }
843
+ .line-card.idle {
844
+ background: var(--frost-bg);
845
+ }
846
+ .line-card.idle .lc-dot {
847
+ box-shadow: none;
848
+ opacity: 0.72;
849
+ }
850
+ .line-card.idle .lc-name {
851
+ color: var(--ink-faint);
852
+ }
853
+ .lc-head { display: flex; gap: 8px; align-items: center; }
854
+ .lc-dot {
855
+ width: 10px;
856
+ height: 10px;
857
+ flex: none;
858
+ background: var(--lc);
859
+ border-radius: 4px;
860
+ box-shadow: 0 0 10px color-mix(in srgb, var(--lc) 70%, transparent);
861
+ }
862
+ .lc-name {
863
+ overflow: hidden;
864
+ font-size: 14.5px;
865
+ color: var(--ink);
866
+ text-overflow: ellipsis;
867
+ white-space: nowrap;
868
+ }
869
+ .lc-state {
870
+ margin-left: auto;
871
+ flex: none;
872
+ font-family: var(--font-mono);
873
+ font-size: 10px;
874
+ color: var(--ink-fainter);
875
+ }
876
+ .lc-state.on { color: var(--tone-success-dot); }
877
+ /* 头部图标钮:编辑/删除,悬停语义色染色 */
878
+ .lc-act {
879
+ display: inline-flex;
880
+ flex: none;
881
+ align-items: center;
882
+ justify-content: center;
883
+ width: 22px;
884
+ height: 22px;
885
+ padding: 0;
886
+ font-size: 13px;
887
+ color: var(--ink-fainter);
888
+ background: transparent;
889
+ border: 0;
890
+ border-radius: 6px;
891
+ cursor: pointer;
892
+ opacity: 0;
893
+ transition:
894
+ color 0.15s ease,
895
+ background 0.15s ease,
896
+ opacity 0.15s ease,
897
+ transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
898
+ }
899
+ .line-card:hover .lc-act,
900
+ .line-card:focus-within .lc-act { opacity: 1; }
901
+ .lc-act:hover { color: var(--tone-info-dot); background: var(--tone-info-bg); }
902
+ .lc-act.danger:hover { color: var(--tone-danger-dot); background: var(--tone-danger-bg); }
903
+ .lc-act:active { transform: scale(0.9); }
904
+ .lc-run { font-size: 10.5px; color: var(--tone-info-dot); }
905
+ .lc-run.dim { color: var(--ink-fainter); }
906
+ /* 无描述时的兜底提示进一步退后(77 张卡同文反复出现即是噪音;不压到 0.55 以下,保浅色可读) */
907
+ .lc-run.dim.ph { opacity: 0.75; }
908
+ .lc-stats {
909
+ display: flex;
910
+ gap: 14px;
911
+ font-size: 10.5px;
912
+ color: var(--ink-faint);
913
+ }
914
+ .lc-stats b { color: var(--ink); font-weight: 600; }
915
+ .lc-ctl { display: flex; gap: 8px; }
916
+ .lc-ctl .inp {
917
+ flex: 1;
918
+ min-width: 0;
919
+ height: 30px;
920
+ padding: 0 9px;
921
+ font-size: 11.5px;
922
+ color: var(--ink);
923
+ background: var(--paper-deep);
924
+ border: 1px solid var(--line-strong);
925
+ border-radius: var(--radius-panel-sm);
926
+ }
927
+ .lc-ctl .inp:focus { outline: none; border-color: var(--tone-success-dot); box-shadow: 0 0 0 3px var(--accent-soft); }
928
+ .pill-btn {
929
+ flex: none;
930
+ height: 30px;
931
+ padding: 0 14px;
932
+ font-size: 11.5px;
933
+ font-weight: 600;
934
+ color: var(--on-accent);
935
+ background: var(--accent);
936
+ border: 0;
937
+ border-radius: var(--radius-panel-sm);
938
+ cursor: pointer;
939
+ transition: background 0.15s, box-shadow 0.15s;
940
+ }
941
+ .pill-btn:hover:not(:disabled) { background: var(--accent-strong); box-shadow: 0 0 14px var(--accent-soft); }
942
+ .pill-btn:disabled { opacity: 0.4; cursor: default; }
943
+ .pill-btn.danger,
944
+ .pill-btn.stop { background: var(--tone-danger-bg); color: var(--tone-danger-dot); }
945
+ .pill-btn.danger:hover:not(:disabled),
946
+ .pill-btn.stop:hover:not(:disabled) { background: color-mix(in srgb, var(--tone-danger-dot) 26%, transparent); box-shadow: none; }
947
+ .lc-manage {
948
+ font-size: 11.5px;
949
+ font-weight: 600;
950
+ color: var(--tone-info-dot);
951
+ text-decoration: none;
952
+ }
953
+ .lc-manage:hover { text-decoration: underline; }
954
+ .new-card {
955
+ align-items: center;
956
+ justify-content: center;
957
+ gap: 6px;
958
+ font-size: 13px;
959
+ font-weight: 600;
960
+ color: var(--ink-faint);
961
+ background: transparent;
962
+ border: 1px dashed var(--line-strong);
963
+ box-shadow: none;
964
+ backdrop-filter: none;
965
+ -webkit-backdrop-filter: none;
966
+ cursor: pointer;
967
+ }
968
+ .new-card:hover { color: var(--tone-success-dot); border-color: var(--tone-success-dot); transform: none; }
969
+ .new-card small { font-weight: 400; font-size: 10px; color: var(--ink-fainter); }
970
+
971
+ .banner { padding: 8px 12px; font-size: 12px; border-radius: 10px; }
972
+ .banner.bad { color: var(--tone-danger-dot); background: var(--tone-danger-bg); border: 1px solid color-mix(in srgb, var(--tone-danger-dot) 30%, transparent); }
973
+
974
+ .modal-mask {
975
+ position: fixed;
976
+ inset: 0;
977
+ z-index: 80;
978
+ display: grid;
979
+ place-items: center;
980
+ background: var(--scrim);
981
+ backdrop-filter: blur(3px);
982
+ }
983
+ /* 弹窗入退场(occasional 频次;防跳变):遮罩淡入淡出,卡片 fade+上浮 8px 入场;
984
+ * modal 不锚定触发器,transform-origin 居中豁免;退场对称快出 */
985
+ .modal-enter-active { transition: opacity 180ms cubic-bezier(0.22, 1, 0.36, 1); }
986
+ .modal-leave-active { transition: opacity 130ms ease; }
987
+ .modal-enter-from,
988
+ .modal-leave-to { opacity: 0; }
989
+ .modal-enter-active .modal { transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1); }
990
+ .modal-enter-from .modal { transform: translateY(8px) scale(0.96); }
991
+ .modal {
992
+ display: flex;
993
+ flex-direction: column;
994
+ gap: 12px;
995
+ width: min(440px, calc(100vw - 40px));
996
+ max-height: 82vh;
997
+ padding: 18px;
998
+ overflow: auto;
999
+ background: var(--surface-glass-strong);
1000
+ backdrop-filter: var(--aurora-blur);
1001
+ -webkit-backdrop-filter: var(--aurora-blur);
1002
+ border: 1px solid var(--glass-line);
1003
+ border-radius: var(--radius-panel);
1004
+ box-shadow: var(--glass-edge), var(--shadow-float);
1005
+ }
1006
+ .modal.wide { width: min(640px, calc(100vw - 40px)); }
1007
+ .m-title { display: flex; gap: 10px; align-items: baseline; font-size: 15px; color: var(--ink); }
1008
+ .m-title.danger-title { align-items: center; color: var(--tone-danger-dot); }
1009
+ .f { display: flex; flex-direction: column; gap: 5px; font-size: 11px; color: var(--ink-faint); }
1010
+ .f em { color: var(--tone-danger-dot); font-style: normal; }
1011
+ .f .inp {
1012
+ height: 30px;
1013
+ padding: 0 9px;
1014
+ font-size: 12px;
1015
+ color: var(--ink);
1016
+ background: var(--paper-deep);
1017
+ border: 1px solid var(--line-strong);
1018
+ border-radius: var(--radius-panel-sm);
1019
+ }
1020
+ .f .inp:focus { outline: none; border-color: var(--tone-success-dot); box-shadow: 0 0 0 3px var(--accent-soft); }
1021
+ .color-row { display: flex; gap: 7px; align-items: center; }
1022
+ .color-dot {
1023
+ width: 20px;
1024
+ height: 20px;
1025
+ border: 2px solid transparent;
1026
+ border-radius: 6px;
1027
+ cursor: pointer;
1028
+ }
1029
+ .color-dot.on { border-color: var(--ink); box-shadow: 0 0 10px currentColor; }
1030
+ .m-actions { display: flex; gap: 8px; justify-content: flex-end; }
1031
+ .m-err { font-size: 11px; color: var(--tone-danger-dot); }
1032
+ .dim { color: var(--ink-fainter); }
1033
+ .sec-label { font-size: 10px; font-weight: 700; color: var(--ink-fainter); letter-spacing: 0.16em; }
1034
+ /* 删除确认:摘要行 + 级联勾选 */
1035
+ .del-summary { font-size: 12px; line-height: 1.6; color: var(--ink-soft); }
1036
+ .del-purge { display: flex; gap: 8px; align-items: center; font-size: 12px; color: var(--ink); cursor: pointer; }
1037
+ .del-purge input {
1038
+ width: 14px;
1039
+ height: 14px;
1040
+ accent-color: var(--tone-danger-dot);
1041
+ }
1042
+ .tpl-hint { font-size: 11px; }
1043
+ .tpl-list { display: flex; flex-direction: column; gap: 6px; }
1044
+ .tpl-row {
1045
+ display: flex;
1046
+ gap: 10px;
1047
+ align-items: center;
1048
+ padding: 7px 10px;
1049
+ font-size: 12px;
1050
+ color: var(--ink);
1051
+ background: var(--frost-bg);
1052
+ border: 1px solid var(--glass-line);
1053
+ border-radius: 9px;
1054
+ }
1055
+ .tpl-row small { flex: 1; }
1056
+ .tpl-tag {
1057
+ padding: 1px 8px;
1058
+ font-family: var(--font-mono);
1059
+ font-size: 9px;
1060
+ color: var(--tone-info-dot);
1061
+ border: 1px solid color-mix(in srgb, var(--tone-info-dot) 40%, transparent);
1062
+ border-radius: 5px;
1063
+ }
1064
+ .tpl-tag.builtin { color: var(--ink-fainter); border-color: var(--line-strong); }
1065
+ .tpl-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
1066
+ .tpl-sem { flex: 1; color: var(--ink-faint); }
1067
+ textarea.inp { height: auto; padding: 6px 9px; font-size: 11.5px; resize: vertical; }
1068
+ .mini-btn {
1069
+ padding: 4px 10px;
1070
+ font-size: 10.5px;
1071
+ color: var(--ink-faint);
1072
+ background: var(--paper-deep);
1073
+ border: 1px solid var(--line-strong);
1074
+ border-radius: 7px;
1075
+ cursor: pointer;
1076
+ }
1077
+ .mini-btn:hover { border-color: var(--ink-faint); color: var(--ink); }
1078
+ .mini-btn.danger { color: var(--tone-danger-dot); }
1079
+ </style>