@opencodex/cli 1.0.37

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 (401) hide show
  1. package/README.md +221 -0
  2. package/bin/dscode.exe +12 -0
  3. package/cli-wrapper.cjs +105 -0
  4. package/install.cjs +177 -0
  5. package/package.json +39 -0
  6. package/scripts/figure/compose.py +363 -0
  7. package/scripts/figure/replay-shots.mjs +205 -0
  8. package/web/dist/assets/AssistantsView-OMdgsTIj.js +1 -0
  9. package/web/dist/assets/AutomationView-BPwjLbkL.js +1 -0
  10. package/web/dist/assets/ChannelConfigView-JDljUn42.js +8 -0
  11. package/web/dist/assets/ConnectorsView-AemNT1Ae.js +1 -0
  12. package/web/dist/assets/FileTreeView-DYx-0jFV.js +11 -0
  13. package/web/dist/assets/HookConfigView-CS1QqgPx.js +1 -0
  14. package/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  15. package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  16. package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  17. package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  18. package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  19. package/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  20. package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  21. package/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  22. package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  23. package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  24. package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  25. package/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  26. package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  27. package/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  28. package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  29. package/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  30. package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  31. package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  32. package/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  33. package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  34. package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  35. package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  36. package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  37. package/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  38. package/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  39. package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  40. package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  41. package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  42. package/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  43. package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  44. package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  45. package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  46. package/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  47. package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  48. package/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  49. package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  50. package/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  51. package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  52. package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  53. package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  54. package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  55. package/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  56. package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  57. package/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  58. package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  59. package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  60. package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  61. package/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  62. package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  63. package/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  64. package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  65. package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  66. package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  67. package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  68. package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  69. package/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  70. package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  71. package/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  72. package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  73. package/web/dist/assets/ModelsView-B3nHezvJ.js +1 -0
  74. package/web/dist/assets/PluginsView-C4Xc70ec.js +1 -0
  75. package/web/dist/assets/SessionGrid-BGpz0-op.css +1 -0
  76. package/web/dist/assets/SessionGrid-C9-LeJH0.js +47 -0
  77. package/web/dist/assets/SettingsModal-CjWT1C66.js +4 -0
  78. package/web/dist/assets/SkillsView-DTZyXald.js +1 -0
  79. package/web/dist/assets/TemplatesTab-CE-CfCf2.js +1 -0
  80. package/web/dist/assets/ThreeScene-CMqkpyKO.js +2863 -0
  81. package/web/dist/assets/ThreeScene-DvOrMIoG.css +1 -0
  82. package/web/dist/assets/ToolsView-CrEHDTEO.js +1 -0
  83. package/web/dist/assets/abap-BdImnpbu.js +1 -0
  84. package/web/dist/assets/actionscript-3-B3316cI-.js +1 -0
  85. package/web/dist/assets/ada-bCR0ucgS.js +1 -0
  86. package/web/dist/assets/ahk-CsyLZFj1.js +1 -0
  87. package/web/dist/assets/ahk2-8Zs4aa1G.js +1 -0
  88. package/web/dist/assets/andromeeda-C4gqWexZ.js +1 -0
  89. package/web/dist/assets/angular-html-DA-rfuFy.js +1 -0
  90. package/web/dist/assets/angular-ts-BrjP3tb8.js +1 -0
  91. package/web/dist/assets/apache-Pmp26Uib.js +1 -0
  92. package/web/dist/assets/apex-DhZFqWV2.js +1 -0
  93. package/web/dist/assets/apl-CORt7UWP.js +1 -0
  94. package/web/dist/assets/applescript-Co6uUVPk.js +1 -0
  95. package/web/dist/assets/ara-BRHolxvo.js +1 -0
  96. package/web/dist/assets/asciidoc-CSVQ5wI8.js +1 -0
  97. package/web/dist/assets/asm-D_Q5rh1f.js +1 -0
  98. package/web/dist/assets/astro-HNnZUWAn.js +1 -0
  99. package/web/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  100. package/web/dist/assets/awk-DMzUqQB5.js +1 -0
  101. package/web/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
  102. package/web/dist/assets/ayu-light-BA47KaF1.js +1 -0
  103. package/web/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
  104. package/web/dist/assets/ballerina-BFfxhgS-.js +1 -0
  105. package/web/dist/assets/bat-CickPsom.js +1 -0
  106. package/web/dist/assets/beancount-k_qm7-4y.js +1 -0
  107. package/web/dist/assets/berry-uYugtg8r.js +1 -0
  108. package/web/dist/assets/bibtex-CHM0blh-.js +1 -0
  109. package/web/dist/assets/bicep-Bmn6On1c.js +1 -0
  110. package/web/dist/assets/bird2-Bx8U0n9b.js +1 -0
  111. package/web/dist/assets/blade-2xfisSek.js +1 -0
  112. package/web/dist/assets/bsl-DlhNcFeZ.js +1 -0
  113. package/web/dist/assets/c-BIGW1oBm.js +1 -0
  114. package/web/dist/assets/c3-Dp5svz6Z.js +1 -0
  115. package/web/dist/assets/cadence-Bv_4Rxtq.js +1 -0
  116. package/web/dist/assets/cairo-KRGpt6FW.js +1 -0
  117. package/web/dist/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
  118. package/web/dist/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
  119. package/web/dist/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
  120. package/web/dist/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
  121. package/web/dist/assets/chapel-DTp_pixX.js +1 -0
  122. package/web/dist/assets/clarity-Dn5IMItf.js +1 -0
  123. package/web/dist/assets/clojure-P80f7IUj.js +1 -0
  124. package/web/dist/assets/cmake-D1j8_8rp.js +1 -0
  125. package/web/dist/assets/cobol-nBiQ_Alo.js +1 -0
  126. package/web/dist/assets/codeowners-Bp6g37R7.js +1 -0
  127. package/web/dist/assets/codeql-DsOJ9woJ.js +1 -0
  128. package/web/dist/assets/coffee-Ch7k5sss.js +1 -0
  129. package/web/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
  130. package/web/dist/assets/coq-C7JzOVbR.js +1 -0
  131. package/web/dist/assets/cpp-BMRokrvK.js +1 -0
  132. package/web/dist/assets/crystal-DGywbUpC.js +1 -0
  133. package/web/dist/assets/csharp-DSvCPggb.js +1 -0
  134. package/web/dist/assets/css-CLj8gQPS.js +1 -0
  135. package/web/dist/assets/csv-fuZLfV_i.js +1 -0
  136. package/web/dist/assets/cue-D82EKSYY.js +1 -0
  137. package/web/dist/assets/cypher-COkxafJQ.js +1 -0
  138. package/web/dist/assets/d-85-TOEBH.js +1 -0
  139. package/web/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  140. package/web/dist/assets/dart-bE4Kk8sk.js +1 -0
  141. package/web/dist/assets/dax-CEL-wOlO.js +1 -0
  142. package/web/dist/assets/desktop-BmXAJ9_W.js +1 -0
  143. package/web/dist/assets/diff-D97Zzqfu.js +1 -0
  144. package/web/dist/assets/docker-BcOcwvcX.js +1 -0
  145. package/web/dist/assets/dotenv-Da5cRb03.js +1 -0
  146. package/web/dist/assets/dracula-BzJJZx-M.js +1 -0
  147. package/web/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  148. package/web/dist/assets/dream-maker-BtqSS_iP.js +1 -0
  149. package/web/dist/assets/edge-FbVlp4U3.js +1 -0
  150. package/web/dist/assets/elixir-CkH2-t6x.js +1 -0
  151. package/web/dist/assets/elm-DbKCFpqz.js +1 -0
  152. package/web/dist/assets/emacs-lisp-C_m_b--Z.js +1 -0
  153. package/web/dist/assets/erb-DXfck5VN.js +1 -0
  154. package/web/dist/assets/erlang-DsQrWhSR.js +1 -0
  155. package/web/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  156. package/web/dist/assets/everforest-light-C8M2exoo.js +1 -0
  157. package/web/dist/assets/fennel-BYunw83y.js +1 -0
  158. package/web/dist/assets/fish-BvzEVeQv.js +1 -0
  159. package/web/dist/assets/fluent-C4IJs8-o.js +1 -0
  160. package/web/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  161. package/web/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
  162. package/web/dist/assets/fsharp-CXgrBDvD.js +1 -0
  163. package/web/dist/assets/gdresource-TyuKm33G.js +1 -0
  164. package/web/dist/assets/gdscript-DqcFQ5yU.js +1 -0
  165. package/web/dist/assets/gdshader-DkwncUOv.js +1 -0
  166. package/web/dist/assets/genie-D0YGMca9.js +1 -0
  167. package/web/dist/assets/gherkin-DyxjwDmM.js +1 -0
  168. package/web/dist/assets/git-commit-F4YmCXRG.js +1 -0
  169. package/web/dist/assets/git-rebase-r7XF79zn.js +1 -0
  170. package/web/dist/assets/github-dark-DHJKELXO.js +1 -0
  171. package/web/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  172. package/web/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  173. package/web/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  174. package/web/dist/assets/github-light-DAi9KRSo.js +1 -0
  175. package/web/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  176. package/web/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  177. package/web/dist/assets/gleam-BspZqrRM.js +1 -0
  178. package/web/dist/assets/glimmer-js-ByusRIyA.js +1 -0
  179. package/web/dist/assets/glimmer-ts-BfAWNZQY.js +1 -0
  180. package/web/dist/assets/glsl-DplSGwfg.js +1 -0
  181. package/web/dist/assets/gn-n2N0HUVH.js +1 -0
  182. package/web/dist/assets/gnuplot-DdkO51Og.js +1 -0
  183. package/web/dist/assets/go-C27-OAKa.js +1 -0
  184. package/web/dist/assets/graphql-ChdNCCLP.js +1 -0
  185. package/web/dist/assets/groovy-gcz8RCvz.js +1 -0
  186. package/web/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  187. package/web/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  188. package/web/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  189. package/web/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  190. package/web/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  191. package/web/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  192. package/web/dist/assets/hack-BWmVpMyf.js +1 -0
  193. package/web/dist/assets/haml-D5jkg6IW.js +1 -0
  194. package/web/dist/assets/handlebars-BpdQsYii.js +1 -0
  195. package/web/dist/assets/haskell-Df6bDoY_.js +1 -0
  196. package/web/dist/assets/haxe-CfZj7gIn.js +1 -0
  197. package/web/dist/assets/hcl-BWvSN4gD.js +1 -0
  198. package/web/dist/assets/hjson-D5-asLiD.js +1 -0
  199. package/web/dist/assets/hlsl-D3lLCCz7.js +1 -0
  200. package/web/dist/assets/horizon-BUw7H-hv.js +1 -0
  201. package/web/dist/assets/horizon-bright-CUuTKBJd.js +1 -0
  202. package/web/dist/assets/houston-DnULxvSX.js +1 -0
  203. package/web/dist/assets/html-derivative-DlHx6ybY.js +1 -0
  204. package/web/dist/assets/html-pp8916En.js +1 -0
  205. package/web/dist/assets/http-jrhK8wxY.js +1 -0
  206. package/web/dist/assets/hurl-irOxFIW8.js +1 -0
  207. package/web/dist/assets/hxml-2-FPmUDs.js +1 -0
  208. package/web/dist/assets/hy-DFXneXwc.js +1 -0
  209. package/web/dist/assets/icons-f1hlMKvD.js +724 -0
  210. package/web/dist/assets/imba-DGztddWO.js +1 -0
  211. package/web/dist/assets/index-BCHbHIbw.css +1 -0
  212. package/web/dist/assets/index-u1vYyqna.js +592 -0
  213. package/web/dist/assets/ini-BEwlwnbL.js +1 -0
  214. package/web/dist/assets/java-CylS5w8V.js +1 -0
  215. package/web/dist/assets/javascript-wDzz0qaB.js +1 -0
  216. package/web/dist/assets/jinja-f2NsQr07.js +1 -0
  217. package/web/dist/assets/jison-wvAkD_A8.js +1 -0
  218. package/web/dist/assets/json-Cp-IABpG.js +1 -0
  219. package/web/dist/assets/json5-C9tS-k6U.js +1 -0
  220. package/web/dist/assets/jsonc-Des-eS-w.js +1 -0
  221. package/web/dist/assets/jsonl-DcaNXYhu.js +1 -0
  222. package/web/dist/assets/jsonnet-DFQXde-d.js +1 -0
  223. package/web/dist/assets/jssm-C2t-YnRu.js +1 -0
  224. package/web/dist/assets/jsx-g9-lgVsj.js +1 -0
  225. package/web/dist/assets/julia-5Bft2YPA.js +1 -0
  226. package/web/dist/assets/just-Cwhn7H3k.js +1 -0
  227. package/web/dist/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
  228. package/web/dist/assets/kanagawa-lotus-C3DzagqV.js +1 -0
  229. package/web/dist/assets/kanagawa-wave-DFGoQZhC.js +1 -0
  230. package/web/dist/assets/kdl-DV7GczEv.js +1 -0
  231. package/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
  232. package/web/dist/assets/kusto-wEQ09or8.js +1 -0
  233. package/web/dist/assets/laserwave-DUszq2jm.js +1 -0
  234. package/web/dist/assets/latex-D5pSuvFb.js +1 -0
  235. package/web/dist/assets/lean-BZvkOJ9d.js +1 -0
  236. package/web/dist/assets/less-B1dDrJ26.js +1 -0
  237. package/web/dist/assets/light-plus-B7mTdjB0.js +1 -0
  238. package/web/dist/assets/liquid-C0sCDyMI.js +1 -0
  239. package/web/dist/assets/llvm-BZoOZj88.js +1 -0
  240. package/web/dist/assets/log-2UxHyX5q.js +1 -0
  241. package/web/dist/assets/logo-BtOb2qkB.js +1 -0
  242. package/web/dist/assets/lua-BaeVxFsk.js +1 -0
  243. package/web/dist/assets/luau-BnpPk5vE.js +1 -0
  244. package/web/dist/assets/make-CHLpvVh8.js +1 -0
  245. package/web/dist/assets/markdown-Cvjx9yec.js +1 -0
  246. package/web/dist/assets/markdown-DEtuzEyz.js +30 -0
  247. package/web/dist/assets/marko-DjSrsDqO.js +1 -0
  248. package/web/dist/assets/material-theme-D5KoaKCx.js +1 -0
  249. package/web/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  250. package/web/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  251. package/web/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  252. package/web/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  253. package/web/dist/assets/matlab-D7o27uSR.js +1 -0
  254. package/web/dist/assets/mdc-D1_yUvq7.js +1 -0
  255. package/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  256. package/web/dist/assets/mermaid-CQcHuHx7.js +1 -0
  257. package/web/dist/assets/min-dark-CafNBF8u.js +1 -0
  258. package/web/dist/assets/min-light-CTRr51gU.js +1 -0
  259. package/web/dist/assets/mipsasm-CKIfxQSi.js +1 -0
  260. package/web/dist/assets/mojo-DJz3ZmWd.js +1 -0
  261. package/web/dist/assets/monokai-BRVnQi9A.js +1 -0
  262. package/web/dist/assets/moonbit-CHtswR0a.js +1 -0
  263. package/web/dist/assets/move-el3G9tDJ.js +1 -0
  264. package/web/dist/assets/narrat-DRg8JJMk.js +1 -0
  265. package/web/dist/assets/nextflow-C-mBbutL.js +1 -0
  266. package/web/dist/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  267. package/web/dist/assets/nginx-BpAMiNFr.js +1 -0
  268. package/web/dist/assets/night-owl-C39BiMTA.js +1 -0
  269. package/web/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
  270. package/web/dist/assets/nim-BIad80T-.js +1 -0
  271. package/web/dist/assets/nix-CwoSXNpI.js +1 -0
  272. package/web/dist/assets/nord-Ddv68eIx.js +1 -0
  273. package/web/dist/assets/nsis-BlV79W_Q.js +1 -0
  274. package/web/dist/assets/nushell-D3jzshHO.js +1 -0
  275. package/web/dist/assets/objective-c-DXmwc3jG.js +1 -0
  276. package/web/dist/assets/objective-cpp-CLxacb5B.js +1 -0
  277. package/web/dist/assets/ocaml-C0hk2d4L.js +1 -0
  278. package/web/dist/assets/odin-BBf5iR-q.js +1 -0
  279. package/web/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  280. package/web/dist/assets/one-light-C3Wv6jpd.js +1 -0
  281. package/web/dist/assets/openscad-C4EeE6gA.js +1 -0
  282. package/web/dist/assets/org-DM6o9KBp.js +1 -0
  283. package/web/dist/assets/panels-Bn-2wCur.js +1 -0
  284. package/web/dist/assets/pascal-D93ZcfNL.js +1 -0
  285. package/web/dist/assets/perl-B9cMNwum.js +1 -0
  286. package/web/dist/assets/php-Csjmro_R.js +1 -0
  287. package/web/dist/assets/pkl-u5AG7uiY.js +1 -0
  288. package/web/dist/assets/plastic-3e1v2bzS.js +1 -0
  289. package/web/dist/assets/plsql-ChMvpjG-.js +1 -0
  290. package/web/dist/assets/po-BTJTHyun.js +1 -0
  291. package/web/dist/assets/poimandres-CS3Unz2-.js +1 -0
  292. package/web/dist/assets/polar-C0HS_06l.js +1 -0
  293. package/web/dist/assets/postcss-CXtECtnM.js +1 -0
  294. package/web/dist/assets/powerquery-CEu0bR-o.js +1 -0
  295. package/web/dist/assets/powershell-BmBUJMz7.js +1 -0
  296. package/web/dist/assets/prisma-Vru482bI.js +1 -0
  297. package/web/dist/assets/prolog-CbFg5uaA.js +1 -0
  298. package/web/dist/assets/proto-C7zT0LnQ.js +1 -0
  299. package/web/dist/assets/pug-DKIMFp6K.js +1 -0
  300. package/web/dist/assets/puppet-BMWR74SV.js +1 -0
  301. package/web/dist/assets/purescript-CklMAg4u.js +1 -0
  302. package/web/dist/assets/python-B6aJPvgy.js +1 -0
  303. package/web/dist/assets/qml-3beO22l8.js +1 -0
  304. package/web/dist/assets/qmldir-C8lEn-DE.js +1 -0
  305. package/web/dist/assets/qss-IeuSbFQv.js +1 -0
  306. package/web/dist/assets/r-Cf5RLm7j.js +1 -0
  307. package/web/dist/assets/racket-BqYA7rlc.js +1 -0
  308. package/web/dist/assets/raku-DXvB9xmW.js +1 -0
  309. package/web/dist/assets/razor-BjBPvh-w.js +1 -0
  310. package/web/dist/assets/rbs-CpoqiR4B.js +1 -0
  311. package/web/dist/assets/react-vendor-ClBKp0t-.js +9 -0
  312. package/web/dist/assets/red-hvxz__6c.js +1 -0
  313. package/web/dist/assets/reg-C-SQnVFl.js +1 -0
  314. package/web/dist/assets/regexp-CDVJQ6XC.js +1 -0
  315. package/web/dist/assets/rel-C3B-1QV4.js +1 -0
  316. package/web/dist/assets/riscv-BM1_JUlF.js +1 -0
  317. package/web/dist/assets/ron-D8l8udqQ.js +1 -0
  318. package/web/dist/assets/rose-pine-5qJOZa0Y.js +1 -0
  319. package/web/dist/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
  320. package/web/dist/assets/rose-pine-moon-C9pEdX9L.js +1 -0
  321. package/web/dist/assets/rosmsg-BJDFO7_C.js +1 -0
  322. package/web/dist/assets/rst-bs7f0vWN.js +1 -0
  323. package/web/dist/assets/ruby-C0TQ7zu5.js +1 -0
  324. package/web/dist/assets/rust-B1yitclQ.js +1 -0
  325. package/web/dist/assets/sas-DEy46yEz.js +1 -0
  326. package/web/dist/assets/sass-Cj5Yp3dK.js +1 -0
  327. package/web/dist/assets/scala-CqE71os6.js +1 -0
  328. package/web/dist/assets/scheme-C98Dy4si.js +1 -0
  329. package/web/dist/assets/scss-D5BDwBP9.js +1 -0
  330. package/web/dist/assets/sdbl-DVxCFoDh.js +1 -0
  331. package/web/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
  332. package/web/dist/assets/shellscript-Yzrsuije.js +1 -0
  333. package/web/dist/assets/shellsession-BADoaaVG.js +1 -0
  334. package/web/dist/assets/slack-dark-BthQWCQV.js +1 -0
  335. package/web/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  336. package/web/dist/assets/smalltalk-BOQMe2GC.js +1 -0
  337. package/web/dist/assets/smithy-cds9vsN8.js +1 -0
  338. package/web/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  339. package/web/dist/assets/solarized-dark-CpvCGNkr.js +1 -0
  340. package/web/dist/assets/solarized-light-Dlz6yCKv.js +1 -0
  341. package/web/dist/assets/solidity-DijEV5ha.js +1 -0
  342. package/web/dist/assets/soy-8wufbnw4.js +1 -0
  343. package/web/dist/assets/sparql-rVzFXLq3.js +1 -0
  344. package/web/dist/assets/splunk-BtCnVYZw.js +1 -0
  345. package/web/dist/assets/sql-CRqJ_cUM.js +1 -0
  346. package/web/dist/assets/ssh-config-_ykCGR6B.js +1 -0
  347. package/web/dist/assets/stata-DI20mbqo.js +1 -0
  348. package/web/dist/assets/state-xD58vmym.js +17 -0
  349. package/web/dist/assets/sttLanguages-BNRptAQA.js +1 -0
  350. package/web/dist/assets/stylus-BEDo0Tqx.js +1 -0
  351. package/web/dist/assets/surrealql-Cjom0U5J.js +1 -0
  352. package/web/dist/assets/svelte-Cy7k_4gC.js +1 -0
  353. package/web/dist/assets/swift-C2oV4EkX.js +1 -0
  354. package/web/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  355. package/web/dist/assets/system-verilog-0hqHdDBg.js +1 -0
  356. package/web/dist/assets/systemd-4A_iFExJ.js +1 -0
  357. package/web/dist/assets/talonscript-CkByrt1z.js +1 -0
  358. package/web/dist/assets/tasl-QIJgUcNo.js +1 -0
  359. package/web/dist/assets/tcl-dwOrl1Do.js +1 -0
  360. package/web/dist/assets/templ-DhtptRzy.js +1 -0
  361. package/web/dist/assets/terraform-BETggiCN.js +1 -0
  362. package/web/dist/assets/tex-D96PA37w.js +1 -0
  363. package/web/dist/assets/tokyo-night-hegEt444.js +1 -0
  364. package/web/dist/assets/toml-vGWfd6FD.js +1 -0
  365. package/web/dist/assets/ts-tags-D351s5mN.js +1 -0
  366. package/web/dist/assets/tsv-B_m7g4N7.js +1 -0
  367. package/web/dist/assets/tsx-COt5Ahok.js +1 -0
  368. package/web/dist/assets/turtle-BsS91CYL.js +1 -0
  369. package/web/dist/assets/twig-27uCiNez.js +1 -0
  370. package/web/dist/assets/typescript-BPQ3VLAy.js +1 -0
  371. package/web/dist/assets/typespec-CAFt9gP4.js +1 -0
  372. package/web/dist/assets/typst-BUadGCkm.js +1 -0
  373. package/web/dist/assets/v-BGw2Nkan.js +1 -0
  374. package/web/dist/assets/vala-CsfeWuGM.js +1 -0
  375. package/web/dist/assets/vb-Cu-pLBUe.js +1 -0
  376. package/web/dist/assets/verilog-nZwndyjY.js +1 -0
  377. package/web/dist/assets/vesper-DRje8inN.js +1 -0
  378. package/web/dist/assets/vhdl-CeAyd5Ju.js +1 -0
  379. package/web/dist/assets/viml-CJc9bBzg.js +1 -0
  380. package/web/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  381. package/web/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  382. package/web/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  383. package/web/dist/assets/vue-BqiEGhQt.js +1 -0
  384. package/web/dist/assets/vue-html-AaS7Mt5G.js +1 -0
  385. package/web/dist/assets/vue-vine-BoDAl6tE.js +1 -0
  386. package/web/dist/assets/vyper-CDx5xZoG.js +1 -0
  387. package/web/dist/assets/wasm-CG6Dc4jp.js +1 -0
  388. package/web/dist/assets/wasm-MzD3tlZU.js +1 -0
  389. package/web/dist/assets/wenyan-BV7otONQ.js +1 -0
  390. package/web/dist/assets/wgsl-Dx-B1_4e.js +1 -0
  391. package/web/dist/assets/wikitext-BhOHFoWU.js +1 -0
  392. package/web/dist/assets/wit-5i3qLPDT.js +1 -0
  393. package/web/dist/assets/wolfram-lXgVvXCa.js +1 -0
  394. package/web/dist/assets/xml-sdJ4AIDG.js +1 -0
  395. package/web/dist/assets/xsl-CtQFsRM5.js +1 -0
  396. package/web/dist/assets/yaml-Buea-lGh.js +1 -0
  397. package/web/dist/assets/zenscript-DVFEvuxE.js +1 -0
  398. package/web/dist/assets/zig-VOosw3JB.js +1 -0
  399. package/web/dist/favicon.svg +40 -0
  400. package/web/dist/index.html +26 -0
  401. package/web/dist/logo.svg +40 -0
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ compose.py — 把终端回放截图合成带标注的判读图(流水线第 ③ 阶段)。
4
+
5
+ 配套使用:
6
+
7
+ packages/cli/scripts/slash-toggle-capture.tsx # ① 采集累计 ANSI 字节流 -> steps.json
8
+ scripts/figure/replay-shots.mjs # ② xterm.js 回放 -> shot-*.png
9
+ scripts/figure/compose.py # ③ 合成判读图 -> verdict-*.png ← 本脚本
10
+
11
+ 所有内容都靠命令行参数描述,不往脚本里写死任何路径 —— 这样同一份脚本既能画
12
+ 「四步视口对照」,也能画「修复前/修复后 + 指标表」,不用每次手写一遍 PIL。
13
+
14
+ 版块顺序:标题 → 指标表 → 截图网格 → 前后对照 → 结论条(缺省参数即跳过该版块)。
15
+
16
+ 用法:
17
+ py -3.12 scripts/figure/compose.py \
18
+ --shots packages/cli/.playwright-mcp/slash-toggle/fixb \
19
+ --out packages/cli/.playwright-mcp/slash-toggle/verdict.png \
20
+ --title "命令面板开合「溢出」验证 · 终端 120×30 · xterm.js 回放真实 ANSI 字节流" \
21
+ --subtitle "(欢迎横幅的 8fps ASCII 动画已冻结,保证前后可逐字节比对)" \
22
+ --panel "initial|① 初始|空输入 · 欢迎横幅" \
23
+ --panel "slash-open|② 输入 /|命令面板弹出(文档 17→36 行)" \
24
+ --panel "deleted|③ 退格删除|面板关闭(与 ① 逐字节相同)" \
25
+ --panel "slash-open-2|④ 再输入 /|面板再次弹出(与 ② 逐字节相同)" \
26
+ --conclusion "视口结论:不溢出、不错位。修复前/后 4 张截图逐字节完全相同(MD5 一致);" \
27
+ --conclusion "关闭后横幅完整复原,第 2 次弹出与第 1 次完全一致 —— 可见区没有任何叠加或残留。"
28
+
29
+ 指标表 + 前后对照(--table-row 第 4 段可选 green/red/amber,给最后一格上色):
30
+ --table-row "指标|修复前|修复后|" \
31
+ --table-row "终端原生回滚区行数|70 行|0 行|green" \
32
+ --compare "prefix|fixb|after-cycles-scrolltop|修复前:回滚区 0 行 —— 干净|修复后:回滚区 70 行 —— 残带堆叠"
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import argparse
38
+ import os
39
+ import sys
40
+
41
+ try:
42
+ from PIL import Image, ImageDraw, ImageFont
43
+ except ImportError: # pragma: no cover
44
+ sys.exit(
45
+ "[figure-compose] 缺少 Pillow。本机默认 python 没有,请用 `py -3.12` 运行,"
46
+ "或先 `pip install pillow`。"
47
+ )
48
+
49
+ # ── 调色板(与仓库既有判读图保持一致) ────────────────────────────────────────
50
+ BG = (245, 246, 248)
51
+ INK = (24, 26, 30)
52
+ SUB = (110, 116, 128)
53
+ LINE = (210, 214, 222)
54
+ HEADBG = (232, 234, 238)
55
+ RED = (220, 50, 47)
56
+ GREEN = (30, 150, 80)
57
+ AMBER = (191, 120, 0)
58
+ RED_BG, RED_INK = (253, 236, 235), (150, 26, 24)
59
+ GREEN_BG, GREEN_INK = (232, 246, 238), (18, 100, 55)
60
+ AMBER_BG, AMBER_INK = (253, 244, 226), (120, 76, 0)
61
+
62
+ ACCENTS = {
63
+ "red": (RED, RED_BG, RED_INK),
64
+ "green": (GREEN, GREEN_BG, GREEN_INK),
65
+ "amber": (AMBER, AMBER_BG, AMBER_INK),
66
+ }
67
+
68
+ # 中文字体:Windows 用微软雅黑,Linux/macOS 兜底
69
+ FONT_CANDIDATES = [
70
+ r"C:\Windows\Fonts\msyh.ttc",
71
+ r"C:\Windows\Fonts\simhei.ttf",
72
+ "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
73
+ "/System/Library/Fonts/PingFang.ttc",
74
+ ]
75
+
76
+
77
+ def pick_font(explicit: str | None) -> str | None:
78
+ for path in ([explicit] if explicit else []) + FONT_CANDIDATES:
79
+ if path and os.path.exists(path):
80
+ return path
81
+ print("[figure-compose] 警告:没找到中文字体,中文可能显示为方块(--font 可指定)", file=sys.stderr)
82
+ return None
83
+
84
+
85
+ def font_at(path: str | None, size: int):
86
+ if path is None:
87
+ return ImageFont.load_default()
88
+ return ImageFont.truetype(path, size)
89
+
90
+
91
+ def load_scaled(path: str, width: int) -> Image.Image:
92
+ if not os.path.exists(path):
93
+ sys.exit(f"[figure-compose] 找不到截图:{path}")
94
+ im = Image.open(path).convert("RGB")
95
+ return im.resize((width, round(im.height * width / im.width)), Image.LANCZOS)
96
+
97
+
98
+ def shot_path(directory: str, name: str) -> str:
99
+ """把 step 名解析成 shot-<name>.png(允许 name 里带相对路径)。"""
100
+ return os.path.join(directory, f"shot-{name}.png")
101
+
102
+
103
+ def split_spec(spec: str, parts: int, flag: str, min_parts: int) -> list[str]:
104
+ """按 | 切分参数;不足 min_parts 段直接报错,多于 parts 段的并入最后一段。
105
+
106
+ 分隔符用 | 而不是 :,因为 Windows 路径本身就含冒号。
107
+ """
108
+ got = spec.split("|")
109
+ if len(got) < min_parts:
110
+ sys.exit(
111
+ f"[figure-compose] {flag} 至少需要 {min_parts} 段(用 | 分隔),收到:{spec!r}"
112
+ )
113
+ # 多余的段并入最后一段,避免描述文字里的 | 被误切
114
+ while len(got) > parts:
115
+ got[parts - 1] = got[parts - 1] + "|" + got.pop()
116
+ # 缺的尾段补空(可选参数)
117
+ while len(got) < parts:
118
+ got.append("")
119
+ return got
120
+
121
+
122
+ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
123
+ ap = argparse.ArgumentParser(
124
+ prog="compose.py",
125
+ description="把 xterm.js 回放截图合成带标注的终端判读图",
126
+ formatter_class=argparse.RawDescriptionHelpFormatter,
127
+ )
128
+ ap.add_argument("--out", required=True, help="输出 PNG 路径")
129
+ ap.add_argument("--title", required=True, help="标题(单行)")
130
+ ap.add_argument("--subtitle", default=None, help="标题下的灰色小字")
131
+ ap.add_argument("--shots", default=None, help="截图目录,--panel 从里面取 shot-<name>.png")
132
+
133
+ ap.add_argument(
134
+ "--panel",
135
+ action="append",
136
+ default=[],
137
+ metavar="NAME|LABEL|DESC",
138
+ help="截图网格里的一格,可重复;NAME 对应 shot-<NAME>.png",
139
+ )
140
+ ap.add_argument("--cols", type=int, default=2, help="网格列数(默认 2)")
141
+ ap.add_argument("--cell-width", type=int, default=900, help="每格图片像素宽(默认 900)")
142
+
143
+ ap.add_argument(
144
+ "--table-row",
145
+ action="append",
146
+ default=[],
147
+ metavar="CELL|CELL[|CELL][|green|red|amber]",
148
+ help="指标表的一行,可重复;第 1 行当表头;末段为颜色时给最后一格上色",
149
+ )
150
+ ap.add_argument(
151
+ "--compare",
152
+ action="append",
153
+ default=[],
154
+ metavar="LEFT_DIR|RIGHT_DIR|STEP[|LEFT_CAP|RIGHT_CAP]",
155
+ help="前后对照:两侧目录里同名 shot-<STEP>.png 并排,可重复",
156
+ )
157
+ ap.add_argument(
158
+ "--conclusion",
159
+ action="append",
160
+ default=[],
161
+ help="结论条的一行,可重复;默认绿色,--conclusion-red 改红",
162
+ )
163
+ ap.add_argument("--conclusion-red", action="store_true", help="结论条用红色(判为失败时)")
164
+
165
+ ap.add_argument("--scale", type=float, default=1.0, help="整体字号/间距缩放(默认 1.0)")
166
+ ap.add_argument("--font", default=None, help="中文字体文件路径")
167
+ ap.add_argument("--bg", default=None, help="覆盖底色,形如 245,246,248")
168
+ return ap.parse_args(argv)
169
+
170
+
171
+ def main(argv: list[str] | None = None) -> int:
172
+ args = parse_args(argv)
173
+
174
+ if not args.panel and not args.compare and not args.table_row:
175
+ sys.exit("[figure-compose] 至少要给一个 --panel / --table-row / --compare,否则图是空的")
176
+
177
+ global BG
178
+ if args.bg:
179
+ BG = tuple(int(v) for v in args.bg.split(","))
180
+
181
+ s = args.scale
182
+ pad = round(28 * s)
183
+ label_h = round(44 * s)
184
+ row_gap = round(22 * s)
185
+ block_gap = round(26 * s)
186
+ table_rh = round(44 * s)
187
+ caption_h = round(34 * s)
188
+ conc_line = round(32 * s)
189
+ conc_pad = round(16 * s)
190
+
191
+ fp = pick_font(args.font)
192
+ f_title = font_at(fp, round(36 * s))
193
+ f_step = font_at(fp, round(26 * s))
194
+ f_tag = font_at(fp, round(21 * s))
195
+ f_cell = font_at(fp, round(24 * s))
196
+
197
+ # ── 解析参数 ──────────────────────────────────────────────────────────────
198
+ panels = []
199
+ for spec in args.panel:
200
+ if not args.shots:
201
+ sys.exit("[figure-compose] 用了 --panel 就必须给 --shots 目录")
202
+ name, label, desc = split_spec(spec, 3, "--panel", 2)
203
+ panels.append((label, desc, shot_path(args.shots, name)))
204
+
205
+ table_rows = []
206
+ for spec in args.table_row:
207
+ cells = [c.strip() for c in spec.split("|")]
208
+ accent = None
209
+ if cells and cells[-1].lower() in ACCENTS:
210
+ accent = ACCENTS[cells.pop().lower()]
211
+ table_rows.append((cells, accent))
212
+
213
+ compares = []
214
+ for spec in args.compare:
215
+ left_dir, right_dir, step, left_cap, right_cap = split_spec(spec, 5, "--compare", 3)
216
+ compares.append(
217
+ (
218
+ shot_path(left_dir, step),
219
+ shot_path(right_dir, step),
220
+ left_cap,
221
+ right_cap,
222
+ )
223
+ )
224
+
225
+ # ── 画布宽度:由版块自然宽度决定 ──────────────────────────────────────────
226
+ cell_w = round(args.cell_width * s)
227
+ cols = max(1, args.cols)
228
+ grid_w = pad * 2 + cols * cell_w + (cols - 1) * pad if panels else 0
229
+ cmp_w = pad * 2 + 2 * cell_w + pad if compares else 0
230
+ width = max(grid_w, cmp_w, round(900 * s))
231
+
232
+ # 对照版块在最终宽度下重新均分两栏
233
+ cmp_cell_w = (width - pad * 3) // 2
234
+
235
+ # ── 预加载图片(高度要参与排版,所以先全读进来) ──────────────────────────
236
+ panel_imgs = [load_scaled(p, cell_w) for _, _, p in panels]
237
+ compare_imgs = [
238
+ (load_scaled(l, cmp_cell_w), load_scaled(r, cmp_cell_w), lc, rc)
239
+ for l, r, lc, rc in compares
240
+ ]
241
+
242
+ # ── 版块高度 ──────────────────────────────────────────────────────────────
243
+ title_band = round(72 * s) + (round(34 * s) if args.subtitle else 0)
244
+
245
+ table_h = table_rh * len(table_rows)
246
+
247
+ grid_h = 0
248
+ if panels:
249
+ for r in range(0, len(panels), cols):
250
+ chunk = panel_imgs[r : r + cols]
251
+ grid_h += label_h + max(im.height for im in chunk) + row_gap
252
+
253
+ compare_h = 0
254
+ for left, right, _, _ in compare_imgs:
255
+ compare_h += max(left.height, right.height) + caption_h + row_gap
256
+
257
+ conc_h = 0
258
+ if args.conclusion:
259
+ conc_h = conc_pad * 2 + conc_line * len(args.conclusion)
260
+
261
+ blocks = [h for h in (table_h, grid_h, compare_h) if h > 0]
262
+ height = title_band + sum(blocks) + block_gap * max(0, len(blocks) - 1) + conc_h + pad
263
+
264
+ canvas = Image.new("RGB", (width, height), BG)
265
+ d = ImageDraw.Draw(canvas)
266
+
267
+ # ── 标题 ──────────────────────────────────────────────────────────────────
268
+ d.text((pad, round(20 * s)), args.title, font=f_title, fill=INK)
269
+ if args.subtitle:
270
+ d.text((pad, round(20 * s) + round(48 * s)), args.subtitle, font=f_tag, fill=SUB)
271
+
272
+ y = title_band
273
+ first_block = True
274
+
275
+ def begin_block() -> None:
276
+ nonlocal y, first_block
277
+ if not first_block:
278
+ y += block_gap
279
+ first_block = False
280
+
281
+ # ── 指标表 ────────────────────────────────────────────────────────────────
282
+ if table_rows:
283
+ begin_block()
284
+ ncols = max(len(cells) for cells, _ in table_rows)
285
+ col_step = (width - pad * 2 - 28) // max(1, ncols)
286
+ col_x = [pad + 14 + i * col_step for i in range(ncols)]
287
+
288
+ for r, (cells, accent) in enumerate(table_rows):
289
+ ty = y + r * table_rh
290
+ if r == 0:
291
+ d.rectangle([pad, ty, width - pad, ty + table_rh], fill=HEADBG)
292
+ d.line([pad, ty, width - pad, ty], fill=LINE, width=1)
293
+ for i, cell in enumerate(cells):
294
+ if i >= ncols:
295
+ break
296
+ if r == 0:
297
+ fill = SUB
298
+ elif accent and i == len(cells) - 1:
299
+ fill = accent[2]
300
+ else:
301
+ fill = INK
302
+ d.text((col_x[i], ty + round(10 * s)), cell, font=f_cell, fill=fill)
303
+ d.line(
304
+ [pad, y + table_rh * len(table_rows), width - pad, y + table_rh * len(table_rows)],
305
+ fill=LINE,
306
+ width=1,
307
+ )
308
+ y += table_h
309
+
310
+ # ── 截图网格 ──────────────────────────────────────────────────────────────
311
+ if panels:
312
+ begin_block()
313
+ row_y = y
314
+ for r in range(0, len(panels), cols):
315
+ chunk = panel_imgs[r : r + cols]
316
+ row_h = max(im.height for im in chunk)
317
+ for c, im in enumerate(chunk):
318
+ label, desc, _ = panels[r + c]
319
+ x = pad + c * (cell_w + pad)
320
+ d.text((x, row_y), label, font=f_step, fill=INK)
321
+ d.text((x + round(150 * s), row_y + round(5 * s)), desc, font=f_tag, fill=SUB)
322
+ canvas.paste(im, (x, row_y + label_h))
323
+ d.rectangle(
324
+ [x - 2, row_y + label_h - 2, x + cell_w + 1, row_y + label_h + im.height + 1],
325
+ outline=LINE,
326
+ width=2,
327
+ )
328
+ row_y += label_h + row_h + row_gap
329
+ y = row_y
330
+
331
+ # ── 前后对照 ──────────────────────────────────────────────────────────────
332
+ if compare_imgs:
333
+ begin_block()
334
+ for left, right, left_cap, right_cap in compare_imgs:
335
+ ih = max(left.height, right.height)
336
+ for x, im, cap, accent in (
337
+ (pad, left, left_cap, ACCENTS["red"]),
338
+ (pad * 2 + cmp_cell_w, right, right_cap, ACCENTS["green"]),
339
+ ):
340
+ canvas.paste(im, (x, y))
341
+ d.rectangle([x - 2, y - 2, x + cmp_cell_w + 1, y + im.height + 1], outline=accent[0], width=4)
342
+ if cap:
343
+ d.text((x, y + im.height + round(10 * s)), cap, font=f_tag, fill=accent[2])
344
+ y += ih + caption_h + row_gap
345
+
346
+ # ── 结论条 ────────────────────────────────────────────────────────────────
347
+ if args.conclusion:
348
+ begin_block()
349
+ stroke, fill_bg, fill_ink = ACCENTS["red" if args.conclusion_red else "green"]
350
+ d.rectangle([pad, y, width - pad, y + conc_h], fill=fill_bg, outline=stroke, width=3)
351
+ for i, line in enumerate(args.conclusion):
352
+ d.text((pad + round(20 * s), y + conc_pad + i * conc_line), line, font=f_tag, fill=fill_ink)
353
+ y += conc_h
354
+
355
+ out_dir = os.path.dirname(os.path.abspath(args.out))
356
+ os.makedirs(out_dir, exist_ok=True)
357
+ canvas.save(args.out)
358
+ print(f"[figure-compose] {args.out} {canvas.size[0]}x{canvas.size[1]}")
359
+ return 0
360
+
361
+
362
+ if __name__ == "__main__":
363
+ raise SystemExit(main())
@@ -0,0 +1,205 @@
1
+ /**
2
+ * 把 steps.json 里累计的 ANSI 字节流回放成 PNG 截图(终端判读图流水线第 ② 阶段)。
3
+ *
4
+ * - 用真实的 xterm.js 5.3.0(Chromium 内)回放「累计 ANSI 流」,
5
+ * terminal 固定 120x30,与采集时的 VirtualTerminal 尺寸一致 —— 因此截图
6
+ * 就是「终端里真实长什么样」的忠实还原。
7
+ * - 同时把每步的 scrollback(回滚缓冲)审计出来:横幅是否被重复夯进历史。
8
+ *
9
+ * SHOT_DIR=<采集产物目录> node --experimental-strip-types scripts/figure/replay-shots.mjs
10
+ *
11
+ * 环境变量:SHOT_DIR(必填)· SHOT_COLS · SHOT_ROWS · SHOT_FONT · SHOT_SCROLLTOP
12
+ * 上游采集器:packages/cli/scripts/slash-toggle-capture.tsx(产物含 steps.json)
13
+ */
14
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'
15
+ import { resolve, dirname } from 'node:path'
16
+ import { fileURLToPath } from 'node:url'
17
+ import { createRequire } from 'node:module'
18
+
19
+ const require = createRequire(import.meta.url)
20
+ const HERE = dirname(fileURLToPath(import.meta.url))
21
+
22
+ const SHOT_DIR = process.env.SHOT_DIR
23
+ if (!SHOT_DIR) {
24
+ console.error(
25
+ '[shot] 需要 SHOT_DIR 指向采集产物目录(里面要有 steps.json)。\n' +
26
+ '[shot] 例:SHOT_DIR=.playwright-mcp/slash-toggle node --experimental-strip-types ' +
27
+ 'scripts/figure/replay-shots.mjs',
28
+ )
29
+ process.exit(1)
30
+ }
31
+
32
+ const DIR = resolve(SHOT_DIR)
33
+ const IN = resolve(DIR, 'steps.json')
34
+ const OUT = DIR
35
+
36
+ const COLS = Number(process.env.SHOT_COLS || 120)
37
+ const ROWS = Number(process.env.SHOT_ROWS || 30)
38
+ const FONT_SIZE = Number(process.env.SHOT_FONT || 14)
39
+
40
+ const steps = JSON.parse(readFileSync(IN, 'utf8'))
41
+
42
+ // ── 1. 回滚缓冲审计(纯文本,不依赖浏览器) ────────────────────────────────
43
+ const BANNER = 'DeepSeekCode'
44
+ const countOf = (hay, needle) => {
45
+ let n = 0
46
+ for (let i = hay.indexOf(needle); i !== -1; i = hay.indexOf(needle, i + needle.length)) n++
47
+ return n
48
+ }
49
+
50
+ console.log('================ 回滚缓冲审计 ================')
51
+ const audit = []
52
+ for (const s of steps) {
53
+ const screenRows = s.screen.split('\n').length
54
+ const histRows = s.history.split('\n').length
55
+ const scrollbackRows = histRows - screenRows
56
+ const bannerTotal = countOf(s.history, BANNER)
57
+ const bannerScrollback = countOf(s.history.split('\n').slice(0, scrollbackRows).join('\n'), BANNER)
58
+ audit.push({
59
+ name: s.name,
60
+ bytes: s.bytes,
61
+ bannerWrites: s.bannerWrites,
62
+ screenRows,
63
+ scrollbackRows,
64
+ bannerTotal,
65
+ bannerScrollback,
66
+ })
67
+ }
68
+ console.table(audit)
69
+
70
+ // 打印每步回滚区(viewport 之上的那几行),看是否有半截横幅残留
71
+ for (const s of steps) {
72
+ const screenRows = s.screen.split('\n').length
73
+ const hist = s.history.split('\n')
74
+ const sb = hist.slice(0, hist.length - screenRows)
75
+ if (sb.length) {
76
+ console.log(`\n--- ${s.name}: scrollback ${sb.length} 行 ---`)
77
+ sb.forEach((l, i) => console.log(String(i).padStart(3) + '|' + l))
78
+ }
79
+ }
80
+
81
+ // ── 2. 用 xterm.js 渲染 PNG ────────────────────────────────────────────────
82
+ const xtermJs = readFileSync(require.resolve('xterm/lib/xterm.js'), 'utf8')
83
+ const xtermCss = readFileSync(require.resolve('xterm/css/xterm.css'), 'utf8')
84
+
85
+ const { chromium } = require('playwright')
86
+
87
+ const THEME = {
88
+ background: '#0c0c0c',
89
+ foreground: '#d4d4d4',
90
+ cursor: '#d4d4d4',
91
+ black: '#000000',
92
+ red: '#cd3131',
93
+ green: '#0dbc79',
94
+ yellow: '#e5e510',
95
+ blue: '#2472c8',
96
+ magenta: '#bc3fbc',
97
+ cyan: '#11a8cd',
98
+ white: '#e5e5e5',
99
+ brightBlack: '#666666',
100
+ brightRed: '#f14c4c',
101
+ brightGreen: '#23d18b',
102
+ brightYellow: '#f5f543',
103
+ brightBlue: '#3b8eea',
104
+ brightMagenta: '#d670d6',
105
+ brightCyan: '#29b8db',
106
+ brightWhite: '#e5e5e5',
107
+ }
108
+
109
+ function pageHtml() {
110
+ return `<!doctype html><html><head><meta charset="utf-8">
111
+ <style>${xtermCss}</style>
112
+ <style>
113
+ html,body{margin:0;padding:0;background:#0c0c0c;}
114
+ #wrap{display:inline-block;padding:0;}
115
+ .xterm-viewport{overflow:hidden !important;}
116
+ </style></head>
117
+ <body><div id="wrap"></div></body></html>`
118
+ }
119
+
120
+ // 本仓库的 playwright 走 --remote-debugging-pipe 会抛 `Invalid URL: undefined`,
121
+ // 按 scripts/repro-browser.mjs 的做法改用 port 传输。
122
+ const browser = await chromium.launch({
123
+ headless: true,
124
+ args: ['--no-sandbox', '--remote-debugging-port=0'],
125
+ ignoreDefaultArgs: ['--remote-debugging-pipe'],
126
+ })
127
+ mkdirSync(OUT, { recursive: true })
128
+ const written = []
129
+
130
+ try {
131
+ for (const step of steps) {
132
+ const page = await browser.newPage({
133
+ viewport: { width: 1600, height: 900 },
134
+ deviceScaleFactor: 2,
135
+ })
136
+ await page.setContent(pageHtml(), { waitUntil: 'load' })
137
+ await page.addScriptTag({ content: xtermJs })
138
+
139
+ const info = await page.evaluate(
140
+ async ({ cols, rows, ansi, fontSize, theme, scrollTop }) => {
141
+ // eslint-disable-next-line no-undef
142
+ const term = new window.Terminal({
143
+ cols,
144
+ rows,
145
+ fontSize,
146
+ fontFamily: 'Consolas, "Cascadia Mono", "Microsoft YaHei", monospace',
147
+ theme,
148
+ allowProposedApi: true,
149
+ convertEol: false,
150
+ // 保留回滚区,好把「被推入原生 scrollback 的残带」也拍出来
151
+ scrollback: 5000,
152
+ })
153
+ const wrap = document.getElementById('wrap')
154
+ term.open(wrap)
155
+ await new Promise((res) => term.write(ansi, res))
156
+ if (scrollTop) {
157
+ term.scrollToTop()
158
+ await new Promise((r) => setTimeout(r, 80))
159
+ }
160
+ await new Promise((r) => setTimeout(r, 120))
161
+ const buf = term.buffer.active
162
+ const el = document.querySelector('#wrap .xterm')
163
+ return {
164
+ width: el.getBoundingClientRect().width,
165
+ height: el.getBoundingClientRect().height,
166
+ scrollbackRows: buf.baseY,
167
+ }
168
+ },
169
+ {
170
+ cols: COLS,
171
+ rows: ROWS,
172
+ ansi: step.ansi,
173
+ fontSize: FONT_SIZE,
174
+ theme: THEME,
175
+ scrollTop: Boolean(process.env.SHOT_SCROLLTOP),
176
+ },
177
+ )
178
+
179
+ // 视口裁到真实像素尺寸
180
+ await page.setViewportSize({
181
+ width: Math.ceil(info.width) + 2,
182
+ height: Math.ceil(info.height) + 2,
183
+ })
184
+ await page.evaluate(() => {
185
+ document.body.style.background = '#0c0c0c'
186
+ })
187
+ await new Promise((r) => setTimeout(r, 120))
188
+
189
+ const el = await page.$('#wrap .xterm')
190
+ const suffix = process.env.SHOT_SCROLLTOP ? '-scrolltop' : ''
191
+ const file = resolve(OUT, `shot-${step.name}${suffix}.png`)
192
+ await el.screenshot({ path: file })
193
+ written.push(file)
194
+ console.log(
195
+ `[shot] ${step.name}${suffix}: ${Math.round(info.width)}x${Math.round(info.height)}css ` +
196
+ `scrollbackRows=${info.scrollbackRows} -> ${file}`,
197
+ )
198
+ await page.close()
199
+ }
200
+ } finally {
201
+ await browser.close()
202
+ }
203
+
204
+ writeFileSync(resolve(OUT, 'audit.json'), JSON.stringify(audit, null, 2), 'utf8')
205
+ console.log('\n[shot] wrote:\n' + written.map((f) => ' ' + f).join('\n'))
@@ -0,0 +1 @@
1
+ import{j as e}from"./markdown-DEtuzEyz.js";import{a as r,au as j,h as N,L as v,be as w,b7 as S,b3 as k,aU as C,a9 as I,l as z}from"./icons-f1hlMKvD.js";import{u as g,a as y,a9 as A,aa as E}from"./index-u1vYyqna.js";import"./react-vendor-ClBKp0t-.js";import"./state-xD58vmym.js";import"./panels-Bn-2wCur.js";const R={idle:"bg-green-500",running:"bg-yellow-500 animate-pulse",paused:"bg-gray-500",error:"bg-red-500"};function q(d){return R[d]??"bg-gray-500"}const O=r.memo(function({onOpenSession:s,onNavigate:m}){const{t:a}=g(),[l,u]=r.useState([]),[n,c]=r.useState(!0),[p,h]=r.useState(null),[x,f]=r.useState(null),o=r.useCallback(async()=>{try{const t=await y.assistants.list();t.success&&Array.isArray(t.data)?(u(t.data),h(null)):h(t.error||a("assistants.loadFailed"))}catch(t){h(t instanceof Error?t.message:String(t))}finally{c(!1)}},[a]);r.useEffect(()=>{o();const t=window.setInterval(()=>{o()},8e3);return()=>window.clearInterval(t)},[o]);const b=r.useCallback(async(t,i)=>{(await y.assistants.update(t,i)).success&&o()},[o]);return e.jsxs("div",{className:"flex h-full flex-col overflow-hidden",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-dark-border px-4 py-2.5",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(j,{size:16,className:"text-accent-blue","aria-hidden":"true"}),e.jsx("h2",{className:"text-sm font-semibold text-dark-text",children:a("assistants.title")}),!n&&e.jsx("span",{className:"rounded-full bg-dark-hover px-1.5 py-0.5 text-[10px] text-dark-muted",children:l.length})]}),e.jsxs("button",{type:"button",onClick:()=>void o(),className:"flex items-center gap-1 rounded px-2 py-1 text-xs text-dark-muted hover:bg-dark-hover hover:text-dark-text",title:a("common.refresh"),children:[e.jsx(N,{size:12,className:n?"animate-spin":"","aria-hidden":"true"}),a("common.refresh")]})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-3",children:n&&l.length===0?e.jsxs("div",{className:"flex items-center justify-center py-12 text-xs text-dark-muted",children:[e.jsx(v,{size:14,className:"mr-1.5 animate-spin","aria-hidden":"true"}),a("assistants.loading")]}):p&&l.length===0?e.jsx("div",{className:"py-8 text-center text-xs text-red-400",children:p}):l.length===0?e.jsxs("div",{className:"py-10 text-center text-xs text-dark-muted",children:[e.jsx(j,{size:24,className:"mx-auto mb-2 text-dark-muted","aria-hidden":"true"}),e.jsx("p",{children:a("assistants.empty")}),e.jsx("p",{className:"mt-1 text-[11px]",children:a("assistants.emptyHint")}),e.jsx("button",{type:"button",onClick:()=>m("channels"),className:"mt-3 rounded border border-dark-border px-2.5 py-1 text-[11px] text-dark-text hover:bg-dark-hover",children:a("assistants.goChannels")})]}):e.jsx("div",{className:"flex flex-col gap-2",children:l.map(t=>e.jsx(T,{assistant:t,expanded:x===t.sessionId,onToggleExpand:()=>f(x===t.sessionId?null:t.sessionId),onOpen:()=>s(t.sessionId,t.title),onUpdate:i=>void b(t.sessionId,i)},t.sessionId))})})]})}),T=r.memo(function({assistant:s,expanded:m,onToggleExpand:a,onOpen:l,onUpdate:u}){const{t:n}=g(),c=s.channel.peerKind==="group";return e.jsxs("div",{className:"rounded-lg border border-dark-border bg-dark-surface",children:[e.jsxs("div",{className:"flex items-center gap-2 px-3 py-2",children:[e.jsx("span",{className:"text-base leading-none","aria-hidden":"true",children:A(s.channel.type)}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:"min-w-0 flex-1 truncate text-xs font-medium text-dark-text",title:s.title,children:s.title}),e.jsx("span",{className:`h-1.5 w-1.5 shrink-0 rounded-full ${q(s.status)}`,title:n(`assistants.status.${s.status}`,{defaultValue:s.status})}),c?e.jsx(w,{size:11,className:"shrink-0 text-dark-muted","aria-label":n("assistants.group")}):e.jsx(S,{size:11,className:"shrink-0 text-dark-muted","aria-label":n("assistants.dm")})]}),e.jsxs("div",{className:"mt-0.5 flex items-center gap-1.5 text-[10px] text-dark-muted",children:[e.jsx("span",{className:"shrink-0",children:E(s.channel.type)}),e.jsx("span",{className:"min-w-0 flex-1 truncate",title:s.channel.peerId,children:s.channel.peerId}),s.stats.unread>0&&e.jsx("span",{className:"shrink-0 rounded-full bg-red-500/80 px-1.5 text-[9px] font-medium text-white",children:s.stats.unread})]}),s.preview&&e.jsx("p",{className:"mt-1 line-clamp-2 text-[11px] leading-snug text-dark-muted",title:s.preview,children:s.preview})]}),e.jsx(U,{mode:s.mode}),s.trust.level==="owner"?e.jsx(k,{size:12,className:"shrink-0 text-green-500","aria-label":n("assistants.trust.owner")}):s.trust.level==="public"?e.jsx(C,{size:12,className:"shrink-0 text-yellow-600","aria-label":n("assistants.trust.public")}):e.jsx(k,{size:12,className:"shrink-0 text-accent-blue","aria-label":n("assistants.trust.team")})]}),e.jsxs("div",{className:"flex items-center gap-1.5 border-t border-dark-border px-3 py-1.5",children:[e.jsxs("button",{type:"button",onClick:l,className:"flex items-center gap-1 rounded px-2 py-0.5 text-[11px] text-accent-blue hover:bg-dark-hover",children:[e.jsx(I,{size:11,"aria-hidden":"true"}),n("assistants.openSession")]}),e.jsx("button",{type:"button",onClick:a,className:"flex items-center gap-1 rounded px-2 py-0.5 text-[11px] text-dark-muted hover:bg-dark-hover hover:text-dark-text",children:n("assistants.policy")}),e.jsx("span",{className:"flex-1"}),e.jsx("span",{className:"text-[10px] text-dark-muted",children:n("assistants.groupPolicy",{mention:String(c?s.policy.mentionRequired:!1)})})]}),m&&e.jsx(M,{assistant:s,isGroup:c,onUpdate:u})]})});function U({mode:d}){const{t:s}=g();return e.jsx("span",{className:`shrink-0 rounded px-1.5 py-0.5 text-[9px] font-medium ${d==="delegate"?"bg-accent-blue/15 text-accent-blue":"bg-dark-hover text-dark-muted"}`,children:s(`assistants.mode.${d}`)})}function M({assistant:d,isGroup:s,onUpdate:m}){const{t:a}=g(),[l,u]=r.useState(d.mode),[n,c]=r.useState(d.policy.mentionRequired),[p,h]=r.useState(String(d.policy.cooldownSeconds)),[x,f]=r.useState((d.policy.keywords??[]).join(", ")),[o,b]=r.useState(!1),t=()=>{b(!0),m({mode:l,policy:{mentionRequired:s?n:!1,cooldownSeconds:Math.max(0,Number(p)||0),...x.trim()?{keywords:x.split(/[,,]/).map(i=>i.trim()).filter(Boolean)}:{}}}),window.setTimeout(()=>b(!1),600)};return e.jsxs("div",{className:"border-t border-dark-border px-3 py-2.5 text-[11px]",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-2",children:[e.jsxs("label",{className:"flex items-center gap-1.5 text-dark-muted",children:[a("assistants.mode.label"),e.jsxs("select",{value:l,onChange:i=>u(i.target.value),className:"rounded border border-dark-border bg-dark-surface px-1.5 py-0.5 text-dark-text",children:[e.jsx("option",{value:"delegate",children:a("assistants.mode.delegate")}),e.jsx("option",{value:"direct",children:a("assistants.mode.direct")})]})]}),s&&e.jsxs("label",{className:"flex items-center gap-1.5 text-dark-muted",children:[e.jsx("input",{type:"checkbox",checked:n,onChange:i=>c(i.target.checked)}),a("assistants.mentionRequired")]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-dark-muted",children:[a("assistants.cooldown"),e.jsx("input",{type:"number",min:0,value:p,onChange:i=>h(i.target.value),className:"w-14 rounded border border-dark-border bg-dark-surface px-1.5 py-0.5 text-dark-text"}),"s"]})]}),e.jsxs("label",{className:"mt-2 flex items-center gap-1.5 text-dark-muted",children:[a("assistants.keywords"),e.jsx("input",{type:"text",value:x,placeholder:a("assistants.keywordsPlaceholder")??"",onChange:i=>f(i.target.value),className:"min-w-0 flex-1 rounded border border-dark-border bg-dark-surface px-1.5 py-0.5 text-dark-text"})]}),e.jsx("div",{className:"mt-2 flex justify-end",children:e.jsxs("button",{type:"button",onClick:t,disabled:o,className:"flex items-center gap-1 rounded bg-accent-blue px-2.5 py-1 text-[11px] font-medium text-white disabled:opacity-60",children:[o?e.jsx(v,{size:11,className:"animate-spin","aria-hidden":"true"}):e.jsx(z,{size:11,"aria-hidden":"true"}),a("common.save")]})})]})}export{O as AssistantsView};
@@ -0,0 +1 @@
1
+ import{j as e}from"./markdown-DEtuzEyz.js";import{a as n,ay as v,h as $,aa as D,A as I,bp as L,f as O,aE as J,b2 as U,X as B}from"./icons-f1hlMKvD.js";import{u as M,ab as V}from"./index-u1vYyqna.js";import"./react-vendor-ClBKp0t-.js";import"./state-xD58vmym.js";import"./panels-Bn-2wCur.js";function Q(){const{t:a}=M(),{automations:r,runsByAutomation:d,fetchAutomations:o,createAutomation:N,deleteAutomation:y,toggleAutomation:w,runAutomation:C,fetchRuns:A,error:l}=V(),[S,x]=n.useState(!1),[m,P]=n.useState(null),[u,p]=n.useState(""),[h,b]=n.useState(""),[i,g]=n.useState("0 9 * * *"),[f,j]=n.useState("");n.useEffect(()=>{o()},[o]);const z=async t=>{const s=f.trim();if(!s)return;const[c,...T]=s.split(/\s+/),k=T.join(" ");c&&k&&(await fetch(`/api/automations/${encodeURIComponent(t)}/permissions`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({tool:c,target:k})})).ok&&(j(""),await o())},E=async(t,s)=>{(await fetch(`/api/automations/${encodeURIComponent(t)}/permissions`,{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({entry:s})})).ok&&await o()},R=async t=>{const s=m===t?null:t;P(s),s&&await A(t)};return e.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden p-4",children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsxs("h2",{className:"text-lg font-semibold flex items-center gap-2",children:[e.jsx(v,{size:20,className:"text-accent-blue"}),a("automation.title"),e.jsxs("span",{className:"text-xs text-dark-muted font-normal",children:["(",r.length,")"]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:()=>void o(),className:"p-2 rounded-lg hover:bg-dark-hover text-dark-muted",title:a("automation.refresh"),children:e.jsx($,{size:16})}),e.jsxs("button",{onClick:()=>x(t=>!t),className:"flex items-center gap-1 px-3 py-1.5 rounded-lg bg-accent-blue/20 text-accent-blue hover:bg-accent-blue/30 text-sm font-medium",children:[e.jsx(D,{size:16})," ",a("automation.new")]})]})]}),l&&e.jsxs("div",{className:"mb-3 p-3 rounded-lg bg-accent-red/10 text-accent-red text-sm flex items-center gap-2",children:[e.jsx(I,{size:16})," ",l]}),S&&e.jsxs("div",{className:"mb-4 rounded-lg border border-dark-border bg-dark-surface p-4 space-y-3",children:[e.jsx("input",{value:u,onChange:t=>p(t.target.value),placeholder:a("automation.namePlaceholder"),className:"w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-sm focus:outline-none focus:border-accent-blue"}),e.jsx("textarea",{value:h,onChange:t=>b(t.target.value),placeholder:a("automation.instructionsPlaceholder"),rows:3,className:"w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-sm focus:outline-none focus:border-accent-blue resize-none"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("input",{value:i,onChange:t=>g(t.target.value),placeholder:a("automation.cronPlaceholder"),className:"flex-1 px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-sm focus:outline-none focus:border-accent-blue"}),e.jsx("button",{onClick:async()=>{await N({name:u,instructions:h,schedule:{kind:"cron",cron:i}})&&(p(""),b(""),x(!1))},className:"px-4 py-2 rounded-lg bg-accent-blue text-white text-sm font-medium hover:bg-accent-blue/80",children:a("automation.create")})]}),e.jsx("div",{className:"flex gap-2 flex-wrap",children:[["0 9 * * *",a("automation.cronDaily9")],["0 9 * * 1-5",a("automation.cronWeekday9")],["*/30 * * * *",a("automation.cronEvery30Min")]].map(([t,s])=>e.jsxs("button",{onClick:()=>g(t),className:`px-2 py-1 rounded text-xs ${i===t?"bg-accent-blue/20 text-accent-blue":"bg-dark-hover text-dark-muted hover:text-dark-text"}`,children:[s," (",t,")"]},t))})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto space-y-2",children:[r.length===0&&e.jsx("p",{className:"text-dark-muted text-center py-8",children:a("automation.empty")}),r.map(t=>e.jsxs("div",{className:"rounded-lg border border-dark-border bg-dark-surface",children:[e.jsxs("div",{className:"flex items-center justify-between p-3",children:[e.jsxs("button",{onClick:()=>void R(t.id),className:"flex items-center gap-2 min-w-0 text-left",children:[e.jsx(v,{size:16,className:"text-accent-blue shrink-0"}),e.jsx("span",{className:"font-medium text-sm truncate",children:t.name}),e.jsx("span",{className:`text-xs px-1.5 py-0.5 rounded shrink-0 ${t.status==="enabled"?"bg-accent-green/20 text-accent-green":"bg-dark-hover text-dark-muted"}`,children:t.status})]}),e.jsxs("div",{className:"flex items-center gap-1 shrink-0",children:[e.jsxs("span",{className:"text-xs text-dark-muted hidden lg:inline mr-1",children:[t.schedule.kind==="cron"?t.schedule.cron:t.schedule.fireAt,t.nextRunAt?` · ${new Date(t.nextRunAt).toLocaleString()}`:""]}),e.jsx("button",{onClick:()=>void w(t.id,t.status!=="enabled"),className:"p-1.5 rounded-lg hover:bg-dark-hover text-dark-muted",title:t.status==="enabled"?a("automation.disable"):a("automation.enable"),children:e.jsx(L,{size:15})}),e.jsx("button",{onClick:()=>void C(t.id),className:"p-1.5 rounded-lg hover:bg-dark-hover text-accent-green",title:a("automation.runNow"),children:e.jsx(O,{size:15})}),e.jsx("button",{onClick:()=>void y(t.id),className:"p-1.5 rounded-lg hover:bg-dark-hover text-accent-red",title:a("automation.delete"),children:e.jsx(J,{size:15})})]})]}),m===t.id&&e.jsxs("div",{className:"px-3 pb-3 space-y-3 border-t border-dark-border pt-3",children:[e.jsx("p",{className:"text-xs text-dark-muted whitespace-pre-wrap",children:t.instructions}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-xs font-semibold text-dark-muted mb-1 flex items-center gap-1",children:[e.jsx(U,{size:12})," ",a("automation.historyTitle")]}),(d[t.id]||[]).length===0&&e.jsx("p",{className:"text-xs text-dark-muted",children:a("automation.noRuns")}),(d[t.id]||[]).slice(0,10).map(s=>e.jsxs("div",{className:"flex items-center gap-2 text-xs py-1",children:[e.jsx("span",{className:`px-1.5 py-0.5 rounded ${s.status==="ok"?"bg-accent-green/20 text-accent-green":s.status==="error"?"bg-accent-red/20 text-accent-red":"bg-accent-yellow/20 text-accent-yellow"}`,children:s.status}),e.jsx("span",{className:"text-dark-muted",children:new Date(s.startedAt).toLocaleString()}),e.jsxs("span",{className:"text-dark-muted truncate flex-1",children:["#",s.id.slice(0,8)," · ",s.trigger]})]},s.id))]}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-semibold text-dark-muted mb-1",children:a("automation.permissionsTitle")}),t.permissions.length===0&&e.jsx("p",{className:"text-xs text-dark-muted",children:a("automation.permissionsEmpty")}),e.jsx("div",{className:"flex flex-wrap gap-1 mb-1",children:t.permissions.map(s=>e.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-0.5 rounded bg-accent-blue/10 text-accent-blue text-xs",children:[s,e.jsx("button",{onClick:()=>void E(t.id,s),className:"hover:text-accent-red",children:e.jsx(B,{size:12})})]},s))}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("input",{value:f,onChange:s=>j(s.target.value),placeholder:a("automation.permissionPlaceholder"),className:"flex-1 px-2 py-1.5 rounded-lg bg-dark-bg border border-dark-border text-xs focus:outline-none focus:border-accent-blue"}),e.jsx("button",{onClick:()=>void z(t.id),className:"px-3 py-1.5 rounded-lg bg-dark-hover text-dark-muted hover:text-dark-text text-xs",children:a("automation.add")})]})]})]})]},t.id))]})]})}export{Q as AutomationView};