ompchamber 0.3.0 → 0.5.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 (256) hide show
  1. package/README.md +17 -10
  2. package/dist/client/index.html +1 -1
  3. package/dist/client/static/css/index.5291f58223.css +2 -0
  4. package/dist/client/static/js/async/{1k.544adbf77d.js → 1k.5974df3b87.js} +2 -2
  5. package/dist/client/static/js/async/3j.357c80ff38.js +2 -0
  6. package/dist/client/static/js/async/{3y.22075aa079.js → 3y.ededeebde8.js} +5 -5
  7. package/dist/client/static/js/async/51.95bcae98af.js +2 -0
  8. package/dist/client/static/js/async/6.f034c9b528.js +1 -0
  9. package/dist/client/static/js/async/a.e05da304c8.js +10 -0
  10. package/dist/client/static/js/async/ar.1e14da5588.js +1 -0
  11. package/dist/client/static/js/async/ct.b74928f7a3.js +1 -0
  12. package/dist/client/static/js/async/dc.6dc4b0a04b.js +2 -0
  13. package/dist/client/static/js/async/fw.6a51998a8e.js +6 -0
  14. package/dist/client/static/js/async/g.7a4bbd4798.js +2 -0
  15. package/dist/client/static/js/async/g.7a4bbd4798.js.LICENSE.txt +11 -0
  16. package/dist/client/static/js/async/{k.e878dd350f.js → k.7466330d1a.js} +2 -2
  17. package/dist/client/static/js/async/katex-vendor.de6a87c308.js +1 -0
  18. package/dist/client/static/js/async/kc.bbdb35b771.js +1 -0
  19. package/dist/client/static/js/async/l6.c56640c205.js +1 -0
  20. package/dist/client/static/js/async/{m.3fc017a055.js → m.4b29d34f93.js} +3 -3
  21. package/dist/client/static/js/async/{og.ae7acf03ac.js → og.634fba99ac.js} +2 -2
  22. package/dist/client/static/js/async/q.7fb4d2f374.js +21 -0
  23. package/dist/client/static/js/async/rd.cb6bf495bb.js +2 -0
  24. package/dist/client/static/js/async/rf.d626afc367.js +2 -0
  25. package/dist/client/static/js/async/s.413c2a4683.js +2 -0
  26. package/dist/client/static/js/async/s.413c2a4683.js.LICENSE.txt +6 -0
  27. package/dist/client/static/js/async/sy.d16be843a7.js +94 -0
  28. package/dist/client/static/js/async/t.cb3fcf8615.js +1 -0
  29. package/dist/client/static/js/async/ud.3065bfee31.js +2 -0
  30. package/dist/client/static/js/async/uf.46ad6209fa.js +2 -0
  31. package/dist/client/static/js/async/uf.46ad6209fa.js.LICENSE.txt +6 -0
  32. package/dist/client/static/js/async/v.7d28d3c241.js +34 -0
  33. package/dist/client/static/js/async/v0.394ed37c6b.js +2 -0
  34. package/dist/client/static/js/async/v0.394ed37c6b.js.LICENSE.txt +6 -0
  35. package/dist/client/static/js/async/vt.42416b707d.js +1 -0
  36. package/dist/client/static/js/async/vy.bbe114c4e0.js +2 -0
  37. package/dist/client/static/js/async/vy.bbe114c4e0.js.LICENSE.txt +6 -0
  38. package/dist/client/static/js/async/xw.ae67ba06fd.js +31 -0
  39. package/dist/client/static/js/async/xw.ae67ba06fd.js.LICENSE.txt +4 -0
  40. package/dist/client/static/js/async/yg.35bebd6d0c.js +149 -0
  41. package/dist/client/static/js/async/yv.2fa22f009a.js +1 -0
  42. package/dist/client/static/js/index.0ac93ee878.js +33 -0
  43. package/dist/client/static/js/index.0ac93ee878.js.LICENSE.txt +4 -0
  44. package/dist/client/static/js/{markdown-vendor.9da142c67b.js → markdown-vendor.71e43278b3.js} +1 -1
  45. package/dist/client/static/js/uy.5f791144ce.js +139 -0
  46. package/package.json +2 -2
  47. package/src/cli/lib/args.js +1 -0
  48. package/src/cli/lib/commands/serve.js +7 -0
  49. package/src/client/App.tsx +22 -5
  50. package/src/client/components/common/MarkdownRenderer.tsx +36 -0
  51. package/src/client/components/layout/desktop-layout/index.tsx +1 -1
  52. package/src/client/components/layout/session-sidebar/Modals.tsx +1 -1
  53. package/src/client/components/mobile/LayoutWrapper.tsx +1 -1
  54. package/src/client/components/settings/LazyModal.tsx +46 -0
  55. package/src/client/components/settings/Modal.tsx +1 -1
  56. package/src/client/components/workspace/chat-timeline/EmptyWorkspacePrompt.tsx +43 -29
  57. package/src/client/components/workspace/chat-timeline/MessageItem.tsx +15 -69
  58. package/src/client/components/workspace/chat-timeline/MessageList.tsx +47 -66
  59. package/src/client/components/workspace/chat-timeline/RunFooter.tsx +60 -0
  60. package/src/client/components/workspace/chat-timeline/SubagentView.tsx +37 -21
  61. package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +22 -4
  62. package/src/client/components/workspace/chat-timeline/index.tsx +3 -6
  63. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Edit.tsx +15 -19
  64. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/EnvelopeHeader.tsx +46 -0
  65. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/FallbackOutput.tsx +18 -68
  66. package/src/client/components/workspace/file-explorer/TreeItem.tsx +16 -4
  67. package/src/client/components/workspace/file-explorer/index.tsx +6 -0
  68. package/src/client/data/models/catalog.ts +8 -0
  69. package/src/client/data/samples/dialogue-session.ts +1 -1
  70. package/src/client/data/samples/sample-tools/other.ts +1 -1
  71. package/src/client/hooks/chat/omp/index.ts +9 -10
  72. package/src/client/hooks/chat/omp/revalidation-throttle.ts +6 -5
  73. package/src/client/hooks/chat/timeline/actions.ts +1 -2
  74. package/src/client/hooks/chat/timeline/index.ts +5 -0
  75. package/src/client/hooks/chat/timeline/send.ts +2 -3
  76. package/src/client/hooks/chat/timeline/session-load.ts +3 -4
  77. package/src/client/hooks/ui/syntax-ready.ts +16 -3
  78. package/src/client/main.tsx +2 -5
  79. package/src/client/stubs/elkjs.ts +37 -0
  80. package/src/client/tailwind.css +10 -0
  81. package/src/server/index.ts +53 -0
  82. package/src/server/lib/fs/git-ignore.ts +49 -0
  83. package/src/server/lib/models/provider-registry.server.ts +44 -20
  84. package/src/server/lib/omp/core/cli.ts +3 -2
  85. package/src/server/lib/omp/core/startup.ts +64 -0
  86. package/src/server/lib/omp/rpc/constants.ts +10 -0
  87. package/src/server/lib/omp/rpc/lifecycle.test.ts +138 -0
  88. package/src/server/lib/omp/rpc/manager.ts +32 -4
  89. package/src/server/lib/omp/rpc/process.ts +1 -1
  90. package/src/server/lib/omp/rpc/session-commands.ts +32 -9
  91. package/src/server/lib/omp/rpc/session-registry.ts +3 -2
  92. package/src/server/lib/omp/rpc/subagent-liveness.test.ts +62 -0
  93. package/src/server/lib/omp/rpc/subagent-liveness.ts +100 -0
  94. package/src/server/lib/omp/session/messages.test.ts +112 -0
  95. package/src/server/lib/omp/session/messages.ts +6 -5
  96. package/src/server/mock.server.ts +6 -12
  97. package/src/server/routes/agent/command.ts +34 -16
  98. package/src/server/routes/chat/stream.ts +13 -30
  99. package/src/server/routes/fs/read.ts +15 -7
  100. package/src/server/routes/models/root.ts +5 -1
  101. package/src/shared/lib/chat/notice-row.test.ts +89 -0
  102. package/src/shared/lib/chat/notice-row.ts +78 -0
  103. package/src/shared/lib/chat/omp/abort.ts +41 -0
  104. package/src/shared/lib/chat/stream-service.ts +0 -76
  105. package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +154 -0
  106. package/src/shared/lib/chat/timeline/omp-callbacks.ts +36 -7
  107. package/src/shared/lib/chat/timeline/run-footer.test.ts +84 -0
  108. package/src/shared/lib/chat/timeline/run-footer.ts +96 -0
  109. package/src/shared/lib/chat/tool-output.test.ts +91 -0
  110. package/src/shared/lib/chat/tool-output.ts +54 -0
  111. package/src/shared/lib/chat/xml-envelope.ts +158 -0
  112. package/src/shared/lib/code/highlighter-lazy.ts +106 -0
  113. package/src/shared/lib/code/highlighter.ts +31 -52
  114. package/src/shared/lib/code/syntax-highlight.test.ts +36 -1
  115. package/src/shared/lib/code/syntax-highlight.ts +17 -3
  116. package/src/shared/lib/format/time.ts +19 -0
  117. package/src/shared/lib/fs/paths.ts +19 -0
  118. package/src/shared/lib/markdown/fira-code-fonts.css +51 -0
  119. package/src/shared/lib/markdown/katex-fonts.css +20 -0
  120. package/src/shared/lib/markdown/katex.ts +101 -0
  121. package/src/shared/lib/markdown/marked.ts +23 -14
  122. package/src/shared/lib/markdown/math.test.ts +109 -0
  123. package/src/shared/lib/markdown/mermaid.ts +6 -0
  124. package/src/shared/lib/markdown/sanitize.ts +48 -2
  125. package/src/shared/lib/omp/session/mapper.ts +11 -3
  126. package/src/shared/lib/omp/session/messages-map.ts +12 -12
  127. package/src/shared/types/chat.ts +3 -1
  128. package/src/shared/types/fs.ts +3 -0
  129. package/dist/client/static/css/g.f36aa429fa.css +0 -1
  130. package/dist/client/static/css/index.a0f07455bd.css +0 -2
  131. package/dist/client/static/css/katex-vendor.ca3a0600e8.css +0 -1
  132. package/dist/client/static/font/KaTeX_AMS-Regular.1608a09b4a.woff +0 -0
  133. package/dist/client/static/font/KaTeX_AMS-Regular.4aafdb685c.ttf +0 -0
  134. package/dist/client/static/font/KaTeX_Caligraphic-Bold.b67709187b.woff +0 -0
  135. package/dist/client/static/font/KaTeX_Caligraphic-Bold.cce5b8ecea.ttf +0 -0
  136. package/dist/client/static/font/KaTeX_Caligraphic-Regular.07ef19e7b0.ttf +0 -0
  137. package/dist/client/static/font/KaTeX_Caligraphic-Regular.dad44a7fc6.woff +0 -0
  138. package/dist/client/static/font/KaTeX_Fraktur-Bold.9f256b8593.woff +0 -0
  139. package/dist/client/static/font/KaTeX_Fraktur-Bold.b18f59e1d1.ttf +0 -0
  140. package/dist/client/static/font/KaTeX_Fraktur-Regular.7c1871215e.woff +0 -0
  141. package/dist/client/static/font/KaTeX_Fraktur-Regular.ed38e79f57.ttf +0 -0
  142. package/dist/client/static/font/KaTeX_Main-Bold.b74a1a8b2c.ttf +0 -0
  143. package/dist/client/static/font/KaTeX_Main-Bold.d181c4650d.woff +0 -0
  144. package/dist/client/static/font/KaTeX_Main-BoldItalic.70d8b0a530.ttf +0 -0
  145. package/dist/client/static/font/KaTeX_Main-BoldItalic.e3f82f9d27.woff +0 -0
  146. package/dist/client/static/font/KaTeX_Main-Italic.47373d1e51.ttf +0 -0
  147. package/dist/client/static/font/KaTeX_Main-Italic.9024d815ba.woff +0 -0
  148. package/dist/client/static/font/KaTeX_Main-Regular.7f51fe0340.woff +0 -0
  149. package/dist/client/static/font/KaTeX_Main-Regular.b7f8fe9b5f.ttf +0 -0
  150. package/dist/client/static/font/KaTeX_Math-BoldItalic.a879cf8383.ttf +0 -0
  151. package/dist/client/static/font/KaTeX_Math-BoldItalic.f1035d8d5d.woff +0 -0
  152. package/dist/client/static/font/KaTeX_Math-Italic.5295ba483a.woff +0 -0
  153. package/dist/client/static/font/KaTeX_Math-Italic.939bc64440.ttf +0 -0
  154. package/dist/client/static/font/KaTeX_SansSerif-Bold.94e1e8dc5f.ttf +0 -0
  155. package/dist/client/static/font/KaTeX_SansSerif-Bold.bf59d231c3.woff +0 -0
  156. package/dist/client/static/font/KaTeX_SansSerif-Italic.7c9bc82b17.woff +0 -0
  157. package/dist/client/static/font/KaTeX_SansSerif-Italic.b4c20c84d8.ttf +0 -0
  158. package/dist/client/static/font/KaTeX_SansSerif-Regular.740484788f.woff +0 -0
  159. package/dist/client/static/font/KaTeX_SansSerif-Regular.d4d7ba4804.ttf +0 -0
  160. package/dist/client/static/font/KaTeX_Script-Regular.0750571032.woff +0 -0
  161. package/dist/client/static/font/KaTeX_Script-Regular.fe9cbbe1a0.ttf +0 -0
  162. package/dist/client/static/font/KaTeX_Size1-Regular.e1e279cbdd.woff +0 -0
  163. package/dist/client/static/font/KaTeX_Size1-Regular.fabc004aab.ttf +0 -0
  164. package/dist/client/static/font/KaTeX_Size2-Regular.577270225e.woff +0 -0
  165. package/dist/client/static/font/KaTeX_Size2-Regular.d6b476ecd3.ttf +0 -0
  166. package/dist/client/static/font/KaTeX_Size3-Regular.9acaf01c64.woff +0 -0
  167. package/dist/client/static/font/KaTeX_Size3-Regular.a144ef5840.ttf +0 -0
  168. package/dist/client/static/font/KaTeX_Size4-Regular.7a996c9da2.woff +0 -0
  169. package/dist/client/static/font/KaTeX_Size4-Regular.fbccdabe0a.ttf +0 -0
  170. package/dist/client/static/font/KaTeX_Typewriter-Regular.6258592bdc.woff +0 -0
  171. package/dist/client/static/font/KaTeX_Typewriter-Regular.d97aaf4a1e.ttf +0 -0
  172. package/dist/client/static/font/fira-code-cyrillic-400-normal.5971438a4a.woff2 +0 -0
  173. package/dist/client/static/font/fira-code-cyrillic-400-normal.9bb7bfea9c.woff +0 -0
  174. package/dist/client/static/font/fira-code-cyrillic-500-normal.5486265b18.woff2 +0 -0
  175. package/dist/client/static/font/fira-code-cyrillic-500-normal.89f92bfb31.woff +0 -0
  176. package/dist/client/static/font/fira-code-cyrillic-600-normal.2805b497e5.woff +0 -0
  177. package/dist/client/static/font/fira-code-cyrillic-600-normal.86e0df22a3.woff2 +0 -0
  178. package/dist/client/static/font/fira-code-cyrillic-700-normal.505a88f22a.woff +0 -0
  179. package/dist/client/static/font/fira-code-cyrillic-700-normal.76f5e8d2e2.woff2 +0 -0
  180. package/dist/client/static/font/fira-code-cyrillic-ext-400-normal.3c4b15e57d.woff2 +0 -0
  181. package/dist/client/static/font/fira-code-cyrillic-ext-400-normal.61323debaa.woff +0 -0
  182. package/dist/client/static/font/fira-code-cyrillic-ext-500-normal.0f34210f2a.woff2 +0 -0
  183. package/dist/client/static/font/fira-code-cyrillic-ext-500-normal.2497cfa1aa.woff +0 -0
  184. package/dist/client/static/font/fira-code-cyrillic-ext-600-normal.40fce94af9.woff2 +0 -0
  185. package/dist/client/static/font/fira-code-cyrillic-ext-600-normal.6c8774b829.woff +0 -0
  186. package/dist/client/static/font/fira-code-cyrillic-ext-700-normal.4b742eb865.woff +0 -0
  187. package/dist/client/static/font/fira-code-cyrillic-ext-700-normal.e75eb4fccc.woff2 +0 -0
  188. package/dist/client/static/font/fira-code-greek-400-normal.bb900468b6.woff2 +0 -0
  189. package/dist/client/static/font/fira-code-greek-400-normal.be76e699ba.woff +0 -0
  190. package/dist/client/static/font/fira-code-greek-500-normal.176c9fb001.woff2 +0 -0
  191. package/dist/client/static/font/fira-code-greek-500-normal.d2c596136b.woff +0 -0
  192. package/dist/client/static/font/fira-code-greek-600-normal.490c3deed7.woff +0 -0
  193. package/dist/client/static/font/fira-code-greek-600-normal.7df14376d9.woff2 +0 -0
  194. package/dist/client/static/font/fira-code-greek-700-normal.c82b6072fc.woff2 +0 -0
  195. package/dist/client/static/font/fira-code-greek-700-normal.d5f2b2917b.woff +0 -0
  196. package/dist/client/static/font/fira-code-greek-ext-400-normal.4849a79781.woff +0 -0
  197. package/dist/client/static/font/fira-code-greek-ext-400-normal.7f5bfc4538.woff2 +0 -0
  198. package/dist/client/static/font/fira-code-greek-ext-500-normal.aaccca4075.woff2 +0 -0
  199. package/dist/client/static/font/fira-code-greek-ext-500-normal.e383181256.woff +0 -0
  200. package/dist/client/static/font/fira-code-greek-ext-600-normal.78cb4bff8e.woff +0 -0
  201. package/dist/client/static/font/fira-code-greek-ext-600-normal.d6c93d54f9.woff2 +0 -0
  202. package/dist/client/static/font/fira-code-greek-ext-700-normal.57ec7c92d9.woff +0 -0
  203. package/dist/client/static/font/fira-code-greek-ext-700-normal.8fafdae5e8.woff2 +0 -0
  204. package/dist/client/static/font/fira-code-latin-400-normal.973295532b.woff +0 -0
  205. package/dist/client/static/font/fira-code-latin-500-normal.7ae2be8fb8.woff +0 -0
  206. package/dist/client/static/font/fira-code-latin-600-normal.1dbbe5bc27.woff +0 -0
  207. package/dist/client/static/font/fira-code-latin-700-normal.497a70148b.woff +0 -0
  208. package/dist/client/static/font/fira-code-latin-ext-400-normal.9dfae29aaf.woff +0 -0
  209. package/dist/client/static/font/fira-code-latin-ext-400-normal.e534ae46e2.woff2 +0 -0
  210. package/dist/client/static/font/fira-code-latin-ext-500-normal.24c7b781b0.woff +0 -0
  211. package/dist/client/static/font/fira-code-latin-ext-500-normal.aad44ab072.woff2 +0 -0
  212. package/dist/client/static/font/fira-code-latin-ext-600-normal.7bed0f2f7e.woff +0 -0
  213. package/dist/client/static/font/fira-code-latin-ext-600-normal.c509a7c9f1.woff2 +0 -0
  214. package/dist/client/static/font/fira-code-latin-ext-700-normal.6c66c8b0a9.woff2 +0 -0
  215. package/dist/client/static/font/fira-code-latin-ext-700-normal.88f64e2822.woff +0 -0
  216. package/dist/client/static/font/fira-code-symbols2-400-normal.97d3f4b17c.woff +0 -0
  217. package/dist/client/static/font/fira-code-symbols2-500-normal.b06f1a06e4.woff +0 -0
  218. package/dist/client/static/font/fira-code-symbols2-600-normal.304b653b93.woff +0 -0
  219. package/dist/client/static/font/fira-code-symbols2-700-normal.d8ce0ad586.woff +0 -0
  220. package/dist/client/static/js/async/3j.a2fcea99be.js +0 -2
  221. package/dist/client/static/js/async/51.1938cec320.js +0 -2
  222. package/dist/client/static/js/async/6.7929428505.js +0 -1
  223. package/dist/client/static/js/async/69.a18a10882f.js +0 -1
  224. package/dist/client/static/js/async/a.bce4542392.js +0 -1
  225. package/dist/client/static/js/async/ar.9dc8b1992a.js +0 -2
  226. package/dist/client/static/js/async/bi.cbfe8eaa8e.js +0 -1
  227. package/dist/client/static/js/async/ct.38d9da8a5a.js +0 -1
  228. package/dist/client/static/js/async/fw.fb1a08b92d.js +0 -6
  229. package/dist/client/static/js/async/l6.45f3b62e15.js +0 -1
  230. package/dist/client/static/js/async/q.0b6fb9bf8d.js +0 -21
  231. package/dist/client/static/js/async/rd.b77011ab7f.js +0 -2
  232. package/dist/client/static/js/async/rf.cfbde7f744.js +0 -2
  233. package/dist/client/static/js/async/s.c7d521a8ae.js +0 -2
  234. package/dist/client/static/js/async/sy.7345abcd67.js +0 -94
  235. package/dist/client/static/js/async/t.0dcb01dfde.js +0 -1
  236. package/dist/client/static/js/async/v.5c7a4b087b.js +0 -1
  237. package/dist/client/static/js/async/vu.311f1d202c.js +0 -34
  238. package/dist/client/static/js/async/yg.9bd4e09ecb.js +0 -149
  239. package/dist/client/static/js/async/yv.593a3f7d54.js +0 -1
  240. package/dist/client/static/js/g.124fba0826.js +0 -139
  241. package/dist/client/static/js/index.907fd4670e.js +0 -68
  242. package/dist/client/static/js/katex-vendor.95e48cf65a.js +0 -1
  243. package/src/shared/lib/chat/order.ts +0 -40
  244. /package/dist/client/static/js/async/{1k.544adbf77d.js.LICENSE.txt → 1k.5974df3b87.js.LICENSE.txt} +0 -0
  245. /package/dist/client/static/js/async/{3j.a2fcea99be.js.LICENSE.txt → 3j.357c80ff38.js.LICENSE.txt} +0 -0
  246. /package/dist/client/static/js/async/{3y.22075aa079.js.LICENSE.txt → 3y.ededeebde8.js.LICENSE.txt} +0 -0
  247. /package/dist/client/static/js/async/{51.1938cec320.js.LICENSE.txt → 51.95bcae98af.js.LICENSE.txt} +0 -0
  248. /package/dist/client/static/js/async/{ar.9dc8b1992a.js.LICENSE.txt → a.e05da304c8.js.LICENSE.txt} +0 -0
  249. /package/dist/client/static/js/async/{fw.fb1a08b92d.js.LICENSE.txt → dc.6dc4b0a04b.js.LICENSE.txt} +0 -0
  250. /package/dist/client/static/js/async/{m.3fc017a055.js.LICENSE.txt → fw.6a51998a8e.js.LICENSE.txt} +0 -0
  251. /package/dist/client/static/js/async/{q.0b6fb9bf8d.js.LICENSE.txt → m.4b29d34f93.js.LICENSE.txt} +0 -0
  252. /package/dist/client/static/js/async/{rf.cfbde7f744.js.LICENSE.txt → q.7fb4d2f374.js.LICENSE.txt} +0 -0
  253. /package/dist/client/static/js/async/{rd.b77011ab7f.js.LICENSE.txt → rd.cb6bf495bb.js.LICENSE.txt} +0 -0
  254. /package/dist/client/static/js/async/{s.c7d521a8ae.js.LICENSE.txt → rf.d626afc367.js.LICENSE.txt} +0 -0
  255. /package/dist/client/static/js/{index.907fd4670e.js.LICENSE.txt → async/ud.3065bfee31.js.LICENSE.txt} +0 -0
  256. /package/dist/client/static/js/{g.124fba0826.js.LICENSE.txt → uy.5f791144ce.js.LICENSE.txt} +0 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Tool output sometimes arrives wrapped in a single XML envelope: omp's
