agentworkshop 0.7.39 → 0.7.40

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 (373) hide show
  1. package/.AgentWorkShop/prompts/host-tools.json +55 -0
  2. package/.output/.AgentWorkShop/prompts/host-tools.json +55 -0
  3. package/.output/nitro.json +1 -1
  4. package/.output/public/_nuxt/88GOowgO2.js +1 -0
  5. package/.output/public/_nuxt/{BNT8gYU92.js → BC9wjTJK2.js} +2 -2
  6. package/.output/public/_nuxt/BOt2SP4z.js +1 -0
  7. package/.output/public/_nuxt/BP-NyKVG.js +18 -0
  8. package/.output/public/_nuxt/{HBLJ-wei.js → BXxH0UQE.js} +1 -1
  9. package/.output/public/_nuxt/{zIGh7R49.js → BneN7fbu.js} +1 -1
  10. package/.output/public/_nuxt/BsROmY89.js +1 -0
  11. package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
  12. package/.output/public/_nuxt/CFxtLwjp.js +1 -0
  13. package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
  14. package/.output/public/_nuxt/C_iYD-EB.js +1 -0
  15. package/.output/public/_nuxt/{DAe0h2MB.js → ClILjvWH.js} +1 -1
  16. package/.output/public/_nuxt/CssgYAAL2.js +1 -0
  17. package/.output/public/_nuxt/{CY9LbPCI.js → CzOCSGLe.js} +1 -1
  18. package/.output/public/_nuxt/D8qvkfyY.js +1 -0
  19. package/.output/public/_nuxt/DCn7QZ5N.js +2 -0
  20. package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
  21. package/.output/public/_nuxt/{CojCTLKD.js → DGj-WEBg.js} +1 -1
  22. package/.output/public/_nuxt/{DDShH71m.js → DS8qj8EL.js} +1 -1
  23. package/.output/public/_nuxt/DTFCy6h-.js +1 -0
  24. package/.output/public/_nuxt/{DrYV5i68.js → DUjW5Yq5.js} +1 -1
  25. package/.output/public/_nuxt/DZshyNLa.js +1 -0
  26. package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
  27. package/.output/public/_nuxt/DcsCcxp4.js +1 -0
  28. package/.output/public/_nuxt/{DUrecWPx.js → Dihnimlk.js} +2 -2
  29. package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
  30. package/.output/public/_nuxt/{CUDwzgLV.js → Dr9v6C0f.js} +9 -9
  31. package/.output/public/_nuxt/{8NMOXZOV.js → Du9ZJtnj.js} +1 -1
  32. package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
  33. package/.output/public/_nuxt/_id_.CM4bIgKY.css +1 -0
  34. package/.output/public/_nuxt/_id_.bNV69Dtf.css +1 -0
  35. package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
  36. package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
  37. package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
  38. package/.output/public/_nuxt/builds/latest.json +1 -1
  39. package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +1 -0
  40. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
  41. package/.output/public/_nuxt/daq.B0D3dPAc.css +1 -0
  42. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
  43. package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
  44. package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -0
  45. package/.output/public/_nuxt/logs.BpKzmKsh.css +1 -0
  46. package/.output/public/_nuxt/monitor.D9pe7qN6.css +1 -0
  47. package/.output/public/_nuxt/pages.D7aSDlUI.css +1 -0
  48. package/.output/public/_nuxt/permissions.DwTVlFe6.css +1 -0
  49. package/.output/public/_nuxt/plugins.sb4nDGqr.css +1 -0
  50. package/.output/public/_nuxt/{Cbq-mryP.js → qr6R6Ha1.js} +1 -1
  51. package/.output/public/_nuxt/settings.BTgR-NiE.css +1 -0
  52. package/.output/public/_nuxt/teams.CxnxKVVs.css +1 -0
  53. package/.output/public/_nuxt/tokens.BFY7qv4f.css +1 -0
  54. package/.output/public/_nuxt/{town.DKIAhKnx.css → town.BBXZmeoP.css} +1 -1
  55. package/.output/public/_nuxt/users.DLgZ1D0h.css +1 -0
  56. package/.output/public/_nuxt/workshop.CR_QzeAN.css +1 -0
  57. package/.output/public/_nuxt/{BzHEilTt.js → xh3_vYcu.js} +1 -1
  58. package/.output/public/_nuxt/yF0aYMF1.js +4 -0
  59. package/.output/server/chunks/_/entity.mjs.map +1 -1
  60. package/.output/server/chunks/_/nitro.mjs +2225 -1445
  61. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  62. package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView-BuLAsmpR.mjs} +288 -206
  63. package/.output/server/chunks/build/TownView-BuLAsmpR.mjs.map +1 -0
  64. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-BU1wfOID.mjs} +138 -138
  65. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs.map → _id_-BU1wfOID.mjs.map} +1 -1
  66. package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-BYsEua57.mjs} +33 -32
  67. package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +1 -0
  68. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BxlDcJHg.mjs} +293 -259
  69. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BxlDcJHg.mjs.map} +1 -1
  70. package/.output/server/chunks/build/{agents-Bvin73QY.mjs → agents-CLQqJF2y.mjs} +21 -27
  71. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +1 -0
  72. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs → aml-_7FdFL2p.mjs} +109 -109
  73. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs.map → aml-_7FdFL2p.mjs.map} +1 -1
  74. package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -1
  75. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs → channel-templates-p6SZNjlM.mjs} +19 -19
  76. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs.map → channel-templates-p6SZNjlM.mjs.map} +1 -1
  77. package/.output/server/chunks/build/{daq-C03Uu5UE.mjs → daq-cgyjKmJu.mjs} +188 -122
  78. package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +1 -0
  79. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-CoTxBlAZ.mjs} +45 -45
  80. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-CoTxBlAZ.mjs.map} +1 -1
  81. package/.output/server/chunks/build/{default-CZmZFpqH.mjs → default-BE4r0FZN.mjs} +152 -58
  82. package/.output/server/chunks/build/{default-CZmZFpqH.mjs.map → default-BE4r0FZN.mjs.map} +1 -1
  83. package/.output/server/chunks/build/{logs-B-xhuqln.mjs → logs-BSOeZwwI.mjs} +34 -34
  84. package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs-BSOeZwwI.mjs.map} +1 -1
  85. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs → monitor-C0okQaXz.mjs} +33 -33
  86. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs.map → monitor-C0okQaXz.mjs.map} +1 -1
  87. package/.output/server/chunks/build/{pages-BO7yZsXA.mjs → pages-DFxjn9Xa.mjs} +33 -17
  88. package/.output/server/chunks/build/pages-DFxjn9Xa.mjs.map +1 -0
  89. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs → permissions-68SsNjex.mjs} +12 -12
  90. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs.map → permissions-68SsNjex.mjs.map} +1 -1
  91. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs → plugins-lGlUr1lj.mjs} +17 -17
  92. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs.map → plugins-lGlUr1lj.mjs.map} +1 -1
  93. package/.output/server/chunks/build/{settings-DoQ2Oq3P.mjs → settings-Ddd1qx2Z.mjs} +50 -47
  94. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +1 -0
  95. package/.output/server/chunks/build/{teams-2kz3CusY.mjs → teams-MEDLZdMn.mjs} +38 -34
  96. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +1 -0
  97. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs → tokens-C7fTM5C2.mjs} +14 -14
  98. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs.map → tokens-C7fTM5C2.mjs.map} +1 -1
  99. package/.output/server/chunks/build/{town-BOl6hs9B.mjs → town-qMPql9Se.mjs} +36 -10
  100. package/.output/server/chunks/build/town-qMPql9Se.mjs.map +1 -0
  101. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs +1 -1
  102. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs +1 -1
  103. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
  104. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
  105. package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
  106. package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
  107. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
  108. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
  109. package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
  110. package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
  111. package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
  112. package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
  113. package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
  114. package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
  115. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
  116. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
  117. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
  118. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  120. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  121. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs.map +1 -1
  122. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
  123. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
  124. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
  125. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
  126. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
  127. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
  128. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
  129. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
  131. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
  133. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  134. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
  135. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
  137. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
  139. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
  140. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
  141. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
  142. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
  143. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
  144. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
  145. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
  146. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
  147. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
  148. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
  149. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
  150. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
  151. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  152. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  153. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  154. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  155. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  156. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  157. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +2 -0
  159. package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
  160. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -1
  161. package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
  162. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  164. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  165. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  166. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  167. package/.output/server/chunks/routes/renderer.mjs +3 -3
  168. package/.output/server/chunks/virtual/entry.mjs +51 -25
  169. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  170. package/.output/server/package.json +1 -1
  171. package/README-zh.md +199 -35
  172. package/README.md +739 -591
  173. package/app/app.vue +9 -3
  174. package/app/assets/css/main.css +517 -7
  175. package/app/components/AppHeader.vue +98 -7
  176. package/app/components/AppSidebar.vue +117 -11
  177. package/app/components/AwChart.vue +2 -1
  178. package/app/components/workshop/AgentInspectorDrawer.vue +6 -6
  179. package/app/components/workshop/AgentLanesView.vue +52 -1
  180. package/app/components/workshop/AssetLibrary.vue +4 -2
  181. package/app/components/workshop/ChannelSessionList.vue +4 -2
  182. package/app/components/workshop/Composer.vue +96 -0
  183. package/app/components/workshop/DaqNodeRow.vue +19 -0
  184. package/app/components/workshop/DaqTemplateIcon.vue +33 -0
  185. package/app/components/workshop/DeviceTwinPanel.vue +37 -35
  186. package/app/components/workshop/EventBlock.vue +3 -3
  187. package/app/components/workshop/InspectorPanel.vue +47 -11
  188. package/app/components/workshop/LiveAgentBar.vue +34 -0
  189. package/app/components/workshop/TaskBoardView.vue +83 -33
  190. package/app/components/workshop/TranscriptTimeline.vue +56 -0
  191. package/app/components/workshop/blocks/ClusterError.vue +1 -1
  192. package/app/components/workshop/blocks/ClusterRoute.vue +3 -1
  193. package/app/components/workshop/blocks/ClusterStatus.vue +2 -2
  194. package/app/components/workshop/blocks/ClusterStream.vue +8 -2
  195. package/app/components/workshop/town/ModelPreview3D.vue +1 -1
  196. package/app/components/workshop/town/TownView.vue +507 -82
  197. package/app/composables/useResponsive.ts +88 -0
  198. package/app/composables/useRouteMeta.ts +3 -0
  199. package/app/composables/workshop/useDcwStream.ts +1 -1
  200. package/app/composables/workshop/useVisibleInterval.ts +2 -2
  201. package/app/composables/workshop/useWorkshopApi.ts +1 -1
  202. package/app/layouts/default.vue +46 -2
  203. package/app/pages/aml/index.vue +712 -616
  204. package/app/pages/daq/[id].vue +3 -2
  205. package/app/pages/daq/index.vue +209 -9
  206. package/app/pages/dcw/[id].vue +109 -28
  207. package/app/pages/dcw/index.vue +83 -23
  208. package/app/pages/index.vue +35 -1
  209. package/app/pages/logs/index.vue +147 -13
  210. package/app/pages/monitor.vue +56 -12
  211. package/app/pages/permissions/index.vue +34 -0
  212. package/app/pages/plugins/index.vue +35 -4
  213. package/app/pages/settings.vue +90 -21
  214. package/app/pages/tokens.vue +23 -0
  215. package/app/pages/town.vue +50 -5
  216. package/app/pages/users.vue +23 -0
  217. package/app/pages/workshop/agents.vue +92 -2
  218. package/app/pages/workshop/channel-templates.vue +53 -0
  219. package/app/pages/workshop/index.vue +14 -0
  220. package/app/pages/workshop/teams.vue +66 -6
  221. package/app/pages/workshop/w/[wsId].vue +202 -14
  222. package/app/plugins/aw-plugins.client.ts +13 -8
  223. package/app/stores/app.ts +33 -1
  224. package/app/stores/workshop/user.ts +1 -1
  225. package/app/stores/workshop/workspaces.ts +18 -2
  226. package/app/utils/api-error.ts +1 -1
  227. package/cli/commands/build.mjs +8 -1
  228. package/docker-compose.yml +6 -0
  229. package/i18n/locales/en.ts +2 -0
  230. package/i18n/locales/zh-CN.ts +2 -0
  231. package/nuxt.config.ts +31 -0
  232. package/package.json +9 -1
  233. package/scripts/_dbg-param-map-e2e.ts +242 -0
  234. package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
  235. package/scripts/_dbg-param-real-scenario.mjs +153 -0
  236. package/scripts/api-live-e2e.mjs +40 -8
  237. package/scripts/capture-walkthrough.mjs +232 -0
  238. package/scripts/render-walkthrough-figure.mjs +46 -0
  239. package/scripts/seed-walkthrough-fixture.mjs +143 -0
  240. package/scripts/test-plugin-hardening.mjs +8 -0
  241. package/scripts/ui/admin-shots.mjs +46 -0
  242. package/scripts/ui/audit-layout.mjs +301 -0
  243. package/scripts/ui/bump-microfont.mjs +39 -0
  244. package/scripts/ui/lib.mjs +198 -0
  245. package/scripts/ui/linkcheck2.mjs +37 -0
  246. package/scripts/ui/live-render-check.mjs +72 -0
  247. package/scripts/ui/md-lint.mjs +80 -0
  248. package/scripts/ui/perf-audit.mjs +132 -0
  249. package/scripts/ui/readme-assets.mjs +30 -0
  250. package/scripts/ui/readme-shots.mjs +42 -0
  251. package/scripts/ui/record.mjs +225 -0
  252. package/scripts/ui/seed-admin.mjs +68 -0
  253. package/scripts/ui/seed-demo.mjs +83 -0
  254. package/scripts/ui/shot-hero.mjs +25 -0
  255. package/scripts/ui/shots.mjs +75 -0
  256. package/scripts/ui/sim-ui-shot.mjs +16 -0
  257. package/scripts/ui/verify-nav.mjs +51 -0
  258. package/scripts/ui/verify-responsive.mjs +285 -0
  259. package/server/api/system/config-groups/[id].patch.ts +1 -1
  260. package/server/api/system/config-groups/index.post.ts +1 -1
  261. package/server/api/system/settings/events.get.ts +1 -1
  262. package/server/api/system/settings/index.patch.ts +1 -1
  263. package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
  264. package/server/api/workshop/aml/datasets/[id].get.ts +8 -1
  265. package/server/api/workshop/daq/infra/reconnect.post.ts +1 -1
  266. package/server/api/workshop/daq/templates/[key].delete.ts +1 -0
  267. package/server/api/workshop/daq/templates/[key].patch.ts +1 -0
  268. package/server/api/workshop/daq/templates/index.post.ts +1 -0
  269. package/server/api/workshop/dcw/[id]/write.post.ts +10 -2
  270. package/server/api/workshop/dcw/[id].delete.ts +5 -2
  271. package/server/api/workshop/dcw/index.get.ts +2 -0
  272. package/server/api/workshop/dcw/lines/[id].patch.ts +1 -1
  273. package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -0
  274. package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -0
  275. package/server/api/workshop/dcw/params/[id].delete.ts +19 -0
  276. package/server/api/workshop/dcw/params/[id].patch.ts +23 -0
  277. package/server/api/workshop/dcw/params/index.get.ts +27 -0
  278. package/server/api/workshop/dcw/params/index.post.ts +25 -0
  279. package/server/api/workshop/permissions/index.get.ts +3 -1
  280. package/server/plugins/aw-plugins.ts +14 -3
  281. package/server/plugins/dev-stability-guard.ts +3 -1
  282. package/server/plugins/system-config.ts +6 -1
  283. package/server/services/system-config.ts +28 -9
  284. package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
  285. package/server/services/workshop/agents/agent-interface.ts +8 -6
  286. package/server/services/workshop/agents/base-agent.ts +18 -4
  287. package/server/services/workshop/agents/claude-agent.ts +7 -3
  288. package/server/services/workshop/agents/codex-agent.ts +3 -3
  289. package/server/services/workshop/agents/dsh-agent.ts +3 -3
  290. package/server/services/workshop/agents/goose-agent.ts +0 -1
  291. package/server/services/workshop/agents/harness-terminal.ts +14 -1
  292. package/server/services/workshop/agents/hermes-agent.ts +4 -2
  293. package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
  294. package/server/services/workshop/agents/industrial-context.ts +4 -1
  295. package/server/services/workshop/agents/industrial-tools.ts +164 -6
  296. package/server/services/workshop/agents/omp-agent.ts +15 -9
  297. package/server/services/workshop/agents/opencode-agent.ts +47 -21
  298. package/server/services/workshop/agents/qwen-agent.ts +10 -5
  299. package/server/services/workshop/aml/entity.ts +3 -1
  300. package/server/services/workshop/aml/python-runtime.ts +3 -2
  301. package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
  302. package/server/services/workshop/daq/daq-controller.ts +18 -3
  303. package/server/services/workshop/daq/daq-runtime.ts +5 -3
  304. package/server/services/workshop/daq/drivers.ts +63 -8
  305. package/server/services/workshop/daq/host-ports.ts +3 -2
  306. package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
  307. package/server/services/workshop/db/channel.repo.ts +4 -0
  308. package/server/services/workshop/db/ops.repo.ts +5 -2
  309. package/server/services/workshop/db/retention.ts +8 -3
  310. package/server/services/workshop/dcw/dcw-controller.ts +143 -16
  311. package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
  312. package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
  313. package/server/services/workshop/dcw/drivers.ts +5 -2
  314. package/server/services/workshop/dcw/param-conversion.ts +78 -0
  315. package/server/services/workshop/dcw/param-limits.ts +142 -0
  316. package/server/services/workshop/dcw/param-map.repo.ts +305 -0
  317. package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
  318. package/server/services/workshop/runtime/agent-runtime.ts +4 -0
  319. package/server/services/workshop/runtime/manager.ts +9 -2
  320. package/server/services/workshop/settings.ts +4 -2
  321. package/server/types/pg.d.ts +49 -0
  322. package/server/utils/stability-severity.ts +13 -2
  323. package/shared/config/groups.mjs +22 -1
  324. package/shared/config/home.d.mts +28 -0
  325. package/shared/dcw-protocol.ts +136 -0
  326. package/uno.config.ts +12 -1
  327. package/.output/public/_nuxt/BFkgkW8u.js +0 -4
  328. package/.output/public/_nuxt/C5gUkRwn.js +0 -1
  329. package/.output/public/_nuxt/CM12FuB42.js +0 -1
  330. package/.output/public/_nuxt/CZiw7sEV.js +0 -1
  331. package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
  332. package/.output/public/_nuxt/CdPzkOMV.js +0 -1
  333. package/.output/public/_nuxt/CmsOtOAA2.js +0 -1
  334. package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
  335. package/.output/public/_nuxt/D20Yamr4.js +0 -1
  336. package/.output/public/_nuxt/DmqknNvV.js +0 -18
  337. package/.output/public/_nuxt/DnUuIR83.js +0 -1
  338. package/.output/public/_nuxt/S_fVQwJK.js +0 -1
  339. package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
  340. package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
  341. package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
  342. package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
  343. package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
  344. package/.output/public/_nuxt/ahgEJu2R.js +0 -1
  345. package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
  346. package/.output/public/_nuxt/builds/meta/4457d63f-0866-4cea-b320-04a321f24795.json +0 -1
  347. package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
  348. package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
  349. package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
  350. package/.output/public/_nuxt/default.BXI_fZUi.css +0 -1
  351. package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
  352. package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
  353. package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
  354. package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
  355. package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
  356. package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
  357. package/.output/public/_nuxt/pz2JDAM7.js +0 -2
  358. package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
  359. package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
  360. package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
  361. package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
  362. package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
  363. package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
  364. package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
  365. package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
  366. package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
  367. package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
  368. package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
  369. package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
  370. package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
  371. package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
  372. /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
  373. /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
