@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.
- package/README.md +221 -0
- package/bin/dscode.exe +12 -0
- package/cli-wrapper.cjs +105 -0
- package/install.cjs +177 -0
- package/package.json +39 -0
- package/scripts/figure/compose.py +363 -0
- package/scripts/figure/replay-shots.mjs +205 -0
- package/web/dist/assets/AssistantsView-OMdgsTIj.js +1 -0
- package/web/dist/assets/AutomationView-BPwjLbkL.js +1 -0
- package/web/dist/assets/ChannelConfigView-JDljUn42.js +8 -0
- package/web/dist/assets/ConnectorsView-AemNT1Ae.js +1 -0
- package/web/dist/assets/FileTreeView-DYx-0jFV.js +11 -0
- package/web/dist/assets/HookConfigView-CS1QqgPx.js +1 -0
- package/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/web/dist/assets/ModelsView-B3nHezvJ.js +1 -0
- package/web/dist/assets/PluginsView-C4Xc70ec.js +1 -0
- package/web/dist/assets/SessionGrid-BGpz0-op.css +1 -0
- package/web/dist/assets/SessionGrid-C9-LeJH0.js +47 -0
- package/web/dist/assets/SettingsModal-CjWT1C66.js +4 -0
- package/web/dist/assets/SkillsView-DTZyXald.js +1 -0
- package/web/dist/assets/TemplatesTab-CE-CfCf2.js +1 -0
- package/web/dist/assets/ThreeScene-CMqkpyKO.js +2863 -0
- package/web/dist/assets/ThreeScene-DvOrMIoG.css +1 -0
- package/web/dist/assets/ToolsView-CrEHDTEO.js +1 -0
- package/web/dist/assets/abap-BdImnpbu.js +1 -0
- package/web/dist/assets/actionscript-3-B3316cI-.js +1 -0
- package/web/dist/assets/ada-bCR0ucgS.js +1 -0
- package/web/dist/assets/ahk-CsyLZFj1.js +1 -0
- package/web/dist/assets/ahk2-8Zs4aa1G.js +1 -0
- package/web/dist/assets/andromeeda-C4gqWexZ.js +1 -0
- package/web/dist/assets/angular-html-DA-rfuFy.js +1 -0
- package/web/dist/assets/angular-ts-BrjP3tb8.js +1 -0
- package/web/dist/assets/apache-Pmp26Uib.js +1 -0
- package/web/dist/assets/apex-DhZFqWV2.js +1 -0
- package/web/dist/assets/apl-CORt7UWP.js +1 -0
- package/web/dist/assets/applescript-Co6uUVPk.js +1 -0
- package/web/dist/assets/ara-BRHolxvo.js +1 -0
- package/web/dist/assets/asciidoc-CSVQ5wI8.js +1 -0
- package/web/dist/assets/asm-D_Q5rh1f.js +1 -0
- package/web/dist/assets/astro-HNnZUWAn.js +1 -0
- package/web/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/web/dist/assets/awk-DMzUqQB5.js +1 -0
- package/web/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
- package/web/dist/assets/ayu-light-BA47KaF1.js +1 -0
- package/web/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
- package/web/dist/assets/ballerina-BFfxhgS-.js +1 -0
- package/web/dist/assets/bat-CickPsom.js +1 -0
- package/web/dist/assets/beancount-k_qm7-4y.js +1 -0
- package/web/dist/assets/berry-uYugtg8r.js +1 -0
- package/web/dist/assets/bibtex-CHM0blh-.js +1 -0
- package/web/dist/assets/bicep-Bmn6On1c.js +1 -0
- package/web/dist/assets/bird2-Bx8U0n9b.js +1 -0
- package/web/dist/assets/blade-2xfisSek.js +1 -0
- package/web/dist/assets/bsl-DlhNcFeZ.js +1 -0
- package/web/dist/assets/c-BIGW1oBm.js +1 -0
- package/web/dist/assets/c3-Dp5svz6Z.js +1 -0
- package/web/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- package/web/dist/assets/cairo-KRGpt6FW.js +1 -0
- package/web/dist/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
- package/web/dist/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
- package/web/dist/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
- package/web/dist/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
- package/web/dist/assets/chapel-DTp_pixX.js +1 -0
- package/web/dist/assets/clarity-Dn5IMItf.js +1 -0
- package/web/dist/assets/clojure-P80f7IUj.js +1 -0
- package/web/dist/assets/cmake-D1j8_8rp.js +1 -0
- package/web/dist/assets/cobol-nBiQ_Alo.js +1 -0
- package/web/dist/assets/codeowners-Bp6g37R7.js +1 -0
- package/web/dist/assets/codeql-DsOJ9woJ.js +1 -0
- package/web/dist/assets/coffee-Ch7k5sss.js +1 -0
- package/web/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/web/dist/assets/coq-C7JzOVbR.js +1 -0
- package/web/dist/assets/cpp-BMRokrvK.js +1 -0
- package/web/dist/assets/crystal-DGywbUpC.js +1 -0
- package/web/dist/assets/csharp-DSvCPggb.js +1 -0
- package/web/dist/assets/css-CLj8gQPS.js +1 -0
- package/web/dist/assets/csv-fuZLfV_i.js +1 -0
- package/web/dist/assets/cue-D82EKSYY.js +1 -0
- package/web/dist/assets/cypher-COkxafJQ.js +1 -0
- package/web/dist/assets/d-85-TOEBH.js +1 -0
- package/web/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/web/dist/assets/dart-bE4Kk8sk.js +1 -0
- package/web/dist/assets/dax-CEL-wOlO.js +1 -0
- package/web/dist/assets/desktop-BmXAJ9_W.js +1 -0
- package/web/dist/assets/diff-D97Zzqfu.js +1 -0
- package/web/dist/assets/docker-BcOcwvcX.js +1 -0
- package/web/dist/assets/dotenv-Da5cRb03.js +1 -0
- package/web/dist/assets/dracula-BzJJZx-M.js +1 -0
- package/web/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/web/dist/assets/dream-maker-BtqSS_iP.js +1 -0
- package/web/dist/assets/edge-FbVlp4U3.js +1 -0
- package/web/dist/assets/elixir-CkH2-t6x.js +1 -0
- package/web/dist/assets/elm-DbKCFpqz.js +1 -0
- package/web/dist/assets/emacs-lisp-C_m_b--Z.js +1 -0
- package/web/dist/assets/erb-DXfck5VN.js +1 -0
- package/web/dist/assets/erlang-DsQrWhSR.js +1 -0
- package/web/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/web/dist/assets/everforest-light-C8M2exoo.js +1 -0
- package/web/dist/assets/fennel-BYunw83y.js +1 -0
- package/web/dist/assets/fish-BvzEVeQv.js +1 -0
- package/web/dist/assets/fluent-C4IJs8-o.js +1 -0
- package/web/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- package/web/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
- package/web/dist/assets/fsharp-CXgrBDvD.js +1 -0
- package/web/dist/assets/gdresource-TyuKm33G.js +1 -0
- package/web/dist/assets/gdscript-DqcFQ5yU.js +1 -0
- package/web/dist/assets/gdshader-DkwncUOv.js +1 -0
- package/web/dist/assets/genie-D0YGMca9.js +1 -0
- package/web/dist/assets/gherkin-DyxjwDmM.js +1 -0
- package/web/dist/assets/git-commit-F4YmCXRG.js +1 -0
- package/web/dist/assets/git-rebase-r7XF79zn.js +1 -0
- package/web/dist/assets/github-dark-DHJKELXO.js +1 -0
- package/web/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/web/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/web/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/web/dist/assets/github-light-DAi9KRSo.js +1 -0
- package/web/dist/assets/github-light-default-D7oLnXFd.js +1 -0
- package/web/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/web/dist/assets/gleam-BspZqrRM.js +1 -0
- package/web/dist/assets/glimmer-js-ByusRIyA.js +1 -0
- package/web/dist/assets/glimmer-ts-BfAWNZQY.js +1 -0
- package/web/dist/assets/glsl-DplSGwfg.js +1 -0
- package/web/dist/assets/gn-n2N0HUVH.js +1 -0
- package/web/dist/assets/gnuplot-DdkO51Og.js +1 -0
- package/web/dist/assets/go-C27-OAKa.js +1 -0
- package/web/dist/assets/graphql-ChdNCCLP.js +1 -0
- package/web/dist/assets/groovy-gcz8RCvz.js +1 -0
- package/web/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/web/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/web/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/web/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/web/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/web/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/web/dist/assets/hack-BWmVpMyf.js +1 -0
- package/web/dist/assets/haml-D5jkg6IW.js +1 -0
- package/web/dist/assets/handlebars-BpdQsYii.js +1 -0
- package/web/dist/assets/haskell-Df6bDoY_.js +1 -0
- package/web/dist/assets/haxe-CfZj7gIn.js +1 -0
- package/web/dist/assets/hcl-BWvSN4gD.js +1 -0
- package/web/dist/assets/hjson-D5-asLiD.js +1 -0
- package/web/dist/assets/hlsl-D3lLCCz7.js +1 -0
- package/web/dist/assets/horizon-BUw7H-hv.js +1 -0
- package/web/dist/assets/horizon-bright-CUuTKBJd.js +1 -0
- package/web/dist/assets/houston-DnULxvSX.js +1 -0
- package/web/dist/assets/html-derivative-DlHx6ybY.js +1 -0
- package/web/dist/assets/html-pp8916En.js +1 -0
- package/web/dist/assets/http-jrhK8wxY.js +1 -0
- package/web/dist/assets/hurl-irOxFIW8.js +1 -0
- package/web/dist/assets/hxml-2-FPmUDs.js +1 -0
- package/web/dist/assets/hy-DFXneXwc.js +1 -0
- package/web/dist/assets/icons-f1hlMKvD.js +724 -0
- package/web/dist/assets/imba-DGztddWO.js +1 -0
- package/web/dist/assets/index-BCHbHIbw.css +1 -0
- package/web/dist/assets/index-u1vYyqna.js +592 -0
- package/web/dist/assets/ini-BEwlwnbL.js +1 -0
- package/web/dist/assets/java-CylS5w8V.js +1 -0
- package/web/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/web/dist/assets/jinja-f2NsQr07.js +1 -0
- package/web/dist/assets/jison-wvAkD_A8.js +1 -0
- package/web/dist/assets/json-Cp-IABpG.js +1 -0
- package/web/dist/assets/json5-C9tS-k6U.js +1 -0
- package/web/dist/assets/jsonc-Des-eS-w.js +1 -0
- package/web/dist/assets/jsonl-DcaNXYhu.js +1 -0
- package/web/dist/assets/jsonnet-DFQXde-d.js +1 -0
- package/web/dist/assets/jssm-C2t-YnRu.js +1 -0
- package/web/dist/assets/jsx-g9-lgVsj.js +1 -0
- package/web/dist/assets/julia-5Bft2YPA.js +1 -0
- package/web/dist/assets/just-Cwhn7H3k.js +1 -0
- package/web/dist/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
- package/web/dist/assets/kanagawa-lotus-C3DzagqV.js +1 -0
- package/web/dist/assets/kanagawa-wave-DFGoQZhC.js +1 -0
- package/web/dist/assets/kdl-DV7GczEv.js +1 -0
- package/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/web/dist/assets/kusto-wEQ09or8.js +1 -0
- package/web/dist/assets/laserwave-DUszq2jm.js +1 -0
- package/web/dist/assets/latex-D5pSuvFb.js +1 -0
- package/web/dist/assets/lean-BZvkOJ9d.js +1 -0
- package/web/dist/assets/less-B1dDrJ26.js +1 -0
- package/web/dist/assets/light-plus-B7mTdjB0.js +1 -0
- package/web/dist/assets/liquid-C0sCDyMI.js +1 -0
- package/web/dist/assets/llvm-BZoOZj88.js +1 -0
- package/web/dist/assets/log-2UxHyX5q.js +1 -0
- package/web/dist/assets/logo-BtOb2qkB.js +1 -0
- package/web/dist/assets/lua-BaeVxFsk.js +1 -0
- package/web/dist/assets/luau-BnpPk5vE.js +1 -0
- package/web/dist/assets/make-CHLpvVh8.js +1 -0
- package/web/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/web/dist/assets/markdown-DEtuzEyz.js +30 -0
- package/web/dist/assets/marko-DjSrsDqO.js +1 -0
- package/web/dist/assets/material-theme-D5KoaKCx.js +1 -0
- package/web/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/web/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/web/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/web/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/web/dist/assets/matlab-D7o27uSR.js +1 -0
- package/web/dist/assets/mdc-D1_yUvq7.js +1 -0
- package/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/web/dist/assets/mermaid-CQcHuHx7.js +1 -0
- package/web/dist/assets/min-dark-CafNBF8u.js +1 -0
- package/web/dist/assets/min-light-CTRr51gU.js +1 -0
- package/web/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- package/web/dist/assets/mojo-DJz3ZmWd.js +1 -0
- package/web/dist/assets/monokai-BRVnQi9A.js +1 -0
- package/web/dist/assets/moonbit-CHtswR0a.js +1 -0
- package/web/dist/assets/move-el3G9tDJ.js +1 -0
- package/web/dist/assets/narrat-DRg8JJMk.js +1 -0
- package/web/dist/assets/nextflow-C-mBbutL.js +1 -0
- package/web/dist/assets/nextflow-groovy-vE_lwT2v.js +1 -0
- package/web/dist/assets/nginx-BpAMiNFr.js +1 -0
- package/web/dist/assets/night-owl-C39BiMTA.js +1 -0
- package/web/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
- package/web/dist/assets/nim-BIad80T-.js +1 -0
- package/web/dist/assets/nix-CwoSXNpI.js +1 -0
- package/web/dist/assets/nord-Ddv68eIx.js +1 -0
- package/web/dist/assets/nsis-BlV79W_Q.js +1 -0
- package/web/dist/assets/nushell-D3jzshHO.js +1 -0
- package/web/dist/assets/objective-c-DXmwc3jG.js +1 -0
- package/web/dist/assets/objective-cpp-CLxacb5B.js +1 -0
- package/web/dist/assets/ocaml-C0hk2d4L.js +1 -0
- package/web/dist/assets/odin-BBf5iR-q.js +1 -0
- package/web/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/web/dist/assets/one-light-C3Wv6jpd.js +1 -0
- package/web/dist/assets/openscad-C4EeE6gA.js +1 -0
- package/web/dist/assets/org-DM6o9KBp.js +1 -0
- package/web/dist/assets/panels-Bn-2wCur.js +1 -0
- package/web/dist/assets/pascal-D93ZcfNL.js +1 -0
- package/web/dist/assets/perl-B9cMNwum.js +1 -0
- package/web/dist/assets/php-Csjmro_R.js +1 -0
- package/web/dist/assets/pkl-u5AG7uiY.js +1 -0
- package/web/dist/assets/plastic-3e1v2bzS.js +1 -0
- package/web/dist/assets/plsql-ChMvpjG-.js +1 -0
- package/web/dist/assets/po-BTJTHyun.js +1 -0
- package/web/dist/assets/poimandres-CS3Unz2-.js +1 -0
- package/web/dist/assets/polar-C0HS_06l.js +1 -0
- package/web/dist/assets/postcss-CXtECtnM.js +1 -0
- package/web/dist/assets/powerquery-CEu0bR-o.js +1 -0
- package/web/dist/assets/powershell-BmBUJMz7.js +1 -0
- package/web/dist/assets/prisma-Vru482bI.js +1 -0
- package/web/dist/assets/prolog-CbFg5uaA.js +1 -0
- package/web/dist/assets/proto-C7zT0LnQ.js +1 -0
- package/web/dist/assets/pug-DKIMFp6K.js +1 -0
- package/web/dist/assets/puppet-BMWR74SV.js +1 -0
- package/web/dist/assets/purescript-CklMAg4u.js +1 -0
- package/web/dist/assets/python-B6aJPvgy.js +1 -0
- package/web/dist/assets/qml-3beO22l8.js +1 -0
- package/web/dist/assets/qmldir-C8lEn-DE.js +1 -0
- package/web/dist/assets/qss-IeuSbFQv.js +1 -0
- package/web/dist/assets/r-Cf5RLm7j.js +1 -0
- package/web/dist/assets/racket-BqYA7rlc.js +1 -0
- package/web/dist/assets/raku-DXvB9xmW.js +1 -0
- package/web/dist/assets/razor-BjBPvh-w.js +1 -0
- package/web/dist/assets/rbs-CpoqiR4B.js +1 -0
- package/web/dist/assets/react-vendor-ClBKp0t-.js +9 -0
- package/web/dist/assets/red-hvxz__6c.js +1 -0
- package/web/dist/assets/reg-C-SQnVFl.js +1 -0
- package/web/dist/assets/regexp-CDVJQ6XC.js +1 -0
- package/web/dist/assets/rel-C3B-1QV4.js +1 -0
- package/web/dist/assets/riscv-BM1_JUlF.js +1 -0
- package/web/dist/assets/ron-D8l8udqQ.js +1 -0
- package/web/dist/assets/rose-pine-5qJOZa0Y.js +1 -0
- package/web/dist/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
- package/web/dist/assets/rose-pine-moon-C9pEdX9L.js +1 -0
- package/web/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- package/web/dist/assets/rst-bs7f0vWN.js +1 -0
- package/web/dist/assets/ruby-C0TQ7zu5.js +1 -0
- package/web/dist/assets/rust-B1yitclQ.js +1 -0
- package/web/dist/assets/sas-DEy46yEz.js +1 -0
- package/web/dist/assets/sass-Cj5Yp3dK.js +1 -0
- package/web/dist/assets/scala-CqE71os6.js +1 -0
- package/web/dist/assets/scheme-C98Dy4si.js +1 -0
- package/web/dist/assets/scss-D5BDwBP9.js +1 -0
- package/web/dist/assets/sdbl-DVxCFoDh.js +1 -0
- package/web/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/web/dist/assets/shellscript-Yzrsuije.js +1 -0
- package/web/dist/assets/shellsession-BADoaaVG.js +1 -0
- package/web/dist/assets/slack-dark-BthQWCQV.js +1 -0
- package/web/dist/assets/slack-ochin-DqwNpetd.js +1 -0
- package/web/dist/assets/smalltalk-BOQMe2GC.js +1 -0
- package/web/dist/assets/smithy-cds9vsN8.js +1 -0
- package/web/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- package/web/dist/assets/solarized-dark-CpvCGNkr.js +1 -0
- package/web/dist/assets/solarized-light-Dlz6yCKv.js +1 -0
- package/web/dist/assets/solidity-DijEV5ha.js +1 -0
- package/web/dist/assets/soy-8wufbnw4.js +1 -0
- package/web/dist/assets/sparql-rVzFXLq3.js +1 -0
- package/web/dist/assets/splunk-BtCnVYZw.js +1 -0
- package/web/dist/assets/sql-CRqJ_cUM.js +1 -0
- package/web/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- package/web/dist/assets/stata-DI20mbqo.js +1 -0
- package/web/dist/assets/state-xD58vmym.js +17 -0
- package/web/dist/assets/sttLanguages-BNRptAQA.js +1 -0
- package/web/dist/assets/stylus-BEDo0Tqx.js +1 -0
- package/web/dist/assets/surrealql-Cjom0U5J.js +1 -0
- package/web/dist/assets/svelte-Cy7k_4gC.js +1 -0
- package/web/dist/assets/swift-C2oV4EkX.js +1 -0
- package/web/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/web/dist/assets/system-verilog-0hqHdDBg.js +1 -0
- package/web/dist/assets/systemd-4A_iFExJ.js +1 -0
- package/web/dist/assets/talonscript-CkByrt1z.js +1 -0
- package/web/dist/assets/tasl-QIJgUcNo.js +1 -0
- package/web/dist/assets/tcl-dwOrl1Do.js +1 -0
- package/web/dist/assets/templ-DhtptRzy.js +1 -0
- package/web/dist/assets/terraform-BETggiCN.js +1 -0
- package/web/dist/assets/tex-D96PA37w.js +1 -0
- package/web/dist/assets/tokyo-night-hegEt444.js +1 -0
- package/web/dist/assets/toml-vGWfd6FD.js +1 -0
- package/web/dist/assets/ts-tags-D351s5mN.js +1 -0
- package/web/dist/assets/tsv-B_m7g4N7.js +1 -0
- package/web/dist/assets/tsx-COt5Ahok.js +1 -0
- package/web/dist/assets/turtle-BsS91CYL.js +1 -0
- package/web/dist/assets/twig-27uCiNez.js +1 -0
- package/web/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/web/dist/assets/typespec-CAFt9gP4.js +1 -0
- package/web/dist/assets/typst-BUadGCkm.js +1 -0
- package/web/dist/assets/v-BGw2Nkan.js +1 -0
- package/web/dist/assets/vala-CsfeWuGM.js +1 -0
- package/web/dist/assets/vb-Cu-pLBUe.js +1 -0
- package/web/dist/assets/verilog-nZwndyjY.js +1 -0
- package/web/dist/assets/vesper-DRje8inN.js +1 -0
- package/web/dist/assets/vhdl-CeAyd5Ju.js +1 -0
- package/web/dist/assets/viml-CJc9bBzg.js +1 -0
- package/web/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/web/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/web/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/web/dist/assets/vue-BqiEGhQt.js +1 -0
- package/web/dist/assets/vue-html-AaS7Mt5G.js +1 -0
- package/web/dist/assets/vue-vine-BoDAl6tE.js +1 -0
- package/web/dist/assets/vyper-CDx5xZoG.js +1 -0
- package/web/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/web/dist/assets/wasm-MzD3tlZU.js +1 -0
- package/web/dist/assets/wenyan-BV7otONQ.js +1 -0
- package/web/dist/assets/wgsl-Dx-B1_4e.js +1 -0
- package/web/dist/assets/wikitext-BhOHFoWU.js +1 -0
- package/web/dist/assets/wit-5i3qLPDT.js +1 -0
- package/web/dist/assets/wolfram-lXgVvXCa.js +1 -0
- package/web/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/web/dist/assets/xsl-CtQFsRM5.js +1 -0
- package/web/dist/assets/yaml-Buea-lGh.js +1 -0
- package/web/dist/assets/zenscript-DVFEvuxE.js +1 -0
- package/web/dist/assets/zig-VOosw3JB.js +1 -0
- package/web/dist/favicon.svg +40 -0
- package/web/dist/index.html +26 -0
- 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};
|