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
@@ -16,6 +16,7 @@ import { useCharacterAssets } from '@/app/composables/workshop/useCharacterAsset
16
16
  import { useDeviceTwins, type DeviceTwinView } from '@/app/composables/workshop/useDeviceTwins'
17
17
  import { useSceneLayouts } from '@/app/composables/workshop/useSceneLayouts'
18
18
  import { useHttp } from '@/app/composables/useHttp'
19
+ import { useResponsive } from '@/app/composables/useResponsive'
19
20
  import type { TownScene, TownEntityInput } from './TownScene'
20
21
  import type { TownScene3D, ChannelLayout, AgentRangeLayout } from './TownScene3D'
21
22
  // 频道身份色(与 3D 场景同源:同一 hashHue,UI 用 CSS 色)
@@ -53,6 +54,34 @@ const deviceTwins = useDeviceTwins()
53
54
  const sceneLayouts = useSceneLayouts()
54
55
  const http = useHttp()
55
56
 
57
+ /* ── 窄屏形态:左右轨折成底部抽屉(sheet),底部坞折成可收起横条 ──
58
+ * 断点判据只用 useResponsive(全站唯一出处),不自己监听 innerWidth;
59
+ * DOM 结构不随档位增删(形态切换全部交给 CSS 媒体查询),
60
+ * 避免 SSR/水合不一致。抽屉协议与 AppSidebar 一致:遮罩 + Esc 关闭。 */
61
+ const { isDesktop } = useResponsive()
62
+ /** 当前打开的抽屉:'left' | 'right' | null */
63
+ const sheetOpen = ref<'left' | 'right' | null>(null)
64
+ /** 窄屏底部坞是否展开(默认收起,舞台优先) */
65
+ const dockOpen = ref(false)
66
+ function toggleSheet(side: 'left' | 'right') {
67
+ sheetOpen.value = sheetOpen.value === side ? null : side
68
+ }
69
+ function closeSheet() {
70
+ sheetOpen.value = null
71
+ }
72
+ function onSheetKey(e: KeyboardEvent) {
73
+ if (e.key === 'Escape' && sheetOpen.value) closeSheet()
74
+ }
75
+ onMounted(() => window.addEventListener('keydown', onSheetKey))
76
+ onBeforeUnmount(() => window.removeEventListener('keydown', onSheetKey))
77
+ /** 回到桌面档必须清空抽屉状态,否则遮罩会滞留在屏幕上 */
78
+ watch(isDesktop, (d) => {
79
+ if (d) {
80
+ sheetOpen.value = null
81
+ dockOpen.value = false
82
+ }
83
+ })
84
+
56
85
  const hostRef = ref<HTMLDivElement | null>(null)
57
86
  /** 当前渲染器(TownScene / TownScene3D 之一) */
58
87
  const sceneRef = shallowRef<TownViewScene | null>(null)
@@ -1781,16 +1810,7 @@ function unbindDaq(daqId: string): void {
1781
1810
  })
1782
1811
  }
1783
1812
 
1784
- /** 数采模板图标(设计稿 ICONS;SVG path 直嵌) */
1785
- const DAQ_ICONS: Record<string, string> = {
1786
- thermo: '<path d="M10 4a2 2 0 0 1 4 0v9a4 4 0 1 1-4 0V4Z"/><circle cx="12" cy="16.5" r="1.6"/>',
1787
- pressure: '<circle cx="12" cy="12" r="8"/><path d="m12 12 3.5-3.5"/><circle cx="12" cy="12" r="1.2"/>',
1788
- tension: '<circle cx="12" cy="10" r="4"/><path d="M4 18.5h16M8 18.5V14M16 18.5V14"/><path d="M2.5 10H8M16 10h5.5"/>',
1789
- encoder: '<circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="2"/><path d="M12 4v3M12 17v3M4 12h3M17 12h3"/>',
1790
- camera: '<rect x="3" y="7" width="13" height="10" rx="2"/><path d="m16 11 5-3v8l-5-3"/><circle cx="8" cy="12" r="2.5"/>',
1791
- gateway: '<rect x="8" y="11" width="8" height="10" rx="1.5"/><circle cx="12" cy="5.5" r="1.5"/><path d="M12 10V7M8.5 8.5a5 5 0 0 1 7 0M6 6a8.5 8.5 0 0 1 12 0"/>',
1792
- }
1793
- const daqIcon = (icon: string): string => DAQ_ICONS[icon] ?? DAQ_ICONS.gateway!
1813
+ /** 数采/智控模板图标:注入面收敛在 WorkshopDaqTemplateIcon(唯一 v-html 点) */
1794
1814
 
1795
1815
  /** 最近设备孪生(拖放自动绑定:数采落点 ±95 世界单位内最近的非数采设备) */