@@ -7,7 +7,7 @@
7
7
  import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
8
8
  import { useRoute } from 'vue-router'
9
9
  import { useDcwStream } from '@/app/composables/workshop/useDcwStream'
10
- import { useDaqStream, type DaqFrameLive } from '@/app/composables/workshop/useDaqStream'
10
+ import { useDaqStream, type DaqFrameLive, type DaqTsdbPoint } from '@/app/composables/workshop/useDaqStream'
11
11
  import { useDeviceTwins } from '@/app/composables/workshop/useDeviceTwins'
12
12
  import { daqKeyFromRef, DAQ_DRIVERS, type DaqDriverKind, type DaqNodeState, type DriverConfigField, type DriverTestResult as DaqDriverTestResult } from '#shared/daq-protocol'
13
13
 
@@ -210,7 +210,8 @@ const availableDevices = computed(() =>
210
210
  )
211
211
 
212
212
  // ---------- 历史(时序库) ----------
213
- type ChartRow = { at: number, value?: number, avg?: number, min?: number, max?: number }
213
+ /** 历史点 = 数采时序点(直接用 DaqTsdbPoint:手写副本漏了 state/cnt,模板取值即报错) */
214
+ type ChartRow = DaqTsdbPoint
214
215
  const historyPoints = ref<ChartRow[]>([])
