dsh-better-display 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/DESIGN.md +72 -0
  3. package/LICENSE +24 -0
  4. package/README.en.md +41 -0
  5. package/README.md +41 -0
  6. package/THIRD_PARTY_NOTICES.md +65 -0
  7. package/cordis.patch.yml +5 -0
  8. package/cordis.yml +4 -0
  9. package/dshx.yml +5 -0
  10. package/lib/client.js +49923 -0
  11. package/lib/client.js.map +1 -0
  12. package/lib/dsh-better-display.js +256 -0
  13. package/lib/types/client/Blocks.d.ts +45 -0
  14. package/lib/types/client/Blocks.d.ts.map +1 -0
  15. package/lib/types/client/Blocks.js +136 -0
  16. package/lib/types/client/Blocks.js.map +1 -0
  17. package/lib/types/client/ChoreographedFlow.d.ts +22 -0
  18. package/lib/types/client/ChoreographedFlow.d.ts.map +1 -0
  19. package/lib/types/client/ChoreographedFlow.js +170 -0
  20. package/lib/types/client/ChoreographedFlow.js.map +1 -0
  21. package/lib/types/client/ClosedProcessSummary.d.ts +9 -0
  22. package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -0
  23. package/lib/types/client/ClosedProcessSummary.js +15 -0
  24. package/lib/types/client/ClosedProcessSummary.js.map +1 -0
  25. package/lib/types/client/LiveFold.d.ts +10 -0
  26. package/lib/types/client/LiveFold.d.ts.map +1 -0
  27. package/lib/types/client/LiveFold.js +39 -0
  28. package/lib/types/client/LiveFold.js.map +1 -0
  29. package/lib/types/client/McpAppFrame.d.ts +32 -0
  30. package/lib/types/client/McpAppFrame.d.ts.map +1 -0
  31. package/lib/types/client/McpAppFrame.js +193 -0
  32. package/lib/types/client/McpAppFrame.js.map +1 -0
  33. package/lib/types/client/Reader.d.ts +3 -0
  34. package/lib/types/client/Reader.d.ts.map +1 -0
  35. package/lib/types/client/Reader.js +537 -0
  36. package/lib/types/client/Reader.js.map +1 -0
  37. package/lib/types/client/ReasoningCard.d.ts +11 -0
  38. package/lib/types/client/ReasoningCard.d.ts.map +1 -0
  39. package/lib/types/client/ReasoningCard.js +301 -0
  40. package/lib/types/client/ReasoningCard.js.map +1 -0
  41. package/lib/types/client/StickyLane.d.ts +8 -0
  42. package/lib/types/client/StickyLane.d.ts.map +1 -0
  43. package/lib/types/client/StickyLane.js +28 -0
  44. package/lib/types/client/StickyLane.js.map +1 -0
  45. package/lib/types/client/TimelineRail.d.ts +10 -0
  46. package/lib/types/client/TimelineRail.d.ts.map +1 -0
  47. package/lib/types/client/TimelineRail.js +168 -0
  48. package/lib/types/client/TimelineRail.js.map +1 -0
  49. package/lib/types/client/ToolActivity.d.ts +17 -0
  50. package/lib/types/client/ToolActivity.d.ts.map +1 -0
  51. package/lib/types/client/ToolActivity.js +192 -0
  52. package/lib/types/client/ToolActivity.js.map +1 -0
  53. package/lib/types/client/TurnMetrics.d.ts +11 -0
  54. package/lib/types/client/TurnMetrics.d.ts.map +1 -0
  55. package/lib/types/client/TurnMetrics.js +44 -0
  56. package/lib/types/client/TurnMetrics.js.map +1 -0
  57. package/lib/types/client/WaitingStatus.d.ts +6 -0
  58. package/lib/types/client/WaitingStatus.d.ts.map +1 -0
  59. package/lib/types/client/WaitingStatus.js +19 -0
  60. package/lib/types/client/WaitingStatus.js.map +1 -0
  61. package/lib/types/client/conversation-scroll.d.ts +19 -0
  62. package/lib/types/client/conversation-scroll.d.ts.map +1 -0
  63. package/lib/types/client/conversation-scroll.js +25 -0
  64. package/lib/types/client/conversation-scroll.js.map +1 -0
  65. package/lib/types/client/deliverables.d.ts +24 -0
  66. package/lib/types/client/deliverables.d.ts.map +1 -0
  67. package/lib/types/client/deliverables.js +104 -0
  68. package/lib/types/client/deliverables.js.map +1 -0
  69. package/lib/types/client/entry-policy.d.ts +10 -0
  70. package/lib/types/client/entry-policy.d.ts.map +1 -0
  71. package/lib/types/client/entry-policy.js +24 -0
  72. package/lib/types/client/entry-policy.js.map +1 -0
  73. package/lib/types/client/entry.d.ts +4 -0
  74. package/lib/types/client/entry.d.ts.map +1 -0
  75. package/lib/types/client/entry.js +34 -0
  76. package/lib/types/client/entry.js.map +1 -0
  77. package/lib/types/client/fold-choreography.d.ts +28 -0
  78. package/lib/types/client/fold-choreography.d.ts.map +1 -0
  79. package/lib/types/client/fold-choreography.js +31 -0
  80. package/lib/types/client/fold-choreography.js.map +1 -0
  81. package/lib/types/client/index.d.ts +7 -0
  82. package/lib/types/client/index.d.ts.map +1 -0
  83. package/lib/types/client/index.js +153 -0
  84. package/lib/types/client/index.js.map +1 -0
  85. package/lib/types/client/live-turn.d.ts +58 -0
  86. package/lib/types/client/live-turn.d.ts.map +1 -0
  87. package/lib/types/client/live-turn.js +144 -0
  88. package/lib/types/client/live-turn.js.map +1 -0
  89. package/lib/types/client/markdown/MarkdownText.d.ts +39 -0
  90. package/lib/types/client/markdown/MarkdownText.d.ts.map +1 -0
  91. package/lib/types/client/markdown/MarkdownText.js +171 -0
  92. package/lib/types/client/markdown/MarkdownText.js.map +1 -0
  93. package/lib/types/client/markdown/cjkFriendlyStrong.d.ts +9 -0
  94. package/lib/types/client/markdown/cjkFriendlyStrong.d.ts.map +1 -0
  95. package/lib/types/client/markdown/cjkFriendlyStrong.js +73 -0
  96. package/lib/types/client/markdown/cjkFriendlyStrong.js.map +1 -0
  97. package/lib/types/client/markdown/incremental.d.ts +69 -0
  98. package/lib/types/client/markdown/incremental.d.ts.map +1 -0
  99. package/lib/types/client/markdown/incremental.js +108 -0
  100. package/lib/types/client/markdown/incremental.js.map +1 -0
  101. package/lib/types/client/markdown/katex.d.ts +27 -0
  102. package/lib/types/client/markdown/katex.d.ts.map +1 -0
  103. package/lib/types/client/markdown/katex.js +88 -0
  104. package/lib/types/client/markdown/katex.js.map +1 -0
  105. package/lib/types/client/markdown/mathCompatibility.d.ts +11 -0
  106. package/lib/types/client/markdown/mathCompatibility.d.ts.map +1 -0
  107. package/lib/types/client/markdown/mathCompatibility.js +293 -0
  108. package/lib/types/client/markdown/mathCompatibility.js.map +1 -0
  109. package/lib/types/client/markdown/parse.d.ts +25 -0
  110. package/lib/types/client/markdown/parse.d.ts.map +1 -0
  111. package/lib/types/client/markdown/parse.js +42 -0
  112. package/lib/types/client/markdown/parse.js.map +1 -0
  113. package/lib/types/client/markdown/render.d.ts +119 -0
  114. package/lib/types/client/markdown/render.d.ts.map +1 -0
  115. package/lib/types/client/markdown/render.js +435 -0
  116. package/lib/types/client/markdown/render.js.map +1 -0
  117. package/lib/types/client/mcp-app.d.ts +50 -0
  118. package/lib/types/client/mcp-app.d.ts.map +1 -0
  119. package/lib/types/client/mcp-app.js +328 -0
  120. package/lib/types/client/mcp-app.js.map +1 -0
  121. package/lib/types/client/message-chrome.d.ts +19 -0
  122. package/lib/types/client/message-chrome.d.ts.map +1 -0
  123. package/lib/types/client/message-chrome.js +42 -0
  124. package/lib/types/client/message-chrome.js.map +1 -0
  125. package/lib/types/client/motion.d.ts +38 -0
  126. package/lib/types/client/motion.d.ts.map +1 -0
  127. package/lib/types/client/motion.js +367 -0
  128. package/lib/types/client/motion.js.map +1 -0
  129. package/lib/types/client/native/ContextBody.d.ts +130 -0
  130. package/lib/types/client/native/ContextBody.d.ts.map +1 -0
  131. package/lib/types/client/native/ContextBody.js +398 -0
  132. package/lib/types/client/native/ContextBody.js.map +1 -0
  133. package/lib/types/client/native/ContextInjectionRow.d.ts +26 -0
  134. package/lib/types/client/native/ContextInjectionRow.d.ts.map +1 -0
  135. package/lib/types/client/native/ContextInjectionRow.js +27 -0
  136. package/lib/types/client/native/ContextInjectionRow.js.map +1 -0
  137. package/lib/types/client/native/ReferenceIcon.d.ts +16 -0
  138. package/lib/types/client/native/ReferenceIcon.d.ts.map +1 -0
  139. package/lib/types/client/native/ReferenceIcon.js +16 -0
  140. package/lib/types/client/native/ReferenceIcon.js.map +1 -0
  141. package/lib/types/client/native/tool-call-model.d.ts +58 -0
  142. package/lib/types/client/native/tool-call-model.d.ts.map +1 -0
  143. package/lib/types/client/native/tool-call-model.js +219 -0
  144. package/lib/types/client/native/tool-call-model.js.map +1 -0
  145. package/lib/types/client/open-file.d.ts +24 -0
  146. package/lib/types/client/primitive-labels.d.ts +10 -0
  147. package/lib/types/client/primitive-labels.d.ts.map +1 -0
  148. package/lib/types/client/primitive-labels.js +37 -0
  149. package/lib/types/client/primitive-labels.js.map +1 -0
  150. package/lib/types/client/projection.d.ts +41 -0
  151. package/lib/types/client/projection.d.ts.map +1 -0
  152. package/lib/types/client/projection.js +112 -0
  153. package/lib/types/client/projection.js.map +1 -0
  154. package/lib/types/client/reasoning-follow.d.ts +6 -0
  155. package/lib/types/client/reasoning-follow.d.ts.map +1 -0
  156. package/lib/types/client/reasoning-follow.js +12 -0
  157. package/lib/types/client/reasoning-follow.js.map +1 -0
  158. package/lib/types/client/settings-copy.d.ts +6 -0
  159. package/lib/types/client/settings-slots.d.ts +16 -0
  160. package/lib/types/client/store.d.ts +17 -0
  161. package/lib/types/client/store.d.ts.map +1 -0
  162. package/lib/types/client/store.js +13 -0
  163. package/lib/types/client/store.js.map +1 -0
  164. package/lib/types/client/stream-buffer.d.ts +29 -0
  165. package/lib/types/client/stream-buffer.d.ts.map +1 -0
  166. package/lib/types/client/stream-buffer.js +113 -0
  167. package/lib/types/client/stream-buffer.js.map +1 -0
  168. package/lib/types/client/streaming.d.ts +18 -0
  169. package/lib/types/client/streaming.d.ts.map +1 -0
  170. package/lib/types/client/streaming.js +77 -0
  171. package/lib/types/client/streaming.js.map +1 -0
  172. package/lib/types/client/timeline.d.ts +26 -0
  173. package/lib/types/client/timeline.d.ts.map +1 -0
  174. package/lib/types/client/timeline.js +48 -0
  175. package/lib/types/client/timeline.js.map +1 -0
  176. package/lib/types/client/tool-activity.d.ts +52 -0
  177. package/lib/types/client/tool-activity.d.ts.map +1 -0
  178. package/lib/types/client/tool-activity.js +178 -0
  179. package/lib/types/client/tool-activity.js.map +1 -0
  180. package/lib/types/client/types.d.ts +66 -0
  181. package/lib/types/client/types.d.ts.map +1 -0
  182. package/lib/types/client/types.js +2 -0
  183. package/lib/types/client/types.js.map +1 -0
  184. package/lib/types/client/waiting-clock.d.ts +17 -0
  185. package/lib/types/client/waiting-clock.d.ts.map +1 -0
  186. package/lib/types/client/waiting-clock.js +24 -0
  187. package/lib/types/client/waiting-clock.js.map +1 -0
  188. package/lib/types/client/word-motion.d.ts +16 -0
  189. package/lib/types/client/word-motion.d.ts.map +1 -0
  190. package/lib/types/client/word-motion.js +89 -0
  191. package/lib/types/client/word-motion.js.map +1 -0
  192. package/lib/types/client/word-timeline.d.ts +28 -0
  193. package/lib/types/client/word-timeline.d.ts.map +1 -0
  194. package/lib/types/client/word-timeline.js +100 -0
  195. package/lib/types/client/word-timeline.js.map +1 -0
  196. package/lib/types/dsh-better-display.d.ts +17 -0
  197. package/lib/types/dsh-better-display.d.ts.map +1 -0
  198. package/lib/types/dsh-better-display.js +53 -0
  199. package/lib/types/dsh-better-display.js.map +1 -0
  200. package/package.json +121 -0
  201. package/scripts/build-motion-fixture.mjs +26 -0
  202. package/scripts/link-harness-dependencies.mjs +51 -0
  203. package/skills/generative-mcpapps/SKILL.md +47 -0
  204. package/skills/generative-mcpapps/examples/interactive_quiz.html +153 -0
  205. package/skills/generative-mcpapps/references/html_boilerplate.md +122 -0
  206. package/skills/generative-mcpapps/references/sep1865_protocol.md +54 -0
  207. package/src/client/Blocks.tsx +205 -0
  208. package/src/client/ChoreographedFlow.tsx +182 -0
  209. package/src/client/ClosedProcessSummary.tsx +19 -0
  210. package/src/client/LiveFold.tsx +43 -0
  211. package/src/client/McpAppFrame.module.css +210 -0
  212. package/src/client/McpAppFrame.tsx +317 -0
  213. package/src/client/Reader.module.css +329 -0
  214. package/src/client/Reader.tsx +814 -0
  215. package/src/client/ReasoningCard.tsx +285 -0
  216. package/src/client/SettingsSection.module.css +147 -0
  217. package/src/client/SettingsSection.tsx +117 -0
  218. package/src/client/StickyLane.tsx +24 -0
  219. package/src/client/TimelineRail.module.css +170 -0
  220. package/src/client/TimelineRail.tsx +262 -0
  221. package/src/client/ToolActivity.tsx +214 -0
  222. package/src/client/TurnMetrics.module.css +139 -0
  223. package/src/client/TurnMetrics.tsx +160 -0
  224. package/src/client/WaitingStatus.tsx +22 -0
  225. package/src/client/conversation-scroll.ts +28 -0
  226. package/src/client/deliverables.ts +122 -0
  227. package/src/client/entry-policy.ts +20 -0
  228. package/src/client/entry.tsx +40 -0
  229. package/src/client/fold-choreography.ts +38 -0
  230. package/src/client/index.tsx +198 -0
  231. package/src/client/live-turn.ts +155 -0
  232. package/src/client/markdown/MarkdownText.module.css +304 -0
  233. package/src/client/markdown/MarkdownText.tsx +187 -0
  234. package/src/client/markdown/cjkFriendlyStrong.ts +84 -0
  235. package/src/client/markdown/incremental.ts +131 -0
  236. package/src/client/markdown/katex.tsx +91 -0
  237. package/src/client/markdown/mathCompatibility.ts +350 -0
  238. package/src/client/markdown/parse.ts +45 -0
  239. package/src/client/markdown/render.tsx +630 -0
  240. package/src/client/markdown/upstream.json +14 -0
  241. package/src/client/mcp-app.ts +323 -0
  242. package/src/client/message-chrome.ts +47 -0
  243. package/src/client/motion.tsx +338 -0
  244. package/src/client/native/ContextBody.module.css +161 -0
  245. package/src/client/native/ContextBody.tsx +591 -0
  246. package/src/client/native/ContextInjectionRow.module.css +62 -0
  247. package/src/client/native/ContextInjectionRow.tsx +72 -0
  248. package/src/client/native/ReferenceIcon.tsx +35 -0
  249. package/src/client/native/tool-call-model.ts +248 -0
  250. package/src/client/native/upstream.json +41 -0
  251. package/src/client/open-file.ts +94 -0
  252. package/src/client/pending-submission.ts +49 -0
  253. package/src/client/primitive-labels.ts +53 -0
  254. package/src/client/projection.ts +118 -0
  255. package/src/client/reasoning-follow.ts +12 -0
  256. package/src/client/settings-copy.ts +53 -0
  257. package/src/client/settings-slots.ts +20 -0
  258. package/src/client/settings.tsx +73 -0
  259. package/src/client/skill-status.ts +78 -0
  260. package/src/client/store.ts +30 -0
  261. package/src/client/stream-buffer.ts +100 -0
  262. package/src/client/streaming.tsx +62 -0
  263. package/src/client/style.d.ts +4 -0
  264. package/src/client/timeline.ts +77 -0
  265. package/src/client/tool-activity.ts +152 -0
  266. package/src/client/types.ts +66 -0
  267. package/src/client/waiting-clock.ts +25 -0
  268. package/src/client/word-motion.tsx +91 -0
  269. package/src/client/word-timeline.ts +96 -0
  270. package/src/dsh-better-display.ts +108 -0
  271. package/src/skill-roots.ts +138 -0
  272. package/src/skill-status.ts +81 -0
  273. package/tests/browser-closed-gap.tsx +20 -0
  274. package/tests/browser-motion.tsx +65 -0
  275. package/tests/browser-sticky-lanes.tsx +22 -0
  276. package/tests/browser-waiting-clock.tsx +14 -0
  277. package/tests/chat-flow-hook.test.ts +66 -0
  278. package/tests/cloud-gate.test.ts +34 -0
  279. package/tests/conversation-scroll.test.ts +47 -0
  280. package/tests/deliverables.test.ts +128 -0
  281. package/tests/entry.test.ts +42 -0
  282. package/tests/fixtures/maid-composer-hide.html +31 -0
  283. package/tests/fold-choreography.test.ts +38 -0
  284. package/tests/history-word-regression.test.ts +16 -0
  285. package/tests/live-turn.test.ts +218 -0
  286. package/tests/mcp-apps.test.ts +157 -0
  287. package/tests/message-chrome.test.ts +29 -0
  288. package/tests/open-file.test.ts +103 -0
  289. package/tests/pending-submission.test.ts +45 -0
  290. package/tests/projection.test.ts +184 -0
  291. package/tests/reasoning-follow.test.ts +34 -0
  292. package/tests/settings-copy.test.ts +63 -0
  293. package/tests/skill-status.test.ts +137 -0
  294. package/tests/stock-install.test.ts +50 -0
  295. package/tests/stream-buffer.test.ts +97 -0
  296. package/tests/timeline.test.ts +59 -0
  297. package/tests/waiting-clock.test.ts +24 -0
  298. package/tests/word-timeline.test.ts +136 -0
  299. package/tsconfig.json +11 -0
  300. package/tsdown.config.ts +49 -0
