@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,205 @@
1
+ import { useArchiveModal } from '@/lib/archive-modal-context'
2
+ import { useCliRunner } from '@/lib/use-cli-runner'
3
+ import { useNavigate } from '@tanstack/react-router'
4
+ import { Archive, CheckCircle, Loader2 } from 'lucide-react'
5
+ import { useCallback, useEffect, useState } from 'react'
6
+ import { Dialog } from './dialog'
7
+ import { CliTerminal } from './cli-terminal'
8
+
9
+ /**
10
+ * 全局 Archive Modal(单一对话框,点击 Archive 后直接串行 validate -> archive)
11
+ */
12
+ export function GlobalArchiveModal() {
13
+ const navigate = useNavigate()
14
+ const { state, closeArchiveModal } = useArchiveModal()
15
+ const { open, changeId, changeName } = state
16
+
17
+ const [skipSpecs, setSkipSpecs] = useState(false)
18
+ const [noValidate, setNoValidate] = useState(false)
19
+ const [detectedArchiveId, setDetectedArchiveId] = useState<string | null>(null)
20
+
21
+ const runner = useCliRunner({
22
+ onCreateProcess: (process) => {
23
+ process.on('data', (data) => {
24
+ const match = /Change ['"](.+?)['"] archived as ['"](.+?)['"]/.exec(String(data))
25
+ if (match?.[2]) {
26
+ setDetectedArchiveId(match[2])
27
+ }
28
+ })
29
+ },
30
+ })
31
+ const { lines, status, hasStarted, commands, reset, cancel } = runner
32
+
33
+ // 当 Modal 打开时重置状态
34
+ useEffect(() => {
35
+ if (open) {
36
+ setSkipSpecs(false)
37
+ setNoValidate(false)
38
+ setDetectedArchiveId(null)
39
+ }
40
+ }, [open])
41
+
42
+ // 关闭并重置 - 使用 useCallback 稳定引用
43
+ const handleClose = () => {
44
+ cancel()
45
+ reset()
46
+ setSkipSpecs(false)
47
+ setNoValidate(false)
48
+ setDetectedArchiveId(null)
49
+ closeArchiveModal()
50
+ }
51
+
52
+ const buildQueue = useCallback(() => {
53
+ if (!changeId) return []
54
+ const queue: Array<{ command: string; args?: string[] }> = []
55
+ if (!noValidate) {
56
+ queue.push({ command: 'openspec', args: ['validate', changeId] })
57
+ }
58
+ const archiveArgs = ['archive', '-y', changeId]
59
+ if (skipSpecs) archiveArgs.push('--skip-specs')
60
+ archiveArgs.push('--no-validate')
61
+ queue.push({ command: 'openspec', args: archiveArgs })
62
+ return queue
63
+ }, [changeId, noValidate, skipSpecs])
64
+
65
+ const isRunning = status === 'running'
66
+ const isArchiveSuccess = status === 'success' && !!detectedArchiveId
67
+ const isArchiveOutputMissingId = status === 'success' && !detectedArchiveId
68
+ const archiveStatus = isArchiveSuccess ? 'success' : isArchiveOutputMissingId ? 'error' : status
69
+ const successArchiveId = detectedArchiveId ?? ''
70
+
71
+ // 开始执行 archive(若之前失败则自动重置并重跑)
72
+ const handleStartArchive = () => {
73
+ if (!changeId) return
74
+ commands.runAll()
75
+ }
76
+
77
+ const handleReset = () => {
78
+ reset()
79
+ setSkipSpecs(false)
80
+ setNoValidate(false)
81
+ setDetectedArchiveId(null)
82
+ }
83
+
84
+ useEffect(() => {
85
+ if (!open || !changeId || hasStarted) return
86
+ const queue = buildQueue()
87
+ commands.replaceAll(queue)
88
+ }, [buildQueue, changeId, commands, hasStarted, open])
89
+
90
+ if (!open || !changeId) return null
91
+
92
+ const borderVariant = archiveStatus === 'error' ? 'error' : archiveStatus === 'success' ? 'success' : 'default'
93
+
94
+ const footer = archiveStatus === 'success' && successArchiveId ? (
95
+ <div className="flex w-full items-center justify-between gap-3">
96
+ <div className="text-sm text-green-600">Archived as {successArchiveId}</div>
97
+ <div className="flex items-center gap-2">
98
+ <button onClick={handleClose} className="bg-muted hover:bg-muted/80 rounded-md px-4 py-2">
99
+ Close
100
+ </button>
101
+ <button
102
+ onClick={() => {
103
+ handleClose()
104
+ navigate({ to: '/archive/$changeId', params: { changeId: successArchiveId } })
105
+ }}
106
+ className="bg-primary text-primary-foreground rounded-md px-4 py-2 disabled:cursor-not-allowed disabled:opacity-50"
107
+ disabled={!successArchiveId}
108
+ >
109
+ View Archive
110
+ </button>
111
+ </div>
112
+ </div>
113
+ ) : (
114
+ <>
115
+ <button onClick={handleReset} className="bg-muted hover:bg-muted/80 rounded-md px-4 py-2">
116
+ {archiveStatus === 'error' ? 'Reset & Retry' : 'Reset'}
117
+ </button>
118
+ <button
119
+ onClick={handleClose}
120
+ className="bg-muted hover:bg-muted/80 rounded-md px-4 py-2 disabled:cursor-not-allowed disabled:opacity-50"
121
+ disabled={isRunning}
122
+ >
123
+ Close
124
+ </button>
125
+ <button
126
+ onClick={archiveStatus === 'error' ? handleReset : handleStartArchive}
127
+ disabled={isRunning}
128
+ className="flex items-center gap-2 rounded-md bg-red-600 px-4 py-2 text-white hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-50"
129
+ >
130
+ {isRunning ? <Loader2 className="h-4 w-4 animate-spin" /> : <Archive className="h-4 w-4" />}
131
+ {archiveStatus === 'error' ? 'Reset before Archive' : 'Archive'}
132
+ </button>
133
+ </>
134
+ )
135
+
136
+ return (
137
+ <Dialog
138
+ open={open}
139
+ onClose={handleClose}
140
+ title={
141
+ <div className="flex items-center gap-2">
142
+ {archiveStatus === 'success' ? (
143
+ <CheckCircle className="h-5 w-5 text-green-500" />
144
+ ) : (
145
+ <Archive className="h-5 w-5 text-red-500" />
146
+ )}
147
+ <span className="font-semibold">Archive: {changeName}</span>
148
+ </div>
149
+ }
150
+ footer={footer}
151
+ borderVariant={borderVariant}
152
+ >
153
+ <div className="space-y-4">
154
+ <div className="bg-muted/50 rounded-lg p-3">
155
+ <p className="text-muted-foreground text-sm">Change to archive:</p>
156
+ <p className="font-medium">{changeName}</p>
157
+ <p className="text-muted-foreground mt-1 text-xs">ID: {changeId}</p>
158
+ </div>
159
+
160
+ <CliTerminal
161
+ lines={lines}
162
+ maxHeight="50vh"
163
+ />
164
+
165
+ {isArchiveOutputMissingId && (
166
+ <div className="border-amber-200 bg-amber-100 text-amber-900 rounded-md border px-3 py-2 text-sm">
167
+ Archive output did not include the archived change name. Treating archive as failed.
168
+ </div>
169
+ )}
170
+
171
+ <div className="space-y-3">
172
+ <p className="text-sm font-medium">Options</p>
173
+
174
+ <label className="flex cursor-pointer items-start gap-3">
175
+ <input
176
+ type="checkbox"
177
+ checked={skipSpecs}
178
+ onChange={(e) => setSkipSpecs(e.target.checked)}
179
+ className="border-border mt-1 h-4 w-4 rounded"
180
+ disabled={hasStarted}
181
+ />
182
+ <div>
183
+ <p className="text-sm font-medium">Skip specs update</p>
184
+ <p className="text-muted-foreground text-xs">Don't update spec files with delta changes (--skip-specs)</p>
185
+ </div>
186
+ </label>
187
+
188
+ <label className="flex cursor-pointer items-start gap-3">
189
+ <input
190
+ type="checkbox"
191
+ checked={noValidate}
192
+ onChange={(e) => setNoValidate(e.target.checked)}
193
+ className="border-border mt-1 h-4 w-4 rounded"
194
+ disabled={hasStarted}
195
+ />
196
+ <div>
197
+ <p className="text-sm font-medium">Skip validation</p>
198
+ <p className="text-muted-foreground text-xs">Don't validate the change before archiving (--no-validate)</p>
199
+ </div>
200
+ </label>
201
+ </div>
202
+ </div>
203
+ </Dialog>
204
+ )
205
+ }
@@ -0,0 +1,48 @@
1
+ import { getBasePath } from '@/lib/static-mode'
2
+ import { useDarkMode } from '@/lib/use-dark-mode'
3
+ import { Link } from '@tanstack/react-router'
4
+ import { navItems, settingsItem } from './nav-items'
5
+
6
+ /** Desktop sidebar navigation */
7
+ export function DesktopSidebar() {
8
+ const isDark = useDarkMode()
9
+
10
+ // Get base path from runtime configuration
11
+ const basePath = getBasePath()
12
+
13
+ return (
14
+ <nav className="desktop-sidebar border-border bg-muted/30 flex w-64 shrink-0 flex-col border-r p-4">
15
+ <div className="mb-6">
16
+ <img
17
+ src={
18
+ isDark ? `${basePath}openspec_pixel_dark.svg` : `${basePath}openspec_pixel_light.svg`
19
+ }
20
+ alt="OpenSpec"
21
+ className="h-6"
22
+ />
23
+ </div>
24
+ <ul className="flex-1 space-y-1">
25
+ {navItems.map((item) => (
26
+ <li key={item.to}>
27
+ <Link
28
+ to={item.to}
29
+ className="hover:bg-muted [&.active]:bg-primary [&.active]:text-primary-foreground flex items-center gap-2 rounded-md px-3 py-2"
30
+ >
31
+ <item.icon className="h-4 w-4 shrink-0" />
32
+ <span className="font-nav text-base tracking-[0.04em]">{item.label}</span>
33
+ </Link>
34
+ </li>
35
+ ))}
36
+ </ul>
37
+ <div className="border-border border-t pt-4">
38
+ <Link
39
+ to={settingsItem.to}
40
+ className="hover:bg-muted [&.active]:bg-primary [&.active]:text-primary-foreground flex items-center gap-2 rounded-md px-3 py-2"
41
+ >
42
+ <settingsItem.icon className="h-4 w-4 shrink-0" />
43
+ <span className="font-nav text-base tracking-[0.04em]">{settingsItem.label}</span>
44
+ </Link>
45
+ </div>
46
+ </nav>
47
+ )
48
+ }
@@ -0,0 +1,6 @@
1
+ export { RootLayout } from './root-layout'
2
+ export { DesktopSidebar } from './desktop-sidebar'
3
+ export { MobileHeader } from './mobile-header'
4
+ export { MobileTabBar } from './mobile-tabbar'
5
+ export { StatusIndicator, DesktopStatusBar } from './status-bar'
6
+ export { navItems, settingsItem, type NavItem } from './nav-items'
@@ -0,0 +1,91 @@
1
+ import { getBasePath } from '@/lib/static-mode'
2
+ import { useDarkMode } from '@/lib/use-dark-mode'
3
+ import { useServerStatus } from '@/lib/use-server-status'
4
+ import { Link } from '@tanstack/react-router'
5
+ import { Menu, X } from 'lucide-react'
6
+ import { useState } from 'react'
7
+ import { navItems, settingsItem } from './nav-items'
8
+ import { StatusIndicator } from './status-bar'
9
+
10
+ /** Mobile header with hamburger menu */
11
+ export function MobileHeader() {
12
+ const [menuOpen, setMenuOpen] = useState(false)
13
+ const isDark = useDarkMode()
14
+ const serverStatus = useServerStatus()
15
+ const pageTitle = serverStatus.dirName ?? 'OpenSpec'
16
+
17
+ // Get base path from runtime configuration
18
+ const basePath = getBasePath()
19
+
20
+ return (
21
+ <>
22
+ <header className="mobile-header border-border bg-background flex h-12 items-center justify-between border-b px-4">
23
+ <div className="flex items-center gap-3">
24
+ <button
25
+ onClick={() => setMenuOpen(true)}
26
+ className="hover:bg-muted -ml-1.5 rounded-md p-1.5"
27
+ aria-label="Open menu"
28
+ >
29
+ <Menu className="h-5 w-5" />
30
+ </button>
31
+ <span className="font-nav text-[12px] tracking-[0.04em]">{pageTitle}</span>
32
+ </div>
33
+ <StatusIndicator />
34
+ </header>
35
+
36
+ {/* Mobile menu overlay */}
37
+ {menuOpen && (
38
+ <div className="fixed inset-0 z-50 flex">
39
+ <div
40
+ className="animate-in fade-in absolute inset-0 bg-black/50 duration-200"
41
+ onClick={() => setMenuOpen(false)}
42
+ />
43
+ <nav className="bg-background border-border animate-in slide-in-from-left relative flex w-64 max-w-[80vw] flex-col border-r p-4 duration-200">
44
+ <div className="mb-6 flex items-center justify-between">
45
+ <img
46
+ src={
47
+ isDark
48
+ ? `${basePath}openspec_pixel_dark.svg`
49
+ : `${basePath}openspec_pixel_light.svg`
50
+ }
51
+ alt="OpenSpec"
52
+ className="h-5"
53
+ />
54
+ <button
55
+ onClick={() => setMenuOpen(false)}
56
+ className="hover:bg-muted rounded-md p-1.5"
57
+ aria-label="Close menu"
58
+ >
59
+ <X className="h-5 w-5" />
60
+ </button>
61
+ </div>
62
+ <ul className="flex-1 space-y-1">
63
+ {navItems.map((item) => (
64
+ <li key={item.to}>
65
+ <Link
66
+ to={item.to}
67
+ onClick={() => setMenuOpen(false)}
68
+ className="hover:bg-muted [&.active]:bg-primary [&.active]:text-primary-foreground flex items-center gap-2 rounded-md px-3 py-2"
69
+ >
70
+ <item.icon className="h-4 w-4 shrink-0" />
71
+ <span className="font-nav text-[12px] tracking-[0.04em]">{item.label}</span>
72
+ </Link>
73
+ </li>
74
+ ))}
75
+ </ul>
76
+ <div className="border-border border-t pt-4">
77
+ <Link
78
+ to={settingsItem.to}
79
+ onClick={() => setMenuOpen(false)}
80
+ className="hover:bg-muted [&.active]:bg-primary [&.active]:text-primary-foreground flex items-center gap-2 rounded-md px-3 py-2"
81
+ >
82
+ <settingsItem.icon className="h-4 w-4 shrink-0" />
83
+ <span className="font-nav text-[12px] tracking-[0.04em]">{settingsItem.label}</span>
84
+ </Link>
85
+ </div>
86
+ </nav>
87
+ </div>
88
+ )}
89
+ </>
90
+ )
91
+ }
@@ -0,0 +1,20 @@
1
+ import { Link } from '@tanstack/react-router'
2
+ import { navItems } from './nav-items'
3
+
4
+ /** Mobile bottom tab bar - quick access to main sections */
5
+ export function MobileTabBar() {
6
+ return (
7
+ <nav className="mobile-tabbar h-14 border-t border-border bg-background flex items-stretch">
8
+ {navItems.slice(0, 5).map((item) => (
9
+ <Link
10
+ key={item.to}
11
+ to={item.to}
12
+ className="flex-1 flex flex-col items-center justify-center gap-0.5 text-muted-foreground hover:text-foreground [&.active]:text-primary"
13
+ >
14
+ <item.icon className="w-5 h-5 shrink-0" />
15
+ <span className="text-[10px] font-nav tracking-[0.03em]">{item.label}</span>
16
+ </Link>
17
+ ))}
18
+ </nav>
19
+ )
20
+ }
@@ -0,0 +1,33 @@
1
+ import {
2
+ FileText,
3
+ GitBranch,
4
+ LayoutDashboard,
5
+ Archive,
6
+ Folder,
7
+ Settings,
8
+ type LucideIcon,
9
+ } from 'lucide-react'
10
+
11
+ /** Valid top-level routes in the application */
12
+ type AppRoute = '/' | '/project' | '/specs' | '/changes' | '/archive' | '/settings'
13
+
14
+ export interface NavItem {
15
+ to: AppRoute
16
+ icon: LucideIcon
17
+ label: string
18
+ }
19
+
20
+ /** Navigation items configuration */
21
+ export const navItems: NavItem[] = [
22
+ { to: '/', icon: LayoutDashboard, label: 'Dashboard' },
23
+ { to: '/project', icon: Folder, label: 'Project' },
24
+ { to: '/specs', icon: FileText, label: 'Specs' },
25
+ { to: '/changes', icon: GitBranch, label: 'Changes' },
26
+ { to: '/archive', icon: Archive, label: 'Archive' },
27
+ ]
28
+
29
+ export const settingsItem: NavItem = {
30
+ to: '/settings',
31
+ icon: Settings,
32
+ label: 'Settings',
33
+ }
@@ -0,0 +1,67 @@
1
+ import { Outlet, useRouterState } from '@tanstack/react-router'
2
+ import { DesktopSidebar } from './desktop-sidebar'
3
+ import { MobileHeader } from './mobile-header'
4
+ import { MobileTabBar } from './mobile-tabbar'
5
+ import { DesktopStatusBar } from './status-bar'
6
+ import { GlobalArchiveModal } from '@/components/global-archive-modal'
7
+ import { StaticModeBanner } from '@/components/StaticModeBanner'
8
+ import { flushSync } from 'react-dom'
9
+ import { useLayoutEffect, useState } from 'react'
10
+
11
+ type ViewTransitionCapableDocument = Document & {
12
+ startViewTransition?: (updateCallback: () => void) => { finished: Promise<unknown> }
13
+ }
14
+
15
+ function ViewTransitionOutlet() {
16
+ // Ignore hash-only changes to avoid triggering view transitions on in-page anchor jumps
17
+ const locationKey = useRouterState({
18
+ select: (state) => `${state.location.pathname}${state.location.search}`,
19
+ })
20
+ const [renderKey, setRenderKey] = useState(locationKey)
21
+
22
+ useLayoutEffect(() => {
23
+ if (locationKey === renderKey) return
24
+ const doc = document as ViewTransitionCapableDocument
25
+ const performUpdate = () => setRenderKey(locationKey)
26
+
27
+ if (typeof doc.startViewTransition === 'function') {
28
+ document.documentElement.dataset.vtRunning = '1'
29
+ const transition = doc.startViewTransition(() => {
30
+ flushSync(performUpdate)
31
+ })
32
+ transition.finished
33
+ .catch(() => {
34
+ /* swallow errors; fallback already applied */
35
+ })
36
+ .finally(() => {
37
+ delete document.documentElement.dataset.vtRunning
38
+ })
39
+ } else {
40
+ performUpdate()
41
+ }
42
+ }, [locationKey, renderKey])
43
+
44
+ return <Outlet key={renderKey} />
45
+ }
46
+
47
+ /** Root layout with responsive navigation */
48
+ export function RootLayout() {
49
+ return (
50
+ <div className="fixed inset-0 @container/app" style={{ containerName: 'app' }}>
51
+ <div className="app-layout h-full">
52
+ <DesktopSidebar />
53
+ <div className="app-body flex flex-col flex-1 min-h-0">
54
+ <StaticModeBanner />
55
+ <MobileHeader />
56
+ <main className="main-content flex flex-col view-transition-route">
57
+ <ViewTransitionOutlet />
58
+ </main>
59
+ <MobileTabBar />
60
+ <DesktopStatusBar />
61
+ </div>
62
+ </div>
63
+ {/* 全局 Archive Modal - 在 Router 内部渲染 */}
64
+ <GlobalArchiveModal />
65
+ </div>
66
+ )
67
+ }
@@ -0,0 +1,69 @@
1
+ import { PathMarquee } from '@/components/path-marquee'
2
+ import { useManualReconnect, useServerStatus } from '@/lib/use-server-status'
3
+ import { FolderOpen, RefreshCw, Wifi, WifiOff } from 'lucide-react'
4
+
5
+ /** Status indicator - simplified for mobile, full for desktop */
6
+ export function StatusIndicator() {
7
+ const status = useServerStatus()
8
+ const reconnect = useManualReconnect()
9
+
10
+ if (status.connected) {
11
+ return (
12
+ <div className="status-indicator flex items-center gap-1.5 text-xs">
13
+ <Wifi className="h-3.5 w-3.5 text-green-500" />
14
+ <span className="status-text text-green-600">Live</span>
15
+ </div>
16
+ )
17
+ }
18
+
19
+ // 断开连接时显示重连提示
20
+ return (
21
+ <div className="status-indicator flex items-center gap-1.5 text-xs">
22
+ <WifiOff className="h-3.5 w-3.5 text-red-500" />
23
+ <span className="status-text text-red-600">Offline</span>
24
+ {status.reconnectCountdown !== null && (
25
+ <button
26
+ onClick={reconnect}
27
+ className="text-muted-foreground hover:text-foreground flex cursor-pointer items-center gap-1 transition-colors"
28
+ title="Click to reconnect now"
29
+ >
30
+ <span className="text-xs">({status.reconnectCountdown}s)</span>
31
+ <RefreshCw className="h-3 w-3" />
32
+ </button>
33
+ )}
34
+ </div>
35
+ )
36
+ }
37
+
38
+ /** Desktop status bar - full information */
39
+ export function DesktopStatusBar() {
40
+ const status = useServerStatus()
41
+
42
+ return (
43
+ <div className="desktop-status border-border bg-muted/30 text-muted-foreground flex h-8 items-center justify-between gap-4 border-t px-4 text-xs">
44
+ <div className="flex items-center gap-4">
45
+ <StatusIndicator />
46
+ {status.projectDir && (
47
+ <div className="flex items-center gap-1.5">
48
+ <FolderOpen className="h-3.5 w-3.5 shrink-0" />
49
+ <PathMarquee
50
+ children={status.projectDir}
51
+ maxWidth={300}
52
+ duration={12}
53
+ className="text-xs"
54
+ />
55
+ </div>
56
+ )}
57
+ </div>
58
+ {status.connected && (
59
+ <div className="flex items-center gap-1.5">
60
+ {status.watcherEnabled ? (
61
+ <span className="text-green-600">Watching for changes</span>
62
+ ) : (
63
+ <span className="text-yellow-600">File watcher disabled</span>
64
+ )}
65
+ </div>
66
+ )}
67
+ </div>
68
+ )
69
+ }
@@ -0,0 +1,104 @@
1
+ import { useEffect, useState } from 'react'
2
+ import Markdown, { type Components } from 'react-markdown'
3
+ import remarkGfm from 'remark-gfm'
4
+ import { codeToHtml } from 'shiki'
5
+
6
+ interface MarkdownContentProps {
7
+ children: string
8
+ className?: string
9
+ /** Additional component overrides for react-markdown */
10
+ components?: Components
11
+ }
12
+
13
+ /**
14
+ * Simple markdown renderer with GFM support and shiki code highlighting.
15
+ * For full markdown viewing with ToC, use MarkdownViewer instead.
16
+ */
17
+ export function MarkdownContent({ children, className = '', components }: MarkdownContentProps) {
18
+ return (
19
+ <div className={`markdown-content ${className}`}>
20
+ <Markdown
21
+ remarkPlugins={[remarkGfm]}
22
+ components={{
23
+ code: CodeBlock,
24
+ pre: ({ children }) => <>{children}</>,
25
+ ...components,
26
+ }}
27
+ >
28
+ {children}
29
+ </Markdown>
30
+ </div>
31
+ )
32
+ }
33
+
34
+ interface CodeBlockProps {
35
+ children?: React.ReactNode
36
+ className?: string
37
+ node?: unknown
38
+ }
39
+
40
+ /** Shared code block component with shiki syntax highlighting */
41
+ export function CodeBlock({ children, className }: CodeBlockProps) {
42
+ const [html, setHtml] = useState<string | null>(null)
43
+ const code = String(children).replace(/\n$/, '')
44
+ const match = /language-(\w+)/.exec(className || '')
45
+ const lang = match ? match[1] : undefined
46
+
47
+ // Check if this is inline code (no language, short content)
48
+ const isInline = !lang && !code.includes('\n')
49
+
50
+ useEffect(() => {
51
+ if (isInline) return
52
+
53
+ let mounted = true
54
+ const highlight = async () => {
55
+ try {
56
+ const result = await codeToHtml(code, {
57
+ lang: lang || 'text',
58
+ themes: {
59
+ light: 'github-light',
60
+ dark: 'github-dark',
61
+ },
62
+ })
63
+ if (mounted) setHtml(result)
64
+ } catch {
65
+ // Fallback for unknown languages
66
+ const result = await codeToHtml(code, {
67
+ lang: 'text',
68
+ themes: {
69
+ light: 'github-light',
70
+ dark: 'github-dark',
71
+ },
72
+ })
73
+ if (mounted) setHtml(result)
74
+ }
75
+ }
76
+ highlight()
77
+ return () => {
78
+ mounted = false
79
+ }
80
+ }, [code, lang, isInline])
81
+
82
+ if (isInline) {
83
+ return (
84
+ <code className="bg-muted text-foreground rounded px-1.5 py-0.5 font-mono text-sm">
85
+ {code}
86
+ </code>
87
+ )
88
+ }
89
+
90
+ if (!html) {
91
+ return (
92
+ <pre className="bg-muted/50 border-border overflow-x-auto rounded-md border p-4">
93
+ <code className="text-foreground font-mono text-sm">{code}</code>
94
+ </pre>
95
+ )
96
+ }
97
+
98
+ return (
99
+ <div
100
+ className="shiki-wrapper overflow-x-auto rounded-md text-sm [&_pre]:m-0 [&_pre]:bg-transparent [&_pre]:p-4"
101
+ dangerouslySetInnerHTML={{ __html: html }}
102
+ />
103
+ )
104
+ }