215
216
  const bucketMs = ref<number>(15000)
216
217
  const histLoading = ref(false)
@@ -263,6 +263,75 @@ const filteredNodes = computed<DaqNodeLive[]>(() => daq.nodes.filter((n) => {
263
263
  return true
264
264
  }))
265
265
 
266
+ /* ── 节点表窗口化渲染(virtual window) ────────────────────────────────────────
267
+ * 为什么必须做:节点表是**全量**渲染的,而每一行都带实时读数 + sparkline,
268
+ * 每次读数合批(500ms)都会点亮所有行。实测 498 个节点时:
269
+ * · DOM 节点 13.5 万
270
+ * · 主线程每秒 ≈3.8 万次 DOM 变更(其中 99.6% 来自这张表)
271
+ * · 帧率掉到 24 FPS、92% 的帧超时、单帧最大间隔 361ms、长任务阻塞 936ms
272
+ * 也就是说"数据刷新太频繁导致卡顿"的根因不是刷新频率,而是**离屏行也在被补丁**。
273
+ *
274
+ * 做法:按页面滚动窗口只挂载可见行 + 上下各 OVERSCAN 行,用两条占位 <tr> 撑起
275
+ * 滚动高度。行的实际高度由 contain-intrinsic-size 固定为 42px(见样式),所以
276
+ * 窗口计算不需要测量 DOM。
277
+ *
278
+ * 为什么用**页面滚动**而不是容器滚动:这张表的滚动条是页面的(.table-card 只负责
279
+ * 横向),所以窗口要跟着 window.scrollY 走。
280
+ */
281
+ const VS_ROW_H = 42
282
+ const VS_OVERSCAN = 8
283
+ const nodesTableRef = ref<HTMLTableElement | null>(null)
284
+ const winStart = ref(0)
285
+ const winEnd = ref(60)
286
+
287
+ const visibleNodes = computed<DaqNodeLive[]>(() => {
288
+ const list = filteredNodes.value
289
+ const s = Math.min(winStart.value, Math.max(0, list.length - 1))
290
+ return list.slice(s, winEnd.value)
291
+ })
292
+ const padTopPx = computed(() => winStart.value * VS_ROW_H)
293
+ const padBottomPx = computed(() => Math.max(0, (filteredNodes.value.length - winEnd.value) * VS_ROW_H))
294
+
295
+ let winRaf = 0
296
+ function updateNodeWindow(): void {
297
+ const el = nodesTableRef.value
298
+ if (!el) return
299
+ const total = filteredNodes.value.length
300
+ const vh = window.innerHeight || 800
301
+ // 表格顶部相对文档的坐标(表头 40px 也算进"还没到第一行")
302
+ const tableTop = el.getBoundingClientRect().top + window.scrollY + 40
303
+ const firstVisible = Math.floor((window.scrollY - tableTop) / VS_ROW_H)
304
+ const count = Math.ceil(vh / VS_ROW_H) + VS_OVERSCAN * 2
305
+ const s = Math.max(0, Math.min(firstVisible - VS_OVERSCAN, Math.max(0, total - 1)))
306
+ const e = Math.max(s, Math.min(total, s + count))
307
+ if (s !== winStart.value) winStart.value = s
308
+ if (e !== winEnd.value) winEnd.value = e
309
+ }
310
+ function scheduleNodeWindow(): void {
311
+ if (winRaf) return
312
+ winRaf = requestAnimationFrame(() => {
313
+ winRaf = 0
314
+ updateNodeWindow()
315
+ })
316
+ }
317
+
318
+ onMounted(() => {
319
+ updateNodeWindow()
320
+ window.addEventListener('scroll', scheduleNodeWindow, { passive: true })
321
+ window.addEventListener('resize', scheduleNodeWindow, { passive: true })
322
+ })
323
+ onBeforeUnmount(() => {
324
+ window.removeEventListener('scroll', scheduleNodeWindow)
325
+ window.removeEventListener('resize', scheduleNodeWindow)
326
+ if (winRaf) cancelAnimationFrame(winRaf)
327
+ })
328
+ // 筛选/搜索改变行数后窗口必须重算(否则会停在旧的切片上,看起来"表空了")
329
+ watch(() => filteredNodes.value.length, () => {
330
+ winStart.value = 0
331
+ winEnd.value = Math.min(filteredNodes.value.length, winEnd.value || 60)
332
+ scheduleNodeWindow()
333
+ })
334
+
266
335
  // 设备名映射(device-twins 注册表;绑定筛选下拉展示用)
