@openspecui/web 0.9.0

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 (463) hide show
  1. package/dist/assets/abap-BdImnpbu.js +1 -0
  2. package/dist/assets/actionscript-3-CfeIJUat.js +1 -0
  3. package/dist/assets/ada-bCR0ucgS.js +1 -0
  4. package/dist/assets/andromeeda-C-Jbm3Hp.js +1 -0
  5. package/dist/assets/angular-html-CU67Zn6k.js +1 -0
  6. package/dist/assets/angular-ts-BwZT4LLn.js +1 -0
  7. package/dist/assets/apache-Pmp26Uib.js +1 -0
  8. package/dist/assets/apex-DDbsPZ6N.js +1 -0
  9. package/dist/assets/apl-B4CMkyY2.js +1 -0
  10. package/dist/assets/apl-dKokRX4l.js +1 -0
  11. package/dist/assets/applescript-Co6uUVPk.js +1 -0
  12. package/dist/assets/ara-BRHolxvo.js +1 -0
  13. package/dist/assets/asciiarmor-Df11BRmG.js +1 -0
  14. package/dist/assets/asciidoc-Dv7Oe6Be.js +1 -0
  15. package/dist/assets/asm-D_Q5rh1f.js +1 -0
  16. package/dist/assets/asn1-EdZsLKOL.js +1 -0
  17. package/dist/assets/asterisk-B-8jnY81.js +1 -0
  18. package/dist/assets/astro-CbQHKStN.js +1 -0
  19. package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  20. package/dist/assets/awk-DMzUqQB5.js +1 -0
  21. package/dist/assets/ayu-dark-Cv9koXgw.js +1 -0
  22. package/dist/assets/ballerina-BFfxhgS-.js +1 -0
  23. package/dist/assets/bat-BkioyH1T.js +1 -0
  24. package/dist/assets/beancount-k_qm7-4y.js +1 -0
  25. package/dist/assets/berry-uYugtg8r.js +1 -0
  26. package/dist/assets/bibtex-CHM0blh-.js +1 -0
  27. package/dist/assets/bicep-Bmn6On1c.js +1 -0
  28. package/dist/assets/blade-DVc8C-J4.js +1 -0
  29. package/dist/assets/brainfuck-C4LP7Hcl.js +1 -0
  30. package/dist/assets/bsl-BO_Y6i37.js +1 -0
  31. package/dist/assets/c-BIGW1oBm.js +1 -0
  32. package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
  33. package/dist/assets/cairo-KRGpt6FW.js +1 -0
  34. package/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  35. package/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  36. package/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  37. package/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  38. package/dist/assets/clarity-D53aC0YG.js +1 -0
  39. package/dist/assets/clike-B9uivgTg.js +1 -0
  40. package/dist/assets/clojure-BMjYHr_A.js +1 -0
  41. package/dist/assets/clojure-P80f7IUj.js +1 -0
  42. package/dist/assets/cmake-BQqOBYOt.js +1 -0
  43. package/dist/assets/cmake-D1j8_8rp.js +1 -0
  44. package/dist/assets/cobol-CWcv1MsR.js +1 -0
  45. package/dist/assets/cobol-nwyudZeR.js +1 -0
  46. package/dist/assets/codeowners-Bp6g37R7.js +1 -0
  47. package/dist/assets/codeql-DsOJ9woJ.js +1 -0
  48. package/dist/assets/coffee-Ch7k5sss.js +1 -0
  49. package/dist/assets/coffeescript-S37ZYGWr.js +1 -0
  50. package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
  51. package/dist/assets/commonlisp-DBKNyK5s.js +1 -0
  52. package/dist/assets/coq-DkFqJrB1.js +1 -0
  53. package/dist/assets/cpp-CofmeUqb.js +1 -0
  54. package/dist/assets/crystal-SjHAIU92.js +1 -0
  55. package/dist/assets/crystal-tKQVLTB8.js +1 -0
  56. package/dist/assets/csharp-K5feNrxe.js +1 -0
  57. package/dist/assets/css-BnMrqG3P.js +1 -0
  58. package/dist/assets/css-DPfMkruS.js +1 -0
  59. package/dist/assets/csv-fuZLfV_i.js +1 -0
  60. package/dist/assets/cue-D82EKSYY.js +1 -0
  61. package/dist/assets/cypher-COkxafJQ.js +1 -0
  62. package/dist/assets/cypher-C_CwsFkJ.js +1 -0
  63. package/dist/assets/d-85-TOEBH.js +1 -0
  64. package/dist/assets/d-pRatUO7H.js +1 -0
  65. package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  66. package/dist/assets/dart-CF10PKvl.js +1 -0
  67. package/dist/assets/dax-CEL-wOlO.js +1 -0
  68. package/dist/assets/desktop-BmXAJ9_W.js +1 -0
  69. package/dist/assets/diff-D97Zzqfu.js +1 -0
  70. package/dist/assets/diff-DbItnlRl.js +1 -0
  71. package/dist/assets/docker-BcOcwvcX.js +1 -0
  72. package/dist/assets/dockerfile-BKs6k2Af.js +1 -0
  73. package/dist/assets/dotenv-Da5cRb03.js +1 -0
  74. package/dist/assets/dracula-BzJJZx-M.js +1 -0
  75. package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  76. package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
  77. package/dist/assets/dtd-DF_7sFjM.js +1 -0
  78. package/dist/assets/dylan-DwRh75JA.js +1 -0
  79. package/dist/assets/ebnf-CDyGwa7X.js +1 -0
  80. package/dist/assets/ecl-Cabwm37j.js +1 -0
  81. package/dist/assets/edge-BkV0erSs.js +1 -0
  82. package/dist/assets/eiffel-CnydiIhH.js +1 -0
  83. package/dist/assets/elixir-CDX3lj18.js +1 -0
  84. package/dist/assets/elm-DbKCFpqz.js +1 -0
  85. package/dist/assets/elm-vLlmbW-K.js +1 -0
  86. package/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
  87. package/dist/assets/erb-BOJIQeun.js +1 -0
  88. package/dist/assets/erlang-BNw1qcRV.js +1 -0
  89. package/dist/assets/erlang-DsQrWhSR.js +1 -0
  90. package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  91. package/dist/assets/everforest-light-C8M2exoo.js +1 -0
  92. package/dist/assets/factor-kuTfRLto.js +1 -0
  93. package/dist/assets/fcl-Kvtd6kyn.js +1 -0
  94. package/dist/assets/fennel-BYunw83y.js +1 -0
  95. package/dist/assets/fish-BvzEVeQv.js +1 -0
  96. package/dist/assets/fluent-C4IJs8-o.js +1 -0
  97. package/dist/assets/forth-Ffai-XNe.js +1 -0
  98. package/dist/assets/fortran-DYz_wnZ1.js +1 -0
  99. package/dist/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
  100. package/dist/assets/fortran-free-form-D22FLkUw.js +1 -0
  101. package/dist/assets/fsharp-CXgrBDvD.js +1 -0
  102. package/dist/assets/gas-Bneqetm1.js +1 -0
  103. package/dist/assets/gdresource-B7Tvp0Sc.js +1 -0
  104. package/dist/assets/gdscript-DTMYz4Jt.js +1 -0
  105. package/dist/assets/gdshader-DkwncUOv.js +1 -0
  106. package/dist/assets/genie-D0YGMca9.js +1 -0
  107. package/dist/assets/gherkin-DyxjwDmM.js +1 -0
  108. package/dist/assets/gherkin-heZmZLOM.js +1 -0
  109. package/dist/assets/git-commit-F4YmCXRG.js +1 -0
  110. package/dist/assets/git-rebase-r7XF79zn.js +1 -0
  111. package/dist/assets/github-dark-DHJKELXO.js +1 -0
  112. package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  113. package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  114. package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  115. package/dist/assets/github-light-DAi9KRSo.js +1 -0
  116. package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  117. package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  118. package/dist/assets/gleam-BspZqrRM.js +1 -0
  119. package/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
  120. package/dist/assets/glimmer-ts-U6CK756n.js +1 -0
  121. package/dist/assets/glsl-DplSGwfg.js +1 -0
  122. package/dist/assets/gnuplot-DdkO51Og.js +1 -0
  123. package/dist/assets/go-Dn2_MT6a.js +1 -0
  124. package/dist/assets/graphql-ChdNCCLP.js +1 -0
  125. package/dist/assets/groovy-D9Dt4D0W.js +1 -0
  126. package/dist/assets/groovy-gcz8RCvz.js +1 -0
  127. package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  128. package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  129. package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  130. package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  131. package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  132. package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  133. package/dist/assets/hack-CaT9iCJl.js +1 -0
  134. package/dist/assets/haml-B8DHNrY2.js +1 -0
  135. package/dist/assets/handlebars-BL8al0AC.js +1 -0
  136. package/dist/assets/haskell-Cw1EW3IL.js +1 -0
  137. package/dist/assets/haskell-Df6bDoY_.js +1 -0
  138. package/dist/assets/haxe-CzTSHFRz.js +1 -0
  139. package/dist/assets/haxe-H-WmDvRZ.js +1 -0
  140. package/dist/assets/hcl-BWvSN4gD.js +1 -0
  141. package/dist/assets/hjson-D5-asLiD.js +1 -0
  142. package/dist/assets/hlsl-D3lLCCz7.js +1 -0
  143. package/dist/assets/houston-DnULxvSX.js +1 -0
  144. package/dist/assets/html-GMplVEZG.js +1 -0
  145. package/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
  146. package/dist/assets/http-DBlCnlav.js +1 -0
  147. package/dist/assets/http-jrhK8wxY.js +1 -0
  148. package/dist/assets/hurl-irOxFIW8.js +1 -0
  149. package/dist/assets/hxml-Bvhsp5Yf.js +1 -0
  150. package/dist/assets/hy-DFXneXwc.js +1 -0
  151. package/dist/assets/idl-BEugSyMb.js +1 -0
  152. package/dist/assets/imba-DGztddWO.js +1 -0
  153. package/dist/assets/index-8c6bEJ99.js +1 -0
  154. package/dist/assets/index-AbWe21oh.js +2 -0
  155. package/dist/assets/index-ArhptQw0.js +1 -0
  156. package/dist/assets/index-B1hpa--1.js +3 -0
  157. package/dist/assets/index-Bafja8o4.js +1 -0
  158. package/dist/assets/index-Bp00uZNc.js +1 -0
  159. package/dist/assets/index-BsTieXqQ.js +1 -0
  160. package/dist/assets/index-BvGAWAqS.js +1 -0
  161. package/dist/assets/index-CCfVkFzN.js +1 -0
  162. package/dist/assets/index-D-Urq2hl.css +1 -0
  163. package/dist/assets/index-D3mXuuih.js +1 -0
  164. package/dist/assets/index-DFOLYN6W.js +1 -0
  165. package/dist/assets/index-DpxkOmNJ.js +7 -0
  166. package/dist/assets/index-YZ-iXB95.js +309 -0
  167. package/dist/assets/index-eA_XNQ_L.js +1 -0
  168. package/dist/assets/index-ftYom_wU.js +1 -0
  169. package/dist/assets/index-gvPT4BlL.js +1 -0
  170. package/dist/assets/ini-BEwlwnbL.js +1 -0
  171. package/dist/assets/java-CylS5w8V.js +1 -0
  172. package/dist/assets/javascript-iXu5QeM3.js +1 -0
  173. package/dist/assets/javascript-wDzz0qaB.js +1 -0
  174. package/dist/assets/jinja-4LBKfQ-Z.js +1 -0
  175. package/dist/assets/jison-wvAkD_A8.js +1 -0
  176. package/dist/assets/json-Cp-IABpG.js +1 -0
  177. package/dist/assets/json5-C9tS-k6U.js +1 -0
  178. package/dist/assets/jsonc-Des-eS-w.js +1 -0
  179. package/dist/assets/jsonl-DcaNXYhu.js +1 -0
  180. package/dist/assets/jsonnet-DFQXde-d.js +1 -0
  181. package/dist/assets/jssm-C2t-YnRu.js +1 -0
  182. package/dist/assets/jsx-g9-lgVsj.js +1 -0
  183. package/dist/assets/julia-C8NyazO9.js +1 -0
  184. package/dist/assets/julia-DuME0IfC.js +1 -0
  185. package/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  186. package/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  187. package/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
  188. package/dist/assets/kdl-DV7GczEv.js +1 -0
  189. package/dist/assets/kotlin-BdnUsdx6.js +1 -0
  190. package/dist/assets/kusto-BvAqAH-y.js +1 -0
  191. package/dist/assets/laserwave-DUszq2jm.js +1 -0
  192. package/dist/assets/latex-BdAV_C_H.js +1 -0
  193. package/dist/assets/lean-Bc6EcWN3.js +1 -0
  194. package/dist/assets/less-B1dDrJ26.js +1 -0
  195. package/dist/assets/light-plus-B7mTdjB0.js +1 -0
  196. package/dist/assets/liquid-DYVedYrR.js +1 -0
  197. package/dist/assets/livescript-BwQOo05w.js +1 -0
  198. package/dist/assets/llvm-BtvRca6l.js +1 -0
  199. package/dist/assets/log-2UxHyX5q.js +1 -0
  200. package/dist/assets/logo-BtOb2qkB.js +1 -0
  201. package/dist/assets/lua-BbnMAYS6.js +1 -0
  202. package/dist/assets/lua-BgMRiT3U.js +1 -0
  203. package/dist/assets/luau-CXu1NL6O.js +1 -0
  204. package/dist/assets/make-CHLpvVh8.js +1 -0
  205. package/dist/assets/markdown-Cvjx9yec.js +1 -0
  206. package/dist/assets/marko-CPi9NSCl.js +1 -0
  207. package/dist/assets/material-theme-D5KoaKCx.js +1 -0
  208. package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  209. package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  210. package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  211. package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  212. package/dist/assets/mathematica-DTrFuWx2.js +1 -0
  213. package/dist/assets/matlab-D7o27uSR.js +1 -0
  214. package/dist/assets/mbox-CNhZ1qSd.js +1 -0
  215. package/dist/assets/mdc-DUICxH0z.js +1 -0
  216. package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  217. package/dist/assets/mermaid-DKYwYmdq.js +1 -0
  218. package/dist/assets/min-dark-CafNBF8u.js +1 -0
  219. package/dist/assets/min-light-CTRr51gU.js +1 -0
  220. package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
  221. package/dist/assets/mirc-CjQqDB4T.js +1 -0
  222. package/dist/assets/mllike-CXdrOF99.js +1 -0
  223. package/dist/assets/modelica-Dc1JOy9r.js +1 -0
  224. package/dist/assets/mojo-1DNp92w6.js +1 -0
  225. package/dist/assets/monokai-D4h5O-jR.js +1 -0
  226. package/dist/assets/move-Bu9oaDYs.js +1 -0
  227. package/dist/assets/mscgen-BA5vi2Kp.js +1 -0
  228. package/dist/assets/mumps-BT43cFF4.js +1 -0
  229. package/dist/assets/narrat-DRg8JJMk.js +1 -0
  230. package/dist/assets/nextflow-BrzmwbiE.js +1 -0
  231. package/dist/assets/nginx-DdIZxoE0.js +1 -0
  232. package/dist/assets/nginx-DknmC5AR.js +1 -0
  233. package/dist/assets/night-owl-C39BiMTA.js +1 -0
  234. package/dist/assets/nim-CVrawwO9.js +1 -0
  235. package/dist/assets/nix-c8nO5XWb.js +1 -0
  236. package/dist/assets/nord-Ddv68eIx.js +1 -0
  237. package/dist/assets/nsis-LdVXkNf5.js +1 -0
  238. package/dist/assets/ntriples-BfvgReVJ.js +1 -0
  239. package/dist/assets/nushell-C-sUppwS.js +1 -0
  240. package/dist/assets/objective-c-DXmwc3jG.js +1 -0
  241. package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
  242. package/dist/assets/ocaml-C0hk2d4L.js +1 -0
  243. package/dist/assets/octave-Ck1zUtKM.js +1 -0
  244. package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  245. package/dist/assets/one-light-PoHY5YXO.js +1 -0
  246. package/dist/assets/openscad-C4EeE6gA.js +1 -0
  247. package/dist/assets/oz-BzwKVEFT.js +1 -0
  248. package/dist/assets/pascal--L3eBynH.js +1 -0
  249. package/dist/assets/pascal-D93ZcfNL.js +1 -0
  250. package/dist/assets/perl-C0TMdlhV.js +1 -0
  251. package/dist/assets/perl-CdXCOZ3F.js +1 -0
  252. package/dist/assets/php-CDn_0X-4.js +1 -0
  253. package/dist/assets/pig-CevX1Tat.js +1 -0
  254. package/dist/assets/pkl-u5AG7uiY.js +1 -0
  255. package/dist/assets/plastic-3e1v2bzS.js +1 -0
  256. package/dist/assets/plsql-ChMvpjG-.js +1 -0
  257. package/dist/assets/po-BTJTHyun.js +1 -0
  258. package/dist/assets/poimandres-CS3Unz2-.js +1 -0
  259. package/dist/assets/polar-C0HS_06l.js +1 -0
  260. package/dist/assets/postcss-CXtECtnM.js +1 -0
  261. package/dist/assets/powerquery-CEu0bR-o.js +1 -0
  262. package/dist/assets/powershell-CFHJl5sT.js +1 -0
  263. package/dist/assets/powershell-Dpen1YoG.js +1 -0
  264. package/dist/assets/prisma-Dd19v3D-.js +1 -0
  265. package/dist/assets/prolog-CbFg5uaA.js +1 -0
  266. package/dist/assets/properties-C78fOPTZ.js +1 -0
  267. package/dist/assets/proto-DyJlTyXw.js +1 -0
  268. package/dist/assets/protobuf-ChK-085T.js +1 -0
  269. package/dist/assets/pug-CGlum2m_.js +1 -0
  270. package/dist/assets/pug-DeIclll2.js +1 -0
  271. package/dist/assets/puppet-BMWR74SV.js +1 -0
  272. package/dist/assets/puppet-DMA9R1ak.js +1 -0
  273. package/dist/assets/purescript-CklMAg4u.js +1 -0
  274. package/dist/assets/python-B6aJPvgy.js +1 -0
  275. package/dist/assets/python-BuPzkPfP.js +1 -0
  276. package/dist/assets/q-pXgVlZs6.js +1 -0
  277. package/dist/assets/qml-3beO22l8.js +1 -0
  278. package/dist/assets/qmldir-C8lEn-DE.js +1 -0
  279. package/dist/assets/qss-IeuSbFQv.js +1 -0
  280. package/dist/assets/r-B6wPVr8A.js +1 -0
  281. package/dist/assets/r-DiinP2Uv.js +1 -0
  282. package/dist/assets/racket-BqYA7rlc.js +1 -0
  283. package/dist/assets/raku-DXvB9xmW.js +1 -0
  284. package/dist/assets/razor-CE9lU5zL.js +1 -0
  285. package/dist/assets/red-bN70gL4F.js +1 -0
  286. package/dist/assets/reg-C-SQnVFl.js +1 -0
  287. package/dist/assets/regexp-CDVJQ6XC.js +1 -0
  288. package/dist/assets/rel-C3B-1QV4.js +1 -0
  289. package/dist/assets/riscv-BM1_JUlF.js +1 -0
  290. package/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  291. package/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  292. package/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
  293. package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
  294. package/dist/assets/rpm-CTu-6PCP.js +1 -0
  295. package/dist/assets/rst-B0xPkSld.js +1 -0
  296. package/dist/assets/ruby-B2Rjki9n.js +1 -0
  297. package/dist/assets/ruby-BvKwtOVI.js +1 -0
  298. package/dist/assets/rust-B1yitclQ.js +1 -0
  299. package/dist/assets/sas-B4kiWyti.js +1 -0
  300. package/dist/assets/sas-cz2c8ADy.js +1 -0
  301. package/dist/assets/sass-Cj5Yp3dK.js +1 -0
  302. package/dist/assets/scala-C151Ov-r.js +1 -0
  303. package/dist/assets/scheme-C41bIUwD.js +1 -0
  304. package/dist/assets/scheme-C98Dy4si.js +1 -0
  305. package/dist/assets/scss-OYdSNvt2.js +1 -0
  306. package/dist/assets/sdbl-DVxCFoDh.js +1 -0
  307. package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
  308. package/dist/assets/shell-CjFT_Tl9.js +1 -0
  309. package/dist/assets/shellscript-Yzrsuije.js +1 -0
  310. package/dist/assets/shellsession-BADoaaVG.js +1 -0
  311. package/dist/assets/sieve-C3Gn_uJK.js +1 -0
  312. package/dist/assets/simple-mode-GW_nhZxv.js +1 -0
  313. package/dist/assets/slack-dark-BthQWCQV.js +1 -0
  314. package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  315. package/dist/assets/smalltalk-BERRCDM3.js +1 -0
  316. package/dist/assets/smalltalk-CnHTOXQT.js +1 -0
  317. package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  318. package/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
  319. package/dist/assets/solarized-light-L9t79GZl.js +1 -0
  320. package/dist/assets/solidity-rGO070M0.js +1 -0
  321. package/dist/assets/solr-DehyRSwq.js +1 -0
  322. package/dist/assets/soy-Brmx7dQM.js +1 -0
  323. package/dist/assets/sparql-DkYu6x3z.js +1 -0
  324. package/dist/assets/sparql-rVzFXLq3.js +1 -0
  325. package/dist/assets/splunk-BtCnVYZw.js +1 -0
  326. package/dist/assets/spreadsheet-BCZA_wO0.js +1 -0
  327. package/dist/assets/sql-BLtJtn59.js +1 -0
  328. package/dist/assets/sql-D0XecflT.js +1 -0
  329. package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
  330. package/dist/assets/stata-BH5u7GGu.js +1 -0
  331. package/dist/assets/stex-C3f8Ysf7.js +1 -0
  332. package/dist/assets/stylus-B533Al4x.js +1 -0
  333. package/dist/assets/stylus-BEDo0Tqx.js +1 -0
  334. package/dist/assets/svelte-3Dk4HxPD.js +1 -0
  335. package/dist/assets/swift-BzpIVaGY.js +1 -0
  336. package/dist/assets/swift-Dg5xB15N.js +1 -0
  337. package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  338. package/dist/assets/system-verilog-CnnmHF94.js +1 -0
  339. package/dist/assets/systemd-4A_iFExJ.js +1 -0
  340. package/dist/assets/talonscript-CkByrt1z.js +1 -0
  341. package/dist/assets/tasl-QIJgUcNo.js +1 -0
  342. package/dist/assets/tcl-DVfN8rqt.js +1 -0
  343. package/dist/assets/tcl-dwOrl1Do.js +1 -0
  344. package/dist/assets/templ-W15q3VgB.js +1 -0
  345. package/dist/assets/terraform-BETggiCN.js +1 -0
  346. package/dist/assets/tex-CxkMU7Pf.js +1 -0
  347. package/dist/assets/textile-CnDTJFAw.js +1 -0
  348. package/dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  349. package/dist/assets/tiki-DGYXhP31.js +1 -0
  350. package/dist/assets/tokyo-night-hegEt444.js +1 -0
  351. package/dist/assets/toml-Bm5Em-hy.js +1 -0
  352. package/dist/assets/toml-vGWfd6FD.js +1 -0
  353. package/dist/assets/troff-wAsdV37c.js +1 -0
  354. package/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
  355. package/dist/assets/tsv-B_m7g4N7.js +1 -0
  356. package/dist/assets/tsx-COt5Ahok.js +1 -0
  357. package/dist/assets/ttcn-CfJYG6tj.js +1 -0
  358. package/dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  359. package/dist/assets/turtle-B1tBg_DP.js +1 -0
  360. package/dist/assets/turtle-BsS91CYL.js +1 -0
  361. package/dist/assets/twig-CO9l9SDP.js +1 -0
  362. package/dist/assets/typescript-BPQ3VLAy.js +1 -0
  363. package/dist/assets/typespec-BGHnOYBU.js +1 -0
  364. package/dist/assets/typst-DHCkPAjA.js +1 -0
  365. package/dist/assets/v-BcVCzyr7.js +1 -0
  366. package/dist/assets/vala-CsfeWuGM.js +1 -0
  367. package/dist/assets/vb-CmGdzxic.js +1 -0
  368. package/dist/assets/vb-D17OF-Vu.js +1 -0
  369. package/dist/assets/vbscript-BuJXcnF6.js +1 -0
  370. package/dist/assets/velocity-D8B20fx6.js +1 -0
  371. package/dist/assets/verilog-BQ8w6xss.js +1 -0
  372. package/dist/assets/verilog-C6RDOZhf.js +1 -0
  373. package/dist/assets/vesper-DU1UobuO.js +1 -0
  374. package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
  375. package/dist/assets/vhdl-lSbBsy5d.js +1 -0
  376. package/dist/assets/viml-CJc9bBzg.js +1 -0
  377. package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  378. package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  379. package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  380. package/dist/assets/vue-DnHKYNfI.js +1 -0
  381. package/dist/assets/vue-html-CChd_i61.js +1 -0
  382. package/dist/assets/vue-vine-8moa0y9V.js +1 -0
  383. package/dist/assets/vyper-CDx5xZoG.js +1 -0
  384. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  385. package/dist/assets/wasm-MzD3tlZU.js +1 -0
  386. package/dist/assets/webidl-ZXfAyPTL.js +1 -0
  387. package/dist/assets/wenyan-BV7otONQ.js +1 -0
  388. package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
  389. package/dist/assets/wikitext-BhOHFoWU.js +1 -0
  390. package/dist/assets/wit-5i3qLPDT.js +1 -0
  391. package/dist/assets/wolfram-lXgVvXCa.js +1 -0
  392. package/dist/assets/xml-sdJ4AIDG.js +1 -0
  393. package/dist/assets/xquery-DzFWVndE.js +1 -0
  394. package/dist/assets/xsl-CtQFsRM5.js +1 -0
  395. package/dist/assets/yacas-BJ4BC0dw.js +1 -0
  396. package/dist/assets/yaml-Buea-lGh.js +1 -0
  397. package/dist/assets/z80-Hz9HOZM7.js +1 -0
  398. package/dist/assets/zenscript-DVFEvuxE.js +1 -0
  399. package/dist/assets/zig-VOosw3JB.js +1 -0
  400. package/dist/index.html +47 -0
  401. package/dist/logo.svg +8 -0
  402. package/dist/openspec_pixel_dark.svg +89 -0
  403. package/dist/openspec_pixel_light.svg +89 -0
  404. package/index.html +46 -0
  405. package/package.json +66 -0
  406. package/src/App.tsx +124 -0
  407. package/src/components/StaticModeBanner.tsx +46 -0
  408. package/src/components/change-overview.tsx +156 -0
  409. package/src/components/cli-terminal.tsx +93 -0
  410. package/src/components/code-editor.tsx +232 -0
  411. package/src/components/copyable-path.tsx +44 -0
  412. package/src/components/dialog.tsx +193 -0
  413. package/src/components/folder-editor-viewer.tsx +411 -0
  414. package/src/components/global-archive-modal.tsx +205 -0
  415. package/src/components/layout/desktop-sidebar.tsx +48 -0
  416. package/src/components/layout/index.ts +6 -0
  417. package/src/components/layout/mobile-header.tsx +91 -0
  418. package/src/components/layout/mobile-tabbar.tsx +20 -0
  419. package/src/components/layout/nav-items.ts +33 -0
  420. package/src/components/layout/root-layout.tsx +67 -0
  421. package/src/components/layout/status-bar.tsx +69 -0
  422. package/src/components/markdown-content.tsx +104 -0
  423. package/src/components/markdown-viewer.tsx +415 -0
  424. package/src/components/path-marquee.tsx +104 -0
  425. package/src/components/tabs.tsx +151 -0
  426. package/src/components/tasks-view.test.tsx +45 -0
  427. package/src/components/tasks-view.tsx +209 -0
  428. package/src/components/toc-context.tsx +177 -0
  429. package/src/components/toc.tsx +290 -0
  430. package/src/entry-client.tsx +47 -0
  431. package/src/index.css +481 -0
  432. package/src/lib/api-config.ts +72 -0
  433. package/src/lib/archive-modal-context.tsx +45 -0
  434. package/src/lib/codemirror-markdown-preview.ts +494 -0
  435. package/src/lib/format-time.ts +51 -0
  436. package/src/lib/shiki-highlighter.ts +47 -0
  437. package/src/lib/static-data-provider.ts +386 -0
  438. package/src/lib/static-mode.ts +158 -0
  439. package/src/lib/trpc.ts +107 -0
  440. package/src/lib/use-cli-runner.tsx +433 -0
  441. package/src/lib/use-dark-mode.ts +28 -0
  442. package/src/lib/use-server-status.ts +208 -0
  443. package/src/lib/use-subscription.ts +375 -0
  444. package/src/lib/use-tabs-status-by-query.ts +78 -0
  445. package/src/main.tsx +9 -0
  446. package/src/routes/archive-list.tsx +65 -0
  447. package/src/routes/archive-view.tsx +116 -0
  448. package/src/routes/change-list.tsx +63 -0
  449. package/src/routes/change-view.tsx +188 -0
  450. package/src/routes/dashboard.tsx +204 -0
  451. package/src/routes/project.tsx +272 -0
  452. package/src/routes/settings.tsx +816 -0
  453. package/src/routes/spec-list.tsx +49 -0
  454. package/src/routes/spec-view.tsx +164 -0
  455. package/src/ssg/entry-server.tsx +111 -0
  456. package/src/ssg/index.ts +6 -0
  457. package/src/ssg/prerender.ts +111 -0
  458. package/src/ssg/static-data-context.tsx +49 -0
  459. package/src/ssg/types.ts +5 -0
  460. package/src/test/setup.ts +1 -0
  461. package/src/vite-env.d.ts +6 -0
  462. package/tsconfig.json +18 -0
  463. package/vite.config.ts +58 -0
