@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.
Files changed (439) hide show
  1. package/dist/chunks/UiKitPage-CgNMOqfE.js +172 -0
  2. package/dist/chunks/WorkspaceRoutes-DgRtdie8.js +87297 -0
  3. package/dist/chunks/abap-Y8Dl9g_6.js +6 -0
  4. package/dist/chunks/actionscript-3-DQVEcJUW.js +6 -0
  5. package/dist/chunks/ada-vP6ak0IW.js +6 -0
  6. package/dist/chunks/andromeeda-BbmzSJq1.js +4 -0
  7. package/dist/chunks/angular-html-DPB4Rf0C.js +32 -0
  8. package/dist/chunks/angular-ts-CTOR2ofC.js +21 -0
  9. package/dist/chunks/apache-BUjz-sD2.js +6 -0
  10. package/dist/chunks/apex-OyTfjCYK.js +6 -0
  11. package/dist/chunks/apl-CjNiDgad.js +16 -0
  12. package/dist/chunks/applescript-BPx7YFFu.js +6 -0
  13. package/dist/chunks/ara-Z2fSOxSw.js +6 -0
  14. package/dist/chunks/asciidoc-C1T9ziH6.js +6 -0
  15. package/dist/chunks/asm-BTWLY5ym.js +6 -0
  16. package/dist/chunks/astro-CFFExE8Q.js +18 -0
  17. package/dist/chunks/aurora-x-BwoVEUWZ.js +4 -0
  18. package/dist/chunks/awk-Fb0P9dkn.js +6 -0
  19. package/dist/chunks/ayu-dark-DJoqd4M9.js +4 -0
  20. package/dist/chunks/ayu-light-BzXEJRJ-.js +4 -0
  21. package/dist/chunks/ayu-mirage-BJ2oZGzi.js +4 -0
  22. package/dist/chunks/ballerina-oZK-YekG.js +6 -0
  23. package/dist/chunks/bat-0FvbqU9S.js +6 -0
  24. package/dist/chunks/beancount-DqJEb89h.js +6 -0
  25. package/dist/chunks/berry-DODBq_Ff.js +6 -0
  26. package/dist/chunks/bibtex-EULQRLY5.js +6 -0
  27. package/dist/chunks/bicep-BorU73w0.js +6 -0
  28. package/dist/chunks/bird2-CIjBSNj3.js +6 -0
  29. package/dist/chunks/blade-D0HyNvCw.js +20 -0
  30. package/dist/chunks/bsl-NkNNwerW.js +8 -0
  31. package/dist/chunks/c-eeMepfLm.js +6 -0
  32. package/dist/chunks/c3-BY5XW5ym.js +6 -0
  33. package/dist/chunks/cadence-02UX7mW8.js +6 -0
  34. package/dist/chunks/cairo-DM6WF2e3.js +8 -0
  35. package/dist/chunks/catppuccin-frappe-D3cH2rXe.js +4 -0
  36. package/dist/chunks/catppuccin-latte-C0LRGUW4.js +4 -0
  37. package/dist/chunks/catppuccin-macchiato-c5wQ11TT.js +4 -0
  38. package/dist/chunks/catppuccin-mocha-WMD6Qvya.js +4 -0
  39. package/dist/chunks/clarity-PKm5CwqM.js +6 -0
  40. package/dist/chunks/clojure-CXJfHrL3.js +6 -0
  41. package/dist/chunks/cmake-BJz8BOTU.js +6 -0
  42. package/dist/chunks/cobol-HihOMwht.js +10 -0
  43. package/dist/chunks/codeowners-Bt9yU6NX.js +6 -0
  44. package/dist/chunks/codeql-DHkodjjI.js +6 -0
  45. package/dist/chunks/coffee-DGBR_nr_.js +8 -0
  46. package/dist/chunks/common-lisp-EVqT9Zhp.js +6 -0
  47. package/dist/chunks/coq-B0L9upzn.js +6 -0
  48. package/dist/chunks/cpp-Cj177cuW.js +18 -0
  49. package/dist/chunks/crystal-CRJyc4G-.js +18 -0
  50. package/dist/chunks/csharp-CCgA4A09.js +6 -0
  51. package/dist/chunks/css-CECN5uSL.js +6 -0
  52. package/dist/chunks/csv-CmYOceLb.js +6 -0
  53. package/dist/chunks/cue-ZzumE7IT.js +6 -0
  54. package/dist/chunks/cypher-jpdmjtA6.js +6 -0
  55. package/dist/chunks/d-CBagWSwH.js +6 -0
  56. package/dist/chunks/dark-plus-pUHDTVV0.js +4 -0
  57. package/dist/chunks/dart-6ObCrKr9.js +6 -0
  58. package/dist/chunks/dashboard-XKKZuJGm.js +1736 -0
  59. package/dist/chunks/dax-CcDT-8dH.js +6 -0
  60. package/dist/chunks/define-studio-BYtYDGLX.js +33 -0
  61. package/dist/chunks/desktop-B93p9R9O.js +6 -0
  62. package/dist/chunks/diff-BxzP2J8R.js +6 -0
  63. package/dist/chunks/docker-CsHqm9tx.js +6 -0
  64. package/dist/chunks/dotenv-BMjVjUL1.js +6 -0
  65. package/dist/chunks/dracula-BtZx2Kac.js +4 -0
  66. package/dist/chunks/dracula-soft-BKa-aqBv.js +4 -0
  67. package/dist/chunks/dream-maker-PAx17jaB.js +6 -0
  68. package/dist/chunks/edge-BYQkOzbF.js +12 -0
  69. package/dist/chunks/elixir-Dh036GMp.js +8 -0
  70. package/dist/chunks/elm-BtNbw_Cd.js +8 -0
  71. package/dist/chunks/emacs-lisp-Cfxdx5D-.js +6 -0
  72. package/dist/chunks/erb-ScVOXVpH.js +10 -0
  73. package/dist/chunks/erlang-DNcDT8Hi.js +8 -0
  74. package/dist/chunks/everforest-dark-DMCBqXCK.js +4 -0
  75. package/dist/chunks/everforest-light-BbXl82Em.js +4 -0
  76. package/dist/chunks/fennel-N4WcXuKx.js +6 -0
  77. package/dist/chunks/fish-BTDEUgqH.js +6 -0
  78. package/dist/chunks/fluent-BMXUxfv1.js +6 -0
  79. package/dist/chunks/fortran-fixed-form-B67KFkXH.js +8 -0
  80. package/dist/chunks/fortran-free-form-CwqYvCLJ.js +6 -0
  81. package/dist/chunks/fsharp-CYOiIVNx.js +8 -0
  82. package/dist/chunks/gdresource-CvCIUjhi.js +10 -0
  83. package/dist/chunks/gdscript-DSAxJ_aS.js +6 -0
  84. package/dist/chunks/gdshader-Bk8fF6yr.js +6 -0
  85. package/dist/chunks/genie-DzUvd7U9.js +6 -0
  86. package/dist/chunks/gherkin-DHRaV0YP.js +6 -0
  87. package/dist/chunks/git-commit-Bd32YZ0K.js +8 -0
  88. package/dist/chunks/git-rebase-ZWUFO_T4.js +8 -0
  89. package/dist/chunks/github-dark-DenFmJkN.js +4 -0
  90. package/dist/chunks/github-dark-default-BJPUVz4H.js +4 -0
  91. package/dist/chunks/github-dark-dimmed-DUshB20C.js +4 -0
  92. package/dist/chunks/github-dark-high-contrast-D3aGCnF8.js +4 -0
  93. package/dist/chunks/github-light-JYsPkUQd.js +4 -0
  94. package/dist/chunks/github-light-default-D99KPAby.js +4 -0
  95. package/dist/chunks/github-light-high-contrast-BbmZE-Mp.js +4 -0
  96. package/dist/chunks/gleam-Bv284lvN.js +6 -0
  97. package/dist/chunks/glimmer-js-DkxNrfPT.js +14 -0
  98. package/dist/chunks/glimmer-ts-D6YZEmK_.js +14 -0
  99. package/dist/chunks/glsl-CkUcVZNK.js +8 -0
  100. package/dist/chunks/gn-XtJxiLa4.js +6 -0
  101. package/dist/chunks/gnuplot-yPG9-sE_.js +6 -0
  102. package/dist/chunks/go-CvFNMTvu.js +6 -0
  103. package/dist/chunks/graphql-CBu2ehBn.js +14 -0
  104. package/dist/chunks/groovy-CJQTphOW.js +6 -0
  105. package/dist/chunks/gruvbox-dark-hard-C5HOtKIh.js +4 -0
  106. package/dist/chunks/gruvbox-dark-medium-FVgwJHuz.js +4 -0
  107. package/dist/chunks/gruvbox-dark-soft-B46F314v.js +4 -0
  108. package/dist/chunks/gruvbox-light-hard-CJD38wDZ.js +4 -0
  109. package/dist/chunks/gruvbox-light-medium-BlIhMYTA.js +4 -0
  110. package/dist/chunks/gruvbox-light-soft-DoPHyLVZ.js +4 -0
  111. package/dist/chunks/hack-ItRNKiEb.js +10 -0
  112. package/dist/chunks/haml-NZLVzu_R.js +10 -0
  113. package/dist/chunks/handlebars-mxStpSE7.js +14 -0
  114. package/dist/chunks/haskell-WeIwNIP6.js +6 -0
  115. package/dist/chunks/haxe-TztHsm5T.js +6 -0
  116. package/dist/chunks/hcl-D438OF-I.js +6 -0
  117. package/dist/chunks/hjson-DYBUbqOl.js +6 -0
  118. package/dist/chunks/hlsl-Bk8TCZNL.js +6 -0
  119. package/dist/chunks/horizon-CJQ10nlf.js +4 -0
  120. package/dist/chunks/horizon-bright-CdkBZOMB.js +4 -0
  121. package/dist/chunks/houston-BDYrDoDW.js +4 -0
  122. package/dist/chunks/html-B50bmoVb.js +10 -0
  123. package/dist/chunks/html-derivative-C6bxFbFW.js +8 -0
  124. package/dist/chunks/http-DLvAkeD-.js +14 -0
  125. package/dist/chunks/hurl-CgkFN90t.js +12 -0
  126. package/dist/chunks/hxml-GbqSQCLa.js +8 -0
  127. package/dist/chunks/hy-Brt5EZ7-.js +6 -0
  128. package/dist/chunks/imba-CimUv-Uh.js +6 -0
  129. package/dist/chunks/index-CFAIR8GH.js +18924 -0
  130. package/dist/chunks/ini-BZIuRIvJ.js +6 -0
  131. package/dist/chunks/java-DY6VlHhP.js +6 -0
  132. package/dist/chunks/javascript-C25yR2R2.js +6 -0
  133. package/dist/chunks/jinja-CFW5uAwY.js +11 -0
  134. package/dist/chunks/jison-DdreslRX.js +8 -0
  135. package/dist/chunks/json-DxJze_jm.js +6 -0
  136. package/dist/chunks/json5-BT4Fjg39.js +6 -0
  137. package/dist/chunks/jsonc-CHjZD8gR.js +6 -0
  138. package/dist/chunks/jsonl-BGuvDmy9.js +6 -0
  139. package/dist/chunks/jsonnet-Bx2cfsXg.js +6 -0
  140. package/dist/chunks/jssm-BcADi6EI.js +6 -0
  141. package/dist/chunks/jsx-BtKADgXT.js +6 -0
  142. package/dist/chunks/julia-CZiKXNNP.js +16 -0
  143. package/dist/chunks/just-CEK2SJ_k.js +18 -0
  144. package/dist/chunks/kanagawa-dragon-CiKur4Hl.js +4 -0
  145. package/dist/chunks/kanagawa-lotus-BKu-smKu.js +4 -0
  146. package/dist/chunks/kanagawa-wave-CQwozSzG.js +4 -0
  147. package/dist/chunks/kdl-BNOv9TC3.js +6 -0
  148. package/dist/chunks/kotlin-ByBMgTeR.js +6 -0
  149. package/dist/chunks/kusto-DH_XitHS.js +6 -0
  150. package/dist/chunks/laserwave-6a00oqik.js +4 -0
  151. package/dist/chunks/latex-B8Egmqc5.js +8 -0
  152. package/dist/chunks/lean-BnVxaZxY.js +6 -0
  153. package/dist/chunks/less-B1GLI2Di.js +6 -0
  154. package/dist/chunks/light-plus-CZuVqSLX.js +4 -0
  155. package/dist/chunks/liquid-CHzkx3jT.js +14 -0
  156. package/dist/chunks/llvm-Bk2XctFf.js +6 -0
  157. package/dist/chunks/log-Al8wyEFV.js +6 -0
  158. package/dist/chunks/logo-DBa4JDzV.js +6 -0
  159. package/dist/chunks/lua-Bp3IRv7I.js +8 -0
  160. package/dist/chunks/luau-UQyhudEE.js +6 -0
  161. package/dist/chunks/make-CsMclxtr.js +6 -0
  162. package/dist/chunks/markdown-CrScaQ96.js +6 -0
  163. package/dist/chunks/marko-CJBA24Ri.js +14 -0
  164. package/dist/chunks/material-theme-D6KBX41T.js +4 -0
  165. package/dist/chunks/material-theme-darker-CkRroheE.js +4 -0
  166. package/dist/chunks/material-theme-lighter-BUBw43Yz.js +4 -0
  167. package/dist/chunks/material-theme-ocean-ClGX14Ja.js +4 -0
  168. package/dist/chunks/material-theme-palenight-C1RVm8K1.js +4 -0
  169. package/dist/chunks/matlab-BOAaUVP0.js +6 -0
  170. package/dist/chunks/mdc-C9YSUwVC.js +12 -0
  171. package/dist/chunks/mdx-BOhZZUJ8.js +6 -0
  172. package/dist/chunks/mermaid-C3rz4dYh.js +6 -0
  173. package/dist/chunks/min-dark-C7ak0t6c.js +4 -0
  174. package/dist/chunks/min-light-CKFxVcPp.js +4 -0
  175. package/dist/chunks/mipsasm-CTx18fBl.js +6 -0
  176. package/dist/chunks/mojo-EIZIZRTQ.js +6 -0
  177. package/dist/chunks/monokai-C1KBYcO0.js +4 -0
  178. package/dist/chunks/moonbit-B1TV0fG3.js +6 -0
  179. package/dist/chunks/move-Db4ltDq1.js +6 -0
  180. package/dist/chunks/narrat-DmhDCBs-.js +6 -0
  181. package/dist/chunks/nextflow-47kAzSl1.js +8 -0
  182. package/dist/chunks/nextflow-groovy-Drz_2V0j.js +6 -0
  183. package/dist/chunks/nginx-D7O4WoB1.js +8 -0
  184. package/dist/chunks/night-owl-Bm2rzalh.js +4 -0
  185. package/dist/chunks/night-owl-light-CBI5u5kw.js +4 -0
  186. package/dist/chunks/nim-QAjRdh9w.js +20 -0
  187. package/dist/chunks/nix-fTRXMGic.js +9 -0
  188. package/dist/chunks/nord-CC5OiUXg.js +4 -0
  189. package/dist/chunks/nushell-BGCPRlV5.js +6 -0
  190. package/dist/chunks/objective-c-Itk8tzmv.js +6 -0
  191. package/dist/chunks/objective-cpp-DGt5UKRO.js +6 -0
  192. package/dist/chunks/ocaml-eSVK32Eg.js +6 -0
  193. package/dist/chunks/odin-BItnF517.js +6 -0
  194. package/dist/chunks/one-dark-pro-DTA3VF0_.js +4 -0
  195. package/dist/chunks/one-light-LkMrt1Cf.js +4 -0
  196. package/dist/chunks/openscad-Sp5uZ6f2.js +6 -0
  197. package/dist/chunks/pascal-xy8pJNns.js +6 -0
  198. package/dist/chunks/perl-DkVrgJBg.js +16 -0
  199. package/dist/chunks/php-BIzsF_5x.js +18 -0
  200. package/dist/chunks/pkl-C-zSNmaA.js +6 -0
  201. package/dist/chunks/plastic-CSTz3KZp.js +4 -0
  202. package/dist/chunks/plsql-pVbGZfOv.js +6 -0
  203. package/dist/chunks/po-BNfHvqmm.js +6 -0
  204. package/dist/chunks/poimandres-C-VADXHD.js +4 -0
  205. package/dist/chunks/polar-CAZahv3u.js +6 -0
  206. package/dist/chunks/postcss-05aHdL-n.js +6 -0
  207. package/dist/chunks/powerquery-DI9HkTvs.js +6 -0
  208. package/dist/chunks/powershell-Clc4ydu-.js +6 -0
  209. package/dist/chunks/prisma-FZjmVtSl.js +6 -0
  210. package/dist/chunks/prolog-C5-yg4TO.js +6 -0
  211. package/dist/chunks/proto-C7QgialS.js +6 -0
  212. package/dist/chunks/pug-XOw9Q1Ho.js +12 -0
  213. package/dist/chunks/puppet-CUJHmnxV.js +6 -0
  214. package/dist/chunks/purescript-rUfGld-4.js +6 -0
  215. package/dist/chunks/python-BFNSHbwJ.js +6 -0
  216. package/dist/chunks/qml-rTNXsquU.js +8 -0
  217. package/dist/chunks/qmldir-BInDYbpo.js +6 -0
  218. package/dist/chunks/qss-AeJTysr_.js +6 -0
  219. package/dist/chunks/r-CSmzDPi7.js +6 -0
  220. package/dist/chunks/racket-B83wSAja.js +6 -0
  221. package/dist/chunks/raku-nEQ4ZJJ7.js +6 -0
  222. package/dist/chunks/razor-Ci8D0dQK.js +10 -0
  223. package/dist/chunks/red-7y8PH7HH.js +4 -0
  224. package/dist/chunks/reg-m_s_Kiip.js +6 -0
  225. package/dist/chunks/regexp-BazyLpPg.js +6 -0
  226. package/dist/chunks/rel-BcRfyd6Q.js +6 -0
  227. package/dist/chunks/riscv-Ce8MAQLP.js +6 -0
  228. package/dist/chunks/ron-DCEEQypA.js +6 -0
  229. package/dist/chunks/rose-pine-BKc3yVeu.js +4 -0
  230. package/dist/chunks/rose-pine-dawn-BulJcPZT.js +4 -0
  231. package/dist/chunks/rose-pine-moon-j6jiXKV8.js +4 -0
  232. package/dist/chunks/rosmsg-Cz0w1km8.js +6 -0
  233. package/dist/chunks/rst-DM32Wy3O.js +22 -0
  234. package/dist/chunks/ruby-BeqCs27h.js +30 -0
  235. package/dist/chunks/rust-CLzF9zIN.js +6 -0
  236. package/dist/chunks/sas-fpEvgATw.js +8 -0
  237. package/dist/chunks/sass-DxHp5rTx.js +6 -0
  238. package/dist/chunks/scala-D4grkFkl.js +6 -0
  239. package/dist/chunks/scheme-BCRWuEm4.js +6 -0
  240. package/dist/chunks/scss-B1FaCqwR.js +8 -0
  241. package/dist/chunks/sdbl-B7T8abf4.js +6 -0
  242. package/dist/chunks/shaderlab-Cr62-Wb4.js +8 -0
  243. package/dist/chunks/shellscript-InADTalH.js +6 -0
  244. package/dist/chunks/shellsession-DiDJNQdy.js +8 -0
  245. package/dist/chunks/slack-dark-i7wN4OET.js +4 -0
  246. package/dist/chunks/slack-ochin-ndHf0LoP.js +4 -0
  247. package/dist/chunks/smalltalk-BlI1_OkM.js +6 -0
  248. package/dist/chunks/snazzy-light-BlSJXAu4.js +4 -0
  249. package/dist/chunks/solarized-dark-UTmkh7lw.js +4 -0
  250. package/dist/chunks/solarized-light-BheCkDPT.js +4 -0
  251. package/dist/chunks/solidity-DUWnFhS6.js +6 -0
  252. package/dist/chunks/soy-C1-6njHa.js +8 -0
  253. package/dist/chunks/sparql-B0KWFEri.js +8 -0
  254. package/dist/chunks/splunk-CRXR8A9s.js +6 -0
  255. package/dist/chunks/sql-Cn_v3PB0.js +6 -0
  256. package/dist/chunks/ssh-config-DP-hNVbF.js +6 -0
  257. package/dist/chunks/stata-B8c5fTjX.js +8 -0
  258. package/dist/chunks/stylus-CyKEU1Ej.js +6 -0
  259. package/dist/chunks/surrealql-CQi7GQN7.js +8 -0
  260. package/dist/chunks/svelte-rYri41WT.js +14 -0
  261. package/dist/chunks/swift-DJpUqPLg.js +6 -0
  262. package/dist/chunks/synthwave-84-NU3C_KFZ.js +4 -0
  263. package/dist/chunks/system-verilog-BCm7smPJ.js +6 -0
  264. package/dist/chunks/systemd-C-4qm6XH.js +6 -0
  265. package/dist/chunks/talonscript-CFF3LF_O.js +6 -0
  266. package/dist/chunks/tasl-Cg_WBUAe.js +6 -0
  267. package/dist/chunks/tcl-DN7buRTF.js +6 -0
  268. package/dist/chunks/templ-Crs9Ui8q.js +12 -0
  269. package/dist/chunks/terraform-DGvcn9zM.js +6 -0
  270. package/dist/chunks/tex-AHxmLTL0.js +8 -0
  271. package/dist/chunks/tokyo-night-DP4TmcQl.js +4 -0
  272. package/dist/chunks/toml-DY62mUL_.js +6 -0
  273. package/dist/chunks/ts-tags-ioVe2PKw.js +41 -0
  274. package/dist/chunks/tsv-BtvSkaG0.js +6 -0
  275. package/dist/chunks/tsx-B8rCNbgL.js +6 -0
  276. package/dist/chunks/turtle-_H59FV7D.js +6 -0
  277. package/dist/chunks/twig-DwV2Fi4L.js +18 -0
  278. package/dist/chunks/typescript-RycA9KXf.js +6 -0
  279. package/dist/chunks/typespec-DWqp-kUU.js +6 -0
  280. package/dist/chunks/typst-D_1QKWns.js +6 -0
  281. package/dist/chunks/v-BPCNiyYe.js +6 -0
  282. package/dist/chunks/vala-uxaPR7d1.js +6 -0
  283. package/dist/chunks/vb-D8_c5-KN.js +6 -0
  284. package/dist/chunks/verilog-B-bybjPF.js +6 -0
  285. package/dist/chunks/vesper-BckBta1U.js +4 -0
  286. package/dist/chunks/vhdl-CUlNa8ac.js +6 -0
  287. package/dist/chunks/viml-DsfA-sWm.js +6 -0
  288. package/dist/chunks/vitesse-black-BoGvW84i.js +4 -0
  289. package/dist/chunks/vitesse-dark-Cym-eLtO.js +4 -0
  290. package/dist/chunks/vitesse-light-CcmG315c.js +4 -0
  291. package/dist/chunks/vue-DXEmyXn9.js +31 -0
  292. package/dist/chunks/vue-html-j9xNEdu3.js +8 -0
  293. package/dist/chunks/vue-vine-D41-N99t.js +18 -0
  294. package/dist/chunks/vyper-CPQuu50u.js +6 -0
  295. package/dist/chunks/wasm-BBXxrAl7.js +6 -0
  296. package/dist/chunks/wasm-DQxwEHae.js +7 -0
  297. package/dist/chunks/wenyan-pbVjoM9_.js +6 -0
  298. package/dist/chunks/wgsl-DY4iK1q1.js +6 -0
  299. package/dist/chunks/wikitext-Z-MoUasO.js +6 -0
  300. package/dist/chunks/wit-CQMQOlTg.js +6 -0
  301. package/dist/chunks/wolfram-Dz4KXISs.js +6 -0
  302. package/dist/chunks/xml-C2J0sS9M.js +8 -0
  303. package/dist/chunks/xsl-BmSZphgC.js +8 -0
  304. package/dist/chunks/yaml-DaO7k5B1.js +6 -0
  305. package/dist/chunks/zenscript-CxBjpf9c.js +6 -0
  306. package/dist/chunks/zig-Vm0PO9wB.js +6 -0
  307. package/dist/dashboard.js +8 -0
  308. package/dist/define.js +4 -0
  309. package/dist/index.js +317 -0
  310. package/dist/studio.css +2272 -0
  311. package/dist/styles.js +1 -0
  312. package/package.json +88 -0
  313. package/prose-node-shims/fs.ts +6 -0
  314. package/prose-node-shims/path.ts +16 -0
  315. package/prose-node-shims/sharp.ts +4 -0
  316. package/prose-ui-node-shim.ts +26 -0
  317. package/src/App.tsx +173 -0
  318. package/src/WorkspaceRoutes.tsx +38 -0
  319. package/src/api.ts +833 -0
  320. package/src/auth-client.ts +15 -0
  321. package/src/chat/ChangeCard.tsx +330 -0
  322. package/src/chat/ChatBar.tsx +238 -0
  323. package/src/chat/ChatMessage.tsx +478 -0
  324. package/src/chat/ChatPanel.tsx +406 -0
  325. package/src/chat/ChatProvider.tsx +661 -0
  326. package/src/chat/Composer.tsx +461 -0
  327. package/src/chat/DocListCard.tsx +322 -0
  328. package/src/chat/LiveStatus.tsx +83 -0
  329. package/src/chat/MentionPopover.tsx +164 -0
  330. package/src/chat/ModelPicker.tsx +113 -0
  331. package/src/chat/Shimmer.tsx +39 -0
  332. package/src/chat/ThreadHistory.tsx +268 -0
  333. package/src/chat/chat-context.ts +41 -0
  334. package/src/chat/chat-helpers.ts +402 -0
  335. package/src/chat/chat-state.ts +59 -0
  336. package/src/chat/icons.tsx +171 -0
  337. package/src/chat/useStickToBottom.ts +110 -0
  338. package/src/chrome/AppShell.tsx +100 -0
  339. package/src/chrome/AppSidebar.tsx +305 -0
  340. package/src/chrome/CommandPalette.tsx +559 -0
  341. package/src/chrome/ContentApiPopover.tsx +215 -0
  342. package/src/chrome/EditorActionsContext.tsx +63 -0
  343. package/src/chrome/EditorFocusContext.tsx +80 -0
  344. package/src/chrome/EditorStatusContext.tsx +84 -0
  345. package/src/chrome/NavItem.tsx +75 -0
  346. package/src/chrome/OpenSiteButton.tsx +148 -0
  347. package/src/chrome/StatusBar.tsx +73 -0
  348. package/src/chrome/UserMenu.tsx +130 -0
  349. package/src/chrome/WorkspaceNavContext.tsx +60 -0
  350. package/src/chrome/WorkspaceStatusBar.tsx +940 -0
  351. package/src/chrome/content-api-url.ts +111 -0
  352. package/src/chrome/content-type-groups.ts +32 -0
  353. package/src/chrome/hotkeys.ts +79 -0
  354. package/src/chrome/icons.tsx +191 -0
  355. package/src/chrome/open-site.ts +30 -0
  356. package/src/chrome/status-bar-action.ts +24 -0
  357. package/src/components/AppChromeContext.tsx +32 -0
  358. package/src/components/Shell.tsx +18 -0
  359. package/src/components/WorkspaceSwitcher.tsx +345 -0
  360. package/src/content-blocks/index.tsx +262 -0
  361. package/src/css.d.ts +2 -0
  362. package/src/dashboard.ts +5 -0
  363. package/src/define-studio.tsx +98 -0
  364. package/src/define.ts +12 -0
  365. package/src/index.css +123 -0
  366. package/src/index.ts +19 -0
  367. package/src/last-user.ts +40 -0
  368. package/src/last-workspace.ts +57 -0
  369. package/src/mcp/McpConnectorsPanel.tsx +872 -0
  370. package/src/mcp/parse-json.ts +52 -0
  371. package/src/pages/AcceptInvitePage.tsx +99 -0
  372. package/src/pages/BlockSettingsDialog.tsx +303 -0
  373. package/src/pages/BlocksList.tsx +402 -0
  374. package/src/pages/BodyProse.tsx +236 -0
  375. package/src/pages/DocEditorPage.tsx +1394 -0
  376. package/src/pages/EmbedChatPage.tsx +110 -0
  377. package/src/pages/EmbedPublishPage.tsx +259 -0
  378. package/src/pages/EnterPage.tsx +96 -0
  379. package/src/pages/GithubConnectPage.tsx +142 -0
  380. package/src/pages/LegacyWorkspaceRedirect.tsx +57 -0
  381. package/src/pages/LoginPage.tsx +335 -0
  382. package/src/pages/NewWorkspacePage.tsx +74 -0
  383. package/src/pages/RenderedBody.tsx +598 -0
  384. package/src/pages/ResumeHome.tsx +18 -0
  385. package/src/pages/SelectionBar.tsx +277 -0
  386. package/src/pages/SettingsGeneralPage.tsx +403 -0
  387. package/src/pages/SettingsGithubPage.tsx +277 -0
  388. package/src/pages/SettingsLayout.tsx +127 -0
  389. package/src/pages/SettingsMcpPage.tsx +30 -0
  390. package/src/pages/SettingsSchemaPage.tsx +93 -0
  391. package/src/pages/SettingsUsersPage.tsx +363 -0
  392. package/src/pages/TypeDocsPage.tsx +225 -0
  393. package/src/pages/UiKitPage.tsx +331 -0
  394. package/src/pages/UserSettingsPage.tsx +94 -0
  395. package/src/pages/WorkspaceHomeRedirect.tsx +17 -0
  396. package/src/pages/WorkspaceLayout.tsx +103 -0
  397. package/src/pages/WorkspacesPage.tsx +35 -0
  398. package/src/pages/blocks-list.ts +73 -0
  399. package/src/pages/body-blocks.ts +233 -0
  400. package/src/pages/body-mdx.tsx +82 -0
  401. package/src/pages/compile-body.ts +37 -0
  402. package/src/pages/host-env.ts +22 -0
  403. package/src/pages/html-to-markdown.ts +99 -0
  404. package/src/pages/preview-bridge.ts +46 -0
  405. package/src/pages/preview-split.ts +30 -0
  406. package/src/pages/preview-url.ts +92 -0
  407. package/src/pages/selection-format.ts +179 -0
  408. package/src/pages/type-docs.ts +9 -0
  409. package/src/replica/BootstrapSplash.tsx +80 -0
  410. package/src/replica/ReplicaProvider.tsx +267 -0
  411. package/src/replica/cache.ts +30 -0
  412. package/src/replica/idb.ts +89 -0
  413. package/src/replica/instance.ts +39 -0
  414. package/src/replica/pull.ts +23 -0
  415. package/src/replica/store.ts +497 -0
  416. package/src/replica/sync-cache.ts +51 -0
  417. package/src/replica/types.ts +119 -0
  418. package/src/replica/useReplica.ts +13 -0
  419. package/src/site.tsx +230 -0
  420. package/src/studio-config.ts +81 -0
  421. package/src/styles/themes.ts +49 -0
  422. package/src/styles/tokens.stylex.ts +58 -0
  423. package/src/styles.ts +11 -0
  424. package/src/theme/AccentOverride.tsx +29 -0
  425. package/src/theme/ThemeProvider.tsx +130 -0
  426. package/src/theme/apply-theme-class.ts +12 -0
  427. package/src/ui/BackLink.tsx +28 -0
  428. package/src/ui/Button.tsx +167 -0
  429. package/src/ui/ConfirmDialog.tsx +141 -0
  430. package/src/ui/Field.tsx +45 -0
  431. package/src/ui/Input.tsx +183 -0
  432. package/src/ui/ListRow.tsx +88 -0
  433. package/src/ui/Page.tsx +151 -0
  434. package/src/ui/Text.tsx +77 -0
  435. package/src/ui/Tooltip.tsx +85 -0
  436. package/src/ui/popup.ts +216 -0
  437. package/src/workspace/permissions.ts +17 -0
  438. package/src/workspace/types.ts +12 -0
  439. package/src/workspace-path.ts +98 -0