267
336
  const deviceTwins = useDeviceTwins()
268
337
  const nodeDevices = new Map<string, string>()
@@ -1187,7 +1256,7 @@ async function doReconnect(): Promise<void> {
1187
1256
  class="opt-series"
1188
1257
  >
1189
1258
  <div
1190
- v-for="ch in optSeriesOf[r.id].channels"
1259
+ v-for="ch in (optSeriesOf[r.id]?.channels ?? [])"
1191
1260
  :key="ch.nodeId"
1192
1261
  class="opt-ch"
1193
1262
  >
@@ -1201,7 +1270,7 @@ async function doReconnect(): Promise<void> {
1201
1270
  <span class="mono">{{ ch.ch }} {{ fmtPoint(ch.points.at(-1)?.value ?? ch.points.at(-1)?.avg) }}{{ ch.unit }}</span>
1202
1271
  </div>
1203
1272
  <p
1204
- v-if="optSeriesOf[r.id].channels.length === 0"
1273
+ v-if="(optSeriesOf[r.id]?.channels.length ?? 0) === 0"
1205
1274
  class="opt-empty"
1206
1275
  >
1207
1276
  {{ $t('daq.k1opte002') }}
@@ -1472,7 +1541,10 @@ async function doReconnect(): Promise<void> {
1472
1541
  </div>
1473
1542
  </div>
1474
1543
  </div>
1475
- <table class="nodes-table">
1544
+ <table
1545
+ ref="nodesTableRef"
1546
+ class="nodes-table"
1547
+ >
1476
1548
  <thead>
1477
1549
  <tr>
1478
1550
  <th>{{ $t('daq.k45uio067') }}</th>
@@ -1496,11 +1568,33 @@ async function doReconnect(): Promise<void> {
1496
1568
  </tr>
1497
1569
  </thead>
1498
1570
  <tbody>
1571
+ <!-- 上占位行:撑起"已滚过"的高度,让滚动条与真实全量列表一致 -->
1572
+ <tr
1573
+ v-if="padTopPx > 0"
1574
+ class="vs-pad"
1575
+ aria-hidden="true"
1576
+ >
1577
+ <td
1578
+ :colspan="11"
1579
+ :style="{ height: padTopPx + 'px' }"
1580
+ />
1581
+ </tr>
1499
1582
  <WorkshopDaqNodeRow
1500
- v-for="n in filteredNodes"
1583
+ v-for="n in visibleNodes"
1501
1584
  :key="n.id"
1502
1585
  :n="n"
1503
1586
  />
1587
+ <!-- 下占位行 -->
1588
+ <tr
1589
+ v-if="padBottomPx > 0"
1590
+ class="vs-pad"
1591
+ aria-hidden="true"
1592
+ >
1593
+ <td
1594
+ :colspan="11"
1595
+ :style="{ height: padBottomPx + 'px' }"
1596
+ />
1597
+ </tr>
1504
1598
  <tr v-if="daq.loaded && daq.nodes.length === 0">
1505
1599
  <td colspan="11">
1506
1600
  <div
@@ -1570,9 +1664,15 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
1570
1664
  align-items: center;
1571
1665
  justify-content: space-between;
1572
1666
  }
1573
- .ctrl-left { display: flex; gap: 14px; align-items: center; }
1667
+ /* 窄屏前先写死「不可压缩」:药丸/周期项被 flex 收缩到 min-content → 中文退化成单列竖排。
1668
+ 保持自然宽度,由外层 flex-wrap 决定换行位置。 */
1669
+ .ctrl-left { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; min-width: 0; }
1670
+ .ctrl-left > *, .ctrl-right > * { flex: 0 0 auto; }
1671
+ .aw-pill { flex: 0 0 auto; white-space: nowrap; }
1672
+ /* 行内小按钮(确认/筛选/回滚…)同族:窄屏禁止中文折成两行 */
1673
+ .mini-btn, .mini-act, .pill-btn, .node-toggle { white-space: nowrap; }
1574
1674
  .aw-pill.running { background: var(--accent); }
1575
- .cycle { display: inline-flex; gap: 6px; align-items: center; font-size: 12px; color: var(--ink-faint); }
1675
+ .cycle { display: inline-flex; flex: 0 0 auto; gap: 6px; align-items: center; font-size: 12px; white-space: nowrap; color: var(--ink-faint); }
1576
1676
  .cycle input {
1577
1677
  width: 84px;
1578
1678
  padding: 4px 8px;
@@ -1581,7 +1681,8 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
1581
1681
  border: 1px solid var(--line-strong);
1582
1682
  border-radius: var(--radius-chip);
1583
1683
  }
1584
- .ctrl-metrics { display: flex; gap: 8px; align-items: center; font-size: 11.5px; color: var(--ink-soft); }
1684
+ .ctrl-metrics { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; min-width: 0; font-size: 11.5px; color: var(--ink-soft); }
1685
+ .ctrl-metrics > span { white-space: nowrap; }
1585
1686
  .ctrl-metrics .sep { opacity: 0.4; }
1586
1687
  .ctrl-metrics .warn { color: var(--tone-warning-dot); }
1587
1688
 
@@ -1661,6 +1762,9 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
1661
1762
  }
1662
1763
 
1663
1764
  .table-card { overflow-x: auto; }
1765
+ /* 窗口化占位行:只提供高度,不参与绘制/命中,也不继承单元格内距与下边框
1766
+ (否则占位行会自带一条分割线和一个 9px 内距,滚动高度算不准) */
1767
+ .vs-pad td { padding: 0 !important; border: 0 !important; }
1664
1768
  .nodes-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
1665
1769
  /* 离屏行跳过布局/绘制(102 行长表;contain-intrinsic-size 给出占位高度防滚动条跳动) */
1666
1770
  .nodes-table tbody tr { content-visibility: auto; contain-intrinsic-size: auto 42px; }
@@ -1815,6 +1919,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
1815
1919
  /* ---------- 未确认报警条(S5) ---------- */
1816
1920
  .alarm-bar {
1817
1921
  display: flex;
1922
+ flex-wrap: wrap;
1818
1923
  gap: 12px;
1819
1924
  align-items: center;
1820
1925
  padding: 8px 14px;
@@ -1877,8 +1982,8 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
1877
1982
  .alarm-item .ack:hover { opacity: 0.85; border-color: transparent; color: var(--paper-raised); }
1878
1983
 
1879
1984
  /* ---------- 添加节点向导 ---------- */
1880
- .ctrl-right { display: flex; gap: 12px; align-items: center; }
1881
- .add-btn { padding: 8px 16px; font-size: 13px; }
1985
+ .ctrl-right { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; min-width: 0; }
1986
+ .add-btn { padding: 8px 16px; font-size: 13px; white-space: nowrap; }
1882
1987
  .modal-mask {
1883
1988
  position: fixed;
1884
1989
  z-index: 50;
@@ -2110,4 +2215,99 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2110
2215
  stroke: var(--tone-info-dot);
2111
2216
  stroke-width: 1.5;
2112
2217
  }
2218
+
2219
+ /* ══════════════════════════════════════════════════════════════════════════
2220
+ 页面级自适应加固(375 / 768 / 1440 三档)
2221
+ 根因统一:flex 子项被压缩到 min-content → 中文按钮退化成「单列竖排」,
2222
+ 左右两组控件抢同一行 → 右边缘越界。策略:先禁止压缩(nowrap),
2223
+ 再在窄屏把控制条改成「整行 / 两列」分层排布;筛选区单列堆叠 → 两列网格。
2224
+ ══════════════════════════════════════════════════════════════════════════ */
2225
+ /* —— 标签字号地板(全档位,不只是窄屏)——
2226
+ 本页所有自有小标签原为 9.5–11px,低于「标签 ≥11.5px」验收线;统一抬到 11.5px。
2227
+ 注:节点表行内 10/10.5px 徽标在 app/components/workshop/DaqNodeRow.vue,不在本页职责内。 —— */
2228
+ .badge, .sec-label, .flt, .hint, .strip-label, .strip-label small,
2229
+ .line-pill small, .opt-status, .opt-time, .opt-line2, .mini-act,
2230
+ .src-badge, .kind-chip, .evt-row .t, .opt-count,
2231
+ .infra-banner .txt small { font-size: 11.5px; }
2232
+ .sub { font-size: 13px; }
2233
+ .alarm-item small, .alarm-row small { font-size: 11.5px; }
2234
+
2235
+ @media (max-width: 900px) {
2236
+ /* —— 兜底字号地板:本页任何 small/mono 小字在手持档不得低于 11.5px ——
2237
+ 逐类点名容易漏(实测 infra-banner 的配方名、line-pill 的 mono 小字都漏过),
2238
+ 这里用元素级兜底把整页收口;它放在本媒体块的**最后面**,
2239
+ 同特异度时后写者胜,所以不会破坏上面更细的排版意图。 */
2240
+ .page small,
2241
+ .page small.mono,
2242
+ .page small.mono b {
2243
+ font-size: 11.5px;
2244
+ }
2245
+
2246
+ /* —— 触摸目标:报警「确认」在窄屏只有 44×22,手指点不中 —— */
2247
+ .alarm-item { padding: 6px 10px; }
2248
+ .alarm-item .ack, .mini-btn.ack { min-height: 32px; padding: 4px 12px; white-space: nowrap; }
2249
+ .alarm-row { flex-wrap: wrap; }
2250
+
2251
+ /* —— 窄屏字号地板(正文 ≥12.5px / 标签 ≥11.5px)——
2252
+ 注:侧栏 8.5px/9.5px 属全局层(main.css / AppSidebar.vue),不在本页职责内 —— */
2253
+ .flt, .sec-label, .hint { font-size: 11.5px; }
2254
+ .inp-sel { font-size: 12.5px; }
2255
+ .badge, .strip-label, .line-pill small { font-size: 11.5px; }
2256
+ .ctrl-metrics { font-size: 12px; }
2257
+ .evt-row { font-size: 12.5px; }
2258
+ .evt-row .t, .src-badge, .kind-chip { font-size: 11.5px; }
2259
+ .panel-head { font-size: 13px; }
2260
+ .alarm-item, .alarm-item small, .alarm-row small, .opt-status, .mini-act,
2261
+ .opt-time, .opt-line2, .clear-btn, .opt-count, .strip-label small { font-size: 11.5px; }
2262
+ .tpl-table .range, .opt-empty, .panel-empty { font-size: 12px; }
2263
+ .infra-banner .txt small { font-size: 11.5px; }
2264
+ .m-title { font-size: 16px; }
2265
+ .sub { font-size: 13px; }
2266
+ }
2267
+
2268
+ @media (max-width: 700px) {
2269
+ /* —— 控制器总控条:暂停全部采集 整行;两个周期项两列;两个 CTA 两列 —— */
2270
+ .ctrl-card { padding: 12px 14px 11px; }
2271
+ .ctrl-row { flex-direction: column; align-items: stretch; gap: 12px; }
2272
+ .ctrl-left { gap: 10px; }
2273
+ .ctrl-left > .aw-pill { flex: 1 1 100%; }
2274
+ .ctrl-left > .cycle { flex: 1 1 calc(50% - 5px); justify-content: space-between; }
2275
+ .cycle input { width: 62px; }
2276
+ .ctrl-right { gap: 10px; }
2277
+ .ctrl-metrics { flex: 1 1 100%; }
2278
+ .ctrl-right > .add-btn { flex: 1 1 calc(50% - 5px); }
2279
+
2280
+ /* —— 未确认报警横条:标题独占一行,滚动轨吃满整宽(右侧内容不再被挤掉) —— */
2281
+ .alarm-bar { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px 12px; }
2282
+ .bar-label { justify-content: flex-start; }
2283
+ .alarm-scroll { width: 100%; padding-bottom: 3px; }
2284
+ .alarm-item { max-width: 100%; }
2285
+
2286
+ /* —— 节点筛选工具条:单列下拉堆叠 → 两列网格 —— */
2287
+ .tbl-toolbar { flex-direction: column; align-items: stretch; gap: 8px; padding: 11px 12px; }
2288
+ .filters { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; }
2289
+ .filters > .flt { min-width: 0; }
2290
+ .inp-sel { width: 100%; min-width: 0; }
2291
+ .flt-search { width: 100%; }
2292
+ .clear-btn { grid-column: 1 / -1; min-height: 34px; }
2293
+ .count { padding-bottom: 0; }
2294
+
2295
+ /* —— Agent 优化记录:筛选区两列网格;记录行允许换行,长节点名不再被切 —— */
2296
+ .opt-filters { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
2297
+ .opt-filters > .mini-act { grid-column: 1 / -1; min-height: 34px; }
2298
+ .opt-line1 { flex-wrap: wrap; }
2299
+ .opt-node { flex: 1 1 100%; overflow: visible; white-space: normal; }
2300
+ .opt-time { margin-left: 0; }
2301
+ .opt-ch { flex: 1 1 100%; }
2302
+ .opt-spark { flex: 1 1 auto; width: auto; min-width: 96px; }
2303
+
2304
+ /* —— 主表:压缩单元格内边距,缩短横向滚动距离(表格横滚 + 首列 sticky 是全局层设计) —— */
2305
+ .nodes-table th, .nodes-table :deep(td) { padding: 7px 8px; }
2306
+ .table-card { -webkit-overflow-scrolling: touch; }
2307
+ .opt-body.open { max-height: 70vh; }
2308
+
2309
+ /* —— 实时事件行:摘要原来只剩 141px 被省略号切掉 → 换行后独占整宽 —— */
2310
+ .evt-row { flex-wrap: wrap; }
2311
+ .evt-row .txt { flex: 1 1 100%; white-space: normal; overflow: visible; }
2312
+ }
2113
2313
  </style>
@@ -872,16 +872,16 @@ function fmtPoint(p: { value?: number, avg?: number } | undefined): string {
872
872
 
873
873
  <div class="tpl-chips">
874
874
  <span
875
- v-for="t in dcw.templates"
876
- :key="t.key"
875
+ v-for="tpl in dcw.templates"
876
+ :key="tpl.key"
877
877
  class="tpl-chip"
878
- :title="`${t.code} · ${t.min}~${t.max} ${t.unit}${t.semantics ? ` · ${t.semantics}` : ''}`"
878
+ :title="`${tpl.code} · ${tpl.min}~${tpl.max} ${tpl.unit}${tpl.semantics ? ` · ${tpl.semantics}` : ''}`"
879
879
  >
880
- {{ t.name }} <small class="mono">{{ t.min }}~{{ t.max }}{{ t.unit }}</small>
880
+ {{ tpl.name }} <small class="mono">{{ tpl.min }}~{{ tpl.max }}{{ tpl.unit }}</small>
881
881
  <em
882
882
  class="tpl-tag"
883
- :class="{ builtin: t.builtin }"
884
- >{{ t.builtin ? $t('dcwDetail.k3x23c141') : $t('dcwDetail.k3t616a142') }}</em>
883
+ :class="{ builtin: tpl.builtin }"
884
+ >{{ tpl.builtin ? $t('dcwDetail.k3x23c141') : $t('dcwDetail.k3t616a142') }}</em>
885
885
  </span>
886
886
  </div>
887
887
 
@@ -1042,11 +1042,11 @@ function fmtPoint(p: { value?: number, avg?: number } | undefined): string {
1042
1042
  class="inp"
1043
1043
  >
1044
1044
  <option
1045
- v-for="t in dcw.templates"
1046
- :key="t.key"
1047
- :value="t.key"
1045
+ v-for="tpl in dcw.templates"
1046
+ :key="tpl.key"
1047
+ :value="tpl.key"
1048
1048
  >
1049
- {{ t.name }} · {{ t.ch }}({{ t.min }}~{{ t.max }} {{ t.unit }}){{ t.builtin ? '' : $t('dcwDetail.kr45rk9157') }}
1049
+ {{ tpl.name }} · {{ tpl.ch }}({{ tpl.min }}~{{ tpl.max }} {{ tpl.unit }}){{ tpl.builtin ? '' : $t('dcwDetail.kr45rk9157') }}
1050
1050
  </option>
1051
1051
  </select>
1052
1052
  </label>
@@ -2339,10 +2339,11 @@ function fmtPoint(p: { value?: number, avg?: number } | undefined): string {
2339
2339
  .adopt-list { display: flex; gap: 8px; flex-wrap: wrap; }
2340
2340
  .adopt-chip {
2341
2341
  display: inline-flex;
2342
+ flex-wrap: wrap;
2342
2343
  gap: 6px;
2343
2344
  align-items: center;
2344
2345
  padding: 4px 8px;
2345
- font-size: 11px;
2346
+ font-size: 11.5px;
2346
2347
  color: #e8eef8;
2347
2348
  background: rgba(13, 20, 32, 0.7);
2348
2349
  border: 1px solid rgba(60, 80, 110, 0.5);
@@ -2366,35 +2367,36 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2366
2367
  border: 1px solid var(--line-strong);
2367
2368
  border-radius: var(--radius-chip);
2368
2369
  }
2369
- .tri-cell small { font-size: 10px; color: var(--ink-faint); }
2370
+ .tri-cell small { font-size: 11.5px; color: var(--ink-faint); }
2370
2371
  .tri-cell b { font-size: 17px; color: var(--ink); }
2371
2372
  .ledger-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 12px; }
2372
- .sec-t { display: block; margin-bottom: 6px; font-size: 10px; font-weight: 700; letter-spacing: 0.14em; color: var(--ink-faint); }
2373
- .ledger-list { display: flex; flex-direction: column; gap: 3px; max-height: 200px; margin: 0; padding: 0; overflow: auto; font-size: 11px; color: var(--ink-soft); list-style: none; }
2373
+ .sec-t { display: block; margin-bottom: 6px; font-size: 11.5px; font-weight: 700; letter-spacing: 0.12em; color: var(--ink-faint); }
2374
+ .ledger-list { display: flex; flex-direction: column; gap: 3px; max-height: 200px; margin: 0; padding: 0; overflow: auto; font-size: 11.5px; color: var(--ink-soft); list-style: none; }
2374
2375
  .ledger-rec { display: flex; gap: 8px; align-items: center; }
2375
2376
  .rec-st {
2376
2377
  flex: none;
2377
2378
  padding: 0 6px;
2378
2379
  font-family: var(--font-mono);
2379
- font-size: 9px;
2380
+ font-size: 11.5px;
2380
2381
  color: var(--ink-faint);
2381
2382
  border: 1px solid var(--line-strong);
2382
2383
  border-radius: 4px;
2383
2384
  }
2384
2385
  .rec-st.open { color: var(--tone-warning-dot); border-color: var(--tone-warning-dot); }
2386
+ .tri-cell b { overflow-wrap: anywhere; }
2385
2387
  .rec-st.judged { color: var(--tone-warning-dot); border-color: var(--tone-warning-dot); opacity: 0.85; }
2386
2388
  .rec-st.rolled-back, .rec-st.judged-keep { color: var(--tone-success-dot); border-color: var(--tone-success-dot); }
2387
2389
  .sub { margin: 0; font-size: 12.5px; opacity: 0.6; }
2388
2390
  .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
2389
2391
  .dim { opacity: 0.55; }
2390
2392
  .badges { display: flex; gap: 8px; }
2391
- .badge { padding: 3px 10px; font-size: 11px; letter-spacing: 0.05em; color: var(--ink-soft); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
2393
+ .badge { padding: 3px 10px; font-size: 11.5px; letter-spacing: 0.05em; color: var(--ink-soft); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
2392
2394
 
2393
2395
  .ctrl-card { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; padding: 14px 18px; margin-bottom: 14px; }
2394
2396
  .ctrl-left { display: flex; gap: 14px; align-items: center; }
2395
2397
  .aw-pill.running { background: var(--accent); }
2396
2398
  .ctrl-right { display: flex; gap: 12px; align-items: center; }
2397
- .ctrl-metrics { display: flex; gap: 8px; align-items: center; font-size: 11.5px; color: var(--ink-soft); }
2399
+ .ctrl-metrics { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; font-size: 11.5px; color: var(--ink-soft); }
2398
2400
  .ctrl-metrics .sep { opacity: 0.4; }
2399
2401
  .ctrl-metrics .warn { color: var(--tone-warning-dot); }
2400
2402
  .add-btn { padding: 8px 16px; font-size: 13px; }
@@ -2408,14 +2410,14 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2408
2410
  .nodes-table th, .nodes-table td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--divider-hair); }
2409
2411
  .nodes-table th { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); border-bottom: 1px solid var(--line-strong); }
2410
2412
  .nodes-table td b { margin-left: 8px; }
2411
- .ch { display: block; margin-top: 2px; font-size: 10px; color: var(--ink-faint); }
2413
+ .ch { display: block; margin-top: 2px; font-size: 11.5px; color: var(--ink-faint); }
2412
2414
  .right { text-align: right; }
2413
2415
  .val { font-size: 13px; }
2414
2416
  .val small { margin-left: 3px; color: var(--ink-faint); }
2415
2417
  .read-stale { color: var(--tone-danger-dot, #e05a5a); text-decoration: underline dotted; text-underline-offset: 3px; }
2416
2418
  .read-btn { margin-left: 8px; }
2417
2419
 
2418
- .st-pill { display: inline-block; padding: 2px 9px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.04em; border-radius: var(--radius-pill); }
2420
+ .st-pill { display: inline-block; padding: 3px 9px; font-family: var(--font-mono); font-size: 11.5px; letter-spacing: 0.04em; border-radius: var(--radius-pill); }
2419
2421
  .st-pill.ok { color: var(--tone-success-dot); background: var(--tone-success-bg); }
2420
2422
  .st-pill.writing { color: var(--ink); background: var(--hover-tint); }
2421
2423
  .st-pill.error, .st-pill.alarm { color: var(--tone-danger-dot); background: var(--tone-danger-bg); }
@@ -2434,10 +2436,12 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2434
2436
 
2435
2437
  /* 模板弹窗 */
2436
2438
  .tpl-hint { margin: 0 0 10px; font-size: 12px; }
2439
+ .recipe-hd { flex-wrap: wrap; }
2440
+ .recipe-actions { flex-wrap: wrap; }
2437
2441
  .tpl-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
2438
- .tpl-chip { display: inline-flex; gap: 6px; align-items: center; padding: 3px 9px; font-size: 11.5px; color: var(--ink-soft); background: var(--paper-deep); border: 1px solid var(--line); border-radius: var(--radius-chip); }
2442
+ .tpl-chip { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 3px 9px; font-size: 11.5px; color: var(--ink-soft); background: var(--paper-deep); border: 1px solid var(--line); border-radius: var(--radius-chip); }
2439
2443
  .tpl-chip small { color: var(--ink-faint); }
2440
- .tpl-tag { padding: 1px 6px; font-size: 10px; font-style: normal; color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); border-radius: var(--radius-pill); }
2444
+ .tpl-tag { padding: 1px 6px; font-size: 11.5px; font-style: normal; color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); border-radius: var(--radius-pill); }
2441
2445
  .tpl-tag.builtin { color: var(--ink-faint); border-color: var(--line-strong); }
2442
2446
 
2443
2447
  .write-row { display: flex; gap: 6px; align-items: center; }
@@ -2455,9 +2459,11 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2455
2459
  @keyframes linePulse { 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--tone-success-dot) 25%, transparent); } 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--tone-success-dot) 8%, transparent); } }
