react-native-marcus 0.1.0 → 0.2.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 (294) hide show
  1. package/README.md +220 -63
  2. package/RNMarcus.podspec +6 -0
  3. package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +2 -1
  4. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorView.kt +168 -0
  5. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManager.kt +38 -0
  6. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManagerSpec.kt +19 -0
  7. package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +23 -1
  8. package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPainter.kt +147 -0
  9. package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPass.kt +19 -0
  10. package/android/src/main/java/app/getbullet/marcus/MarkdownCopy.kt +87 -0
  11. package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +652 -113
  12. package/android/src/main/java/app/getbullet/marcus/MarkdownRanges.kt +47 -0
  13. package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +140 -61
  14. package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +37 -7
  15. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownAbsoluteLineHeightSpan.kt +48 -0
  16. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +102 -48
  17. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +22 -4
  18. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBulletSpan.kt +78 -0
  19. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpan.kt +177 -0
  20. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontStyleSpan.kt +32 -0
  21. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontWeightSpan.kt +34 -0
  22. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +24 -11
  23. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLetterSpacingSpan.kt +32 -0
  24. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownTextShadowSpan.kt +32 -0
  25. package/android/src/main/new_arch/CMakeLists.txt +17 -0
  26. package/android/src/main/new_arch/RNMarcusSpec.h +1 -0
  27. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.cpp +235 -0
  28. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.h +62 -0
  29. package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
  30. package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
  31. package/android/src/test/java/app/getbullet/marcus/MarkdownCopyTest.kt +69 -0
  32. package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +170 -25
  33. package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +22 -5
  34. package/android/src/test/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpanTest.kt +91 -0
  35. package/android/src/test/resources/render-model.txt +221 -16
  36. package/apple/MarcusSwizzle.h +25 -0
  37. package/apple/MarcusTextDecoratorComponentView.h +22 -0
  38. package/apple/MarcusTextDecoratorComponentView.mm +72 -0
  39. package/apple/MarcusTextDecoratorShadowNode.h +62 -0
  40. package/apple/MarcusTextDecoratorShadowNode.mm +192 -0
  41. package/apple/MarcusTextDecoratorViewManager.h +4 -0
  42. package/apple/MarcusTextDecoratorViewManager.mm +11 -0
  43. package/apple/MarcusTextInputDecoratorComponentView.mm +1 -1
  44. package/apple/MarcusTextInputDecoratorShadowNode.mm +1 -1
  45. package/apple/MarcusTextLayoutManager.h +40 -0
  46. package/apple/MarcusTextLayoutManager.mm +173 -0
  47. package/apple/MarkdownAttributes.h +23 -2
  48. package/apple/MarkdownAttributes.m +6 -0
  49. package/apple/MarkdownCopy.swift +63 -0
  50. package/apple/MarkdownDecorator.swift +2 -2
  51. package/apple/MarkdownFormatter+React.swift +23 -0
  52. package/apple/MarkdownFormatter.swift +869 -148
  53. package/apple/MarkdownParagraphLayoutManager.swift +455 -0
  54. package/apple/MarkdownSwiftInterop.h +0 -1
  55. package/apple/MarkdownTextBackground.swift +59 -1
  56. package/apple/MarkdownTextFieldObserver.swift +2 -2
  57. package/apple/MarkdownTextLayoutFragment.swift +159 -33
  58. package/apple/MarkdownTextLayoutManagerDelegate.swift +67 -3
  59. package/apple/MarkdownTextStorageDelegate.swift +2 -2
  60. package/apple/MarkdownTextViewObserver.swift +2 -2
  61. package/apple/MarkdownUtils.swift +38 -0
  62. package/apple/MarkdownWritingDirection.swift +132 -0
  63. package/apple/RCTMarcusStyle+Codegen.h +115 -9
  64. package/apple/RCTMarcusStyle.h +128 -31
  65. package/apple/RCTMarcusStyle.m +48 -0
  66. package/apple/RCTParagraphComponentView+Marcus.h +17 -0
  67. package/apple/RCTParagraphComponentView+Marcus.mm +38 -0
  68. package/apple/RCTTextInputComponentView+Marcus.mm +1 -25
  69. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorState.h +37 -0
  70. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h +17 -0
  71. package/lib/commonjs/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  72. package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  73. package/lib/commonjs/MarkdownText.js +330 -0
  74. package/lib/commonjs/MarkdownText.js.map +1 -0
  75. package/lib/commonjs/MarkdownText.web.js +529 -0
  76. package/lib/commonjs/MarkdownText.web.js.map +1 -0
  77. package/lib/commonjs/MarkdownTextInput.js +22 -27
  78. package/lib/commonjs/MarkdownTextInput.js.map +1 -1
  79. package/lib/commonjs/MarkdownTextInput.web.js +12 -7
  80. package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
  81. package/lib/commonjs/atomUtils.js +112 -0
  82. package/lib/commonjs/atomUtils.js.map +1 -0
  83. package/lib/commonjs/childrenUtils.js +36 -0
  84. package/lib/commonjs/childrenUtils.js.map +1 -0
  85. package/lib/commonjs/index.js +8 -7
  86. package/lib/commonjs/index.js.map +1 -1
  87. package/lib/commonjs/math/environment.js +69 -0
  88. package/lib/commonjs/math/environment.js.map +1 -0
  89. package/lib/commonjs/math/index.js +61 -0
  90. package/lib/commonjs/math/index.js.map +1 -0
  91. package/lib/commonjs/math/mathjax.js +548 -0
  92. package/lib/commonjs/math/mathjax.js.map +1 -0
  93. package/lib/commonjs/metro.js +172 -0
  94. package/lib/commonjs/metro.js.map +1 -0
  95. package/lib/commonjs/parser/index.d.ts +3 -0
  96. package/lib/commonjs/parser/index.js +450 -42
  97. package/lib/commonjs/parser/index.js.map +4 -4
  98. package/lib/commonjs/stripUtils.js +743 -0
  99. package/lib/commonjs/stripUtils.js.map +1 -0
  100. package/lib/commonjs/styleUtils.js +194 -23
  101. package/lib/commonjs/styleUtils.js.map +1 -1
  102. package/lib/commonjs/web/MarkdownElements.css +67 -0
  103. package/lib/commonjs/web/MarkdownTextInput.css +9 -7
  104. package/lib/commonjs/web/utils/blockLayout.js +95 -7
  105. package/lib/commonjs/web/utils/blockLayout.js.map +1 -1
  106. package/lib/commonjs/web/utils/blockUtils.js +327 -54
  107. package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
  108. package/lib/commonjs/web/utils/elementUtils.js +80 -0
  109. package/lib/commonjs/web/utils/elementUtils.js.map +1 -0
  110. package/lib/commonjs/web/utils/parserUtils.js +84 -28
  111. package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
  112. package/lib/commonjs/web/utils/treeUtils.js +6 -0
  113. package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
  114. package/lib/commonjs/web/utils/webStyleUtils.js +25 -5
  115. package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
  116. package/lib/module/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  117. package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  118. package/lib/module/MarkdownText.js +325 -0
  119. package/lib/module/MarkdownText.js.map +1 -0
  120. package/lib/module/MarkdownText.web.js +524 -0
  121. package/lib/module/MarkdownText.web.js.map +1 -0
  122. package/lib/module/MarkdownTextInput.js +24 -29
  123. package/lib/module/MarkdownTextInput.js.map +1 -1
  124. package/lib/module/MarkdownTextInput.web.js +12 -7
  125. package/lib/module/MarkdownTextInput.web.js.map +1 -1
  126. package/lib/module/atomUtils.js +106 -0
  127. package/lib/module/atomUtils.js.map +1 -0
  128. package/lib/module/childrenUtils.js +33 -0
  129. package/lib/module/childrenUtils.js.map +1 -0
  130. package/lib/module/index.js +1 -1
  131. package/lib/module/index.js.map +1 -1
  132. package/lib/module/math/environment.js +67 -0
  133. package/lib/module/math/environment.js.map +1 -0
  134. package/lib/module/math/index.js +56 -0
  135. package/lib/module/math/index.js.map +1 -0
  136. package/lib/module/math/mathjax.js +541 -0
  137. package/lib/module/math/mathjax.js.map +1 -0
  138. package/lib/module/metro.js +169 -0
  139. package/lib/module/metro.js.map +1 -0
  140. package/lib/module/parser/index.d.ts +3 -0
  141. package/lib/module/parser/index.js +450 -42
  142. package/lib/module/parser/index.js.map +4 -4
  143. package/lib/module/stripUtils.js +739 -0
  144. package/lib/module/stripUtils.js.map +1 -0
  145. package/lib/module/styleUtils.js +196 -25
  146. package/lib/module/styleUtils.js.map +1 -1
  147. package/lib/module/web/MarkdownElements.css +67 -0
  148. package/lib/module/web/MarkdownTextInput.css +9 -7
  149. package/lib/module/web/utils/blockLayout.js +94 -8
  150. package/lib/module/web/utils/blockLayout.js.map +1 -1
  151. package/lib/module/web/utils/blockUtils.js +328 -56
  152. package/lib/module/web/utils/blockUtils.js.map +1 -1
  153. package/lib/module/web/utils/elementUtils.js +75 -0
  154. package/lib/module/web/utils/elementUtils.js.map +1 -0
  155. package/lib/module/web/utils/parserUtils.js +86 -30
  156. package/lib/module/web/utils/parserUtils.js.map +1 -1
  157. package/lib/module/web/utils/treeUtils.js +6 -0
  158. package/lib/module/web/utils/treeUtils.js.map +1 -1
  159. package/lib/module/web/utils/webStyleUtils.js +25 -5
  160. package/lib/module/web/utils/webStyleUtils.js.map +1 -1
  161. package/lib/typescript/commonjs/e2e/links.test.d.ts +2 -0
  162. package/lib/typescript/commonjs/e2e/links.test.d.ts.map +1 -0
  163. package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
  164. package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
  166. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  167. package/lib/typescript/commonjs/src/MarkdownText.d.ts +127 -0
  168. package/lib/typescript/commonjs/src/MarkdownText.d.ts.map +1 -0
  169. package/lib/typescript/commonjs/src/MarkdownText.web.d.ts +126 -0
  170. package/lib/typescript/commonjs/src/MarkdownText.web.d.ts.map +1 -0
  171. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +23 -1
  172. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -1
  173. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +26 -4
  174. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/src/atomUtils.d.ts +58 -0
  176. package/lib/typescript/commonjs/src/atomUtils.d.ts.map +1 -0
  177. package/lib/typescript/commonjs/src/childrenUtils.d.ts +12 -0
  178. package/lib/typescript/commonjs/src/childrenUtils.d.ts.map +1 -0
  179. package/lib/typescript/commonjs/src/commonTypes.d.ts +39 -3
  180. package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -1
  181. package/lib/typescript/commonjs/src/index.d.ts +2 -1
  182. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/src/math/environment.d.ts +7 -0
  184. package/lib/typescript/commonjs/src/math/environment.d.ts.map +1 -0
  185. package/lib/typescript/commonjs/src/math/index.d.ts +64 -0
  186. package/lib/typescript/commonjs/src/math/index.d.ts.map +1 -0
  187. package/lib/typescript/commonjs/src/math/mathjax.d.ts +63 -0
  188. package/lib/typescript/commonjs/src/math/mathjax.d.ts.map +1 -0
  189. package/lib/typescript/commonjs/src/metro.d.ts +59 -0
  190. package/lib/typescript/commonjs/src/metro.d.ts.map +1 -0
  191. package/lib/typescript/commonjs/src/parser/index.d.ts +3 -3
  192. package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -1
  193. package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/src/rangeUtils.d.ts +1 -1
  195. package/lib/typescript/commonjs/src/stripUtils.d.ts +108 -0
  196. package/lib/typescript/commonjs/src/stripUtils.d.ts.map +1 -0
  197. package/lib/typescript/commonjs/src/styleUtils.d.ts +62 -4
  198. package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -1
  199. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +32 -4
  200. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +20 -8
  202. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -1
  203. package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts +37 -0
  204. package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +5 -5
  206. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -1
  207. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +6 -1
  208. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +2 -2
  210. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -1
  211. package/lib/typescript/module/e2e/links.test.d.ts +2 -0
  212. package/lib/typescript/module/e2e/links.test.d.ts.map +1 -0
  213. package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
  214. package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
  215. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
  216. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  217. package/lib/typescript/module/src/MarkdownText.d.ts +127 -0
  218. package/lib/typescript/module/src/MarkdownText.d.ts.map +1 -0
  219. package/lib/typescript/module/src/MarkdownText.web.d.ts +126 -0
  220. package/lib/typescript/module/src/MarkdownText.web.d.ts.map +1 -0
  221. package/lib/typescript/module/src/MarkdownTextInput.d.ts +23 -1
  222. package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -1
  223. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +26 -4
  224. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -1
  225. package/lib/typescript/module/src/atomUtils.d.ts +58 -0
  226. package/lib/typescript/module/src/atomUtils.d.ts.map +1 -0
  227. package/lib/typescript/module/src/childrenUtils.d.ts +12 -0
  228. package/lib/typescript/module/src/childrenUtils.d.ts.map +1 -0
  229. package/lib/typescript/module/src/commonTypes.d.ts +39 -3
  230. package/lib/typescript/module/src/commonTypes.d.ts.map +1 -1
  231. package/lib/typescript/module/src/index.d.ts +2 -1
  232. package/lib/typescript/module/src/index.d.ts.map +1 -1
  233. package/lib/typescript/module/src/math/environment.d.ts +7 -0
  234. package/lib/typescript/module/src/math/environment.d.ts.map +1 -0
  235. package/lib/typescript/module/src/math/index.d.ts +64 -0
  236. package/lib/typescript/module/src/math/index.d.ts.map +1 -0
  237. package/lib/typescript/module/src/math/mathjax.d.ts +63 -0
  238. package/lib/typescript/module/src/math/mathjax.d.ts.map +1 -0
  239. package/lib/typescript/module/src/metro.d.ts +59 -0
  240. package/lib/typescript/module/src/metro.d.ts.map +1 -0
  241. package/lib/typescript/module/src/parser/index.d.ts +3 -3
  242. package/lib/typescript/module/src/parser/index.d.ts.map +1 -1
  243. package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -1
  244. package/lib/typescript/module/src/rangeUtils.d.ts +1 -1
  245. package/lib/typescript/module/src/stripUtils.d.ts +108 -0
  246. package/lib/typescript/module/src/stripUtils.d.ts.map +1 -0
  247. package/lib/typescript/module/src/styleUtils.d.ts +62 -4
  248. package/lib/typescript/module/src/styleUtils.d.ts.map +1 -1
  249. package/lib/typescript/module/src/web/utils/blockLayout.d.ts +32 -4
  250. package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -1
  251. package/lib/typescript/module/src/web/utils/blockUtils.d.ts +20 -8
  252. package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -1
  253. package/lib/typescript/module/src/web/utils/elementUtils.d.ts +37 -0
  254. package/lib/typescript/module/src/web/utils/elementUtils.d.ts.map +1 -0
  255. package/lib/typescript/module/src/web/utils/parserUtils.d.ts +5 -5
  256. package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -1
  257. package/lib/typescript/module/src/web/utils/treeUtils.d.ts +6 -1
  258. package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -1
  259. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +2 -2
  260. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -1
  261. package/package.json +29 -2
  262. package/react-native.config.js +8 -1
  263. package/src/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  264. package/src/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  265. package/src/MarkdownText.tsx +472 -0
  266. package/src/MarkdownText.web.tsx +668 -0
  267. package/src/MarkdownTextInput.tsx +42 -38
  268. package/src/MarkdownTextInput.web.tsx +36 -13
  269. package/src/atomUtils.ts +112 -0
  270. package/src/childrenUtils.ts +39 -0
  271. package/src/commonTypes.ts +69 -9
  272. package/src/index.tsx +2 -1
  273. package/src/math/environment.ts +68 -0
  274. package/src/math/index.ts +78 -0
  275. package/src/math/mathjax.ts +604 -0
  276. package/src/metro.ts +238 -0
  277. package/src/parser/index.ts +175 -10
  278. package/src/parser/micromark-extension-mentions.ts +104 -11
  279. package/src/stripUtils.ts +919 -0
  280. package/src/styleUtils.ts +290 -26
  281. package/src/web/MarkdownElements.css +67 -0
  282. package/src/web/MarkdownTextInput.css +9 -7
  283. package/src/web/utils/blockLayout.ts +124 -14
  284. package/src/web/utils/blockUtils.ts +365 -59
  285. package/src/web/utils/elementUtils.ts +87 -0
  286. package/src/web/utils/parserUtils.ts +119 -31
  287. package/src/web/utils/treeUtils.ts +6 -1
  288. package/src/web/utils/webStyleUtils.ts +32 -8
  289. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +0 -6
  290. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +0 -6
  291. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +0 -6
  292. package/apple/RCTMarcusStyle+Codegen.mm +0 -64
  293. package/apple/RCTMarcusUtils.h +0 -26
  294. package/apple/RCTMarcusUtils.mm +0 -26