package/src/api.ts ADDED
@@ -0,0 +1,833 @@
1
+ // packages/studio/src/api.ts
2
+ import { encodeDocLocale } from "@backmatter/core";
3
+ import { replicaQuery, type ReplicaCache } from "./replica/cache";
4
+ import type { ReplicaBody } from "./replica/types";
5
+ import { apiCredentials, apiHeaders, apiUrl, reportUnauthorized } from "./studio-config";
6
+
7
+ export type ApiError = Error & {
8
+ status: number;
9
+ code?: string;
10
+ details?: string[];
11
+ };
12
+
13
+ async function api<T>(path: string, init?: RequestInit): Promise<T> {
14
+ const res = await fetch(apiUrl(path), {
15
+ credentials: apiCredentials(),
16
+ ...init,
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ ...apiHeaders(),
20
+ ...(init?.headers ?? {}),
21
+ },
22
+ });
23
+ if (!res.ok) {
24
+ const body = (await res.json().catch(() => ({}))) as {
25
+ error?: string;
26
+ details?: string[];
27
+ };
28
+ if (res.status === 401 || res.status === 426) reportUnauthorized(res.status, body.error);
29
+ const code = body.error ?? `request failed: ${res.status}`;
30
+ const detailSuffix =
31
+ Array.isArray(body.details) && body.details.length > 0
32
+ ? `: ${body.details.join(", ")}`
33
+ : "";
34
+ const err = new Error(`${code}${detailSuffix}`) as ApiError;
35
+ err.status = res.status;
36
+ err.code = body.error;
37
+ err.details = body.details;
38
+ throw err;
39
+ }
40
+ return res.json() as Promise<T>;
41
+ }
42
+
43
+ export async function fetchAuthConfig() {
44
+ const res = await fetch(apiUrl("/auth/config"));
45
+ if (!res.ok) throw new Error(`auth config failed: ${res.status}`);
46
+ return res.json() as Promise<{ github: boolean }>;
47
+ }
48
+
49
+ export async function fetchMe() {
50
+ const res = await fetch(apiUrl("/me"), { credentials: apiCredentials(), headers: apiHeaders() });
51
+ if (res.status === 401) return null;
52
+ if (res.status === 426) {
53
+ const body = (await res.json().catch(() => ({}))) as { error?: string };
54
+ reportUnauthorized(426, body.error);
55
+ return null;
56
+ }
57
+ if (!res.ok) throw new Error(`me failed: ${res.status}`);
58
+ return res.json() as Promise<{
59
+ user: {
60
+ id: string;
61
+ email: string;
62
+ name: string;
63
+ };
64
+ }>;
65
+ }
66
+
67
+ export function createWorkspace(name: string) {
68
+ return api<{
69
+ workspace: {
70
+ id: string;
71
+ name: string;
72
+ slug: string;
73
+ role: string;
74
+ };
75
+ }>("/workspaces", {
76
+ method: "POST",
77
+ body: JSON.stringify({ name }),
78
+ });
79
+ }
80
+
81
+ export type WorkspaceSummary = {
82
+ id: string;
83
+ name: string;
84
+ slug: string;
85
+ role: string;
86
+ };
87
+
88
+ export type WorkspaceMember = {
89
+ userId: string;
90
+ role: string;
91
+ name: string;
92
+ email: string;
93
+ };
94
+
95
+ export type WorkspaceInvitation = {
96
+ id: string;
97
+ email: string;
98
+ role: string;
99
+ expiresAt: string;
100
+ };
101
+
102
+ export function fetchWorkspacePeople(workspaceId: string) {
103
+ return api<{ members: WorkspaceMember[]; invitations: WorkspaceInvitation[] }>(
104
+ `/workspaces/${workspaceId}/members`,
105
+ );
106
+ }
107
+
108
+ export function inviteWorkspaceMember(
109
+ workspaceId: string,
110
+ input: { email: string; role: string },
111
+ ) {
112
+ return api<{ ok: true }>(`/workspaces/${workspaceId}/invitations`, {
113
+ method: "POST",
114
+ body: JSON.stringify(input),
115
+ });
116
+ }
117
+
118
+ export function changeWorkspaceMemberRole(
119
+ workspaceId: string,
120
+ userId: string,
121
+ role: string,
122
+ ) {
123
+ return api<{ ok: true }>(`/workspaces/${workspaceId}/members/${userId}`, {
124
+ method: "PATCH",
125
+ body: JSON.stringify({ role }),
126
+ });
127
+ }
128
+
129
+ export function removeWorkspaceMember(workspaceId: string, userId: string) {
130
+ return api<{ ok: true }>(`/workspaces/${workspaceId}/members/${userId}`, {
131
+ method: "DELETE",
132
+ });
133
+ }
134
+
135
+ export function revokeWorkspaceInvitation(workspaceId: string, invitationId: string) {
136
+ return api<{ ok: true }>(
137
+ `/workspaces/${workspaceId}/invitations/${invitationId}`,
138
+ { method: "DELETE" },
139
+ );
140
+ }
141
+
142
+ export function acceptWorkspaceInvitation(token: string) {
143
+ return api<{ ok: true; workspaceId: string; slug: string }>("/invitations/accept", {
144
+ method: "POST",
145
+ body: JSON.stringify({ token }),
146
+ });
147
+ }
148
+
149
+ export function fetchAllWorkspaces() {
150
+ return api<{ workspaces: WorkspaceSummary[] }>("/workspaces");
151
+ }
152
+
153
+ export type WorkspaceDetail = {
154
+ id: string;
155
+ name: string;
156
+ slug: string;
157
+ role: string;
158
+ githubInstallationId: string | null;
159
+ githubRepoFullName: string | null;
160
+ githubDefaultBranch: string | null;
161
+ contentRoot: string | null;
162
+ githubDirectory: string | null;
163
+ locales: string[];
164
+ siteUrls: string[];
165
+ /** Last `@backmatter/studio` version each site reported, keyed by origin. */
166
+ studioVersions?: Record<string, { version: string; seenAt: string }>;
167
+ hasContentToken: boolean;
168
+ hasPreviewToken: boolean;
169
+ hasHostToken: boolean;
170
+ };
171
+
172
+ export function fetchWorkspace(workspaceId: string) {
173
+ return api<{ workspace: WorkspaceDetail }>(`/workspaces/${workspaceId}`);
174
+ }
175
+
176
+ export function fetchReplica(workspaceId: string, cache?: ReplicaCache) {
177
+ const query = cache ? `?${replicaQuery(cache)}` : "";
178
+ return api<ReplicaBody>(`/workspaces/${workspaceId}/replica${query}`);
179
+ }
180
+
181
+ export function patchWorkspace(
182
+ workspaceId: string,
183
+ input: { siteUrls: string[] },
184
+ ) {
185
+ return api<{ workspace: WorkspaceDetail }>(`/workspaces/${workspaceId}`, {
186
+ method: "PATCH",
187
+ body: JSON.stringify(input),
188
+ });
189
+ }
190
+
191
+ export function rotateContentToken(workspaceId: string) {
192
+ return api<{
193
+ token: string;
194
+ workspace: {
195
+ id: string;
196
+ hasContentToken: boolean;
197
+ hasPreviewToken: boolean;
198
+ hasHostToken: boolean;
199
+ };
200
+ }>(`/workspaces/${workspaceId}/content-token`, { method: "POST" });
201
+ }
202
+
203
+ export function rotatePreviewToken(workspaceId: string) {
204
+ return api<{
205
+ token: string;
206
+ workspace: {
207
+ id: string;
208
+ hasContentToken: boolean;
209
+ hasPreviewToken: boolean;
210
+ hasHostToken: boolean;
211
+ };
212
+ }>(`/workspaces/${workspaceId}/preview-token`, { method: "POST" });
213
+ }
214
+
215
+ export function rotateHostToken(workspaceId: string) {
216
+ return api<{
217
+ token: string;
218
+ workspace: {
219
+ id: string;
220
+ hasContentToken: boolean;
221
+ hasPreviewToken: boolean;
222
+ hasHostToken: boolean;
223
+ };
224
+ }>(`/workspaces/${workspaceId}/host-token`, { method: "POST" });
225
+ }
226
+
227
+ export function createPreviewTicket(
228
+ workspaceId: string,
229
+ input: { redirect: string; branch?: string | null; tree?: string | null },
230
+ ) {
231
+ return api<{ ticket: string; expiresInMs: number }>(`/workspaces/${workspaceId}/preview-ticket`, {
232
+ method: "POST",
233
+ body: JSON.stringify({
234
+ redirect: input.redirect,
235
+ ...(input.branch ? { branch: input.branch } : {}),
236
+ ...(input.tree ? { tree: input.tree } : {}),
237
+ }),
238
+ });
239
+ }
240
+
241
+ export function githubInstallUrl(workspaceId: string) {
242
+ return apiUrl(`/workspaces/${workspaceId}/github/install`);
243
+ }
244
+
245
+ export type GithubInstallationChoice = {
246
+ id: string;
247
+ account: string | null;
248
+ };
249
+
250
+ export function attachGithubInstallation(
251
+ workspaceId: string,
252
+ installationId?: string,
253
+ ) {
254
+ return api<{
255
+ installationId: string | null;
256
+ installations: GithubInstallationChoice[];
257
+ installUrl: string | null;
258
+ }>(`/workspaces/${workspaceId}/github/installation`, {
259
+ method: "POST",
260
+ body: JSON.stringify(installationId ? { installationId } : {}),
261
+ });
262
+ }
263
+
264
+ export function fetchGithubRepos(workspaceId: string) {
265
+ return api<{
266
+ repos: Array<{ fullName: string; defaultBranch: string }>;
267
+ }>(`/workspaces/${workspaceId}/github/repos`);
268
+ }
269
+
270
+ export function bindGithub(
271
+ workspaceId: string,
272
+ input: {
273
+ repoFullName: string;
274
+ defaultBranch: string;
275
+ githubDirectory?: string | null;
276
+ },
277
+ ) {
278
+ return api<{ workspace: unknown; import: unknown }>(
279
+ `/workspaces/${workspaceId}/github`,
280
+ {
281
+ method: "PUT",
282
+ body: JSON.stringify(input),
283
+ },
284
+ );
285
+ }
286
+
287
+ export function unbindGithub(workspaceId: string) {
288
+ return api<{ workspace: WorkspaceDetail }>(
289
+ `/workspaces/${workspaceId}/github`,
290
+ { method: "DELETE" },
291
+ );
292
+ }
293
+
294
+ export type SchemaField = {
295
+ kind: string;
296
+ required?: boolean;
297
+ relationTo?: string;
298
+ object?: string;
299
+ fields?: Record<string, SchemaField>;
300
+ /** Studio input name from `field.text({ input })`; see `defineStudio`. */
301
+ input?: string;
302
+ };
303
+
304
+ export type BlockSnapshot = {
305
+ name: string;
306
+ fields: Record<string, SchemaField>;
307
+ };
308
+
309
+ export type SchemaPayload = {
310
+ version: number;
311
+ models: Array<{
312
+ type: string;
313
+ path: string;
314
+ route?: string | false;
315
+ singleton?: boolean;
316
+ indexSlug?: string;
317
+ format?: "mdx" | "page";
318
+ fields: Record<string, SchemaField>;
319
+ }>;
320
+ blocks?: BlockSnapshot[];
321
+ /** In-content blocks from the client `richtext/` directory. */
322
+ richtext?: BlockSnapshot[];
323
+ objects?: Array<{ name: string; fields: Record<string, SchemaField> }>;
324
+ };
325
+
326
+ export function fetchSchema(workspaceId: string) {
327
+ return api<{
328
+ schema: {
329
+ id: string;
330
+ version: number;
331
+ branch?: string;
332
+ payload: SchemaPayload;
333
+ } | null;
334
+ branch?: string;
335
+ }>(`/workspaces/${workspaceId}/schema`);
336
+ }
337
+
338
+ /** Uncommitted file state against branch HEAD. */
339
+ export type ChangeStatus = "modified" | "added" | "conflict" | "deleted";
340
+ /** Per-doc status in lists; `clean` = identical to HEAD. */
341
+ export type DocStatus = "clean" | ChangeStatus;
342
+
343
+ export type GitChange = {
344
+ locale: string;
345
+ type: string;
346
+ slug: string;
347
+ status: ChangeStatus;
348
+ };
349
+
350
+ export type GitHead = { sha: string; updatedAt: string };
351
+
352
+ /** `git status` for the current user on their checked-out branch. */
353
+ export type GitState = {
354
+ defaultBranch: string;
355
+ currentBranch: string;
356
+ branches: Array<{ name: string }>;
357
+ /** Set when GitHub could not list branches. Status still returns local changes. */
358
+ githubError?: "installation_missing" | "github_list_failed" | null;
359
+ head: GitHead | null;
360
+ changes: GitChange[];
361
+ };
362
+
363
+ export function countConflicts(state: Pick<GitState, "changes"> | null | undefined) {
364
+ return (state?.changes ?? []).filter((c) => c.status === "conflict").length;
365
+ }
366
+
367
+ export function fetchGit(workspaceId: string) {
368
+ return api<GitState>(`/workspaces/${workspaceId}/git`);
369
+ }
370
+
371
+ export function checkoutBranch(workspaceId: string, branch: string) {
372
+ return api<{ currentBranch: string; import: unknown }>(
373
+ `/workspaces/${workspaceId}/git/checkout`,
374
+ {
375
+ method: "POST",
376
+ body: JSON.stringify({ branch }),
377
+ },
378
+ );
379
+ }
380
+
381
+ export function createGitBranch(
382
+ workspaceId: string,
383
+ input: { name: string; from?: string },
384
+ ) {
385
+ return api<{ currentBranch: string; from: string; import: unknown }>(
386
+ `/workspaces/${workspaceId}/git/branches`,
387
+ {
388
+ method: "POST",
389
+ body: JSON.stringify(input),
390
+ },
391
+ );
392
+ }
393
+
394
+ export type CommitResult = {
395
+ commitSha: string;
396
+ message: string;
397
+ branch: string;
398
+ files: Array<{
399
+ locale: string;
400
+ type: string;
401
+ slug: string;
402
+ githubPath: string;
403
+ githubSha: string;
404
+ }>;
405
+ };
406
+
407
+ /** Commit every uncommitted file in my working tree on the checked-out branch. */
408
+ export function commitWorkspace(workspaceId: string, message?: string) {
409
+ return api<CommitResult>(`/workspaces/${workspaceId}/git/commit`, {
410
+ method: "POST",
411
+ body: JSON.stringify(message?.trim() ? { message: message.trim() } : {}),
412
+ });
413
+ }
414
+
415
+ /** Default commit message the server uses when none is given (mirrored for the placeholder). */
416
+ export function defaultCommitMessage(changes: GitChange[]) {
417
+ if (changes.length === 1) {
418
+ const f = changes[0]!;
419
+ return `backmatter: update ${f.type}/${f.slug} (${f.locale})`;
420
+ }
421
+ return `backmatter: update ${changes.length} files`;
422
+ }
423
+
424
+ /** Something about the workspace (branch, schema, binding) changed; reload everything. */
425
+ export function notifyWorkspaceChanged() {
426
+ window.dispatchEvent(new Event("backmatter:workspace-changed"));
427
+ }
428
+
429
+ export type TreeChangeDetail = {
430
+ workspaceId: string;
431
+ locale: string;
432
+ type: string;
433
+ slug: string;
434
+ status?: DocStatus;
435
+ /** Drop the doc from the local list instead of changing its status. */
436
+ remove?: boolean;
437
+ };
438
+
439
+ /** My working tree changed (save, revert, resolve, commit); refresh statuses. */
440
+ export function notifyTreeChanged(detail?: TreeChangeDetail) {
441
+ window.dispatchEvent(
442
+ new CustomEvent<TreeChangeDetail>("backmatter:tree-changed", { detail }),
443
+ );
444
+ }
445
+
446
+ /** Branch HEAD moved (teammate push, pull, commit); clean docs should reload. */
447
+ export function notifyHeadChanged() {
448
+ window.dispatchEvent(new Event("backmatter:head-changed"));
449
+ }
450
+
451
+ /**
452
+ * A commit just landed. Mark those files clean (or drop deletes) before the
453
+ * replica refetch returns, so the top bar count updates on this frame.
454
+ */
455
+ export function publishCommittedFiles(
456
+ workspaceId: string,
457
+ files: Array<{ locale: string; type: string; slug: string; githubSha: string }>,
458
+ ) {
459
+ for (const file of files) {
460
+ notifyTreeChanged({
461
+ workspaceId,
462
+ locale: file.locale,
463
+ type: file.type,
464
+ slug: file.slug,
465
+ ...(file.githubSha ? { status: "clean" } : { remove: true }),
466
+ });
467
+ }
468
+ notifyHeadChanged();
469
+ }
470
+
471
+ export function putSchema(workspaceId: string, payload: SchemaPayload) {
472
+ return api<{
473
+ schema: { id: string; version: number; payload: SchemaPayload };
474
+ }>(`/workspaces/${workspaceId}/schema`, {
475
+ method: "PUT",
476
+ body: JSON.stringify(payload),
477
+ });
478
+ }
479
+
480
+ export type ImportResult = {
481
+ imported: number;
482
+ deleted: number;
483
+ skipped: number;
484
+ errors: string[];
485
+ headSha: string | null;
486
+ };
487
+
488
+ /** Full re-import of the checked-out branch from GitHub. Recovery for a missed webhook. */
489
+ export async function importContent(workspaceId: string) {
490
+ const body = await api<{ result: ImportResult }>(
491
+ `/workspaces/${workspaceId}/content/import`,
492
+ { method: "POST", signal: AbortSignal.timeout(90_000) },
493
+ );
494
+ if (body.result.errors.length > 0) {
495
+ const err = new Error(body.result.errors.join("; ")) as ApiError;
496
+ err.status = 502;
497
+ err.code = "github_import_failed";
498
+ throw err;
499
+ }
500
+ return body;
501
+ }
502
+
503
+ export const pullFromGithub = importContent;
504
+
505
+ export type DocListItem = {
506
+ locale: string;
507
+ type: string;
508
+ slug: string;
509
+ status: DocStatus;
510
+ };
511
+
512
+ export { encodeDocLocale };
513
+
514
+ export function docApiPath(
515
+ workspaceId: string,
516
+ locale: string,
517
+ type: string,
518
+ slug: string,
519
+ suffix = "",
520
+ ) {
521
+ return `/workspaces/${workspaceId}/docs/${encodeURIComponent(encodeDocLocale(locale))}/${encodeURIComponent(type)}/${encodeURIComponent(slug)}${suffix}`;
522
+ }
523
+
524
+ export function fetchDocs(workspaceId: string) {
525
+ return api<{ docs: DocListItem[] }>(`/workspaces/${workspaceId}/docs`);
526
+ }
527
+
528
+ export function fetchDoc(
529
+ workspaceId: string,
530
+ locale: string,
531
+ type: string,
532
+ slug: string,
533
+ ) {
534
+ return api<{
535
+ doc: {
536
+ locale: string;
537
+ type: string;
538
+ slug: string;
539
+ branch: string;
540
+ /** Working tree owner (my user id), for preview URLs. */
541
+ tree: string;
542
+ source: ContentSource;
543
+ status: DocStatus;
544
+ headSha: string | null;
545
+ frontmatter: Record<string, unknown>;
546
+ body: string;
547
+ raw: string;
548
+ model: {
549
+ type: string;
550
+ path: string;
551
+ format?: "mdx" | "page";
552
+ fields: Record<
553
+ string,
554
+ { kind: string; required?: boolean; relationTo?: string }
555
+ >;
556
+ } | null;
557
+ parseError?: string | null;
558
+ };
559
+ }>(docApiPath(workspaceId, locale, type, slug));
560
+ }
561
+
562
+ /** `head` = committed on the branch; `tree` = my uncommitted working copy. */
563
+ export type ContentSource = "head" | "tree";
564
+
565
+ /** Save into my working tree (autosave). */
566
+ export function saveDoc(
567
+ workspaceId: string,
568
+ locale: string,
569
+ type: string,
570
+ slug: string,
571
+ input: { frontmatter: Record<string, unknown>; body: string },
572
+ ) {
573
+ return api<{
574
+ doc: {
575
+ locale: string;
576
+ type: string;
577
+ slug: string;
578
+ source: "tree";
579
+ status: DocStatus;
580
+ headSha: string | null;
581
+ raw: string;
582
+ };
583
+ }>(docApiPath(workspaceId, locale, type, slug), {
584
+ method: "PUT",
585
+ body: JSON.stringify(input),
586
+ });
587
+ }
588
+
589
+ /** Drop my uncommitted copy; the doc reads from HEAD again. */
590
+ export function revertDoc(
591
+ workspaceId: string,
592
+ locale: string,
593
+ type: string,
594
+ slug: string,
595
+ ) {
596
+ return api<{ ok: true }>(
597
+ docApiPath(workspaceId, locale, type, slug, "/tree"),
598
+ { method: "DELETE" },
599
+ );
600
+ }
601
+
602
+ /** Resolve a conflict: keep mine (rebase onto the new HEAD) or take theirs (revert). */
603
+ export function resolveDoc(
604
+ workspaceId: string,
605
+ locale: string,
606
+ type: string,
607
+ slug: string,
608
+ strategy: "mine" | "theirs",
609
+ ) {
610
+ return api<{ ok: true; status: DocStatus; headSha: string | null }>(
611
+ docApiPath(workspaceId, locale, type, slug, "/resolve"),
612
+ { method: "POST", body: JSON.stringify({ strategy }) },
613
+ );
614
+ }
615
+
616
+ // ---------------------------------------------------------------------------
617
+ // AI chat
618
+ // ---------------------------------------------------------------------------
619
+
620
+ export type AiModelInfo = { id: string; provider: string; label: string };
621
+ export type AiTier = "ask" | "content" | "developer";
622
+
623
+ export function fetchAiModels(workspaceId: string) {
624
+ return api<{ models: AiModelInfo[]; defaultModel: string | null; tier: AiTier }>(
625
+ `/workspaces/${workspaceId}/ai/models`,
626
+ );
627
+ }
628
+
629
+ export type ChatThread = {
630
+ id: string;
631
+ workspaceId: string;
632
+ userId: string;
633
+ branch: string;
634
+ title: string;
635
+ createdAt: string;
636
+ updatedAt: string;
637
+ };
638
+
639
+ export type ChatChangeStatus = "applied" | "kept" | "undone";
640
+
641
+ export type ChatChangeDiff = {
642
+ added: number;
643
+ removed: number;
644
+ renamedFrom?: string;
645
+ hunks: Array<{
646
+ oldStart: number;
647
+ oldLines: number;
648
+ newStart: number;
649
+ newLines: number;
650
+ lines: string[];
651
+ }>;
652
+ };
653
+
654
+ export type ChatChangeSummary = {
655
+ id: string;
656
+ threadId: string;
657
+ messageId: string | null;
658
+ workspaceId: string;
659
+ userId: string;
660
+ branch: string;
661
+ resourceKind: "content";
662
+ locale: string;
663
+ type: string;
664
+ slug: string;
665
+ op: "write" | "create";
666
+ beforeSource: ContentSource | null;
667
+ diff: ChatChangeDiff | null;
668
+ status: ChatChangeStatus;
669
+ createdAt: string;
670
+ /** Present on tool outputs only. */
671
+ summary?: string;
672
+ };
673
+
674
+ export function fetchThreads(workspaceId: string) {
675
+ return api<{ threads: ChatThread[] }>(`/workspaces/${workspaceId}/ai/threads`);
676
+ }
677
+
678
+ export function createThread(workspaceId: string, title?: string) {
679
+ return api<{ thread: ChatThread }>(`/workspaces/${workspaceId}/ai/threads`, {
680
+ method: "POST",
681
+ body: JSON.stringify(title ? { title } : {}),
682
+ });
683
+ }
684
+
685
+ export function fetchThread(workspaceId: string, threadId: string) {
686
+ return api<{
687
+ thread: ChatThread;
688
+ messages: unknown[];
689
+ changes: ChatChangeSummary[];
690
+ }>(`/workspaces/${workspaceId}/ai/threads/${threadId}`);
691
+ }
692
+
693
+ export function renameThread(workspaceId: string, threadId: string, title: string) {
694
+ return api<{ thread: ChatThread }>(
695
+ `/workspaces/${workspaceId}/ai/threads/${threadId}`,
696
+ { method: "PATCH", body: JSON.stringify({ title }) },
697
+ );
698
+ }
699
+
700
+ export function deleteThread(workspaceId: string, threadId: string) {
701
+ return api<{ ok: true }>(`/workspaces/${workspaceId}/ai/threads/${threadId}`, {
702
+ method: "DELETE",
703
+ });
704
+ }
705
+
706
+ export function aiMessagesUrl(workspaceId: string, threadId: string) {
707
+ return apiUrl(`/workspaces/${workspaceId}/ai/threads/${threadId}/messages`);
708
+ }
709
+
710
+ export function keepChange(workspaceId: string, changeId: string) {
711
+ return api<{ change: ChatChangeSummary }>(
712
+ `/workspaces/${workspaceId}/ai/changes/${changeId}/keep`,
713
+ { method: "POST" },
714
+ );
715
+ }
716
+
717
+ export function undoChange(workspaceId: string, changeId: string) {
718
+ return api<{ change: ChatChangeSummary }>(
719
+ `/workspaces/${workspaceId}/ai/changes/${changeId}/undo`,
720
+ { method: "POST" },
721
+ );
722
+ }
723
+
724
+ export function keepAllChanges(workspaceId: string, threadId: string, messageId: string) {
725
+ return api<{ changes: ChatChangeSummary[] }>(
726
+ `/workspaces/${workspaceId}/ai/threads/${threadId}/messages/${messageId}/changes/keep-all`,
727
+ { method: "POST" },
728
+ );
729
+ }
730
+
731
+ export function undoAllChanges(workspaceId: string, threadId: string, messageId: string) {
732
+ return api<{ changes: ChatChangeSummary[] }>(
733
+ `/workspaces/${workspaceId}/ai/threads/${threadId}/messages/${messageId}/changes/undo-all`,
734
+ { method: "POST" },
735
+ );
736
+ }
737
+
738
+ export type McpStatus = "unknown" | "connected" | "error" | "needs_auth";
739
+ export type McpScope = "user" | "workspace";
740
+
741
+ export type McpToolCache = {
742
+ name: string;
743
+ description: string;
744
+ enabled: boolean;
745
+ };
746
+
747
+ export type McpConnector = {
748
+ id: string;
749
+ scope: McpScope;
750
+ userId: string | null;
751
+ workspaceId: string | null;
752
+ name: string;
753
+ slug: string;
754
+ url: string;
755
+ enabled: boolean;
756
+ status: McpStatus;
757
+ statusError: string | null;
758
+ lastSeenAt: string | null;
759
+ tools: McpToolCache[];
760
+ createdBy: string;
761
+ createdAt: string;
762
+ updatedAt: string;
763
+ toolCount: number;
764
+ };
765
+
766
+ export type McpWriteBody = {
767
+ name?: string;
768
+ url?: string;
769
+ headers?: Record<string, string>;
770
+ enabled?: boolean;
771
+ tools?: Array<{ name: string; enabled: boolean }>;
772
+ };
773
+
774
+ export function fetchUserMcp() {
775
+ return api<{ connectors: McpConnector[] }>("/me/mcp");
776
+ }
777
+
778
+ export function createUserMcp(body: McpWriteBody) {
779
+ return api<{ connector: McpConnector }>("/me/mcp", {
780
+ method: "POST",
781
+ body: JSON.stringify(body),
782
+ });
783
+ }
784
+
785
+ export function updateUserMcp(id: string, body: McpWriteBody) {
786
+ return api<{ connector: McpConnector }>(`/me/mcp/${id}`, {
787
+ method: "PATCH",
788
+ body: JSON.stringify(body),
789
+ });
790
+ }
791
+
792
+ export function deleteUserMcp(id: string) {
793
+ return api<{ ok: true }>(`/me/mcp/${id}`, { method: "DELETE" });
794
+ }
795
+
796
+ export function testUserMcp(id: string) {
797
+ return api<{ connector: McpConnector }>(`/me/mcp/${id}/test`, { method: "POST" });
798
+ }
799
+
800
+ export function fetchWorkspaceMcp(workspaceId: string) {
801
+ return api<{ connectors: McpConnector[] }>(`/workspaces/${workspaceId}/mcp`);
802
+ }
803
+
804
+ export function createWorkspaceMcp(workspaceId: string, body: McpWriteBody) {
805
+ return api<{ connector: McpConnector }>(`/workspaces/${workspaceId}/mcp`, {
806
+ method: "POST",
807
+ body: JSON.stringify(body),
808
+ });
809
+ }
810
+
811
+ export function updateWorkspaceMcp(
812
+ workspaceId: string,
813
+ id: string,
814
+ body: McpWriteBody,
815
+ ) {
816
+ return api<{ connector: McpConnector }>(`/workspaces/${workspaceId}/mcp/${id}`, {
817
+ method: "PATCH",
818
+ body: JSON.stringify(body),
819
+ });
820
+ }
821
+
822
+ export function deleteWorkspaceMcp(workspaceId: string, id: string) {
823
+ return api<{ ok: true }>(`/workspaces/${workspaceId}/mcp/${id}`, {
824
+ method: "DELETE",
825
+ });
826
+ }
827
+
828
+ export function testWorkspaceMcp(workspaceId: string, id: string) {
829
+ return api<{ connector: McpConnector }>(
830
+ `/workspaces/${workspaceId}/mcp/${id}/test`,
831
+ { method: "POST" },
832
+ );
833
+ }