@@ -0,0 +1,415 @@
1
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from 'react'
2
+ import { MarkdownContent } from './markdown-content'
3
+ import { generateTimelineScope, Toc, type TocItem } from './toc'
4
+ import { slugify, TocCollector, TocLevelProvider, TocProvider, useTocContext } from './toc-context'
5
+
6
+ // ============================================================================
7
+ // Types
8
+ // ============================================================================
9
+
10
+ type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6
11
+
12
+ interface HeadingProps {
13
+ id?: string
14
+ children?: ReactNode
15
+ className?: string
16
+ }
17
+
18
+ type HeadingComponent = (props: HeadingProps) => ReactNode
19
+
20
+ interface SectionProps {
21
+ children?: ReactNode
22
+ className?: string
23
+ }
24
+
25
+ type SectionComponent = (props: SectionProps) => ReactNode
26
+
27
+ export interface BuilderComponents {
28
+ H1: HeadingComponent
29
+ H2: HeadingComponent
30
+ H3: HeadingComponent
31
+ H4: HeadingComponent
32
+ H5: HeadingComponent
33
+ H6: HeadingComponent
34
+ /** Section 会自动将内部内容的 ToC 层级 +1 */
35
+ Section: SectionComponent
36
+ }
37
+
38
+ export type MarkdownBuilderFn = (components: BuilderComponents) => ReactNode
39
+
40
+ export interface MarkdownViewerProps {
41
+ /** Markdown 内容:字符串或 Builder 函数 */
42
+ markdown: string | MarkdownBuilderFn
43
+ className?: string
44
+ /** 渲染和 ToC 建立完成后回调(用于外层占位控制) */
45
+ onReady?: () => void
46
+ }
47
+
48
+ // ============================================================================
49
+ // MarkdownViewer - 主组件
50
+ // ============================================================================
51
+
52
+ /**
53
+ * 统一的 Markdown 文档查看器,支持 ToC 侧边栏和嵌套。
54
+ *
55
+ * 两种使用模式:
56
+ * 1. 字符串模式:`<MarkdownViewer markdown="# Hello" />`
57
+ * 2. Builder 模式:`<MarkdownViewer markdown={({ H1, Section }) => <H1>Hello</H1>} />`
58
+ *
59
+ * 嵌套时自动检测父级 Context,只渲染内容不显示 ToC sidebar。
60
+ */
61
+ export function MarkdownViewer({ markdown, className = '', onReady }: MarkdownViewerProps) {
62
+ const parentCtx = useTocContext()
63
+ const isNested = !!parentCtx
64
+
65
+ if (isNested) {
66
+ // 嵌套模式:只渲染内容,向父级贡献 ToC items
67
+ return <NestedMarkdownViewer markdown={markdown} className={className} />
68
+ }
69
+
70
+ // 顶层模式:渲染完整布局(content + ToC sidebar)
71
+ return <RootMarkdownViewer markdown={markdown} className={className} onReady={onReady} />
72
+ }
73
+
74
+ // ============================================================================
75
+ // RootMarkdownViewer - 顶层模式
76
+ // ============================================================================
77
+
78
+ function RootMarkdownViewer({ markdown, className, onReady }: MarkdownViewerProps) {
79
+ const [tocItems, setTocItems] = useState<TocItem[]>([])
80
+ const collectorRef = useRef<TocCollector>(null!)
81
+ const readyCalledRef = useRef(false)
82
+
83
+ // 每次渲染前重置 collector
84
+ collectorRef.current = new TocCollector()
85
+ const collector = collectorRef.current
86
+
87
+ // 渲染后(首帧前)同步更新 tocItems,避免移动端 ToC 迟到导致的布局抖动
88
+ useLayoutEffect(() => {
89
+ const newItems = collectorRef.current.getItems()
90
+ setTocItems((prev) => {
91
+ if (arraysEqual(prev, newItems)) return prev
92
+ return newItems
93
+ })
94
+ })
95
+
96
+ // 通知外层:内容与 ToC 已经挂载(首个 effect 后触发一次)
97
+ useEffect(() => {
98
+ if (readyCalledRef.current) return
99
+ readyCalledRef.current = true
100
+ onReady?.()
101
+ }, [onReady, tocItems])
102
+
103
+ const timelineScope = useMemo(() => generateTimelineScope(tocItems), [tocItems])
104
+
105
+ // 渲染内容(在 TocProvider 内部,这样嵌套的 MarkdownViewer 能获取 Context)
106
+ const content =
107
+ typeof markdown === 'string' ? (
108
+ <StringMarkdownContent markdown={markdown} collector={collector} levelOffset={0} />
109
+ ) : (
110
+ <BuilderMarkdownContent builder={markdown} collector={collector} levelOffset={0} />
111
+ )
112
+
113
+ return (
114
+ <TocProvider collector={collector} levelOffset={0} isRoot>
115
+ <div className={`@container-[size] h-full ${className}`}>
116
+ <style>{viewerStyles}</style>
117
+ <MarkdownContainer className="viewer-layout gap-6" timelineScope={timelineScope}>
118
+ <Toc items={tocItems} className="viewer-toc" />
119
+ <div className="viewer-content min-w-0">{content}</div>
120
+ </MarkdownContainer>
121
+ </div>
122
+ </TocProvider>
123
+ )
124
+ }
125
+
126
+ // ============================================================================
127
+ // NestedMarkdownViewer - 嵌套模式
128
+ // ============================================================================
129
+
130
+ function NestedMarkdownViewer({ markdown, className }: MarkdownViewerProps) {
131
+ const ctx = useTocContext()!
132
+ const { collector, levelOffset } = ctx
133
+
134
+ // 嵌套模式:使用父级的 collector,但应用当前的 levelOffset
135
+ return typeof markdown === 'string' ? (
136
+ <StringMarkdownContent
137
+ markdown={markdown}
138
+ className={className}
139
+ collector={collector}
140
+ levelOffset={levelOffset}
141
+ />
142
+ ) : (
143
+ <BuilderMarkdownContent
144
+ builder={markdown}
145
+ className={className}
146
+ collector={collector}
147
+ levelOffset={levelOffset}
148
+ />
149
+ )
150
+ }
151
+
152
+ // ============================================================================
153
+ // StringMarkdownContent - 字符串模式内容
154
+ // ============================================================================
155
+
156
+ function StringMarkdownContent({
157
+ markdown,
158
+ collector,
159
+ levelOffset,
160
+ className,
161
+ }: {
162
+ markdown: string
163
+ collector: TocCollector
164
+ levelOffset: number
165
+ className?: string
166
+ }) {
167
+ // 为 markdown 中的标题创建自定义组件
168
+ const components = useMemo(() => {
169
+ const slugCount = new Map<string, number>()
170
+
171
+ const createHeading = (level: HeadingLevel) => {
172
+ return function Heading({ children }: { children?: ReactNode }) {
173
+ const text = extractTextFromChildren(children)
174
+ const baseSlug = slugify(text) || 'heading'
175
+
176
+ // 处理重复 id
177
+ const count = slugCount.get(baseSlug) ?? 0
178
+ slugCount.set(baseSlug, count + 1)
179
+ const id = count > 0 ? `${baseSlug}-${count + 1}` : baseSlug
180
+
181
+ // 应用层级偏移
182
+ const adjustedLevel = Math.min(level + levelOffset, 6) as HeadingLevel
183
+ const { index } = collector.add(text, adjustedLevel, id)
184
+
185
+ return (
186
+ <HeadingElement level={adjustedLevel} id={id} index={index}>
187
+ {children}
188
+ </HeadingElement>
189
+ )
190
+ }
191
+ }
192
+
193
+ return {
194
+ h1: createHeading(1),
195
+ h2: createHeading(2),
196
+ h3: createHeading(3),
197
+ h4: createHeading(4),
198
+ h5: createHeading(5),
199
+ h6: createHeading(6),
200
+ }
201
+ }, [collector, levelOffset])
202
+
203
+ return (
204
+ <MarkdownContent className={className} components={components}>
205
+ {markdown}
206
+ </MarkdownContent>
207
+ )
208
+ }
209
+
210
+ // ============================================================================
211
+ // BuilderMarkdownContent - Builder 模式内容
212
+ // ============================================================================
213
+
214
+ function BuilderMarkdownContent({
215
+ builder,
216
+ collector,
217
+ levelOffset,
218
+ className,
219
+ }: {
220
+ builder: MarkdownBuilderFn
221
+ collector: TocCollector
222
+ levelOffset: number
223
+ className?: string
224
+ }) {
225
+ // 创建 Builder 组件
226
+ const components = useMemo<BuilderComponents>(() => {
227
+ const slugCount = new Map<string, number>()
228
+
229
+ const createHeading = (level: HeadingLevel): HeadingComponent => {
230
+ return function Heading({ id: fixedId, className, children }: HeadingProps) {
231
+ // 从 context 实时获取 levelOffset,支持 Section 嵌套
232
+ const ctx = useTocContext()
233
+ const currentLevelOffset = ctx?.levelOffset ?? levelOffset
234
+
235
+ const text = extractTextFromChildren(children)
236
+ const baseSlug = fixedId ?? (slugify(text) || 'heading')
237
+
238
+ // 处理重复 id
239
+ const count = slugCount.get(baseSlug) ?? 0
240
+ slugCount.set(baseSlug, count + 1)
241
+ const id = count > 0 ? `${baseSlug}-${count + 1}` : baseSlug
242
+
243
+ // 应用层级偏移
244
+ const adjustedLevel = Math.min(level + currentLevelOffset, 6) as HeadingLevel
245
+ const { index } = collector.add(text, adjustedLevel, id)
246
+
247
+ return (
248
+ <HeadingElement level={adjustedLevel} id={id} index={index} className={className}>
249
+ {children}
250
+ </HeadingElement>
251
+ )
252
+ }
253
+ }
254
+
255
+ const Section: SectionComponent = ({ children, className }) => {
256
+ // Section 通过 TocLevelProvider 提供层级 +1
257
+ return (
258
+ <TocLevelProvider additionalOffset={1}>
259
+ <section className={`markdown-section ${className}`}>{children}</section>
260
+ </TocLevelProvider>
261
+ )
262
+ }
263
+
264
+ return {
265
+ H1: createHeading(1),
266
+ H2: createHeading(2),
267
+ H3: createHeading(3),
268
+ H4: createHeading(4),
269
+ H5: createHeading(5),
270
+ H6: createHeading(6),
271
+ Section,
272
+ }
273
+ }, [collector, levelOffset])
274
+
275
+ return <div className={`markdown-content ${className}`}>{builder(components)}</div>
276
+ }
277
+
278
+ // ============================================================================
279
+ // Helper Components
280
+ // ============================================================================
281
+
282
+ function HeadingElement({
283
+ level,
284
+ id,
285
+ index,
286
+ children,
287
+ className,
288
+ }: {
289
+ level: HeadingLevel
290
+ id: string
291
+ index: number
292
+ children?: ReactNode
293
+ className?: string
294
+ }) {
295
+ const style = { viewTimelineName: `--toc-${index}` }
296
+ switch (level) {
297
+ case 1:
298
+ return (
299
+ <h1 id={id} className={className} style={style}>
300
+ {children}
301
+ </h1>
302
+ )
303
+ case 2:
304
+ return (
305
+ <h2 id={id} className={className} style={style}>
306
+ {children}
307
+ </h2>
308
+ )
309
+ case 3:
310
+ return (
311
+ <h3 id={id} className={className} style={style}>
312
+ {children}
313
+ </h3>
314
+ )
315
+ case 4:
316
+ return (
317
+ <h4 id={id} className={className} style={style}>
318
+ {children}
319
+ </h4>
320
+ )
321
+ case 5:
322
+ return (
323
+ <h5 id={id} className={className} style={style}>
324
+ {children}
325
+ </h5>
326
+ )
327
+ case 6:
328
+ return (
329
+ <h6 id={id} className={className} style={style}>
330
+ {children}
331
+ </h6>
332
+ )
333
+ }
334
+ }
335
+
336
+ // ============================================================================
337
+ // MarkdownContainer - 布局容器
338
+ // ============================================================================
339
+
340
+ interface MarkdownContainerProps {
341
+ children: ReactNode
342
+ className?: string
343
+ /** CSS timeline-scope value for ToC scroll tracking */
344
+ timelineScope?: string
345
+ }
346
+
347
+ /**
348
+ * Shared container for markdown-style content with consistent scrolling and padding.
349
+ */
350
+ function MarkdownContainer({ children, className = '', timelineScope }: MarkdownContainerProps) {
351
+ return (
352
+ <div
353
+ className={`scrollbar-thin scrollbar-track-transparent h-full overflow-auto scroll-smooth p-4 ${className}`}
354
+ style={timelineScope ? ({ timelineScope } as React.CSSProperties) : undefined}
355
+ >
356
+ {children}
357
+ </div>
358
+ )
359
+ }
360
+
361
+ // ============================================================================
362
+ // Utilities
363
+ // ============================================================================
364
+
365
+ /** 从 React children 中提取纯文本 */
366
+ function extractTextFromChildren(children: ReactNode): string {
367
+ if (children == null || typeof children === 'boolean') return ''
368
+ if (typeof children === 'string' || typeof children === 'number') return String(children)
369
+ if (Array.isArray(children)) return children.map(extractTextFromChildren).join('')
370
+ if (typeof children === 'object' && 'props' in children) {
371
+ return extractTextFromChildren(
372
+ (children as { props?: { children?: ReactNode } }).props?.children
373
+ )
374
+ }
375
+ return ''
376
+ }
377
+
378
+ /** 比较两个 TocItem 数组是否相等 */
379
+ function arraysEqual(a: TocItem[], b: TocItem[]): boolean {
380
+ if (a.length !== b.length) return false
381
+ return a.every(
382
+ (item, i) => item.id === b[i].id && item.label === b[i].label && item.level === b[i].level
383
+ )
384
+ }
385
+
386
+ // ============================================================================
387
+ // Styles
388
+ // ============================================================================
389
+
390
+ const css = String.raw
391
+ /** CSS for container queries layout */
392
+ const viewerStyles = css`
393
+ /* Container query based layout */
394
+ .viewer-layout {
395
+ display: block;
396
+ }
397
+ .viewer-toc {
398
+ margin-bottom: 1rem;
399
+ }
400
+
401
+ /* Wide container: grid layout with ToC on right */
402
+ @container (min-width: 768px) {
403
+ .viewer-layout {
404
+ display: grid;
405
+ grid-template-columns: 1fr 180px;
406
+ }
407
+ .viewer-toc {
408
+ order: 2;
409
+ margin-bottom: 0;
410
+ }
411
+ .viewer-content {
412
+ order: 1;
413
+ }
414
+ }
415
+ `
@@ -0,0 +1,104 @@
1
+ import { Check, Copy } from 'lucide-react'
2
+ import { useCallback, useEffect, useRef, useState } from 'react'
3
+
4
+ interface PathMarqueeProps {
5
+ /** The path to display */
6
+ children: string
7
+ /** Maximum width of the container (default: 300px) */
8
+ maxWidth?: number | string
9
+ /** Animation duration in seconds (default: 10) */
10
+ duration?: number
11
+ /** Gap between repeated content (default: 20px) */
12
+ gap?: number
13
+ /** Additional className for the container */
14
+ className?: string
15
+ }
16
+
17
+ /**
18
+ * A marquee component for displaying long paths with auto-scrolling animation.
19
+ * - Pure CSS animation using ::after pseudo-element
20
+ * - Pauses on hover
21
+ * - Click to copy path
22
+ * - Only animates when content overflows
23
+ */
24
+ export function PathMarquee({
25
+ children: path,
26
+ maxWidth = 300,
27
+ duration = 10,
28
+ gap = 20,
29
+ className = '',
30
+ }: PathMarqueeProps) {
31
+ const [copied, setCopied] = useState(false)
32
+ const [shouldAnimate, setShouldAnimate] = useState(false)
33
+ const containerRef = useRef<HTMLDivElement>(null)
34
+ const contentRef = useRef<HTMLDivElement>(null)
35
+
36
+ // Check if content overflows and needs animation
37
+ useEffect(() => {
38
+ const checkOverflow = () => {
39
+ if (containerRef.current && contentRef.current) {
40
+ const containerWidth = containerRef.current.offsetWidth
41
+ const contentWidth = contentRef.current.scrollWidth
42
+ setShouldAnimate(contentWidth > containerWidth)
43
+ }
44
+ }
45
+
46
+ checkOverflow()
47
+ window.addEventListener('resize', checkOverflow)
48
+ return () => window.removeEventListener('resize', checkOverflow)
49
+ }, [path])
50
+
51
+ // Handle copy to clipboard
52
+ const handleCopy = useCallback(async () => {
53
+ try {
54
+ await navigator.clipboard.writeText(path)
55
+ setCopied(true)
56
+ setTimeout(() => setCopied(false), 2000)
57
+ } catch (err) {
58
+ console.error('Failed to copy path:', err)
59
+ }
60
+ }, [path])
61
+
62
+ const maxWidthStyle = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth
63
+
64
+ return (
65
+ <button
66
+ onClick={handleCopy}
67
+ className={`hover:bg-muted/50 group flex cursor-pointer items-center gap-1.5 rounded transition-colors ${className}`}
68
+ title={`${path}\n\nClick to copy`}
69
+ >
70
+ {/* Marquee container */}
71
+ <div
72
+ ref={containerRef}
73
+ className="relative overflow-hidden whitespace-nowrap"
74
+ style={{ maxWidth: maxWidthStyle }}
75
+ >
76
+ {/* Animated content */}
77
+ <div
78
+ ref={contentRef}
79
+ data-content={path}
80
+ className={`relative inline-block ${shouldAnimate ? 'animate-marquee group-hover:[animation-play-state:paused]!' : ''}`}
81
+ style={
82
+ shouldAnimate
83
+ ? ({
84
+ '--marquee-duration': `${duration}s`,
85
+ '--marquee-gap': `${gap}px`,
86
+ } as React.CSSProperties)
87
+ : undefined
88
+ }
89
+ >
90
+ {path}
91
+ </div>
92
+ </div>
93
+
94
+ {/* Copy indicator */}
95
+ <span className="text-muted-foreground group-hover:text-foreground shrink-0 transition-colors">
96
+ {copied ? (
97
+ <Check className="h-3.5 w-3.5 text-green-500" />
98
+ ) : (
99
+ <Copy className="h-3.5 w-3.5" />
100
+ )}
101
+ </span>
102
+ </button>
103
+ )
104
+ }
@@ -0,0 +1,151 @@
1
+ import { Activity, useId, useMemo, useState, type ReactNode } from 'react'
2
+
3
+ export interface Tab {
4
+ id: string
5
+ label: ReactNode
6
+ icon?: ReactNode
7
+ content: ReactNode
8
+ /** Unmount the tab content when hidden to avoid heavy components lingering (e.g., Monaco) */
9
+ unmountOnHide?: boolean
10
+ }
11
+
12
+ interface TabsProps {
13
+ tabs: Tab[]
14
+ /** Controlled selected tab id */
15
+ selectedTab?: string
16
+ onTabChange?: (id: string) => void
17
+ className?: string
18
+ }
19
+
20
+ const tabsStyle = (id: string) => {
21
+ const css = String.raw
22
+ const anchorName = `--tabs-button-${id}`
23
+ return (
24
+ <style>
25
+ {css`
26
+ #${id} {
27
+ .tabs-button {
28
+ anchor-name: ${anchorName};
29
+ }
30
+ .tabs-button::scroll-button(*) {
31
+ position-anchor: ${anchorName};
32
+ }
33
+ }
34
+ ` +
35
+ css`
36
+ #${id} {
37
+ .tabs-button {
38
+ overflow-x: auto;
39
+ scroll-behavior: smooth;
40
+ overscroll-behavior-x: contain;
41
+ scroll-snap-type: x mandatory;
42
+ position: relative;
43
+ background-image: linear-gradient(
44
+ to bottom,
45
+ transparent,
46
+ transparent calc(100% - 1px),
47
+ var(--border) calc(100% - 1px),
48
+ var(--border)
49
+ );
50
+ & > button {
51
+ scroll-snap-align: start;
52
+ text-align: center;
53
+ &.tab-selected {
54
+ background-image: linear-gradient(
55
+ to bottom,
56
+ transparent,
57
+ transparent calc(100% - 2px),
58
+ var(--primary) calc(100% - 2px),
59
+ var(--primary)
60
+ );
61
+ }
62
+ }
63
+ }
64
+ .tabs-button::scroll-button(*) {
65
+ position: absolute;
66
+ align-self: anchor-center;
67
+ border: 0;
68
+ font-size: 1.2rem;
69
+ background: none;
70
+ z-index: 2;
71
+ }
72
+ .tabs-button::scroll-button(*):disabled {
73
+ opacity: 0;
74
+ }
75
+ .tabs-button::scroll-button(left) {
76
+ content: '◄';
77
+ right: calc(anchor(left) - 0.5rem);
78
+ transform: scaleX(0.5);
79
+ }
80
+
81
+ .tabs-button::scroll-button(right) {
82
+ content: '►';
83
+ left: calc(anchor(right) - 0.5rem);
84
+ transform: scaleX(0.5);
85
+ }
86
+ }
87
+ `}
88
+ </style>
89
+ )
90
+ }
91
+
92
+ /**
93
+ * Tabs component with React 19 Activity for state preservation.
94
+ * Hidden tabs are pre-rendered at lower priority and preserve their state.
95
+ * Supports both controlled and uncontrolled active tab.
96
+ */
97
+ export function Tabs({ tabs, selectedTab: controlled, onTabChange, className = '' }: TabsProps) {
98
+ const [uncontrolled, setUncontrolled] = useState<string>(tabs[0]?.id ?? '')
99
+ const activeTab = controlled ?? uncontrolled
100
+
101
+ const handleChange = (id: string) => {
102
+ if (!controlled) {
103
+ setUncontrolled(id)
104
+ }
105
+ onTabChange?.(id)
106
+ }
107
+
108
+ if (tabs.length === 0) return null
109
+
110
+ const id = useId()
111
+
112
+ const style = useMemo(() => tabsStyle(id), [id])
113
+
114
+ return (
115
+ <div id={id} className={`flex min-h-0 flex-1 flex-col ${className}`}>
116
+ {style}
117
+ {/* Tab buttons */}
118
+ <div className="tabs-button z-2 bg-background oveflow-x-auto scrollbar-none sticky top-0 flex gap-1">
119
+ {tabs.map((tab) => (
120
+ <button
121
+ key={tab.id}
122
+ onClick={() => handleChange(tab.id)}
123
+ className={`m-0 flex h-full shrink-0 items-center gap-2 px-4 py-2 text-sm font-medium transition-colors ${
124
+ activeTab === tab.id
125
+ ? 'tab-selected text-foreground'
126
+ : 'text-muted-foreground hover:text-foreground'
127
+ }`}
128
+ >
129
+ {tab.icon}
130
+ {tab.label}
131
+ </button>
132
+ ))}
133
+ </div>
134
+
135
+ {/* Tab content with Activity for state preservation */}
136
+ {tabs.map((tab) =>
137
+ tab.unmountOnHide ? (
138
+ activeTab === tab.id && (
139
+ <div key={tab.id} className="min-h-0 flex-1">
140
+ {tab.content}
141
+ </div>
142
+ )
143
+ ) : (
144
+ <Activity key={tab.id} mode={activeTab === tab.id ? 'visible' : 'hidden'}>
145
+ <div className="min-h-0 flex-1">{tab.content}</div>
146
+ </Activity>
147
+ )
148
+ )}
149
+ </div>
150
+ )
151
+ }
@@ -0,0 +1,45 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import { describe, expect, it } from 'vitest'
3
+ import { TasksView, type Task } from './tasks-view'
4
+
5
+ function renderTasks(tasks: Task[]) {
6
+ const completed = tasks.filter((task) => task.completed).length
7
+ return render(
8
+ <TasksView tasks={tasks} progress={{ total: tasks.length, completed }} tocBaseIndex={0} />
9
+ )
10
+ }
11
+
12
+ describe('TasksView', () => {
13
+ it('updates text when task text changes', () => {
14
+ const tasks: Task[] = [
15
+ {
16
+ id: 'task-1',
17
+ text: 'Initial task text',
18
+ completed: false,
19
+ section: 'Setup',
20
+ },
21
+ ]
22
+
23
+ const { rerender } = renderTasks(tasks)
24
+
25
+ expect(screen.getByText('Initial task text')).toBeInTheDocument()
26
+
27
+ const updatedTasks: Task[] = [
28
+ {
29
+ ...tasks[0],
30
+ text: 'Updated task text',
31
+ },
32
+ ]
33
+
34
+ rerender(
35
+ <TasksView
36
+ tasks={updatedTasks}
37
+ progress={{ total: updatedTasks.length, completed: 0 }}
38
+ tocBaseIndex={0}
39
+ />
40
+ )
41
+
42
+ expect(screen.getByText('Updated task text')).toBeInTheDocument()
43
+ expect(screen.queryByText('Initial task text')).not.toBeInTheDocument()
44
+ })
45
+ })