@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/site.tsx ADDED
@@ -0,0 +1,230 @@
1
+ // packages/studio/src/site.tsx
2
+ // Root of a Studio mounted on a customer site. Same pages as the dashboard,
3
+ // one workspace, authenticated by the site session through the host's
4
+ // same-origin proxy. No login here: without a session the browser goes
5
+ // top-level to the dashboard `enter` route and comes back with a ticket.
6
+ import { useEffect, useState, type ReactNode } from "react";
7
+ import * as stylex from "@stylexjs/stylex";
8
+ import { BrowserRouter, Navigate, Route, Routes, useLocation } from "react-router";
9
+ import { studioEnterUrl } from "@backmatter/core";
10
+ import { fetchMe, fetchWorkspace } from "./api";
11
+ import { Shell } from "./components/Shell";
12
+ import { StudioDefinitionProvider, type StudioDefinition } from "./define-studio";
13
+ import { AccentOverride } from "./theme/AccentOverride";
14
+ import { EmbedChatPage } from "./pages/EmbedChatPage";
15
+ import { EmbedPublishPage } from "./pages/EmbedPublishPage";
16
+ import { LegacyWorkspaceRedirect } from "./pages/LegacyWorkspaceRedirect";
17
+ import { BootstrapSplash } from "./replica/BootstrapSplash";
18
+ import { ReplicaGate, ReplicaProvider } from "./replica/ReplicaProvider";
19
+ import { replicaStore } from "./replica/instance";
20
+ import { configureStudio, studioConfig, type StudioConfig } from "./studio-config";
21
+ import { colors, space } from "./styles/tokens.stylex";
22
+ import { ThemeProvider } from "./theme/ThemeProvider";
23
+ import { Text } from "./ui/Text";
24
+ import { workspaceRoutes } from "./WorkspaceRoutes";
25
+
26
+ export type SiteStudioProps = {
27
+ workspaceId: string;
28
+ /** Route the host mounted the Studio on. Default `/backmatter`. */
29
+ basePath?: string;
30
+ /** Dashboard that holds the login. Default `https://dash.backmatter.app`. */
31
+ dashboardUrl?: string;
32
+ /** Same-origin proxy the host exposes. Default `/api/backmatter/cms`. */
33
+ proxyPath?: string;
34
+ /** The CMS API origin, shown in Content API URLs. Requests still go through the proxy. */
35
+ apiUrl?: string;
36
+ /** Package version for the API handshake. */
37
+ version?: string;
38
+ /** Where the host's preview entry route lives; POST clears the site session. */
39
+ previewEntryPath?: string;
40
+ /** Site customization from `defineStudio` in `backmatter.studio.tsx`. */
41
+ studio?: StudioDefinition;
42
+ };
43
+
44
+ const ENTER_GUARD_KEY = "backmatter:enter-at";
45
+ const ENTER_LOOP_MS = 10_000;
46
+
47
+ /**
48
+ * Send the browser to the dashboard to mint a session for this site. Refuses
49
+ * to bounce twice within a few seconds so a broken proxy shows an error
50
+ * instead of a redirect loop.
51
+ */
52
+ export function bounceToEnter(workspaceId: string, dashboardUrl: string): boolean {
53
+ if (typeof window === "undefined") return false;
54
+ const last = Number(window.sessionStorage.getItem(ENTER_GUARD_KEY) ?? 0);
55
+ if (Date.now() - last < ENTER_LOOP_MS) return false;
56
+ window.sessionStorage.setItem(ENTER_GUARD_KEY, String(Date.now()));
57
+ window.location.assign(
58
+ studioEnterUrl(dashboardUrl, { workspaceId, returnUrl: window.location.href }),
59
+ );
60
+ return true;
61
+ }
62
+
63
+ export function configureSiteStudio(props: SiteStudioProps): StudioConfig {
64
+ const dashboardUrl = props.dashboardUrl ?? "https://dash.backmatter.app";
65
+ configureStudio({
66
+ baseUrl: props.proxyPath ?? "/api/backmatter/cms",
67
+ publicApiUrl: props.apiUrl,
68
+ credentials: "same-origin",
69
+ dev: false,
70
+ version: props.version,
71
+ dashboardUrl,
72
+ onUnauthorized: ({ status }) => {
73
+ if (status === 401) bounceToEnter(props.workspaceId, dashboardUrl);
74
+ },
75
+ });
76
+ return studioConfig();
77
+ }
78
+
79
+ export function SiteStudio(props: SiteStudioProps) {
80
+ const basePath = props.basePath ?? "/backmatter";
81
+ // Configure synchronously on first render so child effects already see it.
82
+ const [configured] = useState(() => configureSiteStudio(props));
83
+ void configured;
84
+ return (
85
+ <StudioDefinitionProvider definition={props.studio}>
86
+ <ThemeProvider>
87
+ <AccentOverride />
88
+ <BrowserRouter basename={basePath}>
89
+ <SiteInner {...props} />
90
+ </BrowserRouter>
91
+ </ThemeProvider>
92
+ </StudioDefinitionProvider>
93
+ );
94
+ }
95
+
96
+ type Session =
97
+ | "loading"
98
+ | { user: { id: string; email: string }; slug: string }
99
+ | "unauthorized"
100
+ | "outdated";
101
+
102
+ function SiteInner(props: SiteStudioProps) {
103
+ const location = useLocation();
104
+ const dashboardUrl = props.dashboardUrl ?? "https://dash.backmatter.app";
105
+ const [session, setSession] = useState<Session>("loading");
106
+
107
+ useEffect(() => {
108
+ let cancelled = false;
109
+ const previous = studioConfig().onUnauthorized;
110
+ configureStudio({
111
+ ...studioConfig(),
112
+ onUnauthorized: (info) => {
113
+ if (info.status === 426) setSession("outdated");
114
+ else previous?.(info);
115
+ },
116
+ });
117
+ void fetchMe()
118
+ .then(async (me) => {
119
+ if (cancelled) return;
120
+ if (!me?.user) {
121
+ if (!bounceToEnter(props.workspaceId, dashboardUrl)) setSession("unauthorized");
122
+ return;
123
+ }
124
+ // The Studio routes by slug; the site only knows its workspace id.
125
+ const { workspace } = await fetchWorkspace(props.workspaceId);
126
+ if (cancelled) return;
127
+ setSession({ user: { id: me.user.id, email: me.user.email }, slug: workspace.slug });
128
+ })
129
+ .catch(() => {
130
+ if (cancelled) return;
131
+ setSession((s) => (s === "outdated" ? s : "unauthorized"));
132
+ });
133
+ return () => {
134
+ cancelled = true;
135
+ };
136
+ }, [props.workspaceId, dashboardUrl]);
137
+
138
+ if (session === "loading") return <BootstrapSplash />;
139
+ if (session === "outdated") {
140
+ return (
141
+ <Notice title="This site's editor is out of date">
142
+ Update <code>@backmatter/studio</code> and <code>@backmatter/next</code> on the site, then
143
+ reload.
144
+ </Notice>
145
+ );
146
+ }
147
+ if (session === "unauthorized") {
148
+ return (
149
+ <Notice title="Could not start editing">
150
+ The site did not receive a session from the dashboard. Check that this site's origin is
151
+ listed in the workspace settings and that <code>/api/backmatter</code> routes are
152
+ deployed.
153
+ </Notice>
154
+ );
155
+ }
156
+
157
+ const embedChat = location.pathname.match(/^\/embed\/chat\/([^/]+)\/?$/);
158
+ if (embedChat?.[1]) {
159
+ return <EmbedChatPage workspaceId={decodeURIComponent(embedChat[1])} />;
160
+ }
161
+ const embedPublish = location.pathname.match(/^\/embed\/publish\/([^/]+)\/?$/);
162
+ if (embedPublish?.[1]) {
163
+ return <EmbedPublishPage workspaceId={decodeURIComponent(embedPublish[1])} />;
164
+ }
165
+
166
+ return (
167
+ <ReplicaProvider user={session.user}>
168
+ <ReplicaGate>
169
+ <Shell
170
+ userId={session.user.id}
171
+ email={session.user.email}
172
+ onLogout={() => {
173
+ void exitSite(props.previewEntryPath ?? "/api/backmatter/preview", dashboardUrl);
174
+ }}
175
+ >
176
+ <Routes>
177
+ <Route path="/" element={<Navigate to={`/${session.slug}`} replace />} />
178
+ {/* The bar links by workspace id; this rewrites to the slug route. */}
179
+ <Route path="/workspaces/:workspaceId/*" element={<LegacyWorkspaceRedirect />} />
180
+ {workspaceRoutes()}
181
+ <Route path="*" element={<Navigate to="/" replace />} />
182
+ </Routes>
183
+ </Shell>
184
+ </ReplicaGate>
185
+ </ReplicaProvider>
186
+ );
187
+ }
188
+
189
+ /** Clear the site session and draft mode, then leave for the dashboard. */
190
+ async function exitSite(previewEntryPath: string, dashboardUrl: string) {
191
+ try {
192
+ await fetch(previewEntryPath, { method: "POST", credentials: "same-origin" });
193
+ } catch {
194
+ // The cookie expires on its own; still leave.
195
+ }
196
+ void replicaStore.wipe();
197
+ window.location.assign(dashboardUrl);
198
+ }
199
+
200
+ function Notice({ title, children }: { title: string; children: ReactNode }) {
201
+ return (
202
+ <div {...stylex.props(styles.page)}>
203
+ <div {...stylex.props(styles.card)}>
204
+ <Text size="lg" weight="medium">
205
+ {title}
206
+ </Text>
207
+ <Text tone="muted">{children}</Text>
208
+ </div>
209
+ </div>
210
+ );
211
+ }
212
+
213
+ const styles = stylex.create({
214
+ page: {
215
+ minHeight: "100dvh",
216
+ display: "grid",
217
+ placeItems: "center",
218
+ padding: space.xl,
219
+ },
220
+ card: {
221
+ display: "flex",
222
+ flexDirection: "column",
223
+ gap: space.md,
224
+ maxWidth: 440,
225
+ padding: space.xl,
226
+ borderRadius: 12,
227
+ backgroundColor: colors.bgPanel,
228
+ border: `1px solid ${colors.border}`,
229
+ },
230
+ });
@@ -0,0 +1,81 @@
1
+ // packages/studio/src/studio-config.ts
2
+ // Where the Studio sends requests and how it authenticates. Set once by the
3
+ // host before rendering. The dashboard points at the CMS API with its cookie;
4
+ // a customer site points at its own same-origin proxy with the site session.
5
+
6
+ export type StudioCredentials = "include" | "same-origin";
7
+
8
+ export type StudioConfig = {
9
+ /** Origin and base path for every API request. No trailing slash. */
10
+ baseUrl: string;
11
+ /** `include` for the cross-origin dashboard cookie, `same-origin` for the host proxy. */
12
+ credentials: StudioCredentials;
13
+ /** Dev-only surfaces (UI kit, palette entries). */
14
+ dev: boolean;
15
+ /**
16
+ * The CMS API origin as the outside world reaches it, for URLs the Studio
17
+ * shows rather than fetches (the Content API popover). A site Studio fetches
18
+ * through a relative proxy, so its host passes the real origin here.
19
+ * Defaults to `baseUrl`.
20
+ */
21
+ publicApiUrl?: string;
22
+ /** Package version, sent as `X-Backmatter-Studio` so the API can refuse an old Studio. */
23
+ version?: string;
24
+ /**
25
+ * Where the host can log in. A site Studio without a session bounces here.
26
+ * The dashboard itself leaves this unset.
27
+ */
28
+ dashboardUrl?: string;
29
+ /**
30
+ * Called when the API answers 401 or 426. A site Studio redirects to the
31
+ * dashboard enter route; the dashboard shows its login page instead.
32
+ */
33
+ onUnauthorized?: (info: { status: 401 | 426; code?: string }) => void;
34
+ };
35
+
36
+ const DEFAULT_CONFIG: StudioConfig = {
37
+ baseUrl: "http://localhost:3001",
38
+ credentials: "include",
39
+ dev: false,
40
+ };
41
+
42
+ let current: StudioConfig = DEFAULT_CONFIG;
43
+
44
+ export function configureStudio(config: Partial<StudioConfig> & Pick<StudioConfig, "baseUrl">) {
45
+ current = {
46
+ ...DEFAULT_CONFIG,
47
+ ...config,
48
+ baseUrl: config.baseUrl.replace(/\/+$/, ""),
49
+ publicApiUrl: (config.publicApiUrl ?? config.baseUrl).replace(/\/+$/, ""),
50
+ };
51
+ }
52
+
53
+ export function studioConfig(): StudioConfig {
54
+ return current;
55
+ }
56
+
57
+ export function apiUrl(path = ""): string {
58
+ return `${current.baseUrl}${path}`;
59
+ }
60
+
61
+ /** The API origin to show users; see `StudioConfig.publicApiUrl`. */
62
+ export function publicApiUrl(): string {
63
+ return current.publicApiUrl ?? current.baseUrl;
64
+ }
65
+
66
+ export function apiCredentials(): StudioCredentials {
67
+ return current.credentials;
68
+ }
69
+
70
+ /** Headers every API request carries. */
71
+ export function apiHeaders(): Record<string, string> {
72
+ return current.version ? { "X-Backmatter-Studio": current.version } : {};
73
+ }
74
+
75
+ export function isStudioDev(): boolean {
76
+ return current.dev;
77
+ }
78
+
79
+ export function reportUnauthorized(status: 401 | 426, code?: string) {
80
+ current.onUnauthorized?.({ status, code });
81
+ }
@@ -0,0 +1,49 @@
1
+ // packages/studio/src/styles/themes.ts
2
+ import * as stylex from "@stylexjs/stylex";
3
+ import { colors } from "./tokens.stylex";
4
+
5
+ export const lightTheme = stylex.createTheme(colors, {
6
+ bgApp: "#f7f7f8",
7
+ bgSidebar: "#ececee",
8
+ bgPanel: "#f4f4f5",
9
+ bgPanelInset: "#ececef",
10
+ bgSurface: "#ffffff",
11
+ bgHover: "rgba(24, 24, 27, 0.05)",
12
+ bgActive: "rgba(24, 24, 27, 0.08)",
13
+ bgInput: "#ffffff",
14
+ bgStatus: "#f4f4f5",
15
+ text: "#18181b",
16
+ textMuted: "#71717a",
17
+ textInverse: "#fafafa",
18
+ border: "#e4e4e7",
19
+ accent: "#18181b",
20
+ accentHover: "#3f3f46",
21
+ accentMuted: "#f4f4f5",
22
+ danger: "#dc2626",
23
+ success: "#059669",
24
+ overlay: "rgba(24, 24, 27, 0.45)",
25
+ focus: "#18181b",
26
+ });
27
+
28
+ export const darkTheme = stylex.createTheme(colors, {
29
+ bgApp: "#18181b",
30
+ bgSidebar: "#111113",
31
+ bgPanel: "#131316",
32
+ bgPanelInset: "#0f0f12",
33
+ bgSurface: "#1f1f23",
34
+ bgHover: "rgba(255, 255, 255, 0.06)",
35
+ bgActive: "rgba(255, 255, 255, 0.1)",
36
+ bgInput: "#18181b",
37
+ bgStatus: "#111113",
38
+ text: "#e4e4e7",
39
+ textMuted: "#a1a1aa",
40
+ textInverse: "#18181b",
41
+ border: "#27272a",
42
+ accent: "#fafafa",
43
+ accentHover: "#e4e4e7",
44
+ accentMuted: "rgba(255, 255, 255, 0.1)",
45
+ danger: "#f87171",
46
+ success: "#34d399",
47
+ overlay: "rgba(0, 0, 0, 0.6)",
48
+ focus: "#fafafa",
49
+ });
@@ -0,0 +1,58 @@
1
+ // packages/studio/src/styles/tokens.stylex.ts
2
+ import * as stylex from "@stylexjs/stylex";
3
+
4
+ export const colors = stylex.defineVars({
5
+ bgApp: "#f7f7f8",
6
+ bgSidebar: "#ececee",
7
+ bgPanel: "#f4f4f5",
8
+ bgPanelInset: "#ececef",
9
+ bgSurface: "#ffffff",
10
+ bgHover: "rgba(24, 24, 27, 0.05)",
11
+ bgActive: "rgba(24, 24, 27, 0.08)",
12
+ bgInput: "#ffffff",
13
+ bgStatus: "#f4f4f5",
14
+ text: "#18181b",
15
+ textMuted: "#71717a",
16
+ textInverse: "#fafafa",
17
+ border: "#e4e4e7",
18
+ accent: "#18181b",
19
+ accentHover: "#3f3f46",
20
+ accentMuted: "#f4f4f5",
21
+ danger: "#dc2626",
22
+ success: "#059669",
23
+ overlay: "rgba(24, 24, 27, 0.45)",
24
+ focus: "#18181b",
25
+ });
26
+
27
+ export const space = stylex.defineVars({
28
+ xs: "4px",
29
+ sm: "6px",
30
+ md: "8px",
31
+ lg: "12px",
32
+ xl: "16px",
33
+ xxl: "24px",
34
+ });
35
+
36
+ export const radius = stylex.defineVars({
37
+ sm: "4px",
38
+ md: "6px",
39
+ });
40
+
41
+ export const type = stylex.defineVars({
42
+ sans: "Geist Sans, Inter, ui-sans-serif, system-ui, sans-serif",
43
+ mono: "Geist Mono, ui-monospace, SFMono-Regular, Menlo, monospace",
44
+ xs: "11px",
45
+ sm: "12px",
46
+ md: "13px",
47
+ lg: "14px",
48
+ xl: "16px",
49
+ display: "20px",
50
+ });
51
+
52
+ export const z = stylex.defineVars({
53
+ dropdown: "20",
54
+ overlay: "40",
55
+ palette: "50",
56
+ popover: "60",
57
+ tooltip: "70",
58
+ });
package/src/styles.ts ADDED
@@ -0,0 +1,11 @@
1
+ // packages/studio/src/styles.ts
2
+ // Every stylesheet the Studio needs. Hosts import this once; the library build
3
+ // emits it as a single CSS file next to the StyleX output.
4
+ import "@fontsource/geist-sans/400.css";
5
+ import "@fontsource/geist-sans/500.css";
6
+ import "@fontsource/geist-sans/600.css";
7
+ import "@fontsource/geist-mono/400.css";
8
+ import "@prose-ui/style/prose-ui.css";
9
+ import "@prose-ui/style/katex.css";
10
+ import "react-medium-image-zoom/dist/styles.css";
11
+ import "./index.css";
@@ -0,0 +1,29 @@
1
+ // packages/studio/src/theme/AccentOverride.tsx
2
+ // `defineStudio({ theme: { accent } })` recolors primary actions and focus
3
+ // rings. StyleX tokens compile to CSS variables; an inline value on <html>
4
+ // outranks the theme class that sets them.
5
+ import { useLayoutEffect } from "react";
6
+ import { colors } from "../styles/tokens.stylex";
7
+ import { useStudioDefinition } from "../define-studio";
8
+
9
+ /** `var(--x)` → `--x`, or null when the token is not a variable reference. */
10
+ export function cssVarName(token: string): string | null {
11
+ const match = /^var\((--[^,)]+)/.exec(token);
12
+ return match?.[1] ?? null;
13
+ }
14
+
15
+ const ACCENT_TOKENS = [colors.accent, colors.accentHover, colors.focus] as unknown as string[];
16
+
17
+ export function AccentOverride() {
18
+ const accent = useStudioDefinition().theme?.accent?.trim();
19
+ useLayoutEffect(() => {
20
+ if (!accent) return;
21
+ const root = document.documentElement;
22
+ const names = ACCENT_TOKENS.map(cssVarName).filter((n): n is string => Boolean(n));
23
+ for (const name of names) root.style.setProperty(name, accent);
24
+ return () => {
25
+ for (const name of names) root.style.removeProperty(name);
26
+ };
27
+ }, [accent]);
28
+ return null;
29
+ }
@@ -0,0 +1,130 @@
1
+ // packages/studio/src/theme/ThemeProvider.tsx
2
+ import {
3
+ createContext,
4
+ useCallback,
5
+ useContext,
6
+ useEffect,
7
+ useLayoutEffect,
8
+ useMemo,
9
+ useState,
10
+ } from "react";
11
+ import * as stylex from "@stylexjs/stylex";
12
+ import { colors, type } from "../styles/tokens.stylex";
13
+ import { darkTheme, lightTheme } from "../styles/themes";
14
+ import { applyThemeClass } from "./apply-theme-class";
15
+
16
+ const STORAGE_KEY = "backmatter-theme";
17
+
18
+ export type ThemePreference = "system" | "light" | "dark";
19
+ export type ResolvedTheme = "light" | "dark";
20
+
21
+ type ThemeContextValue = {
22
+ preference: ThemePreference;
23
+ resolved: ResolvedTheme;
24
+ setPreference: (preference: ThemePreference) => void;
25
+ cyclePreference: () => void;
26
+ };
27
+
28
+ const ThemeContext = createContext<ThemeContextValue | null>(null);
29
+
30
+ function readPreference(): ThemePreference {
31
+ try {
32
+ const value = localStorage.getItem(STORAGE_KEY);
33
+ if (value === "light" || value === "dark" || value === "system") {
34
+ return value;
35
+ }
36
+ } catch {
37
+ // ignore
38
+ }
39
+ return "system";
40
+ }
41
+
42
+ function resolvePreference(preference: ThemePreference): ResolvedTheme {
43
+ if (preference === "light" || preference === "dark") return preference;
44
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
45
+ ? "dark"
46
+ : "light";
47
+ }
48
+
49
+ export function ThemeProvider({ children }: { children: React.ReactNode }) {
50
+ const [preference, setPreferenceState] =
51
+ useState<ThemePreference>(readPreference);
52
+ const [resolved, setResolved] = useState<ResolvedTheme>(() =>
53
+ resolvePreference(readPreference()),
54
+ );
55
+
56
+ useEffect(() => {
57
+ function apply() {
58
+ const next = resolvePreference(preference);
59
+ setResolved(next);
60
+ document.documentElement.dataset.theme = next;
61
+ document.documentElement.style.colorScheme = next;
62
+ // Prose UI switches tokens from the `dark` / `light` class on <html>.
63
+ document.documentElement.classList.toggle("dark", next === "dark");
64
+ document.documentElement.classList.toggle("light", next === "light");
65
+ }
66
+ apply();
67
+ if (preference !== "system") return;
68
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
69
+ media.addEventListener("change", apply);
70
+ return () => media.removeEventListener("change", apply);
71
+ }, [preference]);
72
+
73
+ const theme = resolved === "dark" ? darkTheme : lightTheme;
74
+
75
+ // Base UI portals mount on document.body, outside the wrapper that holds the
76
+ // StyleX theme. Put the same theme class on <html> so menus/dialogs inherit it.
77
+ useLayoutEffect(() => {
78
+ return applyThemeClass(
79
+ document.documentElement,
80
+ stylex.props(theme).className,
81
+ );
82
+ }, [theme]);
83
+
84
+ const setPreference = useCallback((next: ThemePreference) => {
85
+ try {
86
+ localStorage.setItem(STORAGE_KEY, next);
87
+ } catch {
88
+ // ignore
89
+ }
90
+ setPreferenceState(next);
91
+ }, []);
92
+
93
+ const cyclePreference = useCallback(() => {
94
+ const order: ThemePreference[] = ["system", "light", "dark"];
95
+ const index = order.indexOf(preference);
96
+ setPreference(order[(index + 1) % order.length]!);
97
+ }, [preference, setPreference]);
98
+
99
+ const value = useMemo(
100
+ () => ({ preference, resolved, setPreference, cyclePreference }),
101
+ [preference, resolved, setPreference, cyclePreference],
102
+ );
103
+
104
+ return (
105
+ <ThemeContext.Provider value={value}>
106
+ <div
107
+ {...stylex.props(theme, styles.root)}
108
+ >
109
+ {children}
110
+ </div>
111
+ </ThemeContext.Provider>
112
+ );
113
+ }
114
+
115
+ export function useTheme(): ThemeContextValue {
116
+ const ctx = useContext(ThemeContext);
117
+ if (!ctx) throw new Error("useTheme must be used within ThemeProvider");
118
+ return ctx;
119
+ }
120
+
121
+ const styles = stylex.create({
122
+ root: {
123
+ minHeight: "100dvh",
124
+ backgroundColor: colors.bgApp,
125
+ color: colors.text,
126
+ fontFamily: type.sans,
127
+ fontSize: type.md,
128
+ lineHeight: 1.45,
129
+ },
130
+ });
@@ -0,0 +1,12 @@
1
+ // packages/studio/src/theme/apply-theme-class.ts
2
+ /** Puts StyleX theme classes on a root (usually <html>) so portaled UI inherits tokens. */
3
+ export function applyThemeClass(
4
+ target: Element,
5
+ className: string | undefined,
6
+ ): () => void {
7
+ const tokens = className?.split(/\s+/).filter(Boolean) ?? [];
8
+ if (tokens.length > 0) target.classList.add(...tokens);
9
+ return () => {
10
+ if (tokens.length > 0) target.classList.remove(...tokens);
11
+ };
12
+ }
@@ -0,0 +1,28 @@
1
+ // packages/studio/src/ui/BackLink.tsx
2
+ import { Link } from "react-router";
3
+ import * as stylex from "@stylexjs/stylex";
4
+ import { colors, space, type } from "../styles/tokens.stylex";
5
+
6
+ export function BackLink({
7
+ to,
8
+ children,
9
+ }: {
10
+ to: string;
11
+ children: React.ReactNode;
12
+ }) {
13
+ return (
14
+ <Link to={to} {...stylex.props(styles.link)}>
15
+ {children}
16
+ </Link>
17
+ );
18
+ }
19
+
20
+ const styles = stylex.create({
21
+ link: {
22
+ display: "inline-block",
23
+ marginBottom: space.sm,
24
+ fontSize: type.xs,
25
+ color: colors.textMuted,
26
+ textDecoration: "none",
27
+ },
28
+ });