2456
2460
  .line-info b { font-size: 14px; }
2457
2461
  .line-info small { display: block; margin-top: 2px; font-size: 11px; }
2458
- .line-ctl { display: flex; gap: 10px; align-items: flex-end; }
2462
+ .line-ctl { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
2459
2463
  .ctl-sel { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: var(--ink-faint); }
2460
2464
  .ctl-sel .inp { min-width: 150px; }
2465
+ /* 配方参数行:窄屏不再按 5 列硬分,否则数值列被压成竖排字 */
2466
+ .param-chip { overflow-wrap: anywhere; }
2461
2467
  .pill-btn.stop { background: var(--tone-danger-dot); border-color: var(--tone-danger-dot); }
2462
2468
  .product-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
2463
2469
  .prod-chip { display: inline-flex; gap: 6px; align-items: center; padding: 4px 12px; font-size: 12px; cursor: pointer; color: var(--ink-soft); background: var(--paper-deep); border: 1px solid var(--line-strong); border-radius: var(--radius-pill); }
@@ -2465,6 +2471,8 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2465
2471
  .prod-del { color: var(--tone-danger-dot); font-weight: 700; }
2466
2472
  .query-card { padding: 14px 18px; margin-bottom: 14px; }
2467
2473
  .q-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; align-items: end; }