@@ -1,14 +1,14 @@
1
- import { StyleSheet, TextInput, processColor } from "react-native"
1
+ import { StyleSheet, TextInput } from "react-native"
2
2
  import React from "react"
3
3
  import type { TextInputProps } from "react-native"
4
4
  import { createSerializable, createWorkletRuntime } from "react-native-worklets"
5
5
  import type { SerializableRef, WorkletFunction, WorkletRuntime } from "react-native-worklets"
6
6
  import MarcusTextInputDecoratorViewNativeComponent from "./MarcusTextInputDecoratorViewNativeComponent"
7
- import type { MarkdownStyle } from "./MarcusTextInputDecoratorViewNativeComponent"
8
7
  import NativeMarcusModule from "./NativeMarcusModule"
9
- import { mergeMarkdownStyleWithDefault } from "./styleUtils"
8
+ import { processMarkdownStyle } from "./styleUtils"
10
9
  import type { PartialMarkdownStyle } from "./styleUtils"
11
- import type { MarkdownRange } from "./commonTypes"
10
+ import { createParser } from "./parser"
11
+ import type { MarkdownParser, MarkdownRange } from "./commonTypes"
12
12
 
13
13
  declare global {
14
14
  // eslint-disable-next-line no-var
@@ -48,7 +48,7 @@ function initializeMarcusIfNeeded() {
48
48
  initialized = true
49
49
  }
50
50
 
51
- function registerParser(parser: (input: string) => MarkdownRange[]): number {
51
+ function registerParser(parser: MarkdownParser): number {
52
52
  initializeMarcusIfNeeded()
53
53
  const serializableWorklet = createSerializable(
54
54
  parser as WorkletFunction<[string], MarkdownRange[]>,
@@ -63,52 +63,56 @@ function unregisterParser(parserId: number) {
63
63
 
64
64
  interface MarkdownTextInputProps extends TextInputProps {
65
65
  markdownStyle?: PartialMarkdownStyle
66
- parser: (value: string) => MarkdownRange[]
66
+ /**
67
+ * GFM's strikethrough (`~~a~~`) and bare autolinks (`www.example.com`).
68
+ *
69
+ * @default false
70
+ */
71
+ gfm?: boolean
72
+ /**
73
+ * `@name` mentions, which no flavour of markdown has a notion of.
74
+ *
75
+ * @default false
76
+ */
77
+ mention?: boolean
78
+ /**
79
+ * LaTeX between double dollar signs: `$$x^2$$` inline, and the same on lines
80
+ * of its own for a block.
81
+ *
82
+ * An input shows what was typed, so the maths stays as written -- what this
83
+ * turns on is recognising it, which greys the dollars as the markup they are
84
+ * and leaves the TeX between them alone.
85
+ *
86
+ * @default false
87
+ */
88
+ math?: boolean
67
89
  }
68
90
 
69
91
  type MarkdownTextInput = TextInput & React.Component<MarkdownTextInputProps>
70
92
 
71
- function processColorsInMarkdownStyle(input: MarkdownStyle): MarkdownStyle {
72
- const output = JSON.parse(JSON.stringify(input))
73
-
74
- Object.keys(output).forEach((key) => {
75
- const obj = output[key]
76
- Object.keys(obj).forEach((prop) => {
77
- // TODO: use ReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes'
78
- if (!(prop === "color" || prop.endsWith("Color"))) {
79
- return
80
- }
81
- obj[prop] = processColor(obj[prop])
82
- })
83
- })
84
-
85
- return output as MarkdownStyle
86
- }
87
-
88
- function processMarkdownStyle(input: PartialMarkdownStyle | undefined): MarkdownStyle {
89
- return processColorsInMarkdownStyle(mergeMarkdownStyleWithDefault(input))
90
- }
91
-
92
93
  const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputProps>(
93
94
  (props, ref) => {
95
+ const { markdownStyle: markdownStyleProp, gfm, mention, math, ...textInputProps } = props
96
+
94
97
  const markdownStyle = React.useMemo(
95
- () => processMarkdownStyle(props.markdownStyle),
96
- [props.markdownStyle],
98
+ () => processMarkdownStyle(markdownStyleProp),
99
+ [markdownStyleProp],
97
100
  )
98
101
 
99
- if (props.parser === undefined) {
100
- throw new Error("[react-native-marcus] `parser` is undefined")
101
- }
102
+ // The same worklet for as long as the flavour is the same one, so the id
103
+ // below is registered once and the native side keeps the parser it has.
104
+ const parser = createParser({ gfm, mention, math })
102
105
 
103
106
  // eslint-disable-next-line no-underscore-dangle
104
- const workletHash = (props.parser as { __workletHash?: number }).__workletHash
105
- if (workletHash === undefined) {
106
- throw new Error("[react-native-marcus] `parser` is not a worklet")
107
+ if ((parser as { __workletHash?: number }).__workletHash === undefined) {
108
+ throw new Error(
109
+ "[react-native-marcus] the parser is not a worklet. Add `react-native-worklets/plugin` to the project's Babel config.",
110
+ )
107
111
  }
108
112
 
109
113
  const parserId = React.useMemo(() => {
110
- return registerParser(props.parser)
111
- }, [props.parser])
114
+ return registerParser(parser)
115
+ }, [parser])
112
116
 
113
117
  React.useEffect(() => {
114
118
  return () => unregisterParser(parserId)
@@ -120,7 +124,7 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
120
124
  markdownStyle={markdownStyle}
121
125
  parserId={parserId}
122
126
  >
123
- <TextInput {...props} ref={ref} />
127
+ <TextInput {...textInputProps} ref={ref} />
124
128
  </MarcusTextInputDecoratorViewNativeComponent>
125
129
  )
126
130
  },
@@ -33,8 +33,9 @@ import {
33
33
  removeSelection,
34
34
  setCursorPosition,
35
35
  } from "./web/utils/cursorUtils"
36
+ import "./web/MarkdownElements.css"
36
37
  import "./web/MarkdownTextInput.css"
37
- import type { MarkdownStyle } from "./MarcusTextInputDecoratorViewNativeComponent"
38
+ import type { PartialMarkdownStyle, ResolvedMarkdownStyle } from "./styleUtils"
38
39
  import {
39
40
  getElementHeight,
40
41
  getPlaceholderValue,
@@ -47,13 +48,36 @@ import {
47
48
  parseToReactDOMStyle,
48
49
  processMarkdownStyle,
49
50
  } from "./web/utils/webStyleUtils"
50
- import type { MarkdownRange } from "./commonTypes"
51
+ import { createParser } from "./parser"
52
+ import type { MarkdownParser } from "./commonTypes"
51
53
 
52
54
  const useClientEffect = typeof window === "undefined" ? useEffect : useLayoutEffect
53
55
 
54
56
  interface MarkdownTextInputProps extends TextInputProps {
55
- markdownStyle?: MarkdownStyle
56
- parser: (text: string) => MarkdownRange[]
57
+ markdownStyle?: PartialMarkdownStyle
58
+ /**
59
+ * GFM's strikethrough (`~~a~~`) and bare autolinks (`www.example.com`).
60
+ *
61
+ * @default false
62
+ */
63
+ gfm?: boolean
64
+ /**
65
+ * `@name` mentions, which no flavour of markdown has a notion of.
66
+ *
67
+ * @default false
68
+ */
69
+ mention?: boolean
70
+ /**
71
+ * LaTeX between double dollar signs: `$$x^2$$` inline, and the same on lines
72
+ * of its own for a block.
73
+ *
74
+ * An input shows what was typed, so the maths stays as written -- what this
75
+ * turns on is recognising it, which greys the dollars as the markup they are
76
+ * and leaves the TeX between them alone.
77
+ *
78
+ * @default false
79
+ */
80
+ math?: boolean
57
81
  onClick?: (e: MouseEvent<HTMLDivElement>) => void
58
82
  dir?: string
59
83
  disabled?: boolean
@@ -114,7 +138,9 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
114
138
  numberOfLines,
115
139
  multiline = false,
116
140
  markdownStyle,
117
- parser,
141
+ gfm,
142
+ mention,
143
+ math,
118
144
  onBlur,
119
145
  onChange,
120
146
  onChangeText,
@@ -139,12 +165,9 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
139
165
  },
140
166
  ref,
141
167
  ) => {
142
- if (parser === undefined) {
143
- throw new Error("[react-native-marcus] `parser` is undefined")
144
- }
145
- if (typeof parser !== "function") {
146
- throw new Error("[react-native-marcus] `parser` is not a function")
147
- }
168
+ // The same function for as long as the flavour is the same one, so every
169
+ // callback below that holds on to it keeps its identity too.
170
+ const parser = createParser({ gfm, mention, math })
148
171
 
149
172
  const divRef = useRef<MarkdownTextInputElement | null>(null)
150
173
  const currentlyFocusedField = useRef<HTMLDivElement | null>(null)
@@ -185,10 +208,10 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
185
208
 
186
209
  const parseText = useCallback(
187
210
  (
188
- parserFunction: (input: string) => MarkdownRange[],
211
+ parserFunction: MarkdownParser,
189
212
  target: MarkdownTextInputElement,
190
213
  text: string | null,
191
- customMarkdownStyles: MarkdownStyle,
214
+ customMarkdownStyles: ResolvedMarkdownStyle,
192
215
  cursorPosition: number | null = null,
193
216
  shouldAddToHistory = true,
194
217
  shouldForceDOMUpdate = false,
@@ -0,0 +1,112 @@
1
+ import type { MarkdownEmbed, MarkdownMath, StrippedMarkdown } from "./stripUtils"
2
+
3
+ // An image and a maths span are the same problem to a display: a run of
4
+ // markdown that is drawn rather than read, that the strip has already replaced
5
+ // with a single placeholder character, and that something has to be mounted
6
+ // over. `stripSyntax` reports them apart because what a caller needs to know
7
+ // about each is different; the two components need them together, in the one
8
+ // order the placeholders appear in.
9
+
10
+ type Atom = ({ kind: "embed" } & MarkdownEmbed) | ({ kind: "math" } & MarkdownMath)
11
+
12
+ /**
13
+ * How a maths span is said out loud, or nothing for one that cannot be said.
14
+ *
15
+ * Passed in rather than imported so that this module knows nothing about
16
+ * typesetting: it is `./math`'s `speakMath` at both call sites, and a display
17
+ * with no maths backend passes one that always declines.
18
+ */
19
+ type SpeakMath = (tex: string, display: boolean, locale?: string) => string | null
20
+
21
+ const NO_ATOMS: Atom[] = []
22
+
23
+ /**
24
+ * Every placeholder in the stripped text, in the order it stands there.
25
+ *
26
+ * Sorting is enough to interleave the two kinds correctly: each index is a
27
+ * distinct character in the same string, so no two atoms can share one.
28
+ */
29
+ function atomsOf(markdown: StrippedMarkdown): Atom[] {
30
+ const { embeds, maths } = markdown
31
+
32
+ if (embeds.length === 0 && maths.length === 0) {
33
+ return NO_ATOMS
34
+ }
35
+
36
+ const atoms: Atom[] = []
37
+
38
+ for (const embed of embeds) {
39
+ atoms.push({ kind: "embed", ...embed })
40
+ }
41
+
42
+ for (const math of maths) {
43
+ atoms.push({ kind: "math", ...math })
44
+ }
45
+
46
+ return atoms.length > 1 ? atoms.sort((a, b) => a.index - b.index) : atoms
47
+ }
48
+
49
+ /**
50
+ * The text as it should be read out, with every placeholder written back out
51
+ * as what it stands for.
52
+ *
53
+ * A drawing costs exactly one character in the string the platform lays out --
54
+ * U+FFFC, the object replacement character -- and there is nothing in that
55
+ * character for a screen reader to say. An image's `alt` is its accessible name
56
+ * by every spec there is, and left empty it declares the image decorative, so
57
+ * it is used as written. A maths span is read as what was drawn, which is what
58
+ * `speak` answers; the TeX behind it is the fallback, because an expression
59
+ * read out as its source is still better than a sentence with a hole in it.
60
+ *
61
+ * `locale` is the language the maths is said in, and is passed straight
62
+ * through: the prose around it is in whatever language it was written in, and
63
+ * nothing here can or should change that.
64
+ */
65
+ function describeAtoms(markdown: StrippedMarkdown, speak: SpeakMath, locale?: string): string {
66
+ const atoms = atomsOf(markdown)
67
+
68
+ if (atoms.length === 0) {
69
+ return markdown.text
70
+ }
71
+
72
+ let described = ""
73
+ let cursor = 0
74
+
75
+ for (const atom of atoms) {
76
+ described += markdown.text.slice(cursor, atom.index)
77
+ described +=
78
+ atom.kind === "embed" ? atom.alt : speak(atom.tex, atom.display, locale) ?? atom.tex
79
+ cursor = atom.index + 1
80
+ }
81
+
82
+ return described + markdown.text.slice(cursor)
83
+ }
84
+
85
+ /**
86
+ * What a placeholder puts on the clipboard, which is the markdown it was
87
+ * written as.
88
+ *
89
+ * A copy of rendered text is plain text: `**bold**` comes off the screen as
90
+ * "bold", because that is what a copy of styled text has always been. A drawing
91
+ * has no plain text at all -- it is one U+FFFC, which pastes as a box -- and
92
+ * the source is the only honest thing to put there. Written back out with its
93
+ * dollar signs, a copied paragraph is markdown again and goes back into an
94
+ * editor as the document it came from.
95
+ *
96
+ * An image copies as nothing, the way an `<img>` does on the web: its alt text
97
+ * describes the picture for someone who cannot see it, and pasting a
98
+ * description where a picture was is not what anyone asked for.
99
+ */
100
+ function sourceOf(atom: Atom): string {
101
+ if (atom.kind === "embed") {
102
+ return ""
103
+ }
104
+
105
+ // A display equation is written on lines of its own, and lands in the
106
+ // clipboard that way: pasted back, it is a display equation again.
107
+ return atom.display ? `$$\n${atom.tex}\n$$` : `$$${atom.tex}$$`
108
+ }
109
+
110
+ export type { Atom, SpeakMath }
111
+
112
+ export { NO_ATOMS, atomsOf, describeAtoms, sourceOf }
@@ -0,0 +1,39 @@
1
+ import type React from "react"
2
+
3
+ /**
4
+ * Flattens children into the string they render as, or null if they are not
5
+ * text.
6
+ *
7
+ * Only strings and numbers are markdown. An element child is content there are
8
+ * no parser offsets for and no way to strip syntax out of, so a subtree
9
+ * containing one renders unformatted rather than half formatted.
10
+ */
11
+ function flattenText(children: React.ReactNode): string | null {
12
+ const parts: string[] = []
13
+
14
+ return collect(children, parts) ? parts.join("") : null
15
+ }
16
+
17
+ function collect(children: React.ReactNode, parts: string[]): boolean {
18
+ if (children === null || children === undefined || typeof children === "boolean") {
19
+ return true
20
+ }
21
+
22
+ if (typeof children === "string") {
23
+ parts.push(children)
24
+ return true
25
+ }
26
+
27
+ if (typeof children === "number") {
28
+ parts.push(String(children))
29
+ return true
30
+ }
31
+
32
+ if (Array.isArray(children)) {
33
+ return children.every((child) => collect(child, parts))
34
+ }
35
+
36
+ return false
37
+ }
38
+
39
+ export { flattenText }
@@ -1,13 +1,26 @@
1
1
  type MarkdownType =
2
- | "bold"
3
- | "italic"
2
+ | "strong"
3
+ | "emphasis"
4
4
  | "strikethrough"
5
5
  | "emoji"
6
- | "mention-here"
7
- | "mention-user"
8
- | "mention-short"
9
- | "mention-report"
6
+ // One type. A mention is a name with an `@` in front of it; who it names is
7
+ // the application's business, not the parser's, and nothing here can tell a
8
+ // person from a room from a report.
9
+ | "mention"
10
10
  | "link"
11
+ // The text between a link's brackets. Its own type rather than `link`: no
12
+ // formatter styles it, so it stays plain while it is being edited, and only
13
+ // the display renderer -- which has removed the brackets -- turns it into one.
14
+ | "label"
15
+ // The text between an image's brackets. Content rather than text: CommonMark
16
+ // flattens it to a string only when it renders the `alt` attribute, and
17
+ // micromark keeps the structure, so emphasis, code, a link or another image
18
+ // nested in there all arrive as ranges of their own inside this one.
19
+ | "alt"
20
+ // The title a link or image carries after its destination. Metadata rather
21
+ // than content: no formatter styles it, and a display renderer removes it
22
+ // along with the destination it belongs to.
23
+ | "title"
11
24
  | "code"
12
25
  | "pre"
13
26
  | "blockquote"
@@ -16,15 +29,62 @@ type MarkdownType =
16
29
  | "block-prefix"
17
30
  | "heading"
18
31
  | "syntax"
19
- | "inline-image"
32
+ | "image"
20
33
  | "codeblock"
34
+ // An inline `$$...$$` span, delimiters and all. One range over the whole
35
+ // thing rather than one over its content: what is between the dollars is TeX,
36
+ // not markdown, and nothing downstream has any business styling parts of it.
37
+ | "math"
38
+ // A `$$` block, which is the same thing set on lines of its own.
39
+ | "math-block"
40
+ // The language a fenced block declares. Metadata rather than content: no
41
+ // formatter styles it and the display renderer removes it outright.
42
+ | "language"
21
43
 
22
44
  interface MarkdownRange {
23
45
  type: MarkdownType
24
46
  start: number
25
47
  length: number
26
48
  depth?: number
27
- syntaxType?: "opening" | "closing"
28
49
  }
29
50
 
30
- export type { MarkdownType, MarkdownRange }
51
+ /**
52
+ * What the parser recognises beyond CommonMark.
53
+ *
54
+ * Every one of them is off unless it is asked for. Each changes what an
55
+ * ordinary character means -- a tilde, an `@`, a dollar sign -- and a document
56
+ * written somewhere else, by someone who never heard of this library, is the
57
+ * one place where guessing at that is least recoverable. They are the whole of
58
+ * the parser's configuration: a caller chooses a flavour, not a parser, and the
59
+ * components take these as props of their own.
60
+ */
61
+ interface ParserOptions {
62
+ /**
63
+ * GFM's strikethrough (`~~a~~`) and bare autolinks (`www.example.com`).
64
+ *
65
+ * @default false
66
+ */
67
+ gfm?: boolean
68
+ /**
69
+ * `@name` mentions, which no flavour of markdown has a notion of.
70
+ *
71
+ * @default false
72
+ */
73
+ mention?: boolean
74
+ /**
75
+ * LaTeX between double dollar signs: `$$x^2$$` inline, and the same on lines
76
+ * of its own for a block.
77
+ *
78
+ * Two dollars, never one: `$5 and $6` is a price list in every document that
79
+ * has ever been written, and single-dollar maths would quietly turn it into a
80
+ * picture of the word "and".
81
+ *
82
+ * @default false
83
+ */
84
+ math?: boolean
85
+ }
86
+
87
+ /** A parser, as the components hold one: text in, ranges out. */
88
+ type MarkdownParser = (markdown: string) => MarkdownRange[]
89
+
90
+ export type { MarkdownType, MarkdownRange, ParserOptions, MarkdownParser }
package/src/index.tsx CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default as MarkdownTextInput, getWorkletRuntime } from "./MarkdownTextInput"
2
2
  export type { MarkdownTextInputProps, MarkdownStyle } from "./MarkdownTextInput"
3
+ export { default as MarkdownText } from "./MarkdownText"
4
+ export type { MarkdownTextProps, OnLinkPress, RenderEmbed, RenderSVG } from "./MarkdownText"
3
5
  export type { MarkdownType, MarkdownRange } from "./commonTypes"
4
- export { parse as parser } from "./parser"
@@ -0,0 +1,68 @@
1
+ /**
2
+ * What MathJax and SRE assume about their host that React Native does not
3
+ * provide. Imported before either of them, and first, because `import`
4
+ * statements are hoisted and run in the order they are written.
5
+ */
6
+
7
+ /**
8
+ * MathJax guesses the operating system from `window.navigator.appVersion`,
9
+ * behind nothing but a `typeof window !== "undefined"` check
10
+ * (`@mathjax/src/mjs/util/context.js`). React Native defines `window` and
11
+ * `navigator` but not `appVersion`, so the check passes and the read throws
12
+ * `Cannot read property 'includes' of undefined` before a line of MathJax's own
13
+ * code runs.
14
+ *
15
+ * What it is guessing at is only ever used to rewrite Windows file paths, which
16
+ * nothing here does, so an empty string is both harmless and enough.
17
+ */
18
+ const nav = (globalThis as unknown as { navigator?: Record<string, unknown> }).navigator
19
+
20
+ for (const key of ["appVersion", "userAgent"]) {
21
+ if (nav && nav[key] === undefined) {
22
+ try {
23
+ nav[key] = ""
24
+ } catch {
25
+ Object.defineProperty(nav, key, { value: "", configurable: true })
26
+ }
27
+ }
28
+ }
29
+
30
+ /**
31
+ * The file system, which SRE reaches for before anything has had a chance to
32
+ * tell it not to.
33
+ *
34
+ * `speech-rule-engine/js/common/system_external.js` decides at import time how
35
+ * it will read its rule tables, and on anything that is not a browser it
36
+ * settles on Node's `fs` and a path worked out from `require.resolve` or
37
+ * `process.cwd()`. React Native has neither: `require` is the bundler's, which
38
+ * throws on a name it was not built with, and `process` is a stub with no
39
+ * `cwd`. Either one is an exception thrown from the top level of a module, so
40
+ * SRE cannot even be imported.
41
+ *
42
+ * None of it is wanted in any case -- the rules are bundled and handed over by
43
+ * a loader of our own -- so both routes are stubbed out here rather than fixed.
44
+ * `__non_webpack_require__` is the escape hatch SRE checks first, and a
45
+ * function that returns nothing for every name satisfies it, leaves `fs` null,
46
+ * and makes the path guessing fail its way harmlessly to the end.
47
+ */
48
+ const scope = globalThis as unknown as {
49
+ __non_webpack_require__?: unknown
50
+ process?: { cwd?: () => string }
51
+ }
52
+
53
+ if (scope.__non_webpack_require__ === undefined) {
54
+ try {
55
+ scope.__non_webpack_require__ = () => null
56
+ } catch {
57
+ Object.defineProperty(scope, "__non_webpack_require__", {
58
+ value: () => null,
59
+ configurable: true,
60
+ })
61
+ }
62
+ }
63
+
64
+ if (scope.process !== undefined && scope.process.cwd === undefined) {
65
+ scope.process.cwd = () => ""
66
+ }
67
+
68
+ export {}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The maths backend, in its "there isn't one" form.
3
+ *
4
+ * Typesetting TeX takes a library an order of magnitude larger than this one,
5
+ * and most applications will never draw an equation. So the real backend --
6
+ * `./mathjax.ts`, right beside this -- is never imported by anything: a
7
+ * bundler would follow the import and pull MathJax into every app that has
8
+ * ever installed this package, whether or not it renders maths.
9
+ *
10
+ * Instead the two are swapped at build time. `react-native-marcus/metro`'s
11
+ * `withMath()` resolves this module to that one, so an application opts in by
12
+ * editing its `metro.config.js` and the default bundle carries nothing.
13
+ *
14
+ * What the swap has to preserve is this shape and nothing else.
15
+ */
16
+
17
+ /** One expression, drawn, with the box it needs. */
18
+ interface RenderedMath {
19
+ /**
20
+ * A standalone SVG document: glyph outlines inline, no `<use>`, no `<defs>`,
21
+ * and the colour already substituted in.
22
+ */
23
+ svg: string
24
+ /** Points, at the font size that was asked for. */
25
+ width: number
26
+ height: number
27
+ /**
28
+ * How far below the baseline the drawing hangs -- zero for `x^2`, real for
29
+ * `\frac{a}{b}`. Part of the picture rather than of the size: `height`
30
+ * already covers it.
31
+ */
32
+ depth: number
33
+ }
34
+
35
+ /**
36
+ * Whether maths can be drawn at all.
37
+ *
38
+ * Read by the display components, which leave a maths span exactly as it was
39
+ * written when this is false. Showing `$$x^2$$` is a good deal better than
40
+ * showing an empty box where an equation should be, and it is the one hint an
41
+ * application will get that its Metro config is missing a line.
42
+ */
43
+ const available = false
44
+
45
+ function renderMath(
46
+ _tex: string,
47
+ _display: boolean,
48
+ _fontSize: number,
49
+ _color: string | undefined,
50
+ ): RenderedMath | null {
51
+ return null
52
+ }
53
+
54
+ /**
55
+ * The expression as it should be read out, or nothing.
56
+ *
57
+ * A drawn equation is a picture, and the TeX behind it is source code: an
58
+ * author reading `\frac{a}{b}` knows what it means, and a reader who asked
59
+ * for a document rather than for its markup hears three words of punctuation.
60
+ * So a display says what it drew -- "StartFraction a Over b EndFraction" --
61
+ * while an input, where the TeX is what is on the screen and what the caret
62
+ * moves through, keeps the TeX.
63
+ *
64
+ * `locale` is the language it should be said in, as a name the backend knows;
65
+ * nothing means the backend's own default. What a document is written in and
66
+ * what its reader speaks are two different questions, and only the application
67
+ * knows the answer to the second.
68
+ *
69
+ * Returning nothing means the caller falls back to the TeX, which is what the
70
+ * stub does and what a backend does for an expression it cannot say.
71
+ */
72
+ function speakMath(_tex: string, _display: boolean, _locale?: string): string | null {
73
+ return null
74
+ }
75
+
76
+ export type { RenderedMath }
77
+
78
+ export { available, renderMath, speakMath }