1796
1816
  function nearestDeviceTwin(x: number, z: number, maxDist: number): DeviceTwinView | null {
@@ -1824,7 +1844,12 @@ function toggleBindPop(key: 'daq' | 'dcw', e: MouseEvent): void {
1824
1844
  if (key === 'daq') bindPopOpen.value = !bindPopOpen.value
1825
1845
  else dcwBindPopOpen.value = !dcwBindPopOpen.value
1826
1846
  }
1827
- interface RailBindChoice { id: string, name: string, tpl: DaqTemplate, devName: string | null, placed: boolean }
1847
+ /**
1848
+ * 绑定候选行(数采/智控两个绑定弹层共用)。
1849
+ * tpl 只声明弹层真正读取、且两套模板目录都有的字段:数采侧是 DaqTemplate,
1850
+ * 智控侧是 dcw.templates 的同构投影 —— 后者本就没有 base/amp 等数采专有字段。
1851
+ */
1852
+ interface RailBindChoice { id: string, name: string, tpl: { icon: string }, devName: string | null, placed: boolean }
1828
1853
  const daqBindChoices = computed<Record<string, RailBindChoice[]>>(() => {
1829
1854
  const devId = selected.value?.kind === 'device' ? selected.value.id : ''
1830
1855
  const map: Record<string, RailBindChoice[]> = {}
@@ -1926,12 +1951,12 @@ const boundDaqRows = computed(() => {
1926
1951
  const id = selected.value?.id
1927
1952
  if (!id) return []
1928
1953
  return daqOfDevice(id).map((daqId) => {
1929
- const t = sceneTwinById(daqId)
1954
+ const twin = sceneTwinById(daqId)
1930
1955
  const st = daqSim.value.get(daqId)
1931
- const tpl = st?.tpl ?? daqTplById(t?.modelRef ?? '')
1956
+ const tpl = st?.tpl ?? daqTplById(twin?.modelRef ?? '')
1932
1957
  return {
1933
1958
  daqId,
1934
- name: t?.name ?? daqId,
1959
+ name: twin?.name ?? daqId,
1935
1960
  ch: tpl?.ch ?? t('townView.k1ef3cls168'),
1936
1961
  icon: tpl?.icon ?? 'gateway',
1937
1962
  value: st ? fmtDaq(st) : '--',
@@ -2879,7 +2904,10 @@ onBeforeUnmount(() => {
2879
2904
  <template>
2880
2905
  <div class="town-view">
2881
2906
  <!-- ================= 顶部导航 ================= -->
2882
- <header class="topnav">
2907
+ <header
2908
+ class="topnav"
2909
+ :class="{ 'sheet-on': sheetOpen }"
2910
+ >
2883
2911
  <div class="brand">
2884
2912
  <svg
2885
2913
  class="brand-glyph"
@@ -2958,9 +2986,9 @@ onBeforeUnmount(() => {
2958
2986
  :class="{ 'nav-bell-warn': activeAlarmCount > 0 }"
2959
2987
  :title="$t('townView.k1fsi3ir002')"
2960
2988
  >◉ {{ activeAlarmCount }}</span>
2961
- <span class="nav-chip mono">{{ fps }} FPS</span>
2989
+ <span class="nav-chip mono nav-fps">{{ fps }} FPS</span>
2962
2990
  <div
2963
- class="avatar-chip"
2991
+ class="avatar-chip nav-user"
2964
2992
  :title="$t('townView.k1demf38003')"
2965
2993
  >
2966
2994
  <div class="avatar-fallback">
@@ -2968,13 +2996,67 @@ onBeforeUnmount(() => {
2968
2996
  </div>
2969
2997
  <span>{{ userStore.user?.name || $t('townView.k1pub99m139') }}</span>
2970
2998
  </div>
2999
+
3000
+ <!-- 窄屏:左/右轨开合(≥1024 由 CSS 隐藏) -->
3001
+ <button
3002
+ type="button"
3003
+ class="sheet-btn"
3004
+ :class="{ on: sheetOpen === 'left' }"
3005
+ aria-controls="town-rail-left"
3006
+ :aria-expanded="sheetOpen === 'left' ? 'true' : 'false'"
3007
+ :title="$t('townView.k1k75lzy030')"
3008
+ @click="toggleSheet('left')"
3009
+ >
3010
+ <svg
3011
+ class="sheet-ico"
3012
+ viewBox="0 0 24 24"
3013
+ aria-hidden="true"
3014
+ ><path d="M4 6h16M4 12h11M4 18h16" /></svg>
3015
+ <span class="sheet-btn-t">{{ $t('townView.k1k75lzy030') }}</span>
3016
+ </button>
3017
+ <button
3018
+ type="button"
3019
+ class="sheet-btn"
3020
+ :class="{ on: sheetOpen === 'right' }"
3021
+ aria-controls="town-rail-right"
3022
+ :aria-expanded="sheetOpen === 'right' ? 'true' : 'false'"
3023
+ :title="$t('townView.k17dkhgd112')"
3024
+ @click="toggleSheet('right')"
3025
+ >
3026
+ <svg
3027
+ class="sheet-ico"
3028
+ viewBox="0 0 24 24"
3029
+ aria-hidden="true"
3030
+ ><path d="M20 6H4M20 12H9M20 18H4" /></svg>
3031
+ <span class="sheet-btn-t">{{ $t('townView.k17dkhgd112') }}</span>
3032
+ </button>
2971
3033
  </div>
2972
3034
  </header>
2973
3035
 
2974
3036
  <!-- ================= 三栏应用区 ================= -->
2975
3037
  <div class="app">
2976
3038
  <!-- 左轨:设备资源 / 数采节点 / 场景管理 -->
2977
- <aside class="rail rail-left">
3039
+ <aside
3040
+ id="town-rail-left"
3041
+ class="rail rail-left"
3042
+ :class="{ 'sheet-open': sheetOpen === 'left' }"
3043
+ >
3044
+ <!-- 窄屏抽屉头(桌面档 CSS 隐藏):抓手 + 标题 + 关闭 -->
3045
+ <div class="sheet-hd">
3046
+ <span
3047
+ class="sheet-grip"
3048
+ aria-hidden="true"
3049
+ />
3050
+ <span class="sheet-hd-t">{{ $t('townView.k1k75lzy030') }}</span>
3051
+ <button
3052
+ type="button"
3053
+ class="sheet-x"
3054
+ aria-label="关闭"
3055
+ @click="closeSheet"
3056
+ >
3057
+ ✕
3058
+ </button>
3059
+ </div>
2978
3060
  <section class="panel">
2979
3061
  <div class="panel-hd">
2980
3062
  <h3>{{ $t('townView.k1k75lzy030') }}</h3>
@@ -3031,10 +3113,9 @@ onBeforeUnmount(() => {
3031
3113
  >
3032
3114
  <span class="daq-caret">▶</span>
3033
3115
  <span class="daq-ico">
3034
- <svg
3116
+ <WorkshopDaqTemplateIcon
3035
3117
  class="daq-svg"
3036
- viewBox="0 0 24 24"
3037
- v-html="daqIcon(tpl.icon)"
3118
+ :icon="tpl.icon"
3038
3119
  />
3039
3120
  </span>
3040
3121
  <div class="daq-meta">
@@ -3116,10 +3197,9 @@ onBeforeUnmount(() => {
3116
3197
  >
3117
3198
  <span class="daq-caret">▶</span>
3118
3199
  <span class="daq-ico">
3119
- <svg
3200
+ <WorkshopDaqTemplateIcon
3120
3201
  class="daq-svg"
3121
- viewBox="0 0 24 24"
3122
- v-html="daqIcon(tpl.icon)"
3202
+ :icon="tpl.icon"
3123
3203
  />
3124
3204
  </span>
3125
3205
  <div class="daq-meta">
@@ -3646,8 +3726,29 @@ onBeforeUnmount(() => {
3646
3726
  </div>
3647
3727
 
3648
3728
  <!-- 底部坞:场景控制(渲染/环境/操作 三组仪表分区) + 趋势分析 -->
3649
- <div class="dock">
3650
- <section class="dock-card">
3729
+ <div
3730
+ class="dock"
3731
+ :class="{ 'dock-open': dockOpen }"
3732
+ >
3733
+ <!-- 窄屏:底部坞折成可收起横条(桌面档 CSS 隐藏),默认收起让舞台占满 -->
3734
+ <button
3735
+ type="button"
3736
+ class="dock-toggle"
3737
+ aria-controls="town-dock-controls"
3738
+ :aria-expanded="dockOpen ? 'true' : 'false'"
3739
+ @click="dockOpen = !dockOpen"
3740
+ >
3741
+ <span class="dock-toggle-t">{{ $t('townView.k1c9iq23060') }}</span>
3742
+ <span class="dock-toggle-hint mono">{{ mode === 'edit' ? $t('townView.k1iipfcs143') : $t('townView.k1l147w1177') }}</span>
3743
+ <span
3744
+ class="dock-caret"
3745
+ aria-hidden="true"
3746
+ >▾</span>
3747
+ </button>
3748
+ <section
3749
+ id="town-dock-controls"
3750
+ class="dock-card"
3751
+ >
3651
3752
  <div class="dock-hd">
3652
3753
  <h3>{{ $t('townView.k1c9iq23060') }}</h3>
3653
3754
  <span class="dock-mode">{{ mode === 'edit' ? $t('townView.k1iipfcs143') : $t('townView.k1l147w1177') }}</span>
@@ -3796,16 +3897,16 @@ onBeforeUnmount(() => {
3796
3897
  :class="{ expanded: trendExpanded }"
3797
3898
  >
3798
3899
  <button
3799
- v-for="t in trendChips"
3800
- :key="t.id"
3900
+ v-for="chip in trendChips"
3901
+ :key="chip.id"
3801
3902
  class="lg-chip"
3802
- :class="{ off: !trendOn(t.id) }"
3803
- @click="toggleTrend(t.id)"
3903
+ :class="{ off: !trendOn(chip.id) }"
3904
+ @click="toggleTrend(chip.id)"
3804
3905
  >
3805
3906
  <span
3806
3907
  class="lg-dot"
3807
- :style="{ background: trendColor(t.id) }"
3808
- />{{ t.name }}
3908
+ :style="{ background: trendColor(chip.id) }"
3909
+ />{{ chip.name }}
3809
3910
  </button>
3810
3911
  <button
3811
3912
  v-if="trendOverflow > 0"
@@ -3832,7 +3933,27 @@ onBeforeUnmount(() => {
3832
3933
  </main>
3833
3934
 
3834
3935
  <!-- 右轨:Inspector / 设备运行状态 / 关键设备 / 实时事件 / 导航 -->
3835
- <aside class="rail rail-right">
3936
+ <aside
3937
+ id="town-rail-right"
3938
+ class="rail rail-right"
3939
+ :class="{ 'sheet-open': sheetOpen === 'right' }"
3940
+ >
3941
+ <!-- 窄屏抽屉头(桌面档 CSS 隐藏) -->
3942
+ <div class="sheet-hd">
3943
+ <span
3944
+ class="sheet-grip"
3945
+ aria-hidden="true"
3946
+ />
3947
+ <span class="sheet-hd-t">{{ $t('townView.k17dkhgd112') }}</span>
3948
+ <button
3949
+ type="button"
3950
+ class="sheet-x"
3951
+ aria-label="关闭"
3952
+ @click="closeSheet"
3953
+ >
3954
+ ✕
3955
+ </button>
3956
+ </div>
3836
3957
  <Transition name="ins">
3837
3958
  <section
3838
3959
  v-if="selected"
@@ -4100,10 +4221,9 @@ onBeforeUnmount(() => {
4100
4221
  class="bind-row"
4101
4222
  >
4102
4223
  <span class="bind-ico">
4103
- <svg
4224
+ <WorkshopDaqTemplateIcon
4104
4225
  class="bind-svg"
4105
- viewBox="0 0 24 24"
4106
- v-html="daqIcon(r.icon)"
4226
+ :icon="r.icon"
4107
4227
  />
4108
4228
  </span>
4109
4229
  <span class="bind-meta">
@@ -4162,10 +4282,9 @@ onBeforeUnmount(() => {
4162
4282
  :title="c.devName ? $t('townView.k2bindr0007', { p0: c.devName }) : $t('townView.k2bindr0008')"
4163
4283
  @click="bindDaqChoice(c.id)"
4164
4284
  >
4165
- <svg
4285
+ <WorkshopDaqTemplateIcon
4166
4286
  class="bind-svg"
4167
- viewBox="0 0 24 24"
4168
- v-html="daqIcon(c.tpl.icon)"
4287
+ :icon="c.tpl.icon"
4169
4288
  />
4170
4289
  <span>{{ c.name }}<i
4171
4290
  v-if="c.devName"
@@ -4197,10 +4316,9 @@ onBeforeUnmount(() => {
4197
4316
  >
4198
4317
  <div class="dcw-top">
4199
4318
  <span class="bind-ico">
4200
- <svg
4319
+ <WorkshopDaqTemplateIcon
4201
4320
  class="bind-svg"
4202
- viewBox="0 0 24 24"
4203
- v-html="daqIcon(r.icon)"
4321
+ :icon="r.icon"
4204
4322
  />
4205
4323
  </span>
4206
4324
  <span class="dcw-id">
@@ -4288,10 +4406,9 @@ onBeforeUnmount(() => {
4288
4406
  :title="c.devName ? $t('townView.k2bindr0007', { p0: c.devName }) : $t('townView.k2bindr0008')"
4289
4407
  @click="bindDcwChoice(c.id)"
4290
4408
  >
4291
- <svg
4409
+ <WorkshopDaqTemplateIcon
4292
4410
  class="bind-svg"
4293
- viewBox="0 0 24 24"
4294
- v-html="daqIcon(c.tpl.icon)"
4411
+ :icon="c.tpl.icon"
4295
4412
  />
4296
4413
  <span>{{ c.name }}<i
4297
4414
  v-if="c.devName"
@@ -4858,19 +4975,19 @@ onBeforeUnmount(() => {
4858
4975
  </div>
4859
4976
  <div class="event-list">
4860
4977
  <div
4861
- v-for="(t, i) in [...ticker].reverse()"
4862
- :key="`${t.at}-${i}`"
4978
+ v-for="(ev, i) in [...ticker].reverse()"
4979
+ :key="`${ev.at}-${i}`"
4863
4980
  class="event-row"
4864
4981
  >
4865
- <span class="ev-time">{{ fmtTime(t.at) }}</span>
4982
+ <span class="ev-time">{{ fmtTime(ev.at) }}</span>
4866
4983
  <span
4867
4984
  class="ev-name"
4868
- :title="t.agentName"
4869
- >{{ t.agentName }}</span>
4985
+ :title="ev.agentName"
4986
+ >{{ ev.agentName }}</span>
4870
4987
  <span
4871
4988
  class="ev-text"
4872
- :title="t.text"
4873
- >{{ t.text }}</span>
4989
+ :title="ev.text"
4990
+ >{{ ev.text }}</span>
4874
4991
  </div>
4875
4992
  <div
4876
4993
  v-if="!ticker.length"
@@ -4970,6 +5087,13 @@ onBeforeUnmount(() => {
4970
5087
  © 2026 ABO · DIGITAL TWIN · {{ $t('townView.k1h5gxpf137') }}</span>
4971
5088
  </footer>
4972
5089
 
5090
+ <!-- 窄屏抽屉遮罩:点击空白关闭(与 AppSidebar 同一套抽屉协议) -->
5091
+ <div
5092
+ v-if="sheetOpen"
5093
+ class="sheet-mask"
5094
+ @click="closeSheet"
5095
+ />
5096
+
4973
5097
  <!-- 加载遮罩(全页) -->
4974
5098
  <div
4975
5099
  v-if="!ready"
@@ -5104,6 +5228,14 @@ onBeforeUnmount(() => {
5104
5228
  background: #0e1626;
5105
5229
  }
5106
5230
  .avatar-chip span { font-size: 12px; color: var(--hud-text); }
5231
+ /* ── 孪生 HUD 字号地板(桌面档) ────────────────────────────────────────────
5232
+ * 孪生面板是"仪表铭牌",8.5–9px 的 mono 微字在 1440 下也被审计稳定判为不可读
5233
+ * (实测每页 ~400 个节点)。10px 起是这套密度还能承受的下限:观感几乎不变,
5234
+ * 但从"看得见"变成"读得了"。窄屏另有更严格的 11.5px 档(见下方媒体查询)。 */
5235
+ .town-view small {
5236
+ font-size: 11px;
5237
+ }
5238
+
5107
5239
  .avatar-fallback {
5108
5240
  width: 24px;
5109
5241
  height: 24px;
@@ -5141,7 +5273,9 @@ onBeforeUnmount(() => {
5141
5273
  flex: 1;
5142
5274
  min-height: 0;
5143
5275
  display: grid;
5144
- grid-template-columns: 250px minmax(540px, 1fr) 342px;
5276
+ /* 轨道随视口收缩(clamp):1440 下仍是 250 / 342(外观不变),
5277
+ * 1024–1131 区间不再因 minmax(540px) 把右轨挤出视口 */
5278
+ grid-template-columns: clamp(212px, 19vw, 250px) minmax(0, 1fr) clamp(284px, 26vw, 342px);
5145
5279
  }
5146
5280
  .rail {
5147
5281
  min-height: 0;
@@ -5266,11 +5400,11 @@ onBeforeUnmount(() => {
5266
5400
  .daq-svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
5267
5401
  .daq-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
5268
5402
  .daq-name { font-size: 12px; font-weight: 600; color: var(--hud-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
5269
- .daq-code { font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.08em; color: var(--hud-faint); }
5403
+ .daq-code { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.08em; color: var(--hud-faint); }
5270
5404
  .daq-count {
5271
5405
  flex: none;
5272
5406
  font-family: var(--font-mono);
5273
- font-size: 9px;
5407
+ font-size: 10px;
5274
5408
  color: var(--hud-accent);
5275
5409
  background: rgba(53, 224, 160, 0.1);
5276
5410
  border: 1px solid rgba(53, 224, 160, 0.28);
@@ -5289,7 +5423,7 @@ onBeforeUnmount(() => {
5289
5423
  .daq-card.tpl:focus-visible { outline: 1px solid var(--hud-accent); outline-offset: 1px; }
5290
5424
  .daq-caret {
5291
5425
  flex: none;
5292
- font-size: 8px;
5426
+ font-size: 10px;
5293
5427
  color: var(--hud-faint);
5294
5428
  transition: transform 0.15s var(--hud-ease);
5295
5429
  }
@@ -5421,7 +5555,7 @@ onBeforeUnmount(() => {
5421
5555
  .scene-row.active .scene-ico { box-shadow: 0 0 10px color-mix(in srgb, var(--ch, var(--hud-accent)) 40%, transparent); }
5422
5556
  .scene-meta-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
5423
5557
  .scene-name { font-size: 12px; font-weight: 600; color: var(--hud-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
5424
- .scene-meta { font-family: var(--font-mono); font-size: 9px; color: var(--hud-faint); }
5558
+ .scene-meta { font-family: var(--font-mono); font-size: 10px; color: var(--hud-faint); }
5425
5559
  .scene-cur, .scene-add {
5426
5560
  margin-left: auto;
5427
5561
  flex: none;
@@ -5746,7 +5880,7 @@ onBeforeUnmount(() => {
5746
5880
  .dock-mode {
5747
5881
  margin-left: auto;
5748
5882
  font-family: var(--font-mono);
5749
- font-size: 9px;
5883
+ font-size: 10px;
5750
5884
  letter-spacing: 0.1em;
5751
5885
  color: var(--hud-faint);
5752
5886
  border: 1px solid var(--hud-line);
@@ -5759,7 +5893,7 @@ onBeforeUnmount(() => {
5759
5893
  margin: 2px 0 7px;
5760
5894
  padding-top: 7px;
5761
5895
  font-family: var(--font-mono);
5762
- font-size: 8.5px;
5896
+ font-size: 10px;
5763
5897
  font-weight: 600;
5764
5898
  letter-spacing: 0.16em;
5765
5899
  text-transform: uppercase;
@@ -6012,7 +6146,7 @@ onBeforeUnmount(() => {
6012
6146
  .xz-seg { display: flex; gap: 6px; margin-bottom: 8px; }
6013
6147
  .xz-seg .seg-btn { flex: 1; }
6014
6148
  .scale-row { display: flex; gap: 8px; align-items: center; }
6015
- .scale-min, .scale-max { font-family: var(--font-mono); font-size: 9px; color: var(--hud-faint); }
6149
+ .scale-min, .scale-max { font-family: var(--font-mono); font-size: 10px; color: var(--hud-faint); }
6016
6150
  .scale-range { flex: 1; accent-color: var(--hud-accent); }
6017
6151
  .scale-val {
6018
6152
  text-align: center;
@@ -6063,7 +6197,7 @@ onBeforeUnmount(() => {
6063
6197
  transition: background 0.13s var(--hud-ease);
6064
6198
  }
6065
6199
  .event-row:hover { background: rgba(53, 224, 160, 0.05); }
6066
- .ev-time { flex: none; font-family: var(--font-mono); font-size: 9px; font-variant-numeric: tabular-nums; color: var(--hud-faint); }
6200
+ .ev-time { flex: none; font-family: var(--font-mono); font-size: 10px; font-variant-numeric: tabular-nums; color: var(--hud-faint); }
6067
6201
  .ev-name {
6068
6202
  flex: none;
6069
6203
  max-width: 70px;
@@ -6213,7 +6347,7 @@ onBeforeUnmount(() => {
6213
6347
  .chat-meta { display: flex; gap: 6px; align-items: center; }
6214
6348
  .chat-state {
6215
6349
  font-family: var(--font-mono);
6216
- font-size: 8.5px;
6350
+ font-size: 10px;
6217
6351
  letter-spacing: 0.1em;
6218
6352
  color: var(--hud-dim);
6219
6353
  padding: 1px 6px;
@@ -6223,7 +6357,7 @@ onBeforeUnmount(() => {
6223
6357
  /* 职务章:Leader(琥珀=指挥)/ Worker(青=执行),与 KPI 语义色同源 */
6224
6358
  .chat-role {
6225
6359
  font-family: var(--font-mono);
6226
- font-size: 8.5px;
6360
+ font-size: 10px;
6227
6361
  font-weight: 700;
6228
6362
  letter-spacing: 0.12em;
6229
6363
  padding: 1px 7px;
@@ -6233,8 +6367,8 @@ onBeforeUnmount(() => {
6233
6367
  .chat-role.r-worker { color: var(--hud-cyan); background: rgba(65, 200, 244, 0.1); border: 1px solid rgba(65, 200, 244, 0.4); }
6234
6368
  .chat-state.s-busy { color: var(--hud-amber); border-color: rgba(246, 196, 83, 0.5); }
6235
6369
  .chat-state.s-stopped { color: var(--hud-danger); border-color: rgba(255, 107, 107, 0.5); }
6236
- .chat-harness { font-family: var(--font-mono); font-size: 8.5px; letter-spacing: 0.1em; color: var(--hud-faint); }
6237
- .chat-count { font-family: var(--font-mono); font-size: 8.5px; color: var(--hud-faint); }
6370
+ .chat-harness { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--hud-faint); }
6371
+ .chat-count { font-family: var(--font-mono); font-size: 10px; color: var(--hud-faint); }
6238
6372
  .chat-refresh {
6239
6373
  flex: none;
6240
6374
  width: 22px;
@@ -6259,7 +6393,7 @@ onBeforeUnmount(() => {
6259
6393
  padding: 10px 12px 12px;
6260
6394
  overflow: hidden auto;
6261
6395
  }
6262
- .rpg-divider { margin: 4px 0 2px; font-family: var(--font-mono); font-size: 8.5px; letter-spacing: 0.2em; color: var(--hud-faint); }
6396
+ .rpg-divider { margin: 4px 0 2px; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.2em; color: var(--hud-faint); }
6263
6397
  .rpg-divider.live { color: var(--hud-amber); }
6264
6398
  .rpg-line { display: flex; gap: 8px; align-items: flex-start; }
6265
6399
  .rpg-time {
@@ -6268,7 +6402,7 @@ onBeforeUnmount(() => {
6268
6402
  text-align: right;
6269
6403
  margin-top: 5px;
6270
6404
  font-family: var(--font-mono);
6271
- font-size: 9px;
6405
+ font-size: 10px;
6272
6406
  font-variant-numeric: tabular-nums;
6273
6407
  color: var(--hud-faint);
6274
6408
  }
@@ -6289,7 +6423,7 @@ onBeforeUnmount(() => {
6289
6423
  background: #14213a;
6290
6424
  border-color: var(--hud-line-hi);
6291
6425
  }
6292
- .rpg-kind { align-self: flex-start; font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.14em; color: var(--hud-faint); }
6426
+ .rpg-kind { align-self: flex-start; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em; color: var(--hud-faint); }
6293
6427
  .rpg-kind.k-artifact { color: var(--hud-accent); }
6294
6428
  .rpg-kind.k-error { color: var(--hud-danger); }
6295
6429
  .rpg-text { font-size: 13px; line-height: 1.6; color: var(--hud-text); word-break: break-word; text-wrap: pretty; }
@@ -6304,7 +6438,7 @@ onBeforeUnmount(() => {
6304
6438
  }
6305
6439
  .ty-dot:nth-child(2) { animation-delay: 0.15s; }
6306
6440
  .ty-dot:nth-child(3) { animation-delay: 0.3s; }
6307
- .ty-label { margin-left: 5px; font-family: var(--font-mono); font-size: 9px; color: var(--hud-faint); }
6441
+ .ty-label { margin-left: 5px; font-family: var(--font-mono); font-size: 10px; color: var(--hud-faint); }
6308
6442
  .rpg-note { font-family: var(--font-mono); font-size: 10px; color: var(--hud-dim); text-align: center; padding: 14px 0; }
6309
6443
 
6310
6444
  /* 频道边界编辑(浮动) */
@@ -6328,7 +6462,7 @@ onBeforeUnmount(() => {
6328
6462
  .bp-title { display: flex; gap: 8px; align-items: center; padding-bottom: 8px; border-bottom: 1px solid var(--hud-line); cursor: grab; touch-action: none; }
6329
6463
  .bp-title:active { cursor: grabbing; }
6330
6464
  .bp-name { font-size: 13px; font-weight: 700; color: var(--hud-text); }
6331
- .bp-sub { font-family: var(--font-mono); font-size: 8.5px; letter-spacing: 0.12em; color: var(--hud-faint); }
6465
+ .bp-sub { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; color: var(--hud-faint); }
6332
6466
  .bp-tabs { display: flex; gap: 4px; margin-left: auto; }
6333
6467
  .bp-tab {
6334
6468
  padding: 3px 10px;
@@ -6407,7 +6541,7 @@ onBeforeUnmount(() => {
6407
6541
  }
6408
6542
  .member-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
6409
6543
  .member-name { font-size: 11px; font-weight: 600; color: var(--hud-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
6410
- .member-role { font-family: var(--font-mono); font-size: 8.5px; letter-spacing: 0.06em; color: var(--hud-faint); }
6544
+ .member-role { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em; color: var(--hud-faint); }
6411
6545
  .member-role.r-lead { color: var(--hud-amber); }
6412
6546
  .member-role.r-worker { color: var(--hud-cyan); }
6413
6547
  .member-select { flex: 1; min-width: 0; font-size: 10px; color: var(--hud-text); background: var(--hud-input); border: 1px solid var(--hud-line); border-radius: 6px; padding: 3px 6px; }
@@ -6541,7 +6675,7 @@ onBeforeUnmount(() => {
6541
6675
  .bp-tpl {
6542
6676
  padding: 6px 10px 3px;
6543
6677
  font-family: var(--font-mono);
6544
- font-size: 9px;
6678
+ font-size: 10px;
6545
6679
  letter-spacing: 0.08em;
6546
6680
  text-transform: uppercase;
6547
6681
  color: var(--hud-faint);
@@ -6593,7 +6727,7 @@ onBeforeUnmount(() => {
6593
6727
  padding: 0 5px;
6594
6728
  font-style: normal;
6595
6729
  font-family: var(--font-mono);
6596
- font-size: 8.5px;
6730
+ font-size: 10px;
6597
6731
  font-weight: 700;
6598
6732
  letter-spacing: 0.08em;
6599
6733
  color: var(--hud-amber);
@@ -6634,7 +6768,7 @@ onBeforeUnmount(() => {
6634
6768
  .dcw-win em {
6635
6769
  flex: none;
6636
6770
  font-style: normal;
6637
- font-size: 8.5px;
6771
+ font-size: 10px;
6638
6772
  font-weight: 700;
6639
6773
  letter-spacing: 0.14em;
6640
6774
  color: var(--hud-faint);
@@ -7046,14 +7180,305 @@ input[type='number'] { -moz-appearance: textfield; appearance: textfield; }
7046
7180
  .rpg-line.live .rpg-bubble, .ty-dot, .loading-spinner { animation: none; }
7047
7181
  }
7048
7182
 
7049
- /* 窄屏:轨道收为抽屉 */
7050
- @media (max-width: 1180px) {
7051
- .app { grid-template-columns: 0 1fr 0; }
7052
- .rail { position: fixed; top: 50px; bottom: 30px; z-index: 70; width: 288px; background: #0a101b; transition: transform 0.22s var(--hud-ease); box-shadow: var(--hud-shadow); }
7053
- .rail-left { left: 0; transform: translateX(-104%); }
7054
- .rail-right { right: 0; transform: translateX(104%); }
7055
- .dock { grid-template-columns: 1fr; }
7056
- .stage-dock { grid-template-columns: 1fr; }
7183
+ /* ============================================================
7184
+ * 窄屏形态零件(≥1024 桌面一律不出现:形态由下面的媒体查询切换)
7185
+ * ============================================================ */
7186
+ .sheet-btn,
7187
+ .dock-toggle,
7188
+ .sheet-hd { display: none; }
7189
+ .sheet-mask {
7190
+ position: fixed;
7191
+ inset: 0;
7192
+ z-index: 69;
7193
+ background: rgba(3, 6, 12, 0.62);
7194
+ backdrop-filter: blur(1.5px);
7195
+ }
7196
+
7197
+ /* ═══════════════════════════════════════════════════════════════
7198
+ * 窄屏(≤1023)= 手持巡检终端,而不是"缩小的控制室":
7199
+ * · 3D 舞台占满视口宽度,纵向吃满顶栏/状态栏之间的全部高度;
7200
+ * · 左右轨从固定侧栏改为底部抽屉页(sheet),顶栏两个按钮开合
7201
+ * (遮罩 / Esc / 点空白关闭,协议与 AppSidebar 抽屉一致);
7202
+ * · 底部坞折成可收起的横条,默认收起,不再压舞台;
7203
+ * · 刻度字号地板:标签 ≥11.5px、正文 ≥13px;可点目标 ≥40px。
7204
+ * 断点与 useResponsive(≥1024 桌面)同源;形态全部由 CSS 决定。
7205
+ * ═══════════════════════════════════════════════════════════════ */
7206
+ @media (max-width: 1023px) {
7207
+ /* 窄屏对比度地板:--hud-faint(#5f6e84)在深面板上只有 3.6:1,
7208
+ * 窄屏(强光/手持)抬到 ≥7:1;桌面配色不动 */
7209
+ .town-view { --hud-faint: #8b9bb0; }
7210
+
7211
+ /* ── 顶栏:折两行(品牌 + 告警 + 抽屉开关 / 模式段) ── */
7212
+ .topnav {
7213
+ height: auto;
7214
+ min-height: 46px;
7215
+ flex-wrap: wrap;
7216
+ align-items: center;
7217
+ gap: 6px 8px;
7218
+ padding: 6px 10px 8px;
7219
+ }
7220
+ .brand { order: 0; flex: 1 1 auto; min-width: 0; gap: 8px; }
7221
+ .brand > div { min-width: 0; overflow: hidden; }
7222
+ .brand-name { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
7223
+ .nav-right { order: 1; flex: none; margin-left: 0; gap: 6px; }
7224
+ /* 模式段整行:不再绝对定位居中(那会与品牌/开关互相压字) */
7225
+ .nav-tabs { order: 2; flex: 1 1 100%; position: static; transform: none; gap: 8px; }
7226
+ .seg button { padding: 0 14px; font-size: 13px; }
7227
+ .nav-action { height: 40px; padding: 0 12px; font-size: 13px; }
7228
+ .save-chip { font-size: 11.5px; }
7229
+ .nav-chip { font-size: 11.5px; padding: 5px 8px; }
7230
+ .avatar-chip { padding: 3px 8px 3px 3px; }
7231
+ .avatar-chip span { font-size: 13px; }
7232
+ .avatar-fallback { width: 26px; height: 26px; font-size: 11.5px; }
7233
+ /* 抽屉开关:图标 + 名称,≥40px 触摸目标 */
7234
+ .sheet-btn {
7235
+ display: inline-flex;
7236
+ align-items: center;
7237
+ gap: 6px;
7238
+ min-height: 40px;
7239
+ padding: 0 10px;
7240
+ border-radius: var(--hud-r-sm);
7241
+ border: 1px solid #27395c;
7242
+ background: #101a2b;
7243
+ color: var(--hud-dim);
7244
+ font-size: 12.5px;
7245
+ font-weight: 600;
7246
+ white-space: nowrap;
7247
+ }
7248
+ .sheet-btn.on {
7249
+ color: #04120c;
7250
+ background: var(--hud-accent);
7251
+ border-color: var(--hud-accent);
7252
+ box-shadow: 0 0 12px rgba(53, 224, 160, 0.3);
7253
+ }
7254
+ .sheet-ico {
7255
+ width: 16px;
7256
+ height: 16px;
7257
+ flex: none;
7258
+ fill: none;
7259
+ stroke: currentColor;
7260
+ stroke-width: 1.8;
7261
+ stroke-linecap: round;
7262
+ stroke-linejoin: round;
7263
+ }
7264
+
7265
+ /* ── 三栏 → 单栏:舞台全宽、吃满高度 ── */
7266
+ .app { grid-template-columns: minmax(0, 1fr); }
7267
+ .stage-col { min-height: 0; }
7268
+ .stage { min-height: 0; }
7269
+ .stage-top { padding: 10px 12px; gap: 8px; flex-wrap: wrap; }
7270
+ .vp-title { padding: 6px 10px; gap: 8px; }
7271
+ .vp-title h2 { font-size: 14px; }
7272
+ .vp-id { font-size: 11.5px; }
7273
+ .vp-tool { width: 40px; height: 40px; }
7274
+ .vp-svg { width: 17px; height: 17px; }
7275
+ .angle-chip { top: 58px; left: 12px; min-height: 40px; padding: 4px 10px; font-size: 13px; }
7276
+ .angle-chip select { font-size: 13px; }
7277
+ /* KPI 条:一行横滑,不再折成多行压住场景 */
7278
+ .kpi-strip {
7279
+ bottom: 10px;
7280
+ gap: 8px;
7281
+ padding: 0 12px;
7282
+ justify-content: flex-start;
7283
+ flex-wrap: nowrap;
7284
+ overflow-x: auto;
7285
+ overscroll-behavior-x: contain;
7286
+ scrollbar-width: none;
7287
+ }
7288
+ .kpi-strip::-webkit-scrollbar { display: none; }
7289
+ .kpi { flex: none; padding: 6px 10px; gap: 8px; }
7290
+ .kpi-ico { width: 28px; height: 28px; }
7291
+ .kpi-label { font-size: 11.5px; }
7292
+ .kpi-val { font-size: 13px; }
7293
+ .kpi-val small { font-size: 11.5px; }
7294
+
7295
+ /* ── 左右轨 → 底部抽屉页(sheet) ── */
7296
+ .rail {
7297
+ position: fixed;
7298
+ left: 0;
7299
+ right: 0;
7300
+ top: auto;
7301
+ bottom: 0;
7302
+ z-index: 70;
7303
+ width: auto;
7304
+ max-height: min(76dvh, 640px);
7305
+ padding: 0 10px 12px;
7306
+ gap: 8px;
7307
+ overflow-y: auto;
7308
+ overscroll-behavior: contain;
7309
+ border: 0;
7310
+ border-top: 1px solid var(--hud-line-hi);
7311
+ border-radius: 16px 16px 0 0;
7312
+ background: linear-gradient(180deg, #0c1420 0%, #080d16 46%);
7313
+ box-shadow: 0 -18px 46px rgba(0, 0, 0, 0.6);
7314
+ transform: translateY(103%);
7315
+ visibility: hidden;
7316
+ /* 关门:位移走完再隐藏(离散属性用延时);开门:立即可见 */
7317
+ transition: transform 0.26s var(--hud-ease), visibility 0s linear 0.26s;
7318
+ }
7319
+ .rail.sheet-open {
7320
+ transform: none;
7321
+ visibility: visible;
7322
+ transition: transform 0.26s var(--hud-ease), visibility 0s;
7323
+ }
7324
+ .topnav.sheet-on { z-index: 71; }
7325
+ .rail-left,
7326
+ .rail-right { border-right: 0; border-left: 0; }
7327
+ .rail .panel { flex: none; }
7328
+ .sheet-hd {
7329
+ display: flex;
7330
+ align-items: center;
7331
+ gap: 8px;
7332
+ position: sticky;
7333
+ top: 0;
7334
+ z-index: 2;
7335
+ margin: 0 -10px 2px;
7336
+ padding: 13px 12px 9px;
7337
+ background: linear-gradient(180deg, #0e1725, #0c1420);
7338
+ border-bottom: 1px solid var(--hud-line-soft);
7339
+ }
7340
+ .sheet-grip {
7341
+ position: absolute;
7342
+ left: 50%;
7343
+ top: 6px;
7344
+ width: 40px;
7345
+ height: 3px;
7346
+ margin-left: -20px;
7347
+ border-radius: 2px;
7348
+ background: #2c4568;
7349
+ }
7350
+ .sheet-hd-t { font-size: 13px; font-weight: 700; color: var(--hud-text); letter-spacing: 0.04em; }
7351
+ .sheet-x {
7352
+ margin-left: auto;
7353
+ width: 40px;
7354
+ height: 40px;
7355
+ flex: none;
7356
+ border-radius: 8px;
7357
+ border: 1px solid var(--hud-line);
7358
+ background: #101a2b;
7359
+ color: var(--hud-dim);
7360
+ font-size: 15px;
7361
+ }
7362
+
7363
+ /* ── 底部坞:可收起横条(默认收起,舞台优先) ── */
7364
+ .dock { display: block; padding: 0; }
7365
+ .dock-toggle {
7366
+ display: flex;
7367
+ align-items: center;
7368
+ gap: 8px;
7369
+ width: 100%;
7370
+ min-height: 44px;
7371
+ padding: 0 12px;
7372
+ border: 0;
7373
+ border-top: 1px solid var(--hud-line-soft);
7374
+ background: linear-gradient(180deg, #101827, #0c131f);
7375
+ color: var(--hud-text);
7376
+ font-size: 13px;
7377
+ font-weight: 600;
7378
+ text-align: left;
7379
+ }
7380
+ .dock-toggle-t { flex: none; }
7381
+ .dock-toggle-hint {
7382
+ font-size: 11.5px;
7383
+ color: var(--hud-faint);
7384
+ letter-spacing: 0.08em;
7385
+ overflow: hidden;
7386
+ text-overflow: ellipsis;
7387
+ white-space: nowrap;
7388
+ }
7389
+ .dock-caret { margin-left: auto; flex: none; color: var(--hud-dim); transition: transform 0.2s var(--hud-ease); }
7390
+ .dock.dock-open .dock-caret { transform: rotate(180deg); }
7391
+ .dock .dock-card { display: none; }
7392
+ .dock.dock-open { padding: 8px; max-height: 52dvh; overflow-y: auto; overscroll-behavior: contain; }
7393
+ .dock.dock-open .dock-card { display: flex; min-height: 0; margin-bottom: 8px; }
7394
+ .dock.dock-open .dock-card:last-child { margin-bottom: 0; }
7395
+ .dock-card { padding: 12px; }
7396
+ .dock-hd h3 { font-size: 13px; }
7397
+ .dock-count,
7398
+ .dock-mode { font-size: 11.5px; }
7399
+ .ctl-sec { font-size: 11.5px; }
7400
+ .ctl-row { flex-wrap: wrap; gap: 6px 10px; }
7401
+ .ctl-name { width: auto; min-width: 62px; font-size: 13px; }
7402
+ .ctl-val { width: auto; min-width: 38px; font-size: 12.5px; }
7403
+ .fps-seg { flex-wrap: wrap; }
7404
+ .fps-seg button { font-size: 13px; padding: 0 10px; }
7405
+ .snap-toggle { font-size: 11.5px; }
7406
+ .lg-chip { font-size: 11.5px; }
7407
+ .trend-empty { font-size: 11.5px; }
7408
+
7409
+ /* ── 状态栏:留连接态 + 统计,FPS 贴右,版权行收起 ── */
7410
+ .statusbar {
7411
+ height: auto;
7412
+ min-height: 30px;
7413
+ flex-wrap: wrap;
7414
+ gap: 6px 12px;
7415
+ padding: 5px 10px;
7416
+ font-size: 11.5px;
7417
+ }
7418
+ .statusbar .copy { display: none; }
7419
+ .sb-lat { position: static; transform: none; margin-left: auto; }
7420
+
7421
+ /* ── 触摸目标:窄屏所有可点元件 ≥40px 高 ── */
7422
+ .town-view button { min-height: 40px; }
7423
+
7424
+ /* ── 刻度字号地板:标签 ≥11.5px(窄屏不允许 8.5–10px 徽标字) ── */
7425
+ .town-view :where(
7426
+ .panel-tag, .daq-code, .daq-count, .daq-caret, .node-val, .node-dev, .daq-empty-hint,
7427
+ .scene-meta, .scene-cur, .scene-add, .scene-hint, .co-label, .co-range, .lg-chip, .lg-more,
7428
+ .ins-chip, .sect-hd, .obj-label, .range-status, .obj-mini, .bp-val, .seg-btn, .scale-min,
7429
+ .scale-max, .scale-val, .ins-empty, .donut-center span, .ev-time, .ev-name, .ev-text,
7430
+ .event-empty, .rail-empty, .al-src, .al-state, .mm-meta, .chat-state, .chat-role,
7431
+ .chat-harness, .chat-count, .rpg-divider, .rpg-time, .rpg-kind, .ty-label, .rpg-note,
7432
+ .bp-sub, .bp-tab, .bp-label, .bp-hint, .bp-btn, .member-ava, .member-role, .member-select,
7433
+ .bind-label, .bind-val, .bp-tpl, .bp-cur, .bp-empty, .dcw-name, .dcw-tag, .dcw-win-num,
7434
+ .dcw-send, .dcw-err, .bind-select, .bind-add-btn, .daq-ctl-cycle, .daq-num, .daq-th-inputs,
7435
+ .approval-ttl, .approval-detail, .dcw-write input::placeholder
7436
+ ) { font-size: 11.5px; }
7437
+ .town-view .statusbar .copy,
7438
+ .town-view .dock .dock-count,
7439
+ .town-view .dock .dock-mode,
7440
+ .town-view .dock .ctl-sec,
7441
+ .town-view .dock .ctl-val,
7442
+ .town-view .dock .dock-toggle-hint { font-size: 11.5px; }
7443
+ .town-view .dock .ctl-val { font-size: 12.5px; }
7444
+ .town-view .dock-hd h3 { font-size: 13px; }
7445
+ .town-view .bind-val b { font-size: 12px; }
7446
+ .town-view .avatar-chip span { font-size: 13px; }
7447
+ .town-view .save-chip { font-size: 11.5px; }
7448
+ .town-view .nav-chip { font-size: 11.5px; }
7449
+ .town-view .vp-id { font-size: 11.5px; }
7450
+ .town-view .loading-text { font-size: 12px; }
7451
+ /* ── 正文 ≥13px ── */
7452
+ .town-view :where(
7453
+ .daq-name, .scene-name, .al-txt, .ev-text, .chat-name, .co-val, .rpg-text, .daq-info-row,
7454
+ .daq-ctl-btn, .rail-empty, .rpg-note, .member-name, .bind-label, .dcw-cur
7455
+ ) { font-size: 13px; }
7456
+ .town-view .statusbar { font-size: 12px; }
7457
+ .town-view .vd-name { font-size: 13px; }
7458
+ }
7459
+
7460
+ /* ── <640 单列档:进一步让位给舞台(品牌文字收成徽记,名字收进头像) ── */
7461
+ @media (max-width: 639px) {
7462
+ .brand { flex: none; }
7463
+ .brand > div { display: none; }
7464
+ .brand-glyph { width: 28px; height: 28px; }
7465
+ .nav-fps { display: none; }
7466
+ .nav-user span { display: none; }
7467
+ .avatar-chip { padding: 3px; }
7468
+ .sheet-btn-t { display: none; }
7469
+ .sheet-btn { padding: 0 10px; }
7470
+ .stage-top { padding: 8px 10px; }
7471
+ .vp-tools { gap: 6px; }
7472
+ .vp-tool { width: 40px; height: 40px; }
7473
+ .angle-chip { top: 54px; left: 10px; }
7474
+ .kpi-strip { padding: 0 10px; }
7475
+ .dock.dock-open { max-height: 58dvh; }
7476
+ }
7477
+
7478
+ /* ── 减少动态偏好:抽屉位移直接切换,不做过渡 ── */
7479
+ @media (prefers-reduced-motion: reduce) {
7480
+ .rail { transition: none; }
7481
+ .dock-caret { transition: none; }
7057
7482
  }
7058
7483
 
7059
7484
  .approval-card {