8
+ * `<system-reminder …>…</system-reminder>`, an MCP server's `<result>…</result>`.
9
+ * The tag is transport, not content — rendered as markdown it would show as
10
+ * escaped markup instead of the message it carries, so the timeline peels the
11
+ * wrapper before rendering the inner text.
12
+ *
13
+ * Only a wrapper that OPENS the output is peeled (a tag in the middle of prose
14
+ * is content: `<div>` inside an HTML sample, `a < b`), its tags must balance
15
+ * and nest, and what follows the closing tag must be free text — a second
16
+ * element means there is no single wrapper, so nothing is peeled.
17
+ *
18
+ * The scan is hand-rolled: this module is shared with the server (no
19
+ * `DOMParser` there) and parses arbitrary tool output, so it must never throw.
20
+ */
21
+
22
+ export interface XmlEnvelope {
23
+ /** Element name, e.g. `system-reminder`. */
24
+ tag: string;
25
+ /** Attributes of the wrapper's start tag, e.g. `{ reason: 'rule_violation' }`. */
26
+ attributes: Record<string, string>;
27
+ /** Text between the wrapper's start and end tags, common indent removed. */
28
+ inner: string;
29
+ /** Free text that followed the closing tag, if any. */
30
+ rest?: string;
31
+ }
32
+
33
+ const NAME_RE = /^[A-Za-z_][A-Za-z0-9_.:-]*/;
34
+ const ATTR_RE = /([A-Za-z_:][A-Za-z0-9_.:-]*)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
35
+
36
+ interface Tag {
37
+ kind: 'start' | 'end' | 'self' | 'other';
38
+ name: string;
39
+ /** Raw text between the name and the closing `>`. */
40
+ attrs: string;
41
+ /** Index just past the tag's `>`. */
42
+ end: number;
43
+ }
44
+
45
+ /** Attributes of a start tag: `reason="rule_violation" flag`. */
46
+ function parseAttributes(raw: string): Record<string, string> {
47
+ const attributes: Record<string, string> = {};
48
+ for (const match of raw.matchAll(ATTR_RE)) {
49
+ attributes[match[1]] = match[2] ?? match[3] ?? match[4] ?? '';
50
+ }
51
+ return attributes;
52
+ }
53
+
54
+ /** Envelope tags that mean "the runtime interrupted with a reminder" — the
55
+ * card header flags them instead of treating them as an ordinary result. */
56
+ export function isReminderTag(tag: string): boolean {
57
+ return tag === 'system-reminder' || tag === 'reminder';
58
+ }
59
+
60
+ /** Index of the tag's closing `>` — an attribute value may contain `>`. */
61
+ function tagEnd(text: string, from: number): number {
62
+ let quote = '';
63
+ for (let i = from; i < text.length; i += 1) {
64
+ const ch = text[i];
65
+ if (quote) {
66
+ if (ch === quote) quote = '';
67
+ } else if (ch === '"' || ch === "'") {
68
+ quote = ch;
69
+ } else if (ch === '>') {
70
+ return i;
71
+ }
72
+ }
73
+ return -1;
74
+ }
75
+
76
+ /** Read the markup starting at `<`. Comments, CDATA and processing
77
+ * instructions come back as `other` so their contents can never be read as
78
+ * tags. Returns undefined for anything malformed. */
79
+ function readTag(text: string, at: number): Tag | undefined {
80
+ const head = text.slice(at, at + 9);
81
+ if (head.startsWith('<!--')) {
82
+ const close = text.indexOf('-->', at + 4);
83
+ return close === -1 ? undefined : { kind: 'other', name: '', attrs: '', end: close + 3 };
84
+ }
85
+ if (head === '<![CDATA[') {
86
+ const close = text.indexOf(']]>', at + 9);
87
+ return close === -1 ? undefined : { kind: 'other', name: '', attrs: '', end: close + 3 };
88
+ }
89
+ if (text[at + 1] === '!' || text[at + 1] === '?') {
90
+ const close = tagEnd(text, at + 2);
91
+ return close === -1 ? undefined : { kind: 'other', name: '', attrs: '', end: close + 1 };
92
+ }
93
+
94
+ const closing = text[at + 1] === '/';
95
+ const nameAt = at + (closing ? 2 : 1);
96
+ const name = NAME_RE.exec(text.slice(nameAt, nameAt + 64))?.[0];
97
+ if (!name) return undefined;
98
+ const close = tagEnd(text, nameAt + name.length);
99
+ if (close === -1) return undefined;
100
+ const attrs = text.slice(nameAt + name.length, close);
101
+ if (closing) return { kind: 'end', name, attrs, end: close + 1 };
102
+ return { kind: text[close - 1] === '/' ? 'self' : 'start', name, attrs, end: close + 1 };
103
+ }
104
+
105
+ /** Drop the indentation every line shares — XML pretty-printing padding that
106
+ * markdown would otherwise read as an indented code block. */
107
+ function stripCommonIndent(text: string): string {
108
+ const lines = text.split('\n');
109
+ // The line breaks hugging the wrapper tags are padding, not content.
110
+ if (lines.length > 1 && !lines[0].trim()) lines.shift();
111
+ while (lines.length > 1 && !lines[lines.length - 1].trim()) lines.pop();
112
+
113
+ let indent = Number.POSITIVE_INFINITY;
114
+ for (const line of lines) {
115
+ if (line.trim()) indent = Math.min(indent, line.length - line.trimStart().length);
116
+ }
117
+ if (!Number.isFinite(indent)) return '';
118
+ if (indent === 0) return lines.join('\n');
119
+ return lines.map((line) => line.slice(Math.min(indent, line.length - line.trimStart().length))).join('\n');
120
+ }
121
+
122
+ /** Peel the XML wrapper when it opens the output and encloses a whole
123
+ * element. Returns undefined when the output is not such an envelope. */
124
+ export function unwrapXmlEnvelope(text: string): XmlEnvelope | undefined {
125
+ const opening = text.search(/\S/);
126
+ if (opening === -1 || text[opening] !== '<') return undefined;
127
+
128
+ const root = readTag(text, opening);
129
+ if (!root || root.kind !== 'start') return undefined;
130
+
131
+ const stack = [root.name];
132
+ for (let i = root.end; i < text.length; ) {
133
+ if (text[i] !== '<') {
134
+ i += 1;
135
+ continue;
136
+ }
137
+ const tag = readTag(text, i);
138
+ if (!tag) return undefined;
139
+ if (tag.kind === 'start') {
140
+ stack.push(tag.name);
141
+ } else if (tag.kind === 'end') {
142
+ if (stack.pop() !== tag.name) return undefined;
143
+ if (stack.length === 0) {
144
+ const rest = text.slice(tag.end).trim();
145
+ // A sibling element means there is no single wrapper to peel.
146
+ if (rest.startsWith('<')) return undefined;
147
+ return {
148
+ tag: root.name,
149
+ attributes: parseAttributes(root.attrs),
150
+ inner: stripCommonIndent(text.slice(root.end, i)),
151
+ ...(rest ? { rest } : {}),
152
+ };
153
+ }
154
+ }
155
+ i = tag.end;
156
+ }
157
+ return undefined;
158
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Demand-driven grammar loading for the Shiki core highlighter.
8
+ *
9
+ * `shiki/core` exposes only a synchronous `codeToHtml`: a grammar that was never
10
+ * passed to `loadLanguage` throws, and there is no internal lazy fetch. So the
11
+ * grammar for a language is fetched the first time that language is actually
12
+ * asked for — not for all 58 at boot, which produced 58 parallel chunk requests
13
+ * and ~8 MB of JS for a session that touches three languages.
14
+ *
15
+ * A grammar load is fire-and-forget: callers report the miss synchronously
16
+ * (falling back to escaped plain text) and subscribe via `onLanguageReady` to be
17
+ * told when the grammar landed, so the same highlight can be retried.
18
+ */
19
+
20
+ import type { HighlighterCore, LanguageRegistration } from 'shiki/core';
21
+
22
+ import { LANG_LOADERS } from '@/shared/lib/code/shiki-langs';
23
+
24
+ /** Shape of a `@shikijs/langs/*` module (the registration array sits on `.default`). */
25
+ type LangModule = { default: LanguageRegistration[] };
26
+
27
+ type Listener = () => void;
28
+
29
+ /** Languages currently being fetched — dedupes concurrent requests per grammar. */
30
+ const pending = new Map<string, Promise<void>>();
31
+ /** Languages that loaded (or that Shiki treats as built-in, e.g. `text`). */
32
+ const loaded = new Set<string>();
33
+ const listeners = new Set<Listener>();
34
+
35
+ /** Shiki rejects special languages; `text` is always available and needs no fetch. */
36
+ const SPECIAL_LANGS: Record<string, true> = {
37
+ text: true,
38
+ plain: true,
39
+ plaintext: true,
40
+ txt: true,
41
+ ansi: true,
42
+ none: true,
43
+ };
44
+
45
+ function notify(): void {
46
+ for (const fn of listeners) fn();
47
+ }
48
+
49
+ /**
50
+ * True once `language` can be highlighted synchronously. Special languages are
51
+ * always usable; every other id is usable only after its grammar resolved.
52
+ */
53
+ export function isLanguageReady(language: string): boolean {
54
+ return SPECIAL_LANGS[language] === true || loaded.has(language);
55
+ }
56
+
57
+ /** Subscribe to grammar arrivals. Returns an unsubscribe fn. */
58
+ export function onLanguageReady(fn: Listener): () => void {
59
+ listeners.add(fn);
60
+ return () => {
61
+ listeners.delete(fn);
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Ensure `language` is loaded, resolving on completion. Callers stay
67
+ * synchronous: they render the plain-text fallback and retry on the next
68
+ * `onLanguageReady` fire, identical to the previous warmup-race behaviour.
69
+ *
70
+ * Unknown ids (not in `LANG_LOADERS`) resolve immediately — the caller's own
71
+ * fallback language handles them.
72
+ */
73
+ export function ensureLanguage(highlighter: HighlighterCore, language: string): Promise<void> {
74
+ if (isLanguageReady(language)) return Promise.resolve();
75
+ const inFlight = pending.get(language);
76
+ if (inFlight) return inFlight;
77
+ const loader = LANG_LOADERS[language];
78
+ if (!loader) return Promise.resolve();
79
+
80
+ const load = highlighter
81
+ .loadLanguage(loader as () => Promise<LangModule>)
82
+ .then(() => {
83
+ loaded.add(language);
84
+ notify();
85
+ })
86
+ .catch(() => {
87
+ // Grammar unavailable under this engine: the caller keeps the plain
88
+ // fallback forever, and the id is left out of `loaded` so a later retry
89
+ // is still possible.
90
+ })
91
+ .finally(() => {
92
+ pending.delete(language);
93
+ });
94
+
95
+ pending.set(language, load);
96
+ return load;
97
+ }
98
+
99
+ /**
100
+ * Load the eager set without blocking boot. Resolves when every eager grammar
101
+ * settled, which is what the previous `WARMUP_BUDGET_MS` race approximated —
102
+ * now nothing else waits on it, so a slow grammar cannot stall readiness.
103
+ */
104
+ export async function ensureLanguages(highlighter: HighlighterCore, languages: readonly string[]): Promise<void> {
105
+ await Promise.all(languages.map((id) => ensureLanguage(highlighter, id)));
106
+ }
@@ -4,23 +4,18 @@
4
4
  */
5
5
 
6
6
  import { createHighlighterCore } from 'shiki/core';
7
- import type { HighlighterCore, LanguageRegistration } from 'shiki/core';
7
+ import type { HighlighterCore } from 'shiki/core';
8
8
  import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
9
9
  import oneLight from '@shikijs/themes/one-light';
10
10
  import oneDarkPro from '@shikijs/themes/one-dark-pro';
11
11
 
12
- import { EAGER_LANGS, LANG_LOADERS } from '@/shared/lib/code/shiki-langs';
13
- import { SHIKI_LIGHT } from '@/shared/lib/code/shiki-themes';
14
-
15
- /** Shape of a `@shikijs/langs/*` module (the registration array sits on `.default`). */
16
- type LangModule = { default: LanguageRegistration[] };
17
-
18
- /**
19
- * The transpile cost of the first highlight for a grammar is ~700 ms, so warming
20
- * all 58 would stall readiness. Cap the warmup: if the eager set is not done in
21
- * this budget the rest load lazily on first use instead of blocking boot.
22
- */
23
- const WARMUP_BUDGET_MS = 1500;
12
+ import {
13
+ ensureLanguage,
14
+ ensureLanguages,
15
+ isLanguageReady,
16
+ onLanguageReady as _onLanguageReady,
17
+ } from '@/shared/lib/code/highlighter-lazy';
18
+ import { EAGER_LANGS } from '@/shared/lib/code/shiki-langs';
24
19
 
25
20
  let instance: HighlighterCore | null = null;
26
21
  let ready = false;
@@ -31,10 +26,6 @@ export function escapeCode(code: string): string {
31
26
  return code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
32
27
  }
33
28
 
34
- function delay(ms: number): Promise<void> {
35
- return new Promise((resolve) => setTimeout(resolve, ms));
36
- }
37
-
38
29
  function notify(): void {
39
30
  const pending = [...listeners];
40
31
  listeners.clear();
@@ -47,38 +38,6 @@ function notify(): void {
47
38
  }
48
39
  }
49
40
 
50
- /**
51
- * Register every grammar so `getLoadedLanguages()` covers the full registry
52
- * from the first paint-after-ready. Registration is a parallel chunk fetch
53
- * (fast); the expensive per-grammar transpile stays in `warmup` below.
54
- */
55
- async function loadAllLanguages(highlighter: HighlighterCore): Promise<void> {
56
- await Promise.allSettled(
57
- Object.keys(LANG_LOADERS).map(async (id) => {
58
- const loader = LANG_LOADERS[id];
59
- if (!loader) return;
60
- try {
61
- await highlighter.loadLanguage(loader as () => Promise<LangModule>);
62
- } catch {
63
- // Grammar unavailable under the JS engine — per-call fallback covers it.
64
- }
65
- }),
66
- );
67
- }
68
-
69
- /** Pre-transpile the eager grammars; each failure is independent and ignored. */
70
- async function warmup(highlighter: HighlighterCore): Promise<void> {
71
- await Promise.all(
72
- EAGER_LANGS.map(async (id) => {
73
- try {
74
- highlighter.codeToHtml('x', { lang: id, theme: SHIKI_LIGHT });
75
- } catch {
76
- // Grammar unavailable under the JS engine — falls back to plain text.
77
- }
78
- }),
79
- );
80
- }
81
-
82
41
  async function boot(): Promise<HighlighterCore | null> {
83
42
  if (typeof window === 'undefined') return null;
84
43
  try {
@@ -88,10 +47,12 @@ async function boot(): Promise<HighlighterCore | null> {
88
47
  engine: createJavaScriptRegexEngine({ forgiving: true }),
89
48
  });
90
49
  instance = highlighter;
91
- await loadAllLanguages(highlighter);
92
- await Promise.race([warmup(highlighter), delay(WARMUP_BUDGET_MS)]);
93
50
  ready = true;
94
51
  notify();
52
+ // Grammars arrive on demand (`ensureLanguage`); the eager set is only a
53
+ // head start for the languages a chat session hits immediately, and boot
54
+ // does not wait on it.
55
+ void ensureLanguages(highlighter, EAGER_LANGS);
95
56
  return highlighter;
96
57
  } catch (error) {
97
58
  console.error('[shiki] highlighter boot failed', error);
@@ -129,7 +90,25 @@ export function onSyntaxReady(fn: () => void): () => void {
129
90
  };
130
91
  }
131
92
 
132
- /** The highlighter, or `null` before warmup completes. */
93
+ /** The highlighter, or `null` before boot completes. */
133
94
  export function getHighlighterSync(): HighlighterCore | null {
134
95
  return ready ? instance : null;
135
96
  }
97
+
98
+ /**
99
+ * Request the grammar for `language`, if the highlighter is booted. Returns
100
+ * whether the grammar is usable synchronously *right now* — callers that get
101
+ * `false` must render the plain-text fallback and re-run once
102
+ * `onLanguageReady`/`onSyntaxReady` fires.
103
+ *
104
+ * This is the only path that pulls a grammar chunk off the network, so it is
105
+ * deliberately pull-based: a language nobody renders is never fetched.
106
+ */
107
+ export function requestLanguage(language: string): boolean {
108
+ if (!instance) return false;
109
+ void ensureLanguage(instance, language);
110
+ return isLanguageReady(language);
111
+ }
112
+
113
+ /** Fires whenever a grammar lands, so pending highlights can retry. */
114
+ export const onLanguageReady = _onLanguageReady;
@@ -5,13 +5,37 @@
5
5
 
6
6
  import { beforeAll, describe, expect, test } from 'bun:test';
7
7
 
8
- import { bootSyntax } from '@/shared/lib/code/highlighter';
8
+ import { bootSyntax, onLanguageReady, requestLanguage } from '@/shared/lib/code/highlighter';
9
9
  import { getLanguageFromPath, highlightCode } from '@/shared/lib/code/syntax-highlight';
10
10
 
11
11
  /** Shiki emits `<span class="shiki">…` with dual-theme CSS variables per token. */
12
12
  const SHIKI_RE = /<span class="shiki">/;
13
13
  const SHIKI_TOKEN_RE = /--shiki-light:/;
14
14
 
15
+ /**
16
+ * Every language these tests highlight. Grammars are fetched on demand, so the
17
+ * suite asks for them up front and waits for the last one to land instead of
18
+ * relying on a boot-time warmup that no longer exists.
19
+ */
20
+ const HIGHLIGHT_LANGS = [
21
+ 'typescript',
22
+ 'tsx',
23
+ 'javascript',
24
+ 'jsx',
25
+ 'json',
26
+ 'markdown',
27
+ 'css',
28
+ 'bash',
29
+ 'yaml',
30
+ 'diff',
31
+ 'python',
32
+ 'go',
33
+ 'rust',
34
+ 'sql',
35
+ 'html',
36
+ 'php',
37
+ ];
38
+
15
39
  beforeAll(async () => {
16
40
  // The isomorphic highlighter only boots with `window` present; the Bun test
17
41
  // runtime has none. Stub it, boot, then remove the stub so other test files
@@ -19,6 +43,17 @@ beforeAll(async () => {
19
43
  Object.assign(globalThis, { window: globalThis });
20
44
  await bootSyntax();
21
45
  Reflect.deleteProperty(globalThis, 'window');
46
+
47
+ await new Promise<void>((resolve) => {
48
+ const settle = () => {
49
+ if (HIGHLIGHT_LANGS.every((lang) => requestLanguage(lang))) {
50
+ off();
51
+ resolve();
52
+ }
53
+ };
54
+ const off = onLanguageReady(settle);
55
+ settle();
56
+ });
22
57
  });
23
58
 
24
59
  describe('highlightCode', () => {
@@ -1,4 +1,6 @@
1
- import { escapeCode, getHighlighterSync } from '@/shared/lib/code/highlighter';
1
+ import type { HighlighterCore } from 'shiki/core';
2
+
3
+ import { escapeCode, getHighlighterSync, requestLanguage } from '@/shared/lib/code/highlighter';
2
4
  import { SHIKI_THEMES } from '@/shared/lib/code/shiki-themes';
3
5
 
4
6
  /** Map file extension to Shiki language id */
@@ -124,12 +126,24 @@ export function isCodeLike(text: string): boolean {
124
126
  return codePatterns.some((pattern) => pattern.test(trimmed));
125
127
  }
126
128
 
129
+ /**
130
+ * Pick a language that can be tokenized *synchronously* right now. A grammar
131
+ * that has not arrived yet is requested here (fire-and-forget) and the caller
132
+ * falls back to javascript for this pass — `onLanguageReady` re-runs the
133
+ * highlight once it lands. `getLoadedLanguages()` stays the authority for
134
+ * embedded/alias names that `LANG_LOADERS` does not list by id.
135
+ */
136
+ function resolveReadyLang(hl: HighlighterCore, language: string): string {
137
+ if (requestLanguage(language) || hl.getLoadedLanguages().includes(language)) return language;
138
+ return 'javascript';
139
+ }
140
+
127
141
  /** Syntax highlight code string safely with fallback */
128
142
  export function highlightCode(code: string, language = 'javascript'): string {
129
143
  if (!code) return '';
130
144
  const hl = getHighlighterSync();
131
145
  if (!hl) return escapeCode(code);
132
- const lang = hl.getLoadedLanguages().includes(language) ? language : 'javascript';
146
+ const lang = resolveReadyLang(hl, language);
133
147
  try {
134
148
  return `<span class="shiki">${hl.codeToHtml(code, {
135
149
  lang,
@@ -264,7 +278,7 @@ export function highlightLines(code: string, language = 'javascript'): string[]
264
278
  if (!code) return rawLines;
265
279
  const hl = getHighlighterSync();
266
280
  if (!hl) return rawLines.map(escapeCode);
267
- const lang = hl.getLoadedLanguages().includes(language) ? language : 'javascript';
281
+ const lang = resolveReadyLang(hl, language);
268
282
  try {
269
283
  const { tokens } = hl.codeToTokens(code, {
270
284
  lang,
@@ -26,6 +26,25 @@ export function parseTodayLabel(raw: string): number | null {
26
26
  return Number.isNaN(parsed.getTime()) ? null : parsed.getTime();
27
27
  }
28
28
 
29
+ /**
30
+ * Timeline stamp for a message row / run footer: `"Today, 10:30 AM"` for the
31
+ * live path, `"Sep 9, 04:43 AM"` for historical rows, empty when unparseable.
32
+ */
33
+ export function formatMessageStamp(msg?: { date?: string; timestamp?: string }): string {
34
+ const raw = msg?.timestamp || msg?.date;
35
+ if (!raw) return '';
36
+ if (raw.startsWith('Today,')) return `Today, ${raw.slice('Today,'.length).trim()}`;
37
+ // Bare "10:30 AM" timestamp (live path) — resolve against today.
38
+ if (/^\d{1,2}:\d{2}\s*(AM|PM)$/i.test(raw)) {
39
+ const parsedMs = parseTodayLabel(`Today, ${raw}`);
40
+ if (parsedMs !== null) return `Today, ${formatClock(parsedMs)}`;
41
+ }
42
+ const date = new Date(raw);
43
+ if (Number.isNaN(date.getTime())) return '';
44
+ const day = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
45
+ return `${day}, ${formatClock(date)}`;
46
+ }
47
+
29
48
  /**
30
49
  * Best-effort tool-detail timestamp: epoch-ms renders as the runtime's default
31
50
  * locale time, a string passes through, anything else renders empty.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Path formatting for the Files panel's copy actions.
3
+ *
4
+ * `/api/fs/dir` emits every entry as a path relative to the listing's base
5
+ * directory — the workspace root, or the selected nested repo — and reports
6
+ * that base back as `root`. Copy Path rebuilds the absolute path from those
7
+ * two halves; a relative path on its own is not resolvable outside the panel
8
+ * (the client-side `rootPath` may be `~`-relative, rejected by the server, or
9
+ * absent entirely in mock mode).
10
+ */
11
+ export function toAbsolutePath(baseDir: string | null | undefined, relPath: string): string {
12
+ // `/`- and `./`-prefixed raws are stripped; a dot-file name (`.env`) is not a prefix.
13
+ const rel = relPath.replace(/^(?:\.?[/\\])+/, '').replace(/[/\\]+$/, '');
14
+ const base = (baseDir ?? '').replace(/[/\\]+$/, '');
15
+ if (!base) return rel;
16
+ if (!rel) return base;
17
+ // Join with the base's own separator: `C:\proj` + `src` → `C:\proj\src`.
18
+ return `${base}${base.includes('\\') && !base.includes('/') ? '\\' : '/'}${rel}`;
19
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /*
7
+ * GENERATED — do not edit by hand. Run `bun run scripts/katex-fonts-woff2.mjs`
8
+ * after upgrading @fontsource/fira-code.
9
+ *
10
+ * Fira Code, latin subset, woff2-only, weights 400/500/600/700 — the faces
11
+ * `main.tsx` imports. Replaces `@fontsource/fira-code/{400,500,600,700}.css`,
12
+ * which pulled 7 subsets (cyrillic, cyrillic-ext, greek, greek-ext, latin,
13
+ * latin-ext, symbols2) x 4 weights x 2 formats = 56 files. This console is
14
+ * English-only, so the other six subsets were dead weight, and the woff
15
+ * fallback is never fetched by a browser that supports woff2.
16
+ *
17
+ * `font-display: swap` and the family/style/weight declarations match upstream,
18
+ * so text metrics are unchanged.
19
+ */
20
+
21
+ @font-face {
22
+ font-family: 'Fira Code';
23
+ font-style: normal;
24
+ font-display: swap;
25
+ font-weight: 400;
26
+ src: url('@fontsource/fira-code/files/fira-code-latin-400-normal.woff2') format('woff2');
27
+ }
28
+
29
+ @font-face {
30
+ font-family: 'Fira Code';
31
+ font-style: normal;
32
+ font-display: swap;
33
+ font-weight: 500;
34
+ src: url('@fontsource/fira-code/files/fira-code-latin-500-normal.woff2') format('woff2');
35
+ }
36
+
37
+ @font-face {
38
+ font-family: 'Fira Code';
39
+ font-style: normal;
40
+ font-display: swap;
41
+ font-weight: 600;
42
+ src: url('@fontsource/fira-code/files/fira-code-latin-600-normal.woff2') format('woff2');
43
+ }
44
+
45
+ @font-face {
46
+ font-family: 'Fira Code';
47
+ font-style: normal;
48
+ font-display: swap;
49
+ font-weight: 700;
50
+ src: url('@fontsource/fira-code/files/fira-code-latin-700-normal.woff2') format('woff2');
51
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /*
7
+ * GENERATED — do not edit by hand. Run `bun run scripts/katex-fonts-woff2.mjs`
8
+ * after upgrading katex.
9
+ *
10
+ * KaTeX stylesheet, woff2-only. Upstream `katex/dist/katex.min.css` lists three
11
+ * sources per @font-face (woff2 -> woff -> truetype); a browser downloads only
12
+ * the first format it supports, so the woff/truetype files were never
13
+ * *requested* while still being emitted into dist/client/static/font
14
+ * (634 kB .woff + 502 kB .ttf). Every src chain below keeps the woff2 source
15
+ * only — the layout rules are byte-identical to upstream.
16
+ *
17
+ * woff2 is supported by Safari 10+, Chrome 36+, Firefox 39+, and Edge 14+;
18
+ * this app targets modern browsers, so the legacy fallbacks buy nothing.
19
+ */
20
+ @font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_AMS-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Caligraphic-Bold.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Caligraphic-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Fraktur-Bold.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Fraktur-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Main-Bold.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Main-BoldItalic.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Main-Italic.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Main-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Math-BoldItalic.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Math-Italic.woff2") format("woff2")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_SansSerif-Bold.woff2") format("woff2")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_SansSerif-Italic.woff2") format("woff2")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_SansSerif-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Script-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Size1-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Size2-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Size3-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Size4-Regular.woff2") format("woff2")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url("../../../../node_modules/katex/dist/fonts/KaTeX_Typewriter-Regular.woff2") format("woff2")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.18.7"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}