2474
+ /* 配方网格:minmax(320px) 在 355px 画布(减去卡片内边距)里会溢出 1px,用 min() 兜住 */
2475
+ .recipe-grid { grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); }
2468
2476
  .q-grid .f { font-size: 11px; color: var(--ink-faint); }
2469
2477
  .q-actions { justify-content: flex-end; }
2470
2478
  .q-result { margin-top: 12px; overflow-x: auto; }
@@ -2472,7 +2480,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2472
2480
  .recipe-card { padding: 16px 18px; margin-bottom: 14px; }
2473
2481
  .recipe-hd { display: flex; gap: 10px; align-items: center; margin-bottom: 6px; }
2474
2482
  .recipe-hd h3 { margin: 0; font-size: 16px; }
2475
- .panel-tag { padding: 2px 8px; font-size: 11px; color: var(--ink-soft); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
2483
+ .panel-tag { padding: 2px 8px; font-size: 11.5px; color: var(--ink-soft); border: 1px solid var(--line-strong); border-radius: var(--radius-chip); }
2476
2484
  .recipe-sub { margin: 0 0 14px; font-size: 12px; color: var(--ink-faint); }
2477
2485
  .recipe-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px; }
2478
2486
  .recipe-item { padding: 12px 14px; background: var(--paper-deep); border: 1px solid var(--line); border-radius: var(--radius-chip); }
