@backmatter/studio 0.0.1
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/dist/chunks/UiKitPage-CgNMOqfE.js +172 -0
- package/dist/chunks/WorkspaceRoutes-DgRtdie8.js +87297 -0
- package/dist/chunks/abap-Y8Dl9g_6.js +6 -0
- package/dist/chunks/actionscript-3-DQVEcJUW.js +6 -0
- package/dist/chunks/ada-vP6ak0IW.js +6 -0
- package/dist/chunks/andromeeda-BbmzSJq1.js +4 -0
- package/dist/chunks/angular-html-DPB4Rf0C.js +32 -0
- package/dist/chunks/angular-ts-CTOR2ofC.js +21 -0
- package/dist/chunks/apache-BUjz-sD2.js +6 -0
- package/dist/chunks/apex-OyTfjCYK.js +6 -0
- package/dist/chunks/apl-CjNiDgad.js +16 -0
- package/dist/chunks/applescript-BPx7YFFu.js +6 -0
- package/dist/chunks/ara-Z2fSOxSw.js +6 -0
- package/dist/chunks/asciidoc-C1T9ziH6.js +6 -0
- package/dist/chunks/asm-BTWLY5ym.js +6 -0
- package/dist/chunks/astro-CFFExE8Q.js +18 -0
- package/dist/chunks/aurora-x-BwoVEUWZ.js +4 -0
- package/dist/chunks/awk-Fb0P9dkn.js +6 -0
- package/dist/chunks/ayu-dark-DJoqd4M9.js +4 -0
- package/dist/chunks/ayu-light-BzXEJRJ-.js +4 -0
- package/dist/chunks/ayu-mirage-BJ2oZGzi.js +4 -0
- package/dist/chunks/ballerina-oZK-YekG.js +6 -0
- package/dist/chunks/bat-0FvbqU9S.js +6 -0
- package/dist/chunks/beancount-DqJEb89h.js +6 -0
- package/dist/chunks/berry-DODBq_Ff.js +6 -0
- package/dist/chunks/bibtex-EULQRLY5.js +6 -0
- package/dist/chunks/bicep-BorU73w0.js +6 -0
- package/dist/chunks/bird2-CIjBSNj3.js +6 -0
- package/dist/chunks/blade-D0HyNvCw.js +20 -0
- package/dist/chunks/bsl-NkNNwerW.js +8 -0
- package/dist/chunks/c-eeMepfLm.js +6 -0
- package/dist/chunks/c3-BY5XW5ym.js +6 -0
- package/dist/chunks/cadence-02UX7mW8.js +6 -0
- package/dist/chunks/cairo-DM6WF2e3.js +8 -0
- package/dist/chunks/catppuccin-frappe-D3cH2rXe.js +4 -0
- package/dist/chunks/catppuccin-latte-C0LRGUW4.js +4 -0
- package/dist/chunks/catppuccin-macchiato-c5wQ11TT.js +4 -0
- package/dist/chunks/catppuccin-mocha-WMD6Qvya.js +4 -0
- package/dist/chunks/clarity-PKm5CwqM.js +6 -0
- package/dist/chunks/clojure-CXJfHrL3.js +6 -0
- package/dist/chunks/cmake-BJz8BOTU.js +6 -0
- package/dist/chunks/cobol-HihOMwht.js +10 -0
- package/dist/chunks/codeowners-Bt9yU6NX.js +6 -0
- package/dist/chunks/codeql-DHkodjjI.js +6 -0
- package/dist/chunks/coffee-DGBR_nr_.js +8 -0
- package/dist/chunks/common-lisp-EVqT9Zhp.js +6 -0
- package/dist/chunks/coq-B0L9upzn.js +6 -0
- package/dist/chunks/cpp-Cj177cuW.js +18 -0
- package/dist/chunks/crystal-CRJyc4G-.js +18 -0
- package/dist/chunks/csharp-CCgA4A09.js +6 -0
- package/dist/chunks/css-CECN5uSL.js +6 -0
- package/dist/chunks/csv-CmYOceLb.js +6 -0
- package/dist/chunks/cue-ZzumE7IT.js +6 -0
- package/dist/chunks/cypher-jpdmjtA6.js +6 -0
- package/dist/chunks/d-CBagWSwH.js +6 -0
- package/dist/chunks/dark-plus-pUHDTVV0.js +4 -0
- package/dist/chunks/dart-6ObCrKr9.js +6 -0
- package/dist/chunks/dashboard-XKKZuJGm.js +1736 -0
- package/dist/chunks/dax-CcDT-8dH.js +6 -0
- package/dist/chunks/define-studio-BYtYDGLX.js +33 -0
- package/dist/chunks/desktop-B93p9R9O.js +6 -0
- package/dist/chunks/diff-BxzP2J8R.js +6 -0
- package/dist/chunks/docker-CsHqm9tx.js +6 -0
- package/dist/chunks/dotenv-BMjVjUL1.js +6 -0
- package/dist/chunks/dracula-BtZx2Kac.js +4 -0
- package/dist/chunks/dracula-soft-BKa-aqBv.js +4 -0
- package/dist/chunks/dream-maker-PAx17jaB.js +6 -0
- package/dist/chunks/edge-BYQkOzbF.js +12 -0
- package/dist/chunks/elixir-Dh036GMp.js +8 -0
- package/dist/chunks/elm-BtNbw_Cd.js +8 -0
- package/dist/chunks/emacs-lisp-Cfxdx5D-.js +6 -0
- package/dist/chunks/erb-ScVOXVpH.js +10 -0
- package/dist/chunks/erlang-DNcDT8Hi.js +8 -0
- package/dist/chunks/everforest-dark-DMCBqXCK.js +4 -0
- package/dist/chunks/everforest-light-BbXl82Em.js +4 -0
- package/dist/chunks/fennel-N4WcXuKx.js +6 -0
- package/dist/chunks/fish-BTDEUgqH.js +6 -0
- package/dist/chunks/fluent-BMXUxfv1.js +6 -0
- package/dist/chunks/fortran-fixed-form-B67KFkXH.js +8 -0
- package/dist/chunks/fortran-free-form-CwqYvCLJ.js +6 -0
- package/dist/chunks/fsharp-CYOiIVNx.js +8 -0
- package/dist/chunks/gdresource-CvCIUjhi.js +10 -0
- package/dist/chunks/gdscript-DSAxJ_aS.js +6 -0
- package/dist/chunks/gdshader-Bk8fF6yr.js +6 -0
- package/dist/chunks/genie-DzUvd7U9.js +6 -0
- package/dist/chunks/gherkin-DHRaV0YP.js +6 -0
- package/dist/chunks/git-commit-Bd32YZ0K.js +8 -0
- package/dist/chunks/git-rebase-ZWUFO_T4.js +8 -0
- package/dist/chunks/github-dark-DenFmJkN.js +4 -0
- package/dist/chunks/github-dark-default-BJPUVz4H.js +4 -0
- package/dist/chunks/github-dark-dimmed-DUshB20C.js +4 -0
- package/dist/chunks/github-dark-high-contrast-D3aGCnF8.js +4 -0
- package/dist/chunks/github-light-JYsPkUQd.js +4 -0
- package/dist/chunks/github-light-default-D99KPAby.js +4 -0
- package/dist/chunks/github-light-high-contrast-BbmZE-Mp.js +4 -0
- package/dist/chunks/gleam-Bv284lvN.js +6 -0
- package/dist/chunks/glimmer-js-DkxNrfPT.js +14 -0
- package/dist/chunks/glimmer-ts-D6YZEmK_.js +14 -0
- package/dist/chunks/glsl-CkUcVZNK.js +8 -0
- package/dist/chunks/gn-XtJxiLa4.js +6 -0
- package/dist/chunks/gnuplot-yPG9-sE_.js +6 -0
- package/dist/chunks/go-CvFNMTvu.js +6 -0
- package/dist/chunks/graphql-CBu2ehBn.js +14 -0
- package/dist/chunks/groovy-CJQTphOW.js +6 -0
- package/dist/chunks/gruvbox-dark-hard-C5HOtKIh.js +4 -0
- package/dist/chunks/gruvbox-dark-medium-FVgwJHuz.js +4 -0
- package/dist/chunks/gruvbox-dark-soft-B46F314v.js +4 -0
- package/dist/chunks/gruvbox-light-hard-CJD38wDZ.js +4 -0
- package/dist/chunks/gruvbox-light-medium-BlIhMYTA.js +4 -0
- package/dist/chunks/gruvbox-light-soft-DoPHyLVZ.js +4 -0
- package/dist/chunks/hack-ItRNKiEb.js +10 -0
- package/dist/chunks/haml-NZLVzu_R.js +10 -0
- package/dist/chunks/handlebars-mxStpSE7.js +14 -0
- package/dist/chunks/haskell-WeIwNIP6.js +6 -0
- package/dist/chunks/haxe-TztHsm5T.js +6 -0
- package/dist/chunks/hcl-D438OF-I.js +6 -0
- package/dist/chunks/hjson-DYBUbqOl.js +6 -0
- package/dist/chunks/hlsl-Bk8TCZNL.js +6 -0
- package/dist/chunks/horizon-CJQ10nlf.js +4 -0
- package/dist/chunks/horizon-bright-CdkBZOMB.js +4 -0
- package/dist/chunks/houston-BDYrDoDW.js +4 -0
- package/dist/chunks/html-B50bmoVb.js +10 -0
- package/dist/chunks/html-derivative-C6bxFbFW.js +8 -0
- package/dist/chunks/http-DLvAkeD-.js +14 -0
- package/dist/chunks/hurl-CgkFN90t.js +12 -0
- package/dist/chunks/hxml-GbqSQCLa.js +8 -0
- package/dist/chunks/hy-Brt5EZ7-.js +6 -0
- package/dist/chunks/imba-CimUv-Uh.js +6 -0
- package/dist/chunks/index-CFAIR8GH.js +18924 -0
- package/dist/chunks/ini-BZIuRIvJ.js +6 -0
- package/dist/chunks/java-DY6VlHhP.js +6 -0
- package/dist/chunks/javascript-C25yR2R2.js +6 -0
- package/dist/chunks/jinja-CFW5uAwY.js +11 -0
- package/dist/chunks/jison-DdreslRX.js +8 -0
- package/dist/chunks/json-DxJze_jm.js +6 -0
- package/dist/chunks/json5-BT4Fjg39.js +6 -0
- package/dist/chunks/jsonc-CHjZD8gR.js +6 -0
- package/dist/chunks/jsonl-BGuvDmy9.js +6 -0
- package/dist/chunks/jsonnet-Bx2cfsXg.js +6 -0
- package/dist/chunks/jssm-BcADi6EI.js +6 -0
- package/dist/chunks/jsx-BtKADgXT.js +6 -0
- package/dist/chunks/julia-CZiKXNNP.js +16 -0
- package/dist/chunks/just-CEK2SJ_k.js +18 -0
- package/dist/chunks/kanagawa-dragon-CiKur4Hl.js +4 -0
- package/dist/chunks/kanagawa-lotus-BKu-smKu.js +4 -0
- package/dist/chunks/kanagawa-wave-CQwozSzG.js +4 -0
- package/dist/chunks/kdl-BNOv9TC3.js +6 -0
- package/dist/chunks/kotlin-ByBMgTeR.js +6 -0
- package/dist/chunks/kusto-DH_XitHS.js +6 -0
- package/dist/chunks/laserwave-6a00oqik.js +4 -0
- package/dist/chunks/latex-B8Egmqc5.js +8 -0
- package/dist/chunks/lean-BnVxaZxY.js +6 -0
- package/dist/chunks/less-B1GLI2Di.js +6 -0
- package/dist/chunks/light-plus-CZuVqSLX.js +4 -0
- package/dist/chunks/liquid-CHzkx3jT.js +14 -0
- package/dist/chunks/llvm-Bk2XctFf.js +6 -0
- package/dist/chunks/log-Al8wyEFV.js +6 -0
- package/dist/chunks/logo-DBa4JDzV.js +6 -0
- package/dist/chunks/lua-Bp3IRv7I.js +8 -0
- package/dist/chunks/luau-UQyhudEE.js +6 -0
- package/dist/chunks/make-CsMclxtr.js +6 -0
- package/dist/chunks/markdown-CrScaQ96.js +6 -0
- package/dist/chunks/marko-CJBA24Ri.js +14 -0
- package/dist/chunks/material-theme-D6KBX41T.js +4 -0
- package/dist/chunks/material-theme-darker-CkRroheE.js +4 -0
- package/dist/chunks/material-theme-lighter-BUBw43Yz.js +4 -0
- package/dist/chunks/material-theme-ocean-ClGX14Ja.js +4 -0
- package/dist/chunks/material-theme-palenight-C1RVm8K1.js +4 -0
- package/dist/chunks/matlab-BOAaUVP0.js +6 -0
- package/dist/chunks/mdc-C9YSUwVC.js +12 -0
- package/dist/chunks/mdx-BOhZZUJ8.js +6 -0
- package/dist/chunks/mermaid-C3rz4dYh.js +6 -0
- package/dist/chunks/min-dark-C7ak0t6c.js +4 -0
- package/dist/chunks/min-light-CKFxVcPp.js +4 -0
- package/dist/chunks/mipsasm-CTx18fBl.js +6 -0
- package/dist/chunks/mojo-EIZIZRTQ.js +6 -0
- package/dist/chunks/monokai-C1KBYcO0.js +4 -0
- package/dist/chunks/moonbit-B1TV0fG3.js +6 -0
- package/dist/chunks/move-Db4ltDq1.js +6 -0
- package/dist/chunks/narrat-DmhDCBs-.js +6 -0
- package/dist/chunks/nextflow-47kAzSl1.js +8 -0
- package/dist/chunks/nextflow-groovy-Drz_2V0j.js +6 -0
- package/dist/chunks/nginx-D7O4WoB1.js +8 -0
- package/dist/chunks/night-owl-Bm2rzalh.js +4 -0
- package/dist/chunks/night-owl-light-CBI5u5kw.js +4 -0
- package/dist/chunks/nim-QAjRdh9w.js +20 -0
- package/dist/chunks/nix-fTRXMGic.js +9 -0
- package/dist/chunks/nord-CC5OiUXg.js +4 -0
- package/dist/chunks/nushell-BGCPRlV5.js +6 -0
- package/dist/chunks/objective-c-Itk8tzmv.js +6 -0
- package/dist/chunks/objective-cpp-DGt5UKRO.js +6 -0
- package/dist/chunks/ocaml-eSVK32Eg.js +6 -0
- package/dist/chunks/odin-BItnF517.js +6 -0
- package/dist/chunks/one-dark-pro-DTA3VF0_.js +4 -0
- package/dist/chunks/one-light-LkMrt1Cf.js +4 -0
- package/dist/chunks/openscad-Sp5uZ6f2.js +6 -0
- package/dist/chunks/pascal-xy8pJNns.js +6 -0
- package/dist/chunks/perl-DkVrgJBg.js +16 -0
- package/dist/chunks/php-BIzsF_5x.js +18 -0
- package/dist/chunks/pkl-C-zSNmaA.js +6 -0
- package/dist/chunks/plastic-CSTz3KZp.js +4 -0
- package/dist/chunks/plsql-pVbGZfOv.js +6 -0
- package/dist/chunks/po-BNfHvqmm.js +6 -0
- package/dist/chunks/poimandres-C-VADXHD.js +4 -0
- package/dist/chunks/polar-CAZahv3u.js +6 -0
- package/dist/chunks/postcss-05aHdL-n.js +6 -0
- package/dist/chunks/powerquery-DI9HkTvs.js +6 -0
- package/dist/chunks/powershell-Clc4ydu-.js +6 -0
- package/dist/chunks/prisma-FZjmVtSl.js +6 -0
- package/dist/chunks/prolog-C5-yg4TO.js +6 -0
- package/dist/chunks/proto-C7QgialS.js +6 -0
- package/dist/chunks/pug-XOw9Q1Ho.js +12 -0
- package/dist/chunks/puppet-CUJHmnxV.js +6 -0
- package/dist/chunks/purescript-rUfGld-4.js +6 -0
- package/dist/chunks/python-BFNSHbwJ.js +6 -0
- package/dist/chunks/qml-rTNXsquU.js +8 -0
- package/dist/chunks/qmldir-BInDYbpo.js +6 -0
- package/dist/chunks/qss-AeJTysr_.js +6 -0
- package/dist/chunks/r-CSmzDPi7.js +6 -0
- package/dist/chunks/racket-B83wSAja.js +6 -0
- package/dist/chunks/raku-nEQ4ZJJ7.js +6 -0
- package/dist/chunks/razor-Ci8D0dQK.js +10 -0
- package/dist/chunks/red-7y8PH7HH.js +4 -0
- package/dist/chunks/reg-m_s_Kiip.js +6 -0
- package/dist/chunks/regexp-BazyLpPg.js +6 -0
- package/dist/chunks/rel-BcRfyd6Q.js +6 -0
- package/dist/chunks/riscv-Ce8MAQLP.js +6 -0
- package/dist/chunks/ron-DCEEQypA.js +6 -0
- package/dist/chunks/rose-pine-BKc3yVeu.js +4 -0
- package/dist/chunks/rose-pine-dawn-BulJcPZT.js +4 -0
- package/dist/chunks/rose-pine-moon-j6jiXKV8.js +4 -0
- package/dist/chunks/rosmsg-Cz0w1km8.js +6 -0
- package/dist/chunks/rst-DM32Wy3O.js +22 -0
- package/dist/chunks/ruby-BeqCs27h.js +30 -0
- package/dist/chunks/rust-CLzF9zIN.js +6 -0
- package/dist/chunks/sas-fpEvgATw.js +8 -0
- package/dist/chunks/sass-DxHp5rTx.js +6 -0
- package/dist/chunks/scala-D4grkFkl.js +6 -0
- package/dist/chunks/scheme-BCRWuEm4.js +6 -0
- package/dist/chunks/scss-B1FaCqwR.js +8 -0
- package/dist/chunks/sdbl-B7T8abf4.js +6 -0
- package/dist/chunks/shaderlab-Cr62-Wb4.js +8 -0
- package/dist/chunks/shellscript-InADTalH.js +6 -0
- package/dist/chunks/shellsession-DiDJNQdy.js +8 -0
- package/dist/chunks/slack-dark-i7wN4OET.js +4 -0
- package/dist/chunks/slack-ochin-ndHf0LoP.js +4 -0
- package/dist/chunks/smalltalk-BlI1_OkM.js +6 -0
- package/dist/chunks/snazzy-light-BlSJXAu4.js +4 -0
- package/dist/chunks/solarized-dark-UTmkh7lw.js +4 -0
- package/dist/chunks/solarized-light-BheCkDPT.js +4 -0
- package/dist/chunks/solidity-DUWnFhS6.js +6 -0
- package/dist/chunks/soy-C1-6njHa.js +8 -0
- package/dist/chunks/sparql-B0KWFEri.js +8 -0
- package/dist/chunks/splunk-CRXR8A9s.js +6 -0
- package/dist/chunks/sql-Cn_v3PB0.js +6 -0
- package/dist/chunks/ssh-config-DP-hNVbF.js +6 -0
- package/dist/chunks/stata-B8c5fTjX.js +8 -0
- package/dist/chunks/stylus-CyKEU1Ej.js +6 -0
- package/dist/chunks/surrealql-CQi7GQN7.js +8 -0
- package/dist/chunks/svelte-rYri41WT.js +14 -0
- package/dist/chunks/swift-DJpUqPLg.js +6 -0
- package/dist/chunks/synthwave-84-NU3C_KFZ.js +4 -0
- package/dist/chunks/system-verilog-BCm7smPJ.js +6 -0
- package/dist/chunks/systemd-C-4qm6XH.js +6 -0
- package/dist/chunks/talonscript-CFF3LF_O.js +6 -0
- package/dist/chunks/tasl-Cg_WBUAe.js +6 -0
- package/dist/chunks/tcl-DN7buRTF.js +6 -0
- package/dist/chunks/templ-Crs9Ui8q.js +12 -0
- package/dist/chunks/terraform-DGvcn9zM.js +6 -0
- package/dist/chunks/tex-AHxmLTL0.js +8 -0
- package/dist/chunks/tokyo-night-DP4TmcQl.js +4 -0
- package/dist/chunks/toml-DY62mUL_.js +6 -0
- package/dist/chunks/ts-tags-ioVe2PKw.js +41 -0
- package/dist/chunks/tsv-BtvSkaG0.js +6 -0
- package/dist/chunks/tsx-B8rCNbgL.js +6 -0
- package/dist/chunks/turtle-_H59FV7D.js +6 -0
- package/dist/chunks/twig-DwV2Fi4L.js +18 -0
- package/dist/chunks/typescript-RycA9KXf.js +6 -0
- package/dist/chunks/typespec-DWqp-kUU.js +6 -0
- package/dist/chunks/typst-D_1QKWns.js +6 -0
- package/dist/chunks/v-BPCNiyYe.js +6 -0
- package/dist/chunks/vala-uxaPR7d1.js +6 -0
- package/dist/chunks/vb-D8_c5-KN.js +6 -0
- package/dist/chunks/verilog-B-bybjPF.js +6 -0
- package/dist/chunks/vesper-BckBta1U.js +4 -0
- package/dist/chunks/vhdl-CUlNa8ac.js +6 -0
- package/dist/chunks/viml-DsfA-sWm.js +6 -0
- package/dist/chunks/vitesse-black-BoGvW84i.js +4 -0
- package/dist/chunks/vitesse-dark-Cym-eLtO.js +4 -0
- package/dist/chunks/vitesse-light-CcmG315c.js +4 -0
- package/dist/chunks/vue-DXEmyXn9.js +31 -0
- package/dist/chunks/vue-html-j9xNEdu3.js +8 -0
- package/dist/chunks/vue-vine-D41-N99t.js +18 -0
- package/dist/chunks/vyper-CPQuu50u.js +6 -0
- package/dist/chunks/wasm-BBXxrAl7.js +6 -0
- package/dist/chunks/wasm-DQxwEHae.js +7 -0
- package/dist/chunks/wenyan-pbVjoM9_.js +6 -0
- package/dist/chunks/wgsl-DY4iK1q1.js +6 -0
- package/dist/chunks/wikitext-Z-MoUasO.js +6 -0
- package/dist/chunks/wit-CQMQOlTg.js +6 -0
- package/dist/chunks/wolfram-Dz4KXISs.js +6 -0
- package/dist/chunks/xml-C2J0sS9M.js +8 -0
- package/dist/chunks/xsl-BmSZphgC.js +8 -0
- package/dist/chunks/yaml-DaO7k5B1.js +6 -0
- package/dist/chunks/zenscript-CxBjpf9c.js +6 -0
- package/dist/chunks/zig-Vm0PO9wB.js +6 -0
- package/dist/dashboard.js +8 -0
- package/dist/define.js +4 -0
- package/dist/index.js +317 -0
- package/dist/studio.css +2272 -0
- package/dist/styles.js +1 -0
- package/package.json +88 -0
- package/prose-node-shims/fs.ts +6 -0
- package/prose-node-shims/path.ts +16 -0
- package/prose-node-shims/sharp.ts +4 -0
- package/prose-ui-node-shim.ts +26 -0
- package/src/App.tsx +173 -0
- package/src/WorkspaceRoutes.tsx +38 -0
- package/src/api.ts +833 -0
- package/src/auth-client.ts +15 -0
- package/src/chat/ChangeCard.tsx +330 -0
- package/src/chat/ChatBar.tsx +238 -0
- package/src/chat/ChatMessage.tsx +478 -0
- package/src/chat/ChatPanel.tsx +406 -0
- package/src/chat/ChatProvider.tsx +661 -0
- package/src/chat/Composer.tsx +461 -0
- package/src/chat/DocListCard.tsx +322 -0
- package/src/chat/LiveStatus.tsx +83 -0
- package/src/chat/MentionPopover.tsx +164 -0
- package/src/chat/ModelPicker.tsx +113 -0
- package/src/chat/Shimmer.tsx +39 -0
- package/src/chat/ThreadHistory.tsx +268 -0
- package/src/chat/chat-context.ts +41 -0
- package/src/chat/chat-helpers.ts +402 -0
- package/src/chat/chat-state.ts +59 -0
- package/src/chat/icons.tsx +171 -0
- package/src/chat/useStickToBottom.ts +110 -0
- package/src/chrome/AppShell.tsx +100 -0
- package/src/chrome/AppSidebar.tsx +305 -0
- package/src/chrome/CommandPalette.tsx +559 -0
- package/src/chrome/ContentApiPopover.tsx +215 -0
- package/src/chrome/EditorActionsContext.tsx +63 -0
- package/src/chrome/EditorFocusContext.tsx +80 -0
- package/src/chrome/EditorStatusContext.tsx +84 -0
- package/src/chrome/NavItem.tsx +75 -0
- package/src/chrome/OpenSiteButton.tsx +148 -0
- package/src/chrome/StatusBar.tsx +73 -0
- package/src/chrome/UserMenu.tsx +130 -0
- package/src/chrome/WorkspaceNavContext.tsx +60 -0
- package/src/chrome/WorkspaceStatusBar.tsx +940 -0
- package/src/chrome/content-api-url.ts +111 -0
- package/src/chrome/content-type-groups.ts +32 -0
- package/src/chrome/hotkeys.ts +79 -0
- package/src/chrome/icons.tsx +191 -0
- package/src/chrome/open-site.ts +30 -0
- package/src/chrome/status-bar-action.ts +24 -0
- package/src/components/AppChromeContext.tsx +32 -0
- package/src/components/Shell.tsx +18 -0
- package/src/components/WorkspaceSwitcher.tsx +345 -0
- package/src/content-blocks/index.tsx +262 -0
- package/src/css.d.ts +2 -0
- package/src/dashboard.ts +5 -0
- package/src/define-studio.tsx +98 -0
- package/src/define.ts +12 -0
- package/src/index.css +123 -0
- package/src/index.ts +19 -0
- package/src/last-user.ts +40 -0
- package/src/last-workspace.ts +57 -0
- package/src/mcp/McpConnectorsPanel.tsx +872 -0
- package/src/mcp/parse-json.ts +52 -0
- package/src/pages/AcceptInvitePage.tsx +99 -0
- package/src/pages/BlockSettingsDialog.tsx +303 -0
- package/src/pages/BlocksList.tsx +402 -0
- package/src/pages/BodyProse.tsx +236 -0
- package/src/pages/DocEditorPage.tsx +1394 -0
- package/src/pages/EmbedChatPage.tsx +110 -0
- package/src/pages/EmbedPublishPage.tsx +259 -0
- package/src/pages/EnterPage.tsx +96 -0
- package/src/pages/GithubConnectPage.tsx +142 -0
- package/src/pages/LegacyWorkspaceRedirect.tsx +57 -0
- package/src/pages/LoginPage.tsx +335 -0
- package/src/pages/NewWorkspacePage.tsx +74 -0
- package/src/pages/RenderedBody.tsx +598 -0
- package/src/pages/ResumeHome.tsx +18 -0
- package/src/pages/SelectionBar.tsx +277 -0
- package/src/pages/SettingsGeneralPage.tsx +403 -0
- package/src/pages/SettingsGithubPage.tsx +277 -0
- package/src/pages/SettingsLayout.tsx +127 -0
- package/src/pages/SettingsMcpPage.tsx +30 -0
- package/src/pages/SettingsSchemaPage.tsx +93 -0
- package/src/pages/SettingsUsersPage.tsx +363 -0
- package/src/pages/TypeDocsPage.tsx +225 -0
- package/src/pages/UiKitPage.tsx +331 -0
- package/src/pages/UserSettingsPage.tsx +94 -0
- package/src/pages/WorkspaceHomeRedirect.tsx +17 -0
- package/src/pages/WorkspaceLayout.tsx +103 -0
- package/src/pages/WorkspacesPage.tsx +35 -0
- package/src/pages/blocks-list.ts +73 -0
- package/src/pages/body-blocks.ts +233 -0
- package/src/pages/body-mdx.tsx +82 -0
- package/src/pages/compile-body.ts +37 -0
- package/src/pages/host-env.ts +22 -0
- package/src/pages/html-to-markdown.ts +99 -0
- package/src/pages/preview-bridge.ts +46 -0
- package/src/pages/preview-split.ts +30 -0
- package/src/pages/preview-url.ts +92 -0
- package/src/pages/selection-format.ts +179 -0
- package/src/pages/type-docs.ts +9 -0
- package/src/replica/BootstrapSplash.tsx +80 -0
- package/src/replica/ReplicaProvider.tsx +267 -0
- package/src/replica/cache.ts +30 -0
- package/src/replica/idb.ts +89 -0
- package/src/replica/instance.ts +39 -0
- package/src/replica/pull.ts +23 -0
- package/src/replica/store.ts +497 -0
- package/src/replica/sync-cache.ts +51 -0
- package/src/replica/types.ts +119 -0
- package/src/replica/useReplica.ts +13 -0
- package/src/site.tsx +230 -0
- package/src/studio-config.ts +81 -0
- package/src/styles/themes.ts +49 -0
- package/src/styles/tokens.stylex.ts +58 -0
- package/src/styles.ts +11 -0
- package/src/theme/AccentOverride.tsx +29 -0
- package/src/theme/ThemeProvider.tsx +130 -0
- package/src/theme/apply-theme-class.ts +12 -0
- package/src/ui/BackLink.tsx +28 -0
- package/src/ui/Button.tsx +167 -0
- package/src/ui/ConfirmDialog.tsx +141 -0
- package/src/ui/Field.tsx +45 -0
- package/src/ui/Input.tsx +183 -0
- package/src/ui/ListRow.tsx +88 -0
- package/src/ui/Page.tsx +151 -0
- package/src/ui/Text.tsx +77 -0
- package/src/ui/Tooltip.tsx +85 -0
- package/src/ui/popup.ts +216 -0
- package/src/workspace/permissions.ts +17 -0
- package/src/workspace/types.ts +12 -0
- package/src/workspace-path.ts +98 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// packages/studio/src/chat/chat-helpers.ts
|
|
2
|
+
//
|
|
3
|
+
// Pure helpers used by chat components. Kept free of StyleX/React so they can
|
|
4
|
+
// be unit tested with `bun test`.
|
|
5
|
+
import { getToolName, isToolUIPart, type UIMessage } from "ai";
|
|
6
|
+
import type { ChatChangeSummary, ChatThread, DocStatus } from "../api";
|
|
7
|
+
import type { DocRef } from "../chrome/EditorFocusContext";
|
|
8
|
+
|
|
9
|
+
export type WriteToolOutput =
|
|
10
|
+
| { ok: true; change: ChatChangeSummary }
|
|
11
|
+
| { ok: false; error: string; message: string };
|
|
12
|
+
|
|
13
|
+
export function isWriteTool(name: string) {
|
|
14
|
+
return name === "writeDoc" || name === "createDoc" || name === "renameDoc";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isCommitTool(name: string) {
|
|
18
|
+
return name === "commit";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const TREE_STATUSES = new Set<DocStatus>(["clean", "modified", "added", "conflict", "deleted"]);
|
|
22
|
+
|
|
23
|
+
/** Statuses a tool result wants applied to the local doc list immediately. */
|
|
24
|
+
export type TreeStatusPatch = {
|
|
25
|
+
locale: string;
|
|
26
|
+
type: string;
|
|
27
|
+
slug: string;
|
|
28
|
+
status: DocStatus;
|
|
29
|
+
absent?: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function treeStatusPatches(output: unknown): TreeStatusPatch[] {
|
|
33
|
+
if (!output || typeof output !== "object") return [];
|
|
34
|
+
const body = output as { ok?: boolean; tree?: unknown };
|
|
35
|
+
if (body.ok === false || !Array.isArray(body.tree)) return [];
|
|
36
|
+
const patches: TreeStatusPatch[] = [];
|
|
37
|
+
for (const row of body.tree) {
|
|
38
|
+
if (!row || typeof row !== "object") continue;
|
|
39
|
+
const item = row as Partial<TreeStatusPatch>;
|
|
40
|
+
if (
|
|
41
|
+
typeof item.locale !== "string" ||
|
|
42
|
+
!item.locale ||
|
|
43
|
+
typeof item.type !== "string" ||
|
|
44
|
+
!item.type ||
|
|
45
|
+
typeof item.slug !== "string" ||
|
|
46
|
+
!item.slug ||
|
|
47
|
+
typeof item.status !== "string" ||
|
|
48
|
+
!TREE_STATUSES.has(item.status as DocStatus)
|
|
49
|
+
) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
patches.push({
|
|
53
|
+
locale: item.locale,
|
|
54
|
+
type: item.type,
|
|
55
|
+
slug: item.slug,
|
|
56
|
+
status: item.status as DocStatus,
|
|
57
|
+
...(item.absent ? { absent: true } : {}),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return patches;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function toolLabel(name: string, input: unknown, output: unknown, state: string): string {
|
|
64
|
+
const ref = (input as { docRef?: { type?: string; slug?: string } } | undefined)?.docRef;
|
|
65
|
+
const doc = ref?.type && ref.slug ? `${ref.type}/${ref.slug}` : "";
|
|
66
|
+
const q = (input as { query?: string } | undefined)?.query;
|
|
67
|
+
const running = state !== "output-available" && state !== "output-error";
|
|
68
|
+
switch (name) {
|
|
69
|
+
case "listDocs":
|
|
70
|
+
return running ? "Listing documents…" : "Listed documents";
|
|
71
|
+
case "readDoc":
|
|
72
|
+
return running ? `Reading ${doc}…` : `Read ${doc}`;
|
|
73
|
+
case "searchDocs":
|
|
74
|
+
return running ? `Searching “${q ?? ""}”…` : `Searched “${q ?? ""}”`;
|
|
75
|
+
case "writeDoc":
|
|
76
|
+
return running ? `Editing ${doc}…` : `Edited ${doc}`;
|
|
77
|
+
case "createDoc":
|
|
78
|
+
return running ? `Creating ${doc}…` : `Created ${doc}`;
|
|
79
|
+
case "renameDoc":
|
|
80
|
+
return running ? `Renaming ${doc}…` : `Renamed ${doc}`;
|
|
81
|
+
case "commit": {
|
|
82
|
+
if (running) return "Publishing…";
|
|
83
|
+
const published = output as { ok?: boolean; files?: unknown[] } | undefined;
|
|
84
|
+
if (published?.ok === false) return "Publish failed";
|
|
85
|
+
const count = Array.isArray(published?.files) ? published.files.length : 0;
|
|
86
|
+
return count === 1 ? "Published 1 file" : `Published ${count} files`;
|
|
87
|
+
}
|
|
88
|
+
default: {
|
|
89
|
+
const mcp = mcpToolLabel(name, input, output, state);
|
|
90
|
+
return mcp ?? name;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Latest sentence of a reasoning dump, clipped to one line. */
|
|
96
|
+
export function thoughtSnippet(text: string, max = 72): string {
|
|
97
|
+
const cleaned = text.replace(/\s+/g, " ").trim();
|
|
98
|
+
if (!cleaned) return "Thinking";
|
|
99
|
+
const sentences = cleaned.split(/(?<=[.!?])\s+/);
|
|
100
|
+
const last = sentences[sentences.length - 1] ?? cleaned;
|
|
101
|
+
if (last.length <= max) return last;
|
|
102
|
+
return `${last.slice(0, max - 1).trimEnd()}…`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function formatElapsed(ms: number): string {
|
|
106
|
+
return `${(Math.max(0, ms) / 1000).toFixed(1)}s`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type LiveActivity = { kind: "thinking" | "thought" | "tool"; label: string };
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* One-line status for a live turn. Prefers a running tool, then the latest
|
|
113
|
+
* thought, then "Thinking" — so the UI stays alive between tool rounds.
|
|
114
|
+
*/
|
|
115
|
+
export function liveActivity(
|
|
116
|
+
parts: UIMessage["parts"] | undefined,
|
|
117
|
+
status: "submitted" | "streaming" | "ready" | "error",
|
|
118
|
+
): LiveActivity | null {
|
|
119
|
+
if (status !== "submitted" && status !== "streaming") return null;
|
|
120
|
+
const list = parts ?? [];
|
|
121
|
+
|
|
122
|
+
for (const p of list) {
|
|
123
|
+
if (!isToolUIPart(p)) continue;
|
|
124
|
+
if (p.state === "output-available" || p.state === "output-error") continue;
|
|
125
|
+
return {
|
|
126
|
+
kind: "tool",
|
|
127
|
+
label: toolLabel(getToolName(p), p.input, undefined, p.state),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const last = list[list.length - 1];
|
|
132
|
+
if (last?.type === "text" && last.text.trim() && last.state === "streaming") {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let reasoning = "";
|
|
137
|
+
for (const p of list) {
|
|
138
|
+
if (p.type === "reasoning" && p.text.trim()) reasoning = p.text;
|
|
139
|
+
}
|
|
140
|
+
if (reasoning) return { kind: "thought", label: thoughtSnippet(reasoning) };
|
|
141
|
+
|
|
142
|
+
return { kind: "thinking", label: "Thinking" };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function mcpToolLabel(
|
|
146
|
+
name: string,
|
|
147
|
+
input: unknown,
|
|
148
|
+
output: unknown,
|
|
149
|
+
state: string,
|
|
150
|
+
): string | null {
|
|
151
|
+
const running = state !== "output-available" && state !== "output-error";
|
|
152
|
+
if (name === "importMcp") {
|
|
153
|
+
const id =
|
|
154
|
+
input && typeof input === "object" && "id" in input && typeof input.id === "string"
|
|
155
|
+
? input.id
|
|
156
|
+
: "connector";
|
|
157
|
+
const result =
|
|
158
|
+
output && typeof output === "object"
|
|
159
|
+
? (output as { ok?: boolean; name?: string; tools?: string[] })
|
|
160
|
+
: null;
|
|
161
|
+
if (running) return `Importing ${id}…`;
|
|
162
|
+
if (result?.ok) {
|
|
163
|
+
const count = result.tools?.length ?? 0;
|
|
164
|
+
return `Imported ${result.name ?? id} · ${count} tools`;
|
|
165
|
+
}
|
|
166
|
+
return `Could not import ${id}`;
|
|
167
|
+
}
|
|
168
|
+
if (!name.startsWith("mcp_")) return null;
|
|
169
|
+
const rest = name.slice(4);
|
|
170
|
+
const idx = rest.indexOf("_");
|
|
171
|
+
const slug = idx === -1 ? rest : rest.slice(0, idx);
|
|
172
|
+
const tool = (idx === -1 ? rest : rest.slice(idx + 1)).replace(/_/g, " ");
|
|
173
|
+
return running ? `${slug}: ${tool}…` : `${slug}: ${tool}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Change ids referenced by successful write-tool outputs inside a message. */
|
|
177
|
+
export function changeIdsInMessage(message: UIMessage): string[] {
|
|
178
|
+
const ids: string[] = [];
|
|
179
|
+
for (const part of message.parts) {
|
|
180
|
+
if (!isToolUIPart(part)) continue;
|
|
181
|
+
if (!isWriteTool(getToolName(part))) continue;
|
|
182
|
+
if (part.state !== "output-available") continue;
|
|
183
|
+
const out = part.output as WriteToolOutput | undefined;
|
|
184
|
+
if (out?.ok) ids.push(out.change.id);
|
|
185
|
+
}
|
|
186
|
+
return ids;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Find an active `@query` token ending at the caret. */
|
|
190
|
+
export function mentionQueryAt(
|
|
191
|
+
text: string,
|
|
192
|
+
caret: number,
|
|
193
|
+
): { start: number; query: string } | null {
|
|
194
|
+
const before = text.slice(0, caret);
|
|
195
|
+
const at = before.lastIndexOf("@");
|
|
196
|
+
if (at === -1) return null;
|
|
197
|
+
if (at > 0 && !/\s/.test(before[at - 1]!)) return null;
|
|
198
|
+
const query = before.slice(at + 1);
|
|
199
|
+
if (/\s/.test(query)) return null;
|
|
200
|
+
return { start: at, query };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export type MentionDocItem = DocRef & { status: DocStatus };
|
|
204
|
+
|
|
205
|
+
export function filterDocs(
|
|
206
|
+
docs: MentionDocItem[],
|
|
207
|
+
query: string,
|
|
208
|
+
limit = 8,
|
|
209
|
+
): MentionDocItem[] {
|
|
210
|
+
const q = query.trim().toLowerCase();
|
|
211
|
+
const scored = docs
|
|
212
|
+
.map((d) => {
|
|
213
|
+
const label = `${d.type}/${d.slug}`.toLowerCase();
|
|
214
|
+
let score = -1;
|
|
215
|
+
if (!q) score = 0;
|
|
216
|
+
else if (label.startsWith(q)) score = 3;
|
|
217
|
+
else if (d.slug.toLowerCase().startsWith(q)) score = 2;
|
|
218
|
+
else if (label.includes(q)) score = 1;
|
|
219
|
+
return { d, score };
|
|
220
|
+
})
|
|
221
|
+
.filter((x) => x.score >= 0)
|
|
222
|
+
.sort((a, b) => b.score - a.score || a.d.slug.localeCompare(b.d.slug));
|
|
223
|
+
return scored.slice(0, limit).map((x) => x.d);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function groupThreadsByDay(
|
|
227
|
+
threads: ChatThread[],
|
|
228
|
+
now = new Date(),
|
|
229
|
+
): Array<{ label: string; threads: ChatThread[] }> {
|
|
230
|
+
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
231
|
+
const day = 86_400_000;
|
|
232
|
+
const groups = new Map<string, ChatThread[]>();
|
|
233
|
+
for (const t of threads) {
|
|
234
|
+
const ts = new Date(t.updatedAt).getTime();
|
|
235
|
+
let label: string;
|
|
236
|
+
if (ts >= startOfToday) label = "Today";
|
|
237
|
+
else if (ts >= startOfToday - day) label = "Yesterday";
|
|
238
|
+
else if (ts >= startOfToday - 7 * day) label = "Previous 7 days";
|
|
239
|
+
else if (ts >= startOfToday - 30 * day) label = "Previous 30 days";
|
|
240
|
+
else label = "Older";
|
|
241
|
+
const list = groups.get(label) ?? [];
|
|
242
|
+
list.push(t);
|
|
243
|
+
groups.set(label, list);
|
|
244
|
+
}
|
|
245
|
+
return [...groups.entries()].map(([label, list]) => ({ label, threads: list }));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function chatSuggestions(input: {
|
|
249
|
+
hasFocus: boolean;
|
|
250
|
+
contentTypes: string[];
|
|
251
|
+
}): string[] {
|
|
252
|
+
if (input.hasFocus) {
|
|
253
|
+
return ["Improve this page", "Fix tone and grammar", "Summarize this document"];
|
|
254
|
+
}
|
|
255
|
+
const type = input.contentTypes[0];
|
|
256
|
+
return [
|
|
257
|
+
type ? `Write a new ${type}` : "What can you help with?",
|
|
258
|
+
"What have I changed but not committed?",
|
|
259
|
+
"Find pages mentioning…",
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// Inline document cards
|
|
265
|
+
//
|
|
266
|
+
// The model can write `[[docs]]` on its own line to place the card for its most
|
|
267
|
+
// recent listDocs/searchDocs call at that point in the prose. The card is then
|
|
268
|
+
// not repeated at the tool-call position.
|
|
269
|
+
|
|
270
|
+
export const DOCS_PLACEHOLDER = "[[docs]]";
|
|
271
|
+
const DOCS_PLACEHOLDER_RE = /^[ \t]*\[\[docs\]\][ \t]*$/gm;
|
|
272
|
+
|
|
273
|
+
export type TextSegment = { kind: "text"; text: string } | { kind: "docs" };
|
|
274
|
+
|
|
275
|
+
/** Split text on `[[docs]]` lines. Empty text segments are dropped. */
|
|
276
|
+
export function splitDocsPlaceholders(text: string): TextSegment[] {
|
|
277
|
+
const out: TextSegment[] = [];
|
|
278
|
+
let last = 0;
|
|
279
|
+
for (const m of text.matchAll(DOCS_PLACEHOLDER_RE)) {
|
|
280
|
+
const before = text.slice(last, m.index);
|
|
281
|
+
if (before.trim()) out.push({ kind: "text", text: before });
|
|
282
|
+
out.push({ kind: "docs" });
|
|
283
|
+
last = m.index + m[0].length;
|
|
284
|
+
}
|
|
285
|
+
const rest = text.slice(last);
|
|
286
|
+
if (rest.trim()) out.push({ kind: "text", text: rest });
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* While streaming, hide a trailing partial placeholder ("[[do") so it doesn't
|
|
292
|
+
* flash as literal text before the full token arrives.
|
|
293
|
+
*/
|
|
294
|
+
export function trimPartialPlaceholder(text: string): string {
|
|
295
|
+
const nl = text.lastIndexOf("\n");
|
|
296
|
+
const tail = text.slice(nl + 1).trimStart();
|
|
297
|
+
if (tail.length > 0 && tail.length < DOCS_PLACEHOLDER.length && DOCS_PLACEHOLDER.startsWith(tail)) {
|
|
298
|
+
return text.slice(0, nl + 1);
|
|
299
|
+
}
|
|
300
|
+
return text;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Which doc-list tool calls (by index into `parts`) are rendered inline via a
|
|
305
|
+
* placeholder, and which placeholder maps to which tool part. A placeholder
|
|
306
|
+
* binds to the nearest preceding doc-list tool part; if none precedes it, the
|
|
307
|
+
* next one after it (the model sometimes narrates before calling the tool).
|
|
308
|
+
*/
|
|
309
|
+
export function bindDocsPlaceholders(
|
|
310
|
+
parts: UIMessage["parts"],
|
|
311
|
+
isDocListTool: (name: string) => boolean,
|
|
312
|
+
): { placeholderTarget: Map<string, number>; inlined: Set<number> } {
|
|
313
|
+
const toolIdx: number[] = [];
|
|
314
|
+
parts.forEach((p, i) => {
|
|
315
|
+
if (isToolUIPart(p) && isDocListTool(getToolName(p))) toolIdx.push(i);
|
|
316
|
+
});
|
|
317
|
+
const placeholderTarget = new Map<string, number>();
|
|
318
|
+
const inlined = new Set<number>();
|
|
319
|
+
parts.forEach((p, i) => {
|
|
320
|
+
if (p.type !== "text") return;
|
|
321
|
+
const count = splitDocsPlaceholders(p.text).filter((s) => s.kind === "docs").length;
|
|
322
|
+
for (let n = 0; n < count; n++) {
|
|
323
|
+
const before = toolIdx.filter((t) => t < i);
|
|
324
|
+
const after = toolIdx.filter((t) => t > i);
|
|
325
|
+
const target = before[before.length - 1] ?? after[0];
|
|
326
|
+
if (target === undefined) continue;
|
|
327
|
+
placeholderTarget.set(`${i}:${n}`, target);
|
|
328
|
+
inlined.add(target);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
return { placeholderTarget, inlined };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const LIST_ITEM_RE = /^\s*(?:[-*•]|\d+[.)])\s+(.*)$/;
|
|
335
|
+
const HAS_LIST_ITEM_RE = /^\s*(?:[-*•]|\d+[.)])\s+/m;
|
|
336
|
+
|
|
337
|
+
function normalise(s: string) {
|
|
338
|
+
return s.toLowerCase().replace(/[`"'“”‘’*_]/g, "").trim();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Fallback for models that ignore the instructions and echo the card as a
|
|
343
|
+
* bullet/numbered list anyway: a run of list items in which most items mention
|
|
344
|
+
* a card title, slug or path is replaced by a `[[docs]]` placeholder (or removed
|
|
345
|
+
* entirely if the text already has one), so the card takes its place inline.
|
|
346
|
+
*
|
|
347
|
+
* While streaming, an unfinished trailing item directly after an echoed run is
|
|
348
|
+
* held back too, so items don't flash in before being recognised.
|
|
349
|
+
*/
|
|
350
|
+
export function replaceEchoedDocList(
|
|
351
|
+
text: string,
|
|
352
|
+
names: string[],
|
|
353
|
+
streaming = false,
|
|
354
|
+
): string {
|
|
355
|
+
const keys = names.map(normalise).filter((k) => k.length >= 3);
|
|
356
|
+
if (keys.length === 0 || !HAS_LIST_ITEM_RE.test(text)) return text;
|
|
357
|
+
const hasPlaceholder = splitDocsPlaceholders(text).some((s) => s.kind === "docs");
|
|
358
|
+
const lines = text.split("\n");
|
|
359
|
+
const out: string[] = [];
|
|
360
|
+
let replaced = false;
|
|
361
|
+
let changed = false;
|
|
362
|
+
let i = 0;
|
|
363
|
+
while (i < lines.length) {
|
|
364
|
+
const first = lines[i]!.match(LIST_ITEM_RE);
|
|
365
|
+
if (!first) {
|
|
366
|
+
out.push(lines[i]!);
|
|
367
|
+
i++;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
// Collect a run of consecutive list items.
|
|
371
|
+
let j = i;
|
|
372
|
+
const items: string[] = [];
|
|
373
|
+
while (j < lines.length) {
|
|
374
|
+
const m = lines[j]!.match(LIST_ITEM_RE);
|
|
375
|
+
if (!m) break;
|
|
376
|
+
items.push(m[1]!);
|
|
377
|
+
j++;
|
|
378
|
+
}
|
|
379
|
+
const isLastRun = j === lines.length;
|
|
380
|
+
const complete = isLastRun && streaming ? items.slice(0, -1) : items;
|
|
381
|
+
const hits = complete.filter((it) => {
|
|
382
|
+
const n = normalise(it);
|
|
383
|
+
return keys.some((k) => n.includes(k));
|
|
384
|
+
}).length;
|
|
385
|
+
const echo = complete.length > 0 && hits >= Math.ceil(complete.length / 2);
|
|
386
|
+
if (echo) {
|
|
387
|
+
if (!replaced && !hasPlaceholder) out.push(DOCS_PLACEHOLDER);
|
|
388
|
+
replaced = true;
|
|
389
|
+
changed = true;
|
|
390
|
+
} else if (isLastRun && streaming && complete.length === 0) {
|
|
391
|
+
// Single unfinished item; can't judge yet. Hold it back if it already
|
|
392
|
+
// looks like the start of a document name, otherwise show it.
|
|
393
|
+
const n = normalise(items[0]!);
|
|
394
|
+
if (n.length >= 3 && keys.some((k) => n.includes(k) || k.startsWith(n))) changed = true;
|
|
395
|
+
else out.push(...lines.slice(i, j));
|
|
396
|
+
} else {
|
|
397
|
+
out.push(...lines.slice(i, j));
|
|
398
|
+
}
|
|
399
|
+
i = j;
|
|
400
|
+
}
|
|
401
|
+
return changed ? out.join("\n") : text;
|
|
402
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// packages/studio/src/chat/chat-state.ts
|
|
2
|
+
//
|
|
3
|
+
// Panel state machine for the global chat. Pure so it can be unit tested.
|
|
4
|
+
|
|
5
|
+
export type PanelMode = "closed" | "open" | "minimized" | "fullscreen";
|
|
6
|
+
export type PanelView = "chat" | "history";
|
|
7
|
+
|
|
8
|
+
export type PanelState = {
|
|
9
|
+
mode: PanelMode;
|
|
10
|
+
view: PanelView;
|
|
11
|
+
/** Mode to return to when leaving fullscreen or un-minimizing. */
|
|
12
|
+
lastOpenMode: "open" | "fullscreen";
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const initialPanelState: PanelState = {
|
|
16
|
+
mode: "closed",
|
|
17
|
+
view: "chat",
|
|
18
|
+
lastOpenMode: "open",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type PanelAction =
|
|
22
|
+
| { type: "toggle" }
|
|
23
|
+
| { type: "open" }
|
|
24
|
+
| { type: "close" }
|
|
25
|
+
| { type: "minimize" }
|
|
26
|
+
| { type: "toggleFullscreen" }
|
|
27
|
+
| { type: "showHistory" }
|
|
28
|
+
| { type: "showChat" };
|
|
29
|
+
|
|
30
|
+
export function panelReducer(state: PanelState, action: PanelAction): PanelState {
|
|
31
|
+
switch (action.type) {
|
|
32
|
+
case "toggle":
|
|
33
|
+
return state.mode === "closed" || state.mode === "minimized"
|
|
34
|
+
? { ...state, mode: state.lastOpenMode }
|
|
35
|
+
: { ...state, mode: "closed" };
|
|
36
|
+
case "open":
|
|
37
|
+
return { ...state, mode: state.lastOpenMode, view: "chat" };
|
|
38
|
+
case "close":
|
|
39
|
+
return { ...state, mode: "closed", view: "chat" };
|
|
40
|
+
case "minimize":
|
|
41
|
+
return state.mode === "closed" ? state : { ...state, mode: "minimized" };
|
|
42
|
+
case "toggleFullscreen": {
|
|
43
|
+
const next = state.mode === "fullscreen" ? "open" : "fullscreen";
|
|
44
|
+
return { ...state, mode: next, lastOpenMode: next };
|
|
45
|
+
}
|
|
46
|
+
case "showHistory":
|
|
47
|
+
return {
|
|
48
|
+
...state,
|
|
49
|
+
view: "history",
|
|
50
|
+
mode: state.mode === "closed" || state.mode === "minimized" ? state.lastOpenMode : state.mode,
|
|
51
|
+
};
|
|
52
|
+
case "showChat":
|
|
53
|
+
return { ...state, view: "chat" };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function isPanelVisible(mode: PanelMode): boolean {
|
|
58
|
+
return mode === "open" || mode === "fullscreen";
|
|
59
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// packages/studio/src/chat/icons.tsx
|
|
2
|
+
type Props = { size?: number };
|
|
3
|
+
|
|
4
|
+
function base(size: number) {
|
|
5
|
+
return {
|
|
6
|
+
width: size,
|
|
7
|
+
height: size,
|
|
8
|
+
viewBox: "0 0 16 16",
|
|
9
|
+
fill: "none",
|
|
10
|
+
"aria-hidden": true as const,
|
|
11
|
+
stroke: "currentColor",
|
|
12
|
+
strokeWidth: 1.25,
|
|
13
|
+
strokeLinecap: "round" as const,
|
|
14
|
+
strokeLinejoin: "round" as const,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function SparkIcon({ size = 14 }: Props) {
|
|
19
|
+
return (
|
|
20
|
+
<svg {...base(size)}>
|
|
21
|
+
<path d="M8 1.8 9.4 6l4.2 1.4-4.2 1.4L8 13l-1.4-4.2L2.4 7.4 6.6 6 8 1.8Z" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function HistoryIcon({ size = 14 }: Props) {
|
|
27
|
+
return (
|
|
28
|
+
<svg {...base(size)}>
|
|
29
|
+
<path d="M2.6 8a5.4 5.4 0 1 0 1.6-3.85" />
|
|
30
|
+
<path d="M2.4 2.6v2.9h2.9" />
|
|
31
|
+
<path d="M8 5v3.2l2.2 1.3" />
|
|
32
|
+
</svg>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function CloseIcon({ size = 14 }: Props) {
|
|
37
|
+
return (
|
|
38
|
+
<svg {...base(size)}>
|
|
39
|
+
<path d="M4 4l8 8M12 4l-8 8" />
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function MinimizeIcon({ size = 14 }: Props) {
|
|
45
|
+
return (
|
|
46
|
+
<svg {...base(size)}>
|
|
47
|
+
<path d="M3.5 11h9" />
|
|
48
|
+
</svg>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function ExpandIcon({ size = 14 }: Props) {
|
|
53
|
+
return (
|
|
54
|
+
<svg {...base(size)}>
|
|
55
|
+
<path d="M9.5 2.5h4v4M13.5 2.5 9 7M6.5 13.5h-4v-4M2.5 13.5 7 9" />
|
|
56
|
+
</svg>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function CollapseIcon({ size = 14 }: Props) {
|
|
61
|
+
return (
|
|
62
|
+
<svg {...base(size)}>
|
|
63
|
+
<path d="M13.5 6.5h-4v-4M9.5 6.5 14 2M2.5 9.5h4v4M6.5 9.5 2 14" />
|
|
64
|
+
</svg>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function SendIcon({ size = 14 }: Props) {
|
|
69
|
+
return (
|
|
70
|
+
<svg {...base(size)}>
|
|
71
|
+
<path d="M8 13V3M3.8 7.2 8 3l4.2 4.2" />
|
|
72
|
+
</svg>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function StopIcon({ size = 14 }: Props) {
|
|
77
|
+
return (
|
|
78
|
+
<svg {...base(size)}>
|
|
79
|
+
<rect x="4" y="4" width="8" height="8" rx="1.5" fill="currentColor" stroke="none" />
|
|
80
|
+
</svg>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function ChevronDownIcon({ size = 12 }: Props) {
|
|
85
|
+
return (
|
|
86
|
+
<svg {...base(size)}>
|
|
87
|
+
<path d="M4 6.5 8 10.5l4-4" />
|
|
88
|
+
</svg>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function PlusIcon({ size = 14 }: Props) {
|
|
93
|
+
return (
|
|
94
|
+
<svg {...base(size)}>
|
|
95
|
+
<path d="M8 3v10M3 8h10" />
|
|
96
|
+
</svg>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function AtIcon({ size = 14 }: Props) {
|
|
101
|
+
return (
|
|
102
|
+
<svg {...base(size)}>
|
|
103
|
+
<circle cx="8" cy="8" r="2.4" />
|
|
104
|
+
<path d="M10.4 8v1.2a1.6 1.6 0 0 0 3.2 0V8A5.6 5.6 0 1 0 11 12.6" />
|
|
105
|
+
</svg>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function CheckIcon({ size = 14 }: Props) {
|
|
110
|
+
return (
|
|
111
|
+
<svg {...base(size)}>
|
|
112
|
+
<path d="M3 8.5 6.5 12 13 4.5" />
|
|
113
|
+
</svg>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function UndoIcon({ size = 14 }: Props) {
|
|
118
|
+
return (
|
|
119
|
+
<svg {...base(size)}>
|
|
120
|
+
<path d="M5.5 4 2.5 7l3 3" />
|
|
121
|
+
<path d="M2.8 7h6.4a3.3 3.3 0 0 1 0 6.6H6" />
|
|
122
|
+
</svg>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function FileIcon({ size = 14 }: Props) {
|
|
127
|
+
return (
|
|
128
|
+
<svg {...base(size)}>
|
|
129
|
+
<path d="M4.5 2.5h5.2L12 4.8V13a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5Z" />
|
|
130
|
+
<path d="M9.5 2.5V5H12" />
|
|
131
|
+
</svg>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function SearchIcon({ size = 14 }: Props) {
|
|
136
|
+
return (
|
|
137
|
+
<svg {...base(size)}>
|
|
138
|
+
<circle cx="7" cy="7" r="4.2" />
|
|
139
|
+
<path d="m10.2 10.2 3.3 3.3" />
|
|
140
|
+
</svg>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function TrashIcon({ size = 14 }: Props) {
|
|
145
|
+
return (
|
|
146
|
+
<svg {...base(size)}>
|
|
147
|
+
<path d="M3 4.5h10M6.5 4.5V3h3v1.5M4.5 4.5l.6 8.5h5.8l.6-8.5" />
|
|
148
|
+
</svg>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function EyeIcon({ size = 14 }: Props) {
|
|
153
|
+
return (
|
|
154
|
+
<svg {...base(size)}>
|
|
155
|
+
<path d="M1.8 8s2.3-4 6.2-4 6.2 4 6.2 4-2.3 4-6.2 4-6.2-4-6.2-4Z" />
|
|
156
|
+
<circle cx="8" cy="8" r="1.8" />
|
|
157
|
+
</svg>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Viewfinder corners, used for the include-open-document toggle. */
|
|
162
|
+
export function FocusIcon({ size = 14 }: Props) {
|
|
163
|
+
return (
|
|
164
|
+
<svg {...base(size)}>
|
|
165
|
+
<path d="M2.5 6V4.5a2 2 0 0 1 2-2H6" />
|
|
166
|
+
<path d="M10 2.5h1.5a2 2 0 0 1 2 2V6" />
|
|
167
|
+
<path d="M13.5 10v1.5a2 2 0 0 1-2 2H10" />
|
|
168
|
+
<path d="M6 13.5H4.5a2 2 0 0 1-2-2V10" />
|
|
169
|
+
</svg>
|
|
170
|
+
);
|
|
171
|
+
}
|