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
package/src/styleUtils.ts CHANGED
@@ -1,7 +1,117 @@
1
- import { Platform } from "react-native"
2
- import type { MarkdownStyle } from "./MarcusTextInputDecoratorViewNativeComponent"
1
+ import { Platform, processColor } from "react-native"
2
+ import type { TextStyle, ViewStyle } from "react-native"
3
+ import type {
4
+ MarkdownEmojiStyle,
5
+ MarkdownStyle,
6
+ } from "./MarcusTextInputDecoratorViewNativeComponent"
3
7
 
4
- type PartialMarkdownStyle = Partial<{
8
+ // The public spelling of a run's text attributes: React Native's own
9
+ // `TextStyle`, narrowed to the properties the three formatters can honour.
10
+ // Picked from it rather than restated so the names, the unions and the doc
11
+ // comments are React Native's, and so a style object written for a `Text` can be
12
+ // handed straight to a markdown entry.
13
+ //
14
+ // What is deliberately not here: everything `TextStyle` inherits from
15
+ // `ViewStyle` -- an attributed-string run has no box to lay out -- along with
16
+ // `textAlign`, `textTransform`, `fontVariant` and `writingDirection`, which are
17
+ // paragraph- or string-level rather than run-level and would each mean something
18
+ // different on each platform.
19
+ type MarkdownTextStyleProps = Pick<
20
+ TextStyle,
21
+ | "color"
22
+ | "fontFamily"
23
+ | "fontSize"
24
+ | "fontStyle"
25
+ | "fontWeight"
26
+ | "letterSpacing"
27
+ | "lineHeight"
28
+ | "textDecorationLine"
29
+ | "textDecorationColor"
30
+ | "textDecorationStyle"
31
+ | "textShadowColor"
32
+ | "textShadowOffset"
33
+ | "textShadowRadius"
34
+ >
35
+
36
+ // The room a boxed run holds open, on top of its text attributes. Also picked
37
+ // from React Native's own types, for the same reason.
38
+ type MarkdownBoxStyleProps = MarkdownTextStyleProps &
39
+ Pick<
40
+ ViewStyle,
41
+ | "backgroundColor"
42
+ | "borderColor"
43
+ | "borderWidth"
44
+ | "borderRadius"
45
+ | "padding"
46
+ | "paddingHorizontal"
47
+ | "paddingVertical"
48
+ | "margin"
49
+ | "marginHorizontal"
50
+ | "marginVertical"
51
+ > & {
52
+ // Narrowed from `DimensionValue`: a percentage has nothing to be a
53
+ // percentage of inside a line of text.
54
+ padding?: number
55
+ paddingHorizontal?: number
56
+ paddingVertical?: number
57
+ margin?: number
58
+ marginHorizontal?: number
59
+ marginVertical?: number
60
+ borderWidth?: number
61
+ borderRadius?: number
62
+ }
63
+
64
+ type MarkdownHeadingStyleProps = MarkdownTextStyleProps & {
65
+ scale?: number
66
+ }
67
+
68
+ type MarkdownBlockquoteStyleProps = MarkdownTextStyleProps &
69
+ Pick<ViewStyle, "borderColor"> & {
70
+ borderWidth?: number
71
+ borderRadius?: number
72
+ marginStart?: number
73
+ paddingStart?: number
74
+ }
75
+
76
+ type MarkdownListStyleProps = {
77
+ marginStart?: number
78
+ paddingStart?: number
79
+ markerScale?: number
80
+ markerPadding?: number
81
+ marker?: MarkdownTextStyleProps
82
+ }
83
+
84
+ /**
85
+ * A markdown style as it is written: every entry optional, every property
86
+ * optional, and anything left out taken from the defaults below.
87
+ */
88
+ type PartialMarkdownStyle = {
89
+ syntax?: MarkdownTextStyleProps
90
+ emoji?: MarkdownEmojiStyle
91
+ link?: MarkdownTextStyleProps
92
+ strong?: MarkdownTextStyleProps
93
+ emphasis?: MarkdownTextStyleProps
94
+ strikethrough?: MarkdownTextStyleProps
95
+ heading?: MarkdownHeadingStyleProps
96
+ blockquote?: MarkdownBlockquoteStyleProps
97
+ orderedList?: MarkdownListStyleProps
98
+ unorderedList?: MarkdownListStyleProps
99
+ code?: MarkdownBoxStyleProps
100
+ pre?: MarkdownBoxStyleProps
101
+ mention?: MarkdownBoxStyleProps
102
+ }
103
+
104
+ /**
105
+ * The style once the defaults have been merged in: every entry present, every
106
+ * property still optional.
107
+ *
108
+ * Distinct from `PartialMarkdownStyle`, which is what a caller writes. This is
109
+ * what a renderer reads, and on the web its lengths have been through React
110
+ * Native Web's compiler by the time they get there -- a `fontSize` of `16` is
111
+ * the string `"16px"` -- which is why everything downstream of it either casts
112
+ * or runs the value back through `toNumber`.
113
+ */
114
+ type ResolvedMarkdownStyle = Partial<{
5
115
  [K in keyof MarkdownStyle]: Partial<MarkdownStyle[K]>
6
116
  }>
7
117
 
@@ -16,6 +126,15 @@ const FONT_FAMILY_EMOJI = Platform.select({
16
126
  default: "System, Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji",
17
127
  })
18
128
 
129
+ /**
130
+ * What every renderer draws before a style says otherwise.
131
+ *
132
+ * The marks that used to be hard-coded in the formatters -- a heading's and a
133
+ * strong run's bold, an emphasised run's italic, a link's underline, a
134
+ * strikethrough's line -- are written out here instead, so that the same switch
135
+ * that applies a custom weight applies the default one, and so that naming
136
+ * `strong.fontWeight` replaces the bold rather than fighting it.
137
+ */
19
138
  function makeDefaultMarkdownStyle(): MarkdownStyle {
20
139
  return {
21
140
  syntax: {
@@ -23,9 +142,20 @@ function makeDefaultMarkdownStyle(): MarkdownStyle {
23
142
  },
24
143
  link: {
25
144
  color: "blue",
145
+ textDecorationLine: "underline",
146
+ },
147
+ strong: {
148
+ fontWeight: "bold",
149
+ },
150
+ emphasis: {
151
+ fontStyle: "italic",
152
+ },
153
+ strikethrough: {
154
+ textDecorationLine: "line-through",
26
155
  },
27
156
  heading: {
28
157
  fontSize: 38,
158
+ fontWeight: "bold",
29
159
  scale: 0.85,
30
160
  },
31
161
  emoji: {
@@ -35,47 +165,131 @@ function makeDefaultMarkdownStyle(): MarkdownStyle {
35
165
  blockquote: {
36
166
  borderColor: "gray",
37
167
  borderWidth: 6,
38
- marginLeft: 6,
39
- paddingLeft: 6,
168
+ borderRadius: 0,
169
+ marginStart: 6,
170
+ paddingStart: 6,
40
171
  },
41
172
  orderedList: {
42
- marginLeft: 6,
43
- paddingLeft: 18,
173
+ marginStart: 6,
174
+ paddingStart: 18,
175
+ markerScale: 0.8,
176
+ markerPadding: 2,
177
+ marker: {},
44
178
  },
45
179
  unorderedList: {
46
- marginLeft: 6,
47
- paddingLeft: 18,
180
+ marginStart: 6,
181
+ paddingStart: 18,
182
+ markerScale: 0.3,
183
+ markerPadding: 2,
184
+ marker: {},
48
185
  },
49
186
  code: {
50
187
  fontFamily: FONT_FAMILY_MONOSPACE,
51
188
  fontSize: 16,
52
189
  color: "black",
53
190
  backgroundColor: "lightgray",
191
+ borderRadius: 4,
192
+ padding: 2,
193
+ margin: 2,
54
194
  },
55
195
  pre: {
56
196
  fontFamily: FONT_FAMILY_MONOSPACE,
57
197
  fontSize: 16,
58
198
  color: "black",
59
199
  backgroundColor: "lightgray",
200
+ borderRadius: 4,
201
+ padding: 8,
202
+ margin: 4,
60
203
  },
61
- mentionHere: {
62
- color: "green",
63
- backgroundColor: "lime",
64
- borderRadius: 5,
65
- },
66
- mentionUser: {
204
+ mention: {
67
205
  color: "blue",
68
206
  backgroundColor: "cyan",
69
207
  borderRadius: 5,
70
- },
71
- mentionReport: {
72
- color: "red",
73
- backgroundColor: "pink",
74
- borderRadius: 5,
208
+ padding: 2,
209
+ margin: 0,
75
210
  },
76
211
  }
77
212
  }
78
213
 
214
+ // The entries whose values are themselves style objects rather than lengths, and
215
+ // so have to be merged rather than replaced: overriding `orderedList.marker`
216
+ // must not drop the marker's colour from the defaults.
217
+ const NESTED_KEYS = new Set(["marker", "textShadowOffset"])
218
+
219
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
220
+ type AnyObject = Record<string, any>
221
+
222
+ function isPlainObject(value: unknown): value is AnyObject {
223
+ return typeof value === "object" && value !== null && !Array.isArray(value)
224
+ }
225
+
226
+ /**
227
+ * Copies `input` over `output` in place, one level deep, and two where the value
228
+ * is itself a style object.
229
+ *
230
+ * `undefined` is skipped rather than written, so spreading a style that spells a
231
+ * property out as `undefined` -- which is what an optional prop destructured
232
+ * from a component's props looks like -- leaves the default in place instead of
233
+ * erasing it.
234
+ */
235
+ function assignStyle(output: AnyObject, input: AnyObject) {
236
+ Object.keys(input).forEach((key) => {
237
+ const value = input[key]
238
+
239
+ if (value === undefined) {
240
+ return
241
+ }
242
+
243
+ if (NESTED_KEYS.has(key) && isPlainObject(value)) {
244
+ const nested = isPlainObject(output[key]) ? output[key] : {}
245
+ assignStyle(nested, value)
246
+ // eslint-disable-next-line no-param-reassign
247
+ output[key] = nested
248
+ return
249
+ }
250
+
251
+ // React Native accepts a numeric font weight; the native decorators read it
252
+ // as a string, because codegen has no union of a number and a string.
253
+ // eslint-disable-next-line no-param-reassign
254
+ output[key] = key === "fontWeight" && typeof value === "number" ? String(value) : value
255
+ })
256
+ }
257
+
258
+ // The entries drawn as a box, and so written with lengths that may be given for
259
+ // both axes at once.
260
+ const BOX_KEYS = ["code", "pre", "mention"] as const
261
+ const LIST_KEYS = ["orderedList", "unorderedList"] as const
262
+
263
+ /**
264
+ * Settles the two questions a renderer would otherwise have to ask about where a
265
+ * value came from, so that none of the three has to ask them.
266
+ *
267
+ * A box's `padding` and `margin` cover both axes and `paddingHorizontal` and the
268
+ * rest win over them, but native cannot tell a length that was left out from one
269
+ * that was written as zero -- codegen has no optional numbers -- so the pair is
270
+ * resolved into the two axes here, once, rather than three times over.
271
+ *
272
+ * A list marker's style is `syntax` with the list's own `marker` over it, which
273
+ * is what keeps `syntax.color` meaning what it always meant for a marker while
274
+ * letting a list say something different.
275
+ */
276
+ function resolveMarkdownStyle(style: MarkdownStyle): MarkdownStyle {
277
+ BOX_KEYS.forEach((key) => {
278
+ const box = style[key]
279
+
280
+ box.paddingHorizontal = box.paddingHorizontal ?? box.padding ?? 0
281
+ box.paddingVertical = box.paddingVertical ?? box.padding ?? 0
282
+ box.marginHorizontal = box.marginHorizontal ?? box.margin ?? 0
283
+ box.marginVertical = box.marginVertical ?? box.margin ?? 0
284
+ })
285
+
286
+ LIST_KEYS.forEach((key) => {
287
+ style[key].marker = { ...style.syntax, ...style[key].marker }
288
+ })
289
+
290
+ return style
291
+ }
292
+
79
293
  function mergeMarkdownStyleWithDefault(input: PartialMarkdownStyle | undefined): MarkdownStyle {
80
294
  const output = makeDefaultMarkdownStyle()
81
295
 
@@ -85,16 +299,66 @@ function mergeMarkdownStyleWithDefault(input: PartialMarkdownStyle | undefined):
85
299
  return
86
300
  }
87
301
 
88
- const outputValue = output[key as keyof MarkdownStyle]
89
- if (outputValue) {
90
- Object.assign(outputValue, input[key as keyof MarkdownStyle])
302
+ const outputValue = (output as AnyObject)[key]
303
+ const inputValue = (input as AnyObject)[key]
304
+
305
+ if (isPlainObject(outputValue) && isPlainObject(inputValue)) {
306
+ assignStyle(outputValue, inputValue)
91
307
  }
92
308
  })
93
309
  }
94
310
 
95
- return output
311
+ return resolveMarkdownStyle(output)
96
312
  }
97
313
 
98
- export type { PartialMarkdownStyle }
314
+ /**
315
+ * Turns every colour in the style into the platform's own representation.
316
+ *
317
+ * Walks the whole tree rather than its top two levels: a list's `marker` and a
318
+ * run's `textShadowOffset` put style values one level further down than the
319
+ * entries around them.
320
+ */
321
+ function processColorsInMarkdownStyle(input: MarkdownStyle): MarkdownStyle {
322
+ const output = JSON.parse(JSON.stringify(input))
323
+
324
+ const walk = (node: AnyObject) => {
325
+ Object.keys(node).forEach((prop) => {
326
+ const value = node[prop]
327
+
328
+ if (isPlainObject(value)) {
329
+ walk(value)
330
+ return
331
+ }
332
+
333
+ // TODO: use ReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes'
334
+ if (!(prop === "color" || prop.endsWith("Color"))) {
335
+ return
336
+ }
337
+ // eslint-disable-next-line no-param-reassign
338
+ node[prop] = processColor(value)
339
+ })
340
+ }
341
+
342
+ walk(output)
343
+
344
+ return output as MarkdownStyle
345
+ }
346
+
347
+ // The shape the native decorators take: defaults filled in, colours turned into
348
+ // the platform representation. Shared by both components so a style means the
349
+ // same thing whether it is being typed into or read.
350
+ function processMarkdownStyle(input: PartialMarkdownStyle | undefined): MarkdownStyle {
351
+ return processColorsInMarkdownStyle(mergeMarkdownStyleWithDefault(input))
352
+ }
353
+
354
+ export type {
355
+ PartialMarkdownStyle,
356
+ ResolvedMarkdownStyle,
357
+ MarkdownTextStyleProps,
358
+ MarkdownBoxStyleProps,
359
+ MarkdownHeadingStyleProps,
360
+ MarkdownBlockquoteStyleProps,
361
+ MarkdownListStyleProps,
362
+ }
99
363
 
100
- export { mergeMarkdownStyleWithDefault }
364
+ export { mergeMarkdownStyleWithDefault, processMarkdownStyle }
@@ -0,0 +1,67 @@
1
+ /*
2
+ * Undoes what the browser does on a tag's behalf.
3
+ *
4
+ * A markdown element is built as the tag that says what its text is -- `<blockquote>`, `<ul>`,
5
+ * `<h2>`, `<pre>` -- and every one of those arrives with a look of its own: a quote indented forty
6
+ * pixels, a heading sized and spaced by level, a list with a bullet and a gutter, a `<pre>` in a
7
+ * monospace font that refuses to wrap. None of it is wanted. Every measurement this component
8
+ * renders by is computed from the markdown style and set on the element, and the two native
9
+ * formatters draw from the same numbers; a browser default left in place is the one thing that
10
+ * would move the web off them.
11
+ *
12
+ * So the tag is taken for what it means and the look it carries is handed back. Anything the
13
+ * builder sets is an inline style and wins over all of this, which leaves `addStyleToBlock` in
14
+ * charge of the appearance exactly as it was when every element was a span.
15
+ *
16
+ * The class is on the elements rather than on a container: these rules must reach the input and
17
+ * `MarkdownText` alike, and naming the library is what keeps them off everything else on the page.
18
+ */
19
+
20
+ blockquote.react-native-marcus,
21
+ ul.react-native-marcus,
22
+ ol.react-native-marcus,
23
+ li.react-native-marcus,
24
+ pre.react-native-marcus,
25
+ h1.react-native-marcus,
26
+ h2.react-native-marcus,
27
+ h3.react-native-marcus,
28
+ h4.react-native-marcus,
29
+ h5.react-native-marcus,
30
+ h6.react-native-marcus {
31
+ display: inline;
32
+ margin: 0;
33
+ padding: 0;
34
+ }
35
+
36
+ /* The marker a list draws is its own, measured and placed by `layoutBlocks`. */
37
+ ul.react-native-marcus,
38
+ ol.react-native-marcus,
39
+ li.react-native-marcus {
40
+ list-style: none;
41
+ }
42
+
43
+ /* `font` rather than `font-family`: the monospace default comes with a size of its own, and a
44
+ heading's comes with a weight and a size per level. */
45
+ pre.react-native-marcus,
46
+ code.react-native-marcus,
47
+ h1.react-native-marcus,
48
+ h2.react-native-marcus,
49
+ h3.react-native-marcus,
50
+ h4.react-native-marcus,
51
+ h5.react-native-marcus,
52
+ h6.react-native-marcus {
53
+ font: inherit;
54
+ }
55
+
56
+ /* A `<pre>` holds its own `white-space`, so a fenced block would stop wrapping with the rest of
57
+ the input the moment it became one. */
58
+ pre.react-native-marcus {
59
+ white-space: inherit;
60
+ }
61
+
62
+ /* A link is coloured and underlined by `markdownStyle`, in the same values the native formatters
63
+ are given, rather than in the browser's blue. */
64
+ a.react-native-marcus {
65
+ color: inherit;
66
+ text-decoration: none;
67
+ }
@@ -2,11 +2,11 @@
2
2
  white-space: pre-wrap;
3
3
  }
4
4
 
5
- .react-native-marcus-input-singleline p {
5
+ .react-native-marcus-input-singleline *[data-type='line'] {
6
6
  display: inline-block;
7
7
  }
8
8
 
9
- .react-native-marcus-input-multiline p {
9
+ .react-native-marcus-input-multiline *[data-type='line'] {
10
10
  display: block;
11
11
  }
12
12
 
@@ -22,12 +22,13 @@
22
22
  vertical-align: middle;
23
23
  }
24
24
 
25
- .react-native-marcus-input-singleline span[data-type='link'] span[data-type='text'] {
25
+ .react-native-marcus-input-singleline *[data-type='link'] span[data-type='text'] {
26
26
  vertical-align: top;
27
27
  }
28
28
 
29
- .react-native-marcus-input-singleline > p:last-child {
30
- padding-right: 1px !important;
29
+ /* Room for the caret past the last character, on whichever side the line ends. */
30
+ .react-native-marcus-input-singleline > *[data-type='line']:last-child {
31
+ padding-inline-end: 1px !important;
31
32
  }
32
33
 
33
34
  .react-native-marcus-input-singleline:empty::before,
@@ -45,10 +46,11 @@
45
46
  box-sizing: border-box;
46
47
  }
47
48
 
49
+ /* A block is drawn as a box across the line, the way both native formatters
50
+ draw it, rather than as a background hugging the code. Its own padding and
51
+ margin are set on the element by `addCodeBlockStyle`. */
48
52
  .react-native-marcus-input-multiline *[data-type='codeblock'] {
49
53
  position: relative;
50
- width: fit-content;
51
- max-width: 100%;
52
54
  box-sizing: border-box;
53
55
  z-index: 2;
54
56
  display: block;
@@ -1,5 +1,5 @@
1
1
  import type { MarkdownRange, MarkdownType } from "../../commonTypes"
2
- import type { PartialMarkdownStyle } from "../../styleUtils"
2
+ import type { ResolvedMarkdownStyle } from "../../styleUtils"
3
3
  import type { TextMeasurer } from "./measureUtils"
4
4
 
5
5
  const BLOCK_PREFIX = "block-prefix"
@@ -20,6 +20,26 @@ type LineLayout = {
20
20
  gaps: Map<number, number>
21
21
  }
22
22
 
23
+ /**
24
+ * How a list's markers are drawn, which is the one thing a display does differently from an input.
25
+ *
26
+ * A display renders the marker rather than showing it: an unordered item's `-` becomes a bullet, an
27
+ * ordered item's `1.` stays the number it means but at its own scale, and both hold a little room
28
+ * either side. An input shows the marker you typed, in the base font, because there it is text
29
+ * being edited rather than a rendering of it. `MarkdownFormatter` draws the same distinction on
30
+ * both native platforms from the same style values.
31
+ *
32
+ * `fontSize` is what the wrapped element renders at, in pixels, which is what a marker is sized in
33
+ * proportion to.
34
+ */
35
+ type MarkerRendering = {
36
+ display: boolean
37
+ fontSize: number
38
+ }
39
+
40
+ /** An input's markers, and the default everywhere the question does not arise. */
41
+ const SHOWN_MARKERS: MarkerRendering = { display: false, fontSize: 0 }
42
+
23
43
  function toNumber(value: string | number | undefined | null): number {
24
44
  if (typeof value === "number") {
25
45
  return value
@@ -32,29 +52,104 @@ function toNumber(value: string | number | undefined | null): number {
32
52
  }
33
53
 
34
54
  /** The gutter one level of a container reserves, or null if the type is not one. */
35
- function stepOf(type: MarkdownType, markdownStyle: PartialMarkdownStyle): number | null {
55
+ function stepOf(type: MarkdownType, markdownStyle: ResolvedMarkdownStyle): number | null {
36
56
  switch (type) {
37
57
  case "blockquote":
38
58
  return (
39
- toNumber(markdownStyle.blockquote?.marginLeft) +
59
+ toNumber(markdownStyle.blockquote?.marginStart) +
40
60
  toNumber(markdownStyle.blockquote?.borderWidth) +
41
- toNumber(markdownStyle.blockquote?.paddingLeft)
61
+ toNumber(markdownStyle.blockquote?.paddingStart)
42
62
  )
43
63
  case "list-ordered":
44
64
  return (
45
- toNumber(markdownStyle.orderedList?.marginLeft) +
46
- toNumber(markdownStyle.orderedList?.paddingLeft)
65
+ toNumber(markdownStyle.orderedList?.marginStart) +
66
+ toNumber(markdownStyle.orderedList?.paddingStart)
47
67
  )
48
68
  case "list-unordered":
49
69
  return (
50
- toNumber(markdownStyle.unorderedList?.marginLeft) +
51
- toNumber(markdownStyle.unorderedList?.paddingLeft)
70
+ toNumber(markdownStyle.unorderedList?.marginStart) +
71
+ toNumber(markdownStyle.unorderedList?.paddingStart)
52
72
  )
53
73
  default:
54
74
  return null
55
75
  }
56
76
  }
57
77
 
78
+ /** The style entry a list type's markers are drawn from, or null if the type is not a list. */
79
+ function listStyle(type: MarkdownType, markdownStyle: ResolvedMarkdownStyle) {
80
+ switch (type) {
81
+ case "list-ordered":
82
+ return markdownStyle.orderedList ?? null
83
+ case "list-unordered":
84
+ return markdownStyle.unorderedList ?? null
85
+ default:
86
+ return null
87
+ }
88
+ }
89
+
90
+ /** Room held open either side of a marker, or nothing where the marker is shown rather than drawn. */
91
+ function markerPadding(
92
+ type: MarkdownType,
93
+ markdownStyle: ResolvedMarkdownStyle,
94
+ rendering: MarkerRendering,
95
+ ): number {
96
+ if (!rendering.display) {
97
+ return 0
98
+ }
99
+
100
+ return toNumber(listStyle(type, markdownStyle)?.markerPadding)
101
+ }
102
+
103
+ /**
104
+ * The marker at the end of a prefix: the `-` of a bullet or the `1.` of a numbered item, without
105
+ * the indent in front of it or the space after it.
106
+ *
107
+ * Read off the text rather than taken from the `syntax` range covering it, because a prefix runs
108
+ * from wherever the previous container's marker ended and so can carry another container's marker
109
+ * with it -- the ordered list in `- > 1. ` is handed `> 1. `. The last run of non-blanks is this
110
+ * container's own. `MarkdownFormatter` reads it the same way on both native platforms.
111
+ */
112
+ function markerRun(prefix: string): string {
113
+ const trimmed = prefix.replace(/[ \t]+$/, "")
114
+ const match = /[^ \t]+$/.exec(trimmed)
115
+
116
+ return match ? match[0] : ""
117
+ }
118
+
119
+ /**
120
+ * The width a prefix's glyphs take up: what it measures, with whatever the display draws in place
121
+ * of the marker standing in for the marker's own width.
122
+ */
123
+ function markerWidth(
124
+ prefix: string,
125
+ type: MarkdownType,
126
+ markdownStyle: ResolvedMarkdownStyle,
127
+ rendering: MarkerRendering,
128
+ measure: TextMeasurer,
129
+ ): number {
130
+ const width = measure(prefix)
131
+ const style = listStyle(type, markdownStyle)
132
+
133
+ if (!rendering.display || rendering.fontSize <= 0 || !style) {
134
+ return width
135
+ }
136
+
137
+ const run = markerRun(prefix)
138
+ const scale = toNumber(style.markerScale)
139
+
140
+ if (run === "" || scale <= 0) {
141
+ return width
142
+ }
143
+
144
+ // What is left of the prefix -- an indent in front, the space behind -- measures as it always
145
+ // did; only the marker itself is drawn differently.
146
+ const rest = width - measure(run)
147
+
148
+ return type === "list-unordered"
149
+ ? rest + rendering.fontSize * scale
150
+ : rest + measure(run) * scale
151
+ }
152
+
58
153
  /**
59
154
  * Places the containers of every line, left to right -- the web half of `BlockLayout` in
60
155
  * `apple/MarkdownFormatter.swift` and `android/.../MarkdownFormatter.kt`, and deliberately the same
@@ -76,13 +171,14 @@ function stepOf(type: MarkdownType, markdownStyle: PartialMarkdownStyle): number
76
171
  function layoutBlocks(
77
172
  text: string,
78
173
  ranges: MarkdownRange[],
79
- markdownStyle: PartialMarkdownStyle,
174
+ markdownStyle: ResolvedMarkdownStyle,
80
175
  measure: TextMeasurer,
176
+ rendering: MarkerRendering = SHOWN_MARKERS,
81
177
  ): Map<number, LineLayout> {
82
178
  const layouts = new Map<number, LineLayout>()
83
179
  /** Last marker seen for each container type, for the lines that continue it. */
84
180
  const markers = new Map<MarkdownType, MarkdownRange>()
85
- const ribbonMargin = toNumber(markdownStyle.blockquote?.marginLeft)
181
+ const ribbonMargin = toNumber(markdownStyle.blockquote?.marginStart)
86
182
 
87
183
  let pendingPrefix: MarkdownRange | null = null
88
184
  let line: LineLayout | null = null
@@ -137,6 +233,14 @@ function layoutBlocks(
137
233
 
138
234
  offset += gutter
139
235
 
236
+ // The room either side of a list marker is indent on the way in -- the marker moves right with
237
+ // the text -- and a gap after the prefix on the way out, which is where the gutter of a nested
238
+ // container goes too.
239
+ const marker = pendingPrefix ?? markers.get(range.type)
240
+ const padding = marker ? markerPadding(range.type, markdownStyle, rendering) : 0
241
+
242
+ offset += padding
243
+
140
244
  if (pendingPrefix) {
141
245
  markers.set(range.type, pendingPrefix)
142
246
  if (textStart < 0) {
@@ -144,9 +248,15 @@ function layoutBlocks(
144
248
  }
145
249
  }
146
250
 
147
- const marker = pendingPrefix ?? markers.get(range.type)
148
251
  if (marker) {
149
- offset += measure(text.substring(marker.start, marker.start + marker.length))
252
+ const prefix = text.substring(marker.start, marker.start + marker.length)
253
+
254
+ offset += markerWidth(prefix, range.type, markdownStyle, rendering, measure)
255
+ offset += padding
256
+
257
+ if (pendingPrefix && padding > 0) {
258
+ current.gaps.set(pendingPrefix.start, (current.gaps.get(pendingPrefix.start) ?? 0) + padding)
259
+ }
150
260
  }
151
261
 
152
262
  padded = pendingPrefix
@@ -159,5 +269,5 @@ function layoutBlocks(
159
269
  return layouts
160
270
  }
161
271
 
162
- export { layoutBlocks, toNumber }
163
- export type { LineLayout }
272
+ export { layoutBlocks, markerRun, SHOWN_MARKERS, toNumber }
273
+ export type { LineLayout, MarkerRendering }