@@ -2480,14 +2488,14 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2480
2488
  .recipe-name small { margin-left: 8px; }
2481
2489
  .desc { margin: 4px 0; font-size: 12px; }
2482
2490
  .recipe-params { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
2483
- .param-chip { padding: 2px 8px; font-size: 11px; color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); border-radius: var(--radius-chip); }
2491
+ .param-chip { padding: 2px 8px; font-size: 11.5px; color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); border-radius: var(--radius-chip); }
2484
2492
  .recipe-actions { display: flex; gap: 6px; align-items: center; margin-top: 8px; }
2485
2493
  /* ---------- 配方版本历史 ---------- */
2486
2494
  .ver-row { padding: 10px 12px; margin-bottom: 8px; border: 1px solid var(--divider-hair); border-radius: 10px; }
2487
2495
  .ver-row.cur { background: color-mix(in srgb, var(--accent) 6%, transparent); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
2488
2496
  .ver-head { display: flex; gap: 10px; align-items: center; }
2489
2497
  .ver-v { font-weight: 700; font-size: 13px; }
2490
- .ver-src { padding: 1px 8px; font-size: 10.5px; border: 1px solid var(--line-strong); border-radius: var(--radius-pill); }
2498
+ .ver-src { padding: 1px 8px; font-size: 11.5px; border: 1px solid var(--line-strong); border-radius: var(--radius-pill); }
2491
2499
  .ver-src.agent { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, transparent); }