@@ -0,0 +1,630 @@
1
+ // Adapted from DeepSeek Harness b150a551b8d465e31e418e1b2eaf5e79bbb7d28e (MIT). See THIRD_PARTY_NOTICES.md.
2
+ /**
3
+ * Direct mdast→React markdown renderer. Replaces the react-markdown /
4
+ * remark-rehype pipeline with one switch over parsed nodes so streaming can
5
+ * cache frozen blocks as React elements; the rendered DOM is pinned
6
+ * byte-for-byte by `tests/fixtures/markdown-dom` and must not drift.
7
+ *
8
+ * Untrusted-output policy (unchanged from the replaced pipeline): link and
9
+ * image destinations pass a protocol allowlist, images additionally require
10
+ * absolute HTTP(S), raw HTML renders as literal text (no HTML enters the
11
+ * DOM), and KaTeX runs without trusted commands. Fragment-anchor URLs fail
12
+ * the allowlist, so footnote references and back-references render as plain
13
+ * text rather than in-page links.
14
+ *
15
+ * Merge-extensible node unions fall through the documented default (render
16
+ * nothing) rather than ending in assertNever: grammars registered elsewhere
17
+ * may add node types this renderer has no mapping for.
18
+ */
19
+
20
+ import { Fragment, createElement } from 'react'
21
+ import type { Key, ReactNode } from 'react'
22
+ import clsx from 'clsx'
23
+ import type * as Md from 'mdast'
24
+ import type {} from 'mdast-util-math'
25
+ import { normalizeUri } from 'micromark-util-sanitize-uri'
26
+ import { CodeBlock } from '@deepseek-ai/dsh-client-ui-primitives'
27
+ import { renderTexToReact } from './katex.js'
28
+ import { McpAppCodeBlock, StreamingMcpAppPlaceholder } from '../McpAppFrame.js'
29
+ import { isMcpAppCodeBlock, extractMcpAppTitle, extractMcpAppHeight } from '../mcp-app.js'
30
+ import type { PositionedBlock } from './incremental.js'
31
+ import css from './MarkdownText.module.css'
32
+
33
+ /** Copy-button labels forwarded to fence CodeBlocks (this package is cordis-free, so copy arrives via props). */
34
+ export interface MarkdownCodeLabels {
35
+ /** Copy-button idle label. */
36
+ copyLabel?: string | undefined
37
+ /** Copy-button label during the post-copy confirmation window. */
38
+ copiedLabel?: string | undefined
39
+ }
40
+
41
+ function sanitizeUrl(url: string): string {
42
+ try {
43
+ switch (new URL(url).protocol) {
44
+ case 'http:':
45
+ case 'https:':
46
+ case 'mailto:':
47
+ return url
48
+ default:
49
+ return ''
50
+ }
51
+ } catch {
52
+ // Relative and otherwise unparsable destinations are disallowed alongside
53
+ // disallowed protocols; new URL() has no other failure mode for strings.
54
+ return ''
55
+ }
56
+ }
57
+
58
+ function remoteImageUrl(url: string): string | undefined {
59
+ try {
60
+ const protocol = new URL(url).protocol
61
+ return protocol === 'http:' || protocol === 'https:' ? url : undefined
62
+ } catch {
63
+ // Same single failure mode as above: not an absolute URL.
64
+ return undefined
65
+ }
66
+ }
67
+
68
+ /** Link/image reference targets collected from a document (first definition per identifier wins, as in CommonMark). */
69
+ export interface ReferenceTargets {
70
+ /** Link/image definitions keyed by upper-cased identifier. */
71
+ definitions: Map<string, Md.Definition>
72
+ /** Footnote definitions keyed by upper-cased identifier. */
73
+ footnotes: Map<string, Md.FootnoteDefinition>
74
+ }
75
+
76
+ /**
77
+ * Create an empty {@link ReferenceTargets}.
78
+ * @returns Fresh empty maps.
79
+ */
80
+ export function createReferenceTargets(): ReferenceTargets {
81
+ return { definitions: new Map(), footnotes: new Map() }
82
+ }
83
+
84
+ /**
85
+ * Record every definition and footnote definition under `nodes` into
86
+ * `targets`, depth-first, keeping the first definition per identifier.
87
+ * @param nodes - Subtrees to walk (top-level blocks or any nested children).
88
+ * @param targets - Accumulator, typically shared across incremental segments.
89
+ */
90
+ export function collectReferenceTargets(
91
+ nodes: readonly Md.RootContent[],
92
+ targets: ReferenceTargets,
93
+ ): void {
94
+ for (const node of nodes) {
95
+ if (node.type === 'definition') {
96
+ const id = node.identifier.toUpperCase()
97
+ if (!targets.definitions.has(id)) targets.definitions.set(id, node)
98
+ } else if (node.type === 'footnoteDefinition') {
99
+ const id = node.identifier.toUpperCase()
100
+ if (!targets.footnotes.has(id)) targets.footnotes.set(id, node)
101
+ }
102
+ if ('children' in node) collectReferenceTargets(node.children, targets)
103
+ }
104
+ }
105
+
106
+ /**
107
+ * File-mention affordance for inline code: the owner resolves an authored
108
+ * token to the file it names, using its own vocabulary of real files — the
109
+ * renderer never guesses at what looks like a path.
110
+ */
111
+ export interface MarkdownFileMentions {
112
+ /**
113
+ * Resolve one inline-code token.
114
+ * @param value - The authored token, exactly as written.
115
+ * @returns The opener with its accessible label and full-path title, or
116
+ * undefined when the token names no known file — it then stays inert code.
117
+ */
118
+ resolve(value: string): { open: () => void; label: string; title: string } | undefined
119
+ }
120
+
121
+ /**
122
+ * One render pass's state: immutable options and targets plus the footnote
123
+ * numbering accumulated in document order while references render.
124
+ */
125
+ export interface MarkdownRenderContext {
126
+ /** Reader addition: optional semantic text-leaf renderer with an absolute source offset. */
127
+ readonly renderText?: (value: string, offset: number, inline?: boolean) => ReactNode
128
+ /** Reader addition: keep atomic code content behind the source-order gate. */
129
+ readonly renderAtom?: (children: ReactNode, offset: number) => ReactNode
130
+ readonly sourceOffset?: number
131
+ /** Streaming arm: fences render plain and TeX stays literal. */
132
+ readonly streaming: boolean
133
+ /** Localized fence copy-button labels. */
134
+ readonly codeLabels: MarkdownCodeLabels | undefined
135
+ /** Inside a blockquote's children: tables there always fill the quote's width. */
136
+ readonly inBlockquote?: boolean
137
+ /** Inline-code file mentions; absent wherever no opener vocabulary exists. */
138
+ readonly fileMentions: MarkdownFileMentions | undefined
139
+ /** Inside an anchor's children: interactive mentions must not nest there. */
140
+ readonly inLink?: boolean
141
+ /** Reference targets visible to this pass. */
142
+ readonly targets: ReferenceTargets
143
+ /** Footnote identifiers in first-reference order; a footnote's number is its 1-based index here. */
144
+ readonly footnoteOrder: string[]
145
+ /** References rendered per identifier; drives the section's back-reference count. */
146
+ readonly footnoteCounts: Map<string, number>
147
+ }
148
+
149
+ /**
150
+ * Render top-level blocks. Nodes that render nothing (definitions, unmapped
151
+ * types) are dropped rather than kept as null placeholders, matching the
152
+ * replaced pipeline's child lists so separator newlines land identically.
153
+ * @param blocks - Blocks with their stream-stable render keys.
154
+ * @param context - The pass state; footnote numbering mutates in document order.
155
+ * @returns One React node per rendered block.
156
+ */
157
+ export function renderBlocks(
158
+ blocks: readonly PositionedBlock[],
159
+ context: MarkdownRenderContext,
160
+ ): ReactNode[] {
161
+ return blocks
162
+ .map(block => renderNode(block.node, block.key, {
163
+ ...context,
164
+ sourceOffset: block.key - (block.node.position?.start.offset ?? block.key),
165
+ }))
166
+ .filter(element => element !== null)
167
+ }
168
+
169
+ /**
170
+ * Interleave the newline text nodes the replaced pipeline emitted between
171
+ * block-level children. They are invisible between elements but coalesce
172
+ * into adjacent literal raw-HTML text, where the DOM parity fixtures pin
173
+ * them.
174
+ * @param elements - Rendered block children with empty renders already dropped.
175
+ * @param edges - Also emit the leading and trailing newline (hast's loose wrap).
176
+ * @returns The interleaved children.
177
+ */
178
+ export function wrapBlockChildren(elements: readonly ReactNode[], edges: boolean): ReactNode[] {
179
+ const wrapped: ReactNode[] = []
180
+ for (const element of elements) {
181
+ if (edges || wrapped.length > 0) wrapped.push('\n')
182
+ wrapped.push(element)
183
+ }
184
+ if (edges && elements.length > 0) wrapped.push('\n')
185
+ return wrapped
186
+ }
187
+
188
+ /**
189
+ * A block child rendered for a parent that must tell paragraphs apart from
190
+ * other blocks (list items unwrap them when tight; footnote bodies receive
191
+ * their back-references inside the trailing paragraph).
192
+ */
193
+ type BlockEntry = { paragraph: ReactNode[] } | { element: ReactNode }
194
+
195
+ /** Render container children into {@link BlockEntry} values, dropping empty renders. */
196
+ function renderBlockEntries(
197
+ blocks: readonly Md.RootContent[],
198
+ context: MarkdownRenderContext,
199
+ ): BlockEntry[] {
200
+ const entries: BlockEntry[] = []
201
+ for (const [index, block] of blocks.entries()) {
202
+ if (block.type === 'paragraph') {
203
+ entries.push({ paragraph: renderChildren(block.children, context) })
204
+ } else {
205
+ const element = renderNode(block, index, context)
206
+ if (element !== null) entries.push({ element })
207
+ }
208
+ }
209
+ return entries
210
+ }
211
+
212
+ function renderChildren(
213
+ nodes: readonly Md.RootContent[],
214
+ context: MarkdownRenderContext,
215
+ ): ReactNode[] {
216
+ return nodes.map((node, index) => renderNode(node, index, context))
217
+ }
218
+
219
+ function renderNode(node: Md.RootContent, key: Key, context: MarkdownRenderContext): ReactNode {
220
+ switch (node.type) {
221
+ case 'text':
222
+ return context.renderText ? context.renderText(node.value, (context.sourceOffset ?? 0) + (node.position?.start.offset ?? 0)) : node.value
223
+ case 'paragraph':
224
+ return <p key={key}>{renderChildren(node.children, context)}</p>
225
+ case 'heading':
226
+ return createElement(`h${node.depth}`, { key }, ...renderChildren(node.children, context))
227
+ case 'blockquote':
228
+ return (
229
+ <blockquote key={key}>
230
+ {wrapBlockChildren(
231
+ renderChildren(node.children, { ...context, inBlockquote: true }).filter(child => child !== null),
232
+ true,
233
+ )}
234
+ </blockquote>
235
+ )
236
+ case 'thematicBreak':
237
+ return <hr key={key} />
238
+ case 'break':
239
+ // The replaced pipeline emitted a newline text node after each <br>.
240
+ return <Fragment key={key}><br />{'\n'}</Fragment>
241
+ case 'strong':
242
+ return <strong key={key}>{renderChildren(node.children, context)}</strong>
243
+ case 'emphasis':
244
+ return <em key={key}>{renderChildren(node.children, context)}</em>
245
+ case 'delete':
246
+ return <del key={key}>{renderChildren(node.children, context)}</del>
247
+ case 'inlineCode': {
248
+ // Parity with mdast-util-to-hast: inline code renders line endings as spaces.
249
+ const value = node.value.replace(/\r?\n|\r/g, ' ')
250
+ // Native code chips are inline-flex. Keep a single inline formatting
251
+ // context so motion spans do not become flex items or drop code spaces.
252
+ const content = context.renderText ? <span>{context.renderText(value, (context.sourceOffset ?? 0) + (node.position?.start.offset ?? 0), true)}</span> : value
253
+ // An inline-code token that is entirely an absolute HTTP(S) URL keeps
254
+ // its code chrome and gains the same safe external anchor as a link;
255
+ // commands, partial URLs, and other schemes stay inert. The value is
256
+ // authored text, not a parsed destination, so no normalizeUri: port,
257
+ // path, and query render unchanged.
258
+ const href = inlineCodeHttpUrl(value)
259
+ if (href !== undefined) return <code key={key}>{renderSafeLink(href, [content], 'link')}</code>
260
+ // A token the owner's file-mention vocabulary recognizes opens that
261
+ // file; the resolver, not this renderer, decides what names a file.
262
+ // Inside an anchor the token stays inert — a button cannot nest there.
263
+ const mention = context.inLink === true ? undefined : context.fileMentions?.resolve(value)
264
+ if (mention !== undefined) {
265
+ return (
266
+ <code key={key}>
267
+ <button
268
+ type="button"
269
+ className={css.fileMention}
270
+ title={mention.title}
271
+ aria-label={mention.label}
272
+ onClick={mention.open}
273
+ >
274
+ {content}
275
+ </button>
276
+ </code>
277
+ )
278
+ }
279
+ return <code key={key}>{content}</code>
280
+ }
281
+ case 'html':
282
+ // No HTML parser enters the pipeline: raw HTML stays literal text.
283
+ return context.renderText ? context.renderText(node.value, (context.sourceOffset ?? 0) + (node.position?.start.offset ?? 0)) : node.value
284
+ case 'code': {
285
+ const code = renderCode(node, key, context)
286
+ return context.renderAtom ? <Fragment key={key}>{context.renderAtom(code, (context.sourceOffset ?? 0) + (node.position?.start.offset ?? 0))}</Fragment> : code
287
+ }
288
+ case 'math':
289
+ return <Fragment key={key}>{renderTexToReact(node.value, true)}</Fragment>
290
+ case 'inlineMath':
291
+ return <Fragment key={key}>{renderTexToReact(node.value, false)}</Fragment>
292
+ case 'list':
293
+ return renderList(node, key, context)
294
+ case 'listItem':
295
+ // Reachable only in hand-built trees: the grammar emits items inside lists.
296
+ return renderListItem(node, listItemLoose(node), key, context)
297
+ case 'table':
298
+ return renderTable(node, key, context)
299
+ case 'link':
300
+ return renderAnchor(node.url, renderChildren(node.children, { ...context, inLink: true }), key)
301
+ case 'linkReference':
302
+ return renderLinkReference(node, key, context)
303
+ case 'image':
304
+ return renderImage(node.url, node.alt ?? '', key)
305
+ case 'imageReference':
306
+ return renderImageReference(node, key, context)
307
+ case 'footnoteReference':
308
+ return renderFootnoteReference(node, key, context)
309
+ case 'definition':
310
+ case 'footnoteDefinition':
311
+ // Targets render elsewhere: definitions resolve references in place;
312
+ // footnote bodies render in the trailing section.
313
+ return null
314
+ default:
315
+ // Documented default for the merge-extensible union: node types without
316
+ // a mapping (tableRow/tableCell outside a table, frontmatter, future
317
+ // grammar contributions) render nothing.
318
+ return null
319
+ }
320
+ }
321
+
322
+ function renderCode(node: Md.Code, key: Key, context: MarkdownRenderContext): ReactNode {
323
+ const language = node.lang ?? undefined
324
+ if (node.value === '') {
325
+ // Parity: the replaced pipeline kept the stock <pre> for an empty fence.
326
+ return (
327
+ <pre key={key}>
328
+ <code className={language === undefined ? undefined : `language-${language}`} />
329
+ </pre>
330
+ )
331
+ }
332
+ // The replaced pipeline recovered the grammar id from the hast class with
333
+ // /language-([\w-]+)/, which truncates at the first non-word character.
334
+ const lang = language === undefined ? undefined : /^[\w-]+/.exec(language)?.[0]
335
+
336
+ if (isMcpAppCodeBlock(node.lang, node.meta, node.value)) {
337
+ const title = extractMcpAppTitle(node.meta, node.value)
338
+ const initialHeight = extractMcpAppHeight(node.meta)
339
+ if (context.streaming) {
340
+ return <StreamingMcpAppPlaceholder key={key} title={title} />
341
+ }
342
+ return <McpAppCodeBlock key={key} html={node.value} title={title} initialHeight={initialHeight} />
343
+ }
344
+
345
+ if (!context.streaming && lang === 'math') {
346
+ // ```math fences render as display TeX once settled (rehype-katex parity);
347
+ // its text extraction saw the code block's trailing newline.
348
+ return <Fragment key={key}>{renderTexToReact(`${node.value}\n`, true)}</Fragment>
349
+ }
350
+ return (
351
+ <CodeBlock
352
+ key={key}
353
+ // The replaced hast pipeline appended one synthetic newline that
354
+ // CodeBlock's display trim removes; feeding the bare value would make
355
+ // that trim eat a REAL trailing blank line inside the fence instead.
356
+ code={`${node.value}\n`}
357
+ lang={context.streaming ? undefined : lang}
358
+ copyLabel={context.codeLabels?.copyLabel ?? '复制代码'}
359
+ copiedLabel={context.codeLabels?.copiedLabel ?? '已复制'}
360
+ />
361
+ )
362
+ }
363
+
364
+ /** A list is loose when it or any of its items is spread; every item then keeps its paragraphs. */
365
+ function listLoose(list: Md.List): boolean {
366
+ return (list.spread ?? false) || list.children.some(listItemLoose)
367
+ }
368
+
369
+ function listItemLoose(item: Md.ListItem): boolean {
370
+ return item.spread ?? item.children.length > 1
371
+ }
372
+
373
+ function renderList(node: Md.List, key: Key, context: MarkdownRenderContext): ReactNode {
374
+ const loose = listLoose(node)
375
+ const properties: { start?: number; className?: string } = {}
376
+ if (typeof node.start === 'number' && node.start !== 1) properties.start = node.start
377
+ if (node.children.some(item => typeof item.checked === 'boolean')) {
378
+ properties.className = 'contains-task-list'
379
+ }
380
+ return createElement(
381
+ node.ordered === true ? 'ol' : 'ul',
382
+ { key, ...properties },
383
+ ...node.children.map((item, index) => renderListItem(item, loose, index, context)),
384
+ )
385
+ }
386
+
387
+ function renderListItem(
388
+ item: Md.ListItem,
389
+ loose: boolean,
390
+ key: Key,
391
+ context: MarkdownRenderContext,
392
+ ): ReactNode {
393
+ const entries = renderBlockEntries(item.children, context)
394
+ const task = typeof item.checked === 'boolean'
395
+ if (task) {
396
+ const checkbox = <input key="task-checkbox" type="checkbox" checked={item.checked === true} disabled />
397
+ const head = entries[0]
398
+ if (head !== undefined && 'paragraph' in head) {
399
+ head.paragraph = head.paragraph.length > 0 ? [checkbox, ' ', ...head.paragraph] : [checkbox]
400
+ } else {
401
+ entries.unshift({ paragraph: [checkbox] })
402
+ }
403
+ }
404
+ // Newline placement and tight-paragraph unwrapping mirror
405
+ // mdast-util-to-hast's list-item handler: a newline before every child
406
+ // except a tight leading paragraph, and after a trailing non-paragraph
407
+ // (or any trailing child when loose).
408
+ const parts: ReactNode[] = []
409
+ for (const [index, entry] of entries.entries()) {
410
+ const isParagraph = 'paragraph' in entry
411
+ if (loose || index !== 0 || !isParagraph) parts.push('\n')
412
+ if (!isParagraph) parts.push(entry.element)
413
+ else if (loose) parts.push(<p key={`p-${index}`}>{entry.paragraph}</p>)
414
+ else parts.push(<Fragment key={`p-${index}`}>{entry.paragraph}</Fragment>)
415
+ }
416
+ const tail = entries[entries.length - 1]
417
+ if (tail !== undefined && (loose || !('paragraph' in tail))) parts.push('\n')
418
+ return (
419
+ <li key={key} className={task ? 'task-list-item' : undefined}>
420
+ {parts}
421
+ </li>
422
+ )
423
+ }
424
+
425
+ function renderTable(node: Md.Table, key: Key, context: MarkdownRenderContext): ReactNode {
426
+ const align = node.align ?? null
427
+ const [headRow, ...bodyRows] = node.children
428
+ const columns = align === null ? headRow?.children.length ?? 0 : align.length
429
+ // Four or more columns read as a comparison matrix: the block keeps the
430
+ // table at natural width and exposes the stable `md-table-wide` hook so a
431
+ // hosting layout (the chat transcript) can widen it past the message
432
+ // column. Narrower tables — and any table inside a blockquote — fill the
433
+ // column and wrap instead (deepsuite chat TableWrapper parity).
434
+ const wide = columns >= 4 && context.inBlockquote !== true
435
+ return (
436
+ // Wide tables rest with overflow-x hidden (the hover-revealed bar in
437
+ // MarkdownText.module.css), which drops Chromium's implicit scroller
438
+ // focusability — the explicit tabindex keeps them keyboard-reachable,
439
+ // and :focus-visible restores scrolling.
440
+ <div
441
+ key={key}
442
+ className={clsx(css.tableScroll, wide ? 'md-table-wide' : css.tableFill)}
443
+ tabIndex={wide ? 0 : undefined}
444
+ >
445
+ <table>
446
+ {headRow !== undefined && <thead>{renderTableRow(headRow, 'th', align, 0, context)}</thead>}
447
+ {bodyRows.length > 0 && (
448
+ <tbody>
449
+ {bodyRows.map((row, index) => renderTableRow(row, 'td', align, index + 1, context))}
450
+ </tbody>
451
+ )}
452
+ </table>
453
+ </div>
454
+ )
455
+ }
456
+
457
+ function renderTableRow(
458
+ row: Md.TableRow,
459
+ cellTag: 'th' | 'td',
460
+ align: readonly Md.AlignType[] | null,
461
+ key: Key,
462
+ context: MarkdownRenderContext,
463
+ ): ReactNode {
464
+ // With column alignment present, every row renders exactly one cell per
465
+ // column, padding or truncating the row (mdast-util-to-hast parity).
466
+ const length = align === null ? row.children.length : align.length
467
+ const cells: ReactNode[] = []
468
+ for (let index = 0; index < length; index++) {
469
+ const cell = row.children[index]
470
+ const alignValue = align?.[index]
471
+ cells.push(createElement(
472
+ cellTag,
473
+ // hast-util-to-jsx-runtime's default tableCellAlignToStyle turned the
474
+ // deprecated align attribute into an inline style; keep that DOM.
475
+ { key: index, style: alignValue == null ? undefined : { textAlign: alignValue } },
476
+ ...(cell === undefined ? [] : renderChildren(cell.children, context)),
477
+ ))
478
+ }
479
+ return <tr key={key}>{cells}</tr>
480
+ }
481
+
482
+ /** Anchor over an already-authored href: allowlisted or unwrapped, external links get the safe attributes. */
483
+ function renderSafeLink(href: string, children: ReactNode[], key: Key): ReactNode {
484
+ const safeHref = sanitizeUrl(href)
485
+ if (safeHref === '') return <Fragment key={key}>{children}</Fragment>
486
+ const external = ['http:', 'https:'].includes(new URL(safeHref).protocol)
487
+ return (
488
+ <a
489
+ key={key}
490
+ href={safeHref}
491
+ {...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
492
+ >
493
+ {children}
494
+ </a>
495
+ )
496
+ }
497
+
498
+ /** Anchor over a parsed markdown destination, which hast normalized before the allowlist saw it. */
499
+ function renderAnchor(url: string, children: ReactNode[], key: Key): ReactNode {
500
+ return renderSafeLink(normalizeUri(url), children, key)
501
+ }
502
+
503
+ /**
504
+ * The complete inline-code value when it is exactly an absolute HTTP(S) URL
505
+ * (no surrounding whitespace); anything else stays inert code.
506
+ */
507
+ function inlineCodeHttpUrl(value: string): string | undefined {
508
+ if (value.trim() !== value) return undefined
509
+ try {
510
+ const protocol = new URL(value).protocol
511
+ return protocol === 'http:' || protocol === 'https:' ? value : undefined
512
+ } catch {
513
+ // Not an absolute URL at all — the only way new URL() rejects a string.
514
+ return undefined
515
+ }
516
+ }
517
+
518
+ function renderImage(url: string, alt: string, key: Key): ReactNode {
519
+ const imageSrc = remoteImageUrl(sanitizeUrl(normalizeUri(url)))
520
+ if (imageSrc === undefined) {
521
+ return <span key={key} className={css.imageAlt}>{alt}</span>
522
+ }
523
+ return (
524
+ <img
525
+ key={key}
526
+ className={css.image}
527
+ src={imageSrc}
528
+ alt={alt}
529
+ loading="lazy"
530
+ decoding="async"
531
+ referrerPolicy="no-referrer"
532
+ />
533
+ )
534
+ }
535
+
536
+ /** The bracketed source text a reference reverts to when its definition is missing. */
537
+ function referenceSuffix(node: Md.LinkReference | Md.ImageReference): string {
538
+ if (node.referenceType === 'collapsed') return '][]'
539
+ if (node.referenceType === 'full') return `][${node.label ?? node.identifier}]`
540
+ return ']'
541
+ }
542
+
543
+ function renderLinkReference(
544
+ node: Md.LinkReference,
545
+ key: Key,
546
+ context: MarkdownRenderContext,
547
+ ): ReactNode {
548
+ const definition = context.targets.definitions.get(node.identifier.toUpperCase())
549
+ if (definition === undefined) {
550
+ // The grammar only emits references whose definitions exist somewhere in
551
+ // the same parse, but incremental segments and hand-built trees may still
552
+ // present unresolved ones: revert to the bracketed source text — which is
553
+ // not an anchor, so mentions inside it stay live.
554
+ return <Fragment key={key}>{'['}{renderChildren(node.children, context)}{referenceSuffix(node)}</Fragment>
555
+ }
556
+ return renderAnchor(definition.url, renderChildren(node.children, { ...context, inLink: true }), key)
557
+ }
558
+
559
+ function renderImageReference(
560
+ node: Md.ImageReference,
561
+ key: Key,
562
+ context: MarkdownRenderContext,
563
+ ): ReactNode {
564
+ const definition = context.targets.definitions.get(node.identifier.toUpperCase())
565
+ if (definition === undefined) return `![${node.alt ?? ''}${referenceSuffix(node)}`
566
+ return renderImage(definition.url, node.alt ?? '', key)
567
+ }
568
+
569
+ function renderFootnoteReference(
570
+ node: Md.FootnoteReference,
571
+ key: Key,
572
+ context: MarkdownRenderContext,
573
+ ): ReactNode {
574
+ const id = node.identifier.toUpperCase()
575
+ const seen = context.footnoteCounts.get(id)
576
+ if (seen === undefined) context.footnoteOrder.push(id)
577
+ context.footnoteCounts.set(id, (seen ?? 0) + 1)
578
+ // The in-page anchor fails the protocol allowlist, so only the numbered
579
+ // superscript renders (matching the replaced pipeline's unwrapped link).
580
+ return <sup key={key}>{String(context.footnoteOrder.indexOf(id) + 1)}</sup>
581
+ }
582
+
583
+ /**
584
+ * Render the trailing footnote section for every footnote referenced during
585
+ * the pass, in first-reference order, with one plain-text back-reference
586
+ * marker per rendered reference.
587
+ * @param context - The pass state after all blocks rendered.
588
+ * @returns The section, or null when no referenced footnote has a definition.
589
+ */
590
+ export function renderFootnoteSection(context: MarkdownRenderContext): ReactNode | null {
591
+ const items: ReactNode[] = []
592
+ for (const id of context.footnoteOrder) {
593
+ const definition = context.targets.footnotes.get(id)
594
+ if (definition === undefined) continue
595
+ const count = context.footnoteCounts.get(id) ?? 0
596
+ const backrefs: ReactNode[] = []
597
+ for (let reference = 1; reference <= count; reference++) {
598
+ if (backrefs.length > 0) backrefs.push(' ')
599
+ backrefs.push('↩')
600
+ if (reference > 1) backrefs.push(<sup key={`re-${reference}`}>{String(reference)}</sup>)
601
+ }
602
+ const entries = renderBlockEntries(definition.children, context)
603
+ const tail = entries[entries.length - 1]
604
+ const body: ReactNode[] = entries.map((entry, index) => (
605
+ 'paragraph' in entry
606
+ ? (
607
+ <p key={`p-${index}`}>
608
+ {entry.paragraph}
609
+ {entry === tail && <>{' '}{backrefs}</>}
610
+ </p>
611
+ )
612
+ : entry.element
613
+ ))
614
+ // Without a trailing paragraph the back-references join the block list
615
+ // itself (and pick up the wrap newlines), as in the replaced pipeline.
616
+ if (tail === undefined || !('paragraph' in tail)) body.push(...backrefs)
617
+ items.push(
618
+ <li key={id} id={`user-content-fn-${normalizeUri(id.toLowerCase())}`}>
619
+ {wrapBlockChildren(body, true)}
620
+ </li>,
621
+ )
622
+ }
623
+ if (items.length === 0) return null
624
+ return (
625
+ <section key="footnotes" data-footnotes className="footnotes">
626
+ <h2 id="footnote-label" className="sr-only">Footnotes</h2>
627
+ <ol>{items}</ol>
628
+ </section>
629
+ )
630
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "repository": "https://github.com/deepseek-ai/deepseek-harness",
3
+ "commit": "b150a551b8d465e31e418e1b2eaf5e79bbb7d28e",
4
+ "sha256BeforeAdaptation": {
5
+ "MarkdownText.tsx": "b0ce4cf0986cadf624436136186b16f108ed2365aec0350b195f02dae036017a",
6
+ "MarkdownText.module.css": "e226e878c45037b1cf537e4361f59e052ef979321593be3369bd31200d0342bc",
7
+ "render.tsx": "d4fefca41ab709526933e47144a01df14bd3d5619558cd6ed35b663d1640d779",
8
+ "parse.ts": "6be57559995c4e717d6d704207eb2fec86db9714c2a4bcc5b0c3f6648c8990f0",
9
+ "incremental.ts": "2975a332ac74b931a7cb489e2eea28469aa3d9596328d5a474d41379b6c7b640",
10
+ "cjkFriendlyStrong.ts": "bc4adf3183f7d0feea17e3a176e7c4b1db5bdddc29e7fc87261f627ff8a39b53",
11
+ "mathCompatibility.ts": "5f8cec86fd613fd2f67510e22b0a9d661f4173b3d49fb13fef71b25f4abb652b",
12
+ "katex.tsx": "8227e04d29333d2568021ed012143237b457852bce25d82cf394d72b941e74a5"
13
+ }
14
+ }