2492
2500
  .ver-src.user { color: var(--ink-soft); }
2493
2501
  .ver-src.system { color: var(--ink-faint); }
@@ -2511,7 +2519,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2511
2519
  .param-del { margin-bottom: 2px; }
2512
2520
  .f { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; color: var(--ink-faint); }
2513
2521
  .f em { margin-left: 3px; font-style: normal; }
2514
- .hint { font-size: 10px; color: var(--ink-faint); }
2522
+ .hint { font-size: 11.5px; color: var(--ink-faint); }
2515
2523
  .test-row { display: flex; gap: 10px; align-items: center; margin: 6px 0 4px; }
2516
2524
  .test-result { font-family: var(--font-mono); font-size: 11px; }
2517
2525
  .test-result.good { color: var(--tone-success-dot); }
@@ -2520,12 +2528,85 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2520
2528
  .m-note { margin: 8px 0 0; font-size: 12px; color: var(--ink-faint); }
2521
2529
  /* 失效配方参数:灰化 + 徽标 */
2522
2530
  .param-chip.stale { color: var(--ink-faint); background: color-mix(in srgb, var(--ink) 5%, transparent); border-color: var(--divider-hair); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--ink-faint) 60%, transparent); }
2523
- .chip-stale-tag { margin-left: 5px; padding: 0 5px; font-size: 9.5px; font-style: normal; color: var(--tone-danger-dot); background: color-mix(in srgb, var(--tone-danger-dot) 8%, transparent); border: 1px solid color-mix(in srgb, var(--tone-danger-dot) 30%, transparent); border-radius: var(--radius-chip); text-decoration: none; }
2524
- .row-stale { flex: none; padding: 1px 7px; font-size: 10.5px; color: var(--tone-danger-dot); border: 1px solid color-mix(in srgb, var(--tone-danger-dot) 30%, transparent); border-radius: var(--radius-chip); }
2531
+ .chip-stale-tag { margin-left: 5px; padding: 0 5px; font-size: 11.5px; font-style: normal; color: var(--tone-danger-dot); background: color-mix(in srgb, var(--tone-danger-dot) 8%, transparent); border: 1px solid color-mix(in srgb, var(--tone-danger-dot) 30%, transparent); border-radius: var(--radius-chip); text-decoration: none; }
2532
+ .row-stale { flex: none; padding: 1px 7px; font-size: 11.5px; color: var(--tone-danger-dot); border: 1px solid color-mix(in srgb, var(--tone-danger-dot) 30%, transparent); border-radius: var(--radius-chip); }
2525
2533
  .m-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 16px; }
2526
2534
  .aw-pill.outline { color: var(--ink); background: var(--paper-raised); border-color: var(--line-strong); }
2527
2535
  .err { margin-top: 14px; font-size: 13px; color: var(--tone-danger-dot); }
2528
2536
  @media (prefers-reduced-motion: no-preference) {
2529
2537
  .nodes-table tbody tr:hover { background: var(--hover-tint); }
2530
2538
  }
2539
+
2540
+ /* ══ 窄屏自适应层(≤900 手持/平板竖,≤640 单列)════════════════════════════
2541
+ 375px 下本页的三类实测缺陷:网关总控条右侧三件套不换行(整块顶出画布)、
2542
+ 运行控制行的两个 150px 选择器 + 按钮并排超宽、微标签 9~10.5px。 */
2543
+ @media (max-width: 900px) {
2544
+ .ctrl-card { gap: 12px; padding: 12px; }
2545
+ .ctrl-right { flex-wrap: wrap; gap: 8px 10px; width: 100%; }
2546
+ .ctrl-metrics { width: 100%; }
2547
+ .line-card { gap: 12px; padding: 12px; }
2548
+ .line-status { min-width: 0; }
2549
+ .line-ctl { width: 100%; }
2550
+ .ctl-sel { flex: 1 1 140px; min-width: 0; }
2551
+ .ctl-sel .inp { min-width: 0; width: 100%; }
2552
+ .line-ctl .pill-btn { flex: 1 1 120px; }
2553
+
2554
+ /* 控制节点表:保留列语义,给一条可横扫的卷轴 + 首列钉住 */
2555
+ .table-card { overflow-x: auto; -webkit-overflow-scrolling: touch; }
2556
+ .nodes-table { min-width: 1080px; }
2557
+ .nodes-table th,
2558
+ .nodes-table td { white-space: nowrap; }
2559
+ .nodes-table thead > tr > th:first-child,
2560
+ .nodes-table tbody > tr > td:first-child {
2561
+ position: sticky;
2562
+ left: 0;
2563
+ z-index: 2;
2564
+ background: var(--paper-raised);
2565
+ box-shadow: 1px 0 0 var(--line);
2566
+ }
2567
+
2568
+ .adopt-card { padding: 12px; }
2569
+ .ledger-card,
2570
+ .recipe-card,
2571
+ .query-card { padding: 12px; }
2572
+ .ledger-cols { grid-template-columns: 1fr; }
2573
+ .ledger-tri { flex-wrap: wrap; }
2574
+
2575
+ /* 触摸目标:手持命中区 ≥40px(main.css 只在 pointer:coarse 下兜底) */
2576
+ .mini-btn,
2577
+ .pill-btn,
2578
+ .aw-pill,
2579
+ .ctrl-toggle,
2580
+ .inp,
2581
+ .write-inp,
2582
+ .prod-chip,
2583
+ .tpl-chip {
2584
+ min-height: 40px;
2585
+ }
2586
+
2587
+ .write-inp { width: 100%; min-width: 90px; }
2588
+ .write-row { flex-wrap: wrap; }
2589
+
2590
+ /* 弹窗表单:两列/三列在窄屏一律落成一列,否则标签被压成竖排字 */
2591
+ .f-grid,
2592
+ .driver-form,
2593
+ .cal-form { grid-template-columns: 1fr; }
2594
+ .q-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
2595
+ .param-row { grid-template-columns: 1fr 1fr; }
2596
+ .modal {
2597
+ width: calc(100vw - 20px);
2598
+ max-width: calc(100vw - 20px);
2599
+ padding: 16px 14px;
2600
+ }
2601
+ .seg { padding: 10px 0; min-height: 40px; }
2602
+ .m-actions { flex-wrap: wrap; }
2603
+ .m-actions > * { flex: 1 1 auto; }
2604
+ .recipe-hd .pill-btn,
2605
+ .recipe-hd .mini-btn { min-height: 40px; }
2606
+ }
2607
+
2608
+ @media (max-width: 640px) {
2609
+ h1 { font-size: 22px; }
2610
+ .q-grid { grid-template-columns: 1fr; }
2611
+ }
2531
2612
  </style>