react-native-enriched-markdown-scaffold 0.6.0-scaffold.8 → 0.7.4-scaffold.10

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 (213) hide show
  1. package/ReactNativeEnrichedMarkdown.podspec +62 -11
  2. package/android/build.gradle +5 -0
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +3 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +1 -0
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +12 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +4 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +3 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +1 -0
  9. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +25 -0
  10. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +793 -0
  11. package/android/src/main/baseline-prof.txt +75 -2
  12. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +62 -0
  13. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +10 -2
  14. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +10 -0
  15. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +9 -2
  16. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +10 -0
  17. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +6 -4
  18. package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/AccessibilityLabels.kt +15 -0
  19. package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +44 -7
  20. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +72 -0
  21. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +110 -12
  22. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockSyntaxSentinel.kt +69 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +19 -0
  24. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +84 -6
  25. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputRemend.kt +1 -0
  26. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputLayoutManager.kt +30 -2
  27. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +13 -2
  28. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +3 -0
  29. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +54 -13
  30. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +17 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +1 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +4 -4
  33. package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +8 -1
  34. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +27 -1
  35. package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +5 -1
  36. package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathFontWeight.kt +25 -0
  37. package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathStyle.kt +12 -1
  38. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +37 -7
  39. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +10 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/SpannableExtensions.kt +12 -4
  41. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkEvents.kt +22 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +36 -7
  43. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +74 -4
  44. package/android/src/main/jni/CMakeLists.txt +15 -1
  45. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +2 -0
  46. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  47. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlinePlaceholderSpan.kt +7 -3
  48. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +8 -5
  49. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathTypeface.kt +30 -0
  50. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +27 -3
  51. package/app.plugin.js +2 -1
  52. package/cpp/parser/MD4CParser.cpp +1 -1
  53. package/cpp/wasm/build.sh +9 -9
  54. package/docs/ACCESSIBILITY.md +172 -0
  55. package/docs/API_REFERENCE.md +957 -0
  56. package/docs/COPY_OPTIONS.md +127 -0
  57. package/docs/ELEMENTS_STRUCTURE.md +112 -0
  58. package/docs/IMAGE_CACHING.md +25 -0
  59. package/docs/INPUT.md +281 -0
  60. package/docs/LATEX_MATH.md +198 -0
  61. package/docs/MACOS.md +19 -0
  62. package/docs/MARKDOWN_STREAMING.md +36 -0
  63. package/docs/MENTIONS.md +97 -0
  64. package/docs/RTL.md +82 -0
  65. package/docs/STYLES.md +522 -0
  66. package/docs/TEXT.md +110 -0
  67. package/docs/WEB.md +49 -0
  68. package/ios/EnrichedMarkdown.mm +126 -37
  69. package/ios/EnrichedMarkdownText.mm +84 -16
  70. package/ios/attachments/ENRMMathInlineAttachment+macOS.m +5 -1
  71. package/ios/attachments/ENRMMathInlineAttachment.h +3 -0
  72. package/ios/attachments/ENRMMathInlineAttachment.m +2 -1
  73. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +25 -0
  74. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +793 -0
  75. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +15 -0
  76. package/ios/input/ENRMBlockSyntaxSentinel.h +25 -0
  77. package/ios/input/ENRMBlockSyntaxSentinel.mm +80 -0
  78. package/ios/input/ENRMFormattingStore.h +5 -0
  79. package/ios/input/ENRMFormattingStore.mm +34 -0
  80. package/ios/input/ENRMInputFormatter.h +4 -0
  81. package/ios/input/ENRMInputFormatter.mm +2 -0
  82. package/ios/input/ENRMInputParser.h +5 -0
  83. package/ios/input/ENRMInputParser.mm +79 -6
  84. package/ios/input/ENRMInputTextView.h +2 -0
  85. package/ios/input/ENRMInputTextView.mm +8 -17
  86. package/ios/input/ENRMMarkdownSerializer.mm +1 -1
  87. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +71 -53
  88. package/ios/input/EnrichedMarkdownTextInput+Internal.h +26 -0
  89. package/ios/input/EnrichedMarkdownTextInput.h +1 -0
  90. package/ios/input/EnrichedMarkdownTextInput.mm +249 -31
  91. package/ios/input/InputStylePropsUtils.h +13 -0
  92. package/ios/input/styles/ENRMBoldStyleHandler.mm +3 -2
  93. package/ios/input/styles/ENRMItalicStyleHandler.mm +3 -2
  94. package/ios/internals/MeasurementCache.h +1 -0
  95. package/ios/math/ENRMRaTeXBridge.swift +52 -3
  96. package/ios/parser/ENRMMarkdownParser.h +8 -0
  97. package/ios/parser/ENRMMarkdownParser.mm +11 -0
  98. package/ios/renderer/AttributedRenderer.m +51 -6
  99. package/ios/renderer/ENRMMathInlineRenderer.m +8 -1
  100. package/ios/renderer/ListItemRenderer.m +8 -5
  101. package/ios/styles/ENRMFontSlot.h +1 -1
  102. package/ios/styles/StyleConfig.h +6 -0
  103. package/ios/styles/StyleConfig.mm +36 -0
  104. package/ios/utils/ENRMAccessibilityLabels.h +36 -0
  105. package/ios/utils/ENRMAccessibilityLabels.m +5 -0
  106. package/ios/utils/ENRMMathFontWeight.h +18 -0
  107. package/ios/utils/EditMenuUtils+macOS.m +4 -5
  108. package/ios/utils/EditMenuUtils.h +70 -0
  109. package/ios/utils/EditMenuUtils.m +14 -11
  110. package/ios/utils/LastElementUtils.h +39 -2
  111. package/ios/utils/MarkdownAccessibilityElementBuilder.h +18 -16
  112. package/ios/utils/MarkdownAccessibilityElementBuilder.m +64 -32
  113. package/ios/utils/ParagraphStyleUtils.m +21 -2
  114. package/ios/utils/StylePropsUtils.h +21 -0
  115. package/ios/views/ENRMMathContainerView.h +8 -0
  116. package/ios/views/ENRMMathContainerView.m +10 -6
  117. package/ios/views/ENRMTableIOSGridView.h +40 -0
  118. package/ios/views/ENRMTableIOSGridView.m +216 -0
  119. package/ios/views/EnrichedMarkdownInternalText.h +2 -0
  120. package/ios/views/EnrichedMarkdownInternalText.m +2 -0
  121. package/ios/views/TableContainerView.h +8 -0
  122. package/ios/views/TableContainerView.m +72 -128
  123. package/lib/module/EnrichedMarkdownNativeComponent.ts +40 -0
  124. package/lib/module/EnrichedMarkdownTextInput.js +50 -1
  125. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  126. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +47 -0
  127. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +40 -0
  128. package/lib/module/accessibilityLabelDefaults.js +64 -0
  129. package/lib/module/accessibilityLabelDefaults.js.map +1 -0
  130. package/lib/module/index.js +1 -0
  131. package/lib/module/index.js.map +1 -1
  132. package/lib/module/native/EnrichedMarkdownText.js +72 -4
  133. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  134. package/lib/module/normalizeMarkdownStyle.js +5 -2
  135. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  136. package/lib/module/normalizeMarkdownStyle.web.js +5 -2
  137. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  138. package/lib/module/normalizeMarkdownTextInputStyle.js +7 -3
  139. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  140. package/lib/module/normalizeMenuItem.js +38 -0
  141. package/lib/module/normalizeMenuItem.js.map +1 -0
  142. package/lib/module/types/AccessibilityLabels.js +2 -0
  143. package/lib/module/types/AccessibilityLabels.js.map +1 -0
  144. package/lib/module/web/globalStyles.js +8 -1
  145. package/lib/module/web/globalStyles.js.map +1 -1
  146. package/lib/module/web/styles.js +3 -1
  147. package/lib/module/web/styles.js.map +1 -1
  148. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +37 -0
  149. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  150. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +57 -1
  151. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  152. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +42 -0
  153. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  154. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +37 -0
  155. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  156. package/lib/typescript/src/accessibilityLabelDefaults.d.ts +16 -0
  157. package/lib/typescript/src/accessibilityLabelDefaults.d.ts.map +1 -0
  158. package/lib/typescript/src/index.d.ts +4 -2
  159. package/lib/typescript/src/index.d.ts.map +1 -1
  160. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
  161. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  162. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  163. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  164. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +1 -0
  165. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  166. package/lib/typescript/src/normalizeMenuItem.d.ts +7 -0
  167. package/lib/typescript/src/normalizeMenuItem.d.ts.map +1 -0
  168. package/lib/typescript/src/types/AccessibilityLabels.d.ts +114 -0
  169. package/lib/typescript/src/types/AccessibilityLabels.d.ts.map +1 -0
  170. package/lib/typescript/src/types/MarkdownStyle.d.ts +32 -0
  171. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  172. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +3 -0
  173. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  174. package/lib/typescript/src/types/MarkdownTextProps.d.ts +78 -9
  175. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  176. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  177. package/package.json +4 -2
  178. package/plugin/build/withAndroidMath.js +19 -0
  179. package/plugin/build/withIosMath.js +28 -0
  180. package/plugin/build/withReactNativeEnrichedMarkdown.js +11 -0
  181. package/plugin/tsconfig.build.json +16 -0
  182. package/src/EnrichedMarkdownNativeComponent.ts +40 -0
  183. package/src/EnrichedMarkdownTextInput.tsx +121 -1
  184. package/src/EnrichedMarkdownTextInputNativeComponent.ts +47 -0
  185. package/src/EnrichedMarkdownTextNativeComponent.ts +40 -0
  186. package/src/accessibilityLabelDefaults.ts +83 -0
  187. package/src/index.tsx +11 -0
  188. package/src/native/EnrichedMarkdownText.tsx +120 -6
  189. package/src/normalizeMarkdownStyle.ts +6 -1
  190. package/src/normalizeMarkdownStyle.web.ts +6 -1
  191. package/src/normalizeMarkdownTextInputStyle.ts +5 -0
  192. package/src/normalizeMenuItem.ts +52 -0
  193. package/src/types/AccessibilityLabels.ts +118 -0
  194. package/src/types/MarkdownStyle.ts +32 -0
  195. package/src/types/MarkdownStyleInternal.ts +3 -0
  196. package/src/types/MarkdownTextProps.ts +74 -9
  197. package/src/web/globalStyles.ts +7 -0
  198. package/src/web/styles.ts +5 -1
  199. package/lib/module/plugin/withAndroidMath.js +0 -20
  200. package/lib/module/plugin/withAndroidMath.js.map +0 -1
  201. package/lib/module/plugin/withIosMath.js +0 -23
  202. package/lib/module/plugin/withIosMath.js.map +0 -1
  203. package/lib/module/plugin/withReactNativeEnrichedMarkdown.js +0 -16
  204. package/lib/module/plugin/withReactNativeEnrichedMarkdown.js.map +0 -1
  205. package/lib/typescript/src/plugin/withAndroidMath.d.ts +0 -5
  206. package/lib/typescript/src/plugin/withAndroidMath.d.ts.map +0 -1
  207. package/lib/typescript/src/plugin/withIosMath.d.ts +0 -5
  208. package/lib/typescript/src/plugin/withIosMath.d.ts.map +0 -1
  209. package/lib/typescript/src/plugin/withReactNativeEnrichedMarkdown.d.ts +0 -6
  210. package/lib/typescript/src/plugin/withReactNativeEnrichedMarkdown.d.ts.map +0 -1
  211. /package/{src/plugin → plugin/src}/withAndroidMath.ts +0 -0
  212. /package/{src/plugin → plugin/src}/withIosMath.ts +0 -0
  213. /package/{src/plugin → plugin/src}/withReactNativeEnrichedMarkdown.ts +0 -0
@@ -0,0 +1,198 @@
1
+ # LaTeX Math
2
+
3
+ LaTeX math rendering is supported for both block and inline equations:
4
+
5
+ - **Block math (`$$...$$`)**: Rendered as a standalone display element. Requires `flavor="github"`.
6
+ - **Inline math (`$...$`)**: Rendered within the text flow. Works with both `flavor="commonmark"` and `flavor="github"`.
7
+
8
+ ## Usage
9
+
10
+ ```tsx
11
+ <EnrichedMarkdownText
12
+ flavor="github"
13
+ markdown={`
14
+ The quadratic formula:
15
+
16
+ $$x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}$$
17
+
18
+ Einstein's mass-energy equivalence $E = mc^2$ is one of the most famous equations.
19
+ `}
20
+ markdownStyle={{
21
+ math: {
22
+ fontSize: 20,
23
+ color: '#1F2937',
24
+ backgroundColor: '#F3F4F6',
25
+ padding: 12,
26
+ textAlign: 'center',
27
+ },
28
+ inlineMath: {
29
+ color: '#1F2937',
30
+ paddingVertical: 4,
31
+ },
32
+ }}
33
+ />
34
+ ```
35
+
36
+ Block math equations are rendered as standalone display elements with spacing and an optional background. Inline math inherits the surrounding block's typography.
37
+
38
+ ### Spacing around inline formulas
39
+
40
+ An inline formula reports its own height, and tall constructs such as `\frac` are routinely taller than the
41
+ surrounding text. The block's `lineHeight` cannot loosen those lines: it is applied expand-only, so a value
42
+ below the formula's natural height is ignored rather than compressing the formula out of its box. Two wrapped
43
+ lines that each contain a fraction therefore sit flush against each other.
44
+
45
+ `inlineMath.paddingVertical` (points, default `4`) adds empty space above and below the formula box, which is
46
+ what pushes those lines apart. It affects only lines that actually contain inline math — prose-only lines keep
47
+ the line height they had. Set it to `0` to get the old flush behaviour back. Block math (`$$...$$`) has its own
48
+ `math.padding` instead.
49
+
50
+ ### Bold formulas
51
+
52
+ `math.fontWeight` / `inlineMath.fontWeight` (`'normal'` | `'bold'`, default `'normal'`) typeset a
53
+ formula at bold weight. Set it wherever the surrounding text is bold so the formula follows it.
54
+
55
+ The embolden is **synthetic on every platform** — no real bold face is ever used.
56
+
57
+ On **iOS and Android** it is a stroke on the glyph outline and the platform's fake-bold
58
+ respectively, rather than a switch to the bundled KaTeX bold faces. That is deliberate. The native
59
+ engine has no weight parameter, and by the time a style applies, glyph positions are already laid
60
+ out: the bold faces' advances run up to 15% wider than the regular ones the layout reserved, so
61
+ swapping them in would make glyphs collide. Stroking and fake-bold both preserve every advance.
62
+
63
+ Two consequences worth knowing:
64
+
65
+ - Fraction bars and rules keep their own thickness, so they do not thicken with the glyphs.
66
+ - On iOS the stroke width also applies to the handful of decorations the engine strokes itself
67
+ (`\cancel`, `\textcircled`, `\corresponds`, `\imageof`). Prefer turning bold on per surface
68
+ rather than globally.
69
+
70
+ On **web** the prop is a plain CSS declaration on the KaTeX output — but the library renders in
71
+ [MathML output mode](#web) and loads no KaTeX stylesheet or fonts, so `font-weight` is resolved by
72
+ the browser's own math font (STIX Two Math, Latin Modern Math, Cambria Math). Most of those ship no
73
+ bold face, so the browser synthesizes one there too. Treat web as synthetic as well, not as a
74
+ reference for how heavy bold "should" look.
75
+
76
+ `EnrichedMarkdownTextInput` honours the same prop for the formula chips typed into the editor, under
77
+ its own style keys — `math.fontWeight` for inline chips (`$...$`) and `mathBlock.fontWeight` for
78
+ block chips (`$$...$$`). `mathBlock` inherits the weight from `math` when it does not set one of its
79
+ own, the way it already inherits `color` and the border props:
80
+
81
+ ```tsx
82
+ <EnrichedMarkdownTextInput
83
+ markdownStyle={{
84
+ math: { fontWeight: 'bold' }, // inline chips bold…
85
+ mathBlock: { fontWeight: 'normal' }, // …block chips opt back out
86
+ }}
87
+ />
88
+ ```
89
+
90
+ The caveats above apply unchanged — the embolden is the same synthetic one, and the chip keeps its
91
+ measured width because fake-bold and stroking both preserve the glyph advances. Editor chips are
92
+ iOS + Android only; there is no web or macOS chip surface.
93
+
94
+ > [!IMPORTANT]
95
+ > LaTeX commands use backslashes (e.g. `\frac`, `\alpha`). In regular JS strings and template literals, backslashes are escape characters. Use `String.raw` or double backslashes (`\\frac`) to preserve them. Block math (`$$...$$`) must be on its own line to render as a display element.
96
+
97
+ ## Web
98
+
99
+ On web the library renders LaTeX via [KaTeX](https://katex.org/) in **MathML output mode**. Browsers render MathML natively — no CSS or font files are required.
100
+
101
+ ### Installation
102
+
103
+ Install the optional peer dependency:
104
+
105
+ ```sh
106
+ npm install katex
107
+ # or
108
+ yarn add katex
109
+ ```
110
+
111
+ KaTeX is loaded lazily the first time a LaTeX node is encountered, so it has no impact on pages that do not render math. No stylesheet or `<link>` tag is needed.
112
+
113
+ If you load `katex/dist/katex.min.css` anyway — some setups do — its `.katex` rule opens with the
114
+ `font` shorthand, which resets `font-weight` and would drop [`fontWeight`](#bold-formulas) on the
115
+ way down to the formula. The library injects `.katex { font-weight: inherit; }` to keep the prop
116
+ working in that case; nothing is required of you.
117
+
118
+ > [!NOTE]
119
+ > MathML is supported natively in Chrome 109+, Firefox, and Safari. Older browsers will display the raw LaTeX source as a text fallback.
120
+
121
+ ### Disabling on web
122
+
123
+ Pass `latexMath: false` in `md4cFlags` to skip parsing and treat `$` as plain text:
124
+
125
+ ```tsx
126
+ <EnrichedMarkdownText markdown="Price is $5" md4cFlags={{ latexMath: false }} />
127
+ ```
128
+
129
+ This also prevents KaTeX from being loaded at runtime.
130
+
131
+ ## Disabling LaTeX Math (reducing bundle size)
132
+
133
+ LaTeX math rendering relies on **RaTeX** — a native, KaTeX-compatible math engine — on both iOS and Android. It is included by default but can be excluded to reduce your app's binary size (~3–5 MB on iOS, varies on Android).
134
+
135
+ ### 1. Disable at the parser level (JS)
136
+
137
+ Set `latexMath: false` in `md4cFlags` so the parser treats `$` as plain text:
138
+
139
+ ```tsx
140
+ <EnrichedMarkdownText markdown="Price is $5" md4cFlags={{ latexMath: false }} />
141
+ ```
142
+
143
+ This alone prevents math rendering without any native changes. The steps below go further by removing the native math libraries from your binary entirely.
144
+
145
+ ### 2. Remove the native iOS dependency
146
+
147
+ Add the following to your Podfile and re-run `pod install`:
148
+
149
+ ```ruby
150
+ ENV['ENRICHED_MARKDOWN_ENABLE_MATH'] = '0'
151
+ ```
152
+
153
+ This excludes **RaTeX** from the build. Rebuild the app after running `pod install`.
154
+
155
+ > [!NOTE]
156
+ > When math is **enabled** (the default), your Podfile must use dynamic frameworks:
157
+ > ```ruby
158
+ > use_frameworks! :linkage => :dynamic
159
+ > ```
160
+ > This is required for CocoaPods to resolve the RaTeX Swift Package dependency.
161
+
162
+ > [!NOTE]
163
+ > **macOS**: LaTeX math is currently not supported on macOS because `react-native-macos` does not support `use_frameworks!` ([microsoft/react-native-macos#1969](https://github.com/microsoft/react-native-macos/issues/1969)). Math is automatically disabled in the macOS example app.
164
+
165
+ ### 3. Remove the native Android dependency
166
+
167
+ Add the following to your project's `gradle.properties`:
168
+
169
+ ```properties
170
+ enrichedMarkdown.enableMath=false
171
+ ```
172
+
173
+ This excludes **RaTeX** from the Android build. Rebuild the app after changing this property.
174
+
175
+ ### 4. Expo config plugin
176
+
177
+ If you are using Expo, you can use the built-in config plugin to disable LaTeX math rendering on both platforms at once.
178
+
179
+ Add the following to your `app.json` or `app.config.js`:
180
+
181
+ ```json
182
+ {
183
+ "expo": {
184
+ "plugins": [
185
+ [
186
+ "react-native-enriched-markdown",
187
+ {
188
+ "enableMath": false
189
+ }
190
+ ]
191
+ ]
192
+ }
193
+ }
194
+ ```
195
+
196
+ This will automatically apply both the [iOS](#2-remove-the-native-ios-dependency) and [Android](#3-remove-the-native-android-dependency) native changes listed above during `npx expo prebuild`.
197
+
198
+ If you later re-enable math (e.g. remove the plugin or set `enableMath: true`), run `npx expo prebuild --clean` so native projects are regenerated without the disable flags, then rebuild.
package/docs/MACOS.md ADDED
@@ -0,0 +1,19 @@
1
+ # macOS Support
2
+
3
+ `react-native-enriched-markdown` supports macOS via [react-native-macos](https://github.com/microsoft/react-native-macos). The native layer shares code with iOS through a platform abstraction header (`ENRMUIKit.h`), with macOS-specific implementations for context menus, text selection, and clipboard handling.
4
+
5
+ The macOS implementation supports the same rendering elements as iOS — CommonMark, GitHub Flavored Markdown (tables, task lists, strikethrough), images, code blocks, blockquotes, and all other supported elements. `EnrichedMarkdownTextInput` is also available on macOS with full support for inline styles, links, and the native context menu.
6
+
7
+ ## Known limitations
8
+
9
+ These will be addressed in upcoming releases:
10
+
11
+ - **LaTeX math** (both inline and block) is not available on macOS. The math engine (RaTeX) is distributed as a Swift Package, which requires `use_frameworks!` in CocoaPods. `react-native-macos` does not currently support `use_frameworks!` due to circular dependencies between `React-Core` and `React-RCTText` ([microsoft/react-native-macos#1969](https://github.com/microsoft/react-native-macos/issues/1969)). Math will be enabled once this upstream issue is resolved.
12
+ - **Tail fade-in animation** falls back to instant reveal (no `CADisplayLink` on macOS)
13
+ - **VoiceOver** accessibility is stubbed (pending `NSAccessibility` implementation)
14
+ - **Font scale observation** does not respond to system font size changes
15
+ - **`selectionColor`** affects only the selection background. The iOS-style caret + handle tinting isn't available on macOS, since AppKit's `NSTextView` doesn't expose them via `tintColor`.
16
+
17
+ ## Example app
18
+
19
+ See the [react-native-macos-example/](../apps/react-native-macos-example/) directory for a working example app.
@@ -0,0 +1,36 @@
1
+ # Markdown Streaming
2
+
3
+ If you need to render markdown that streams token-by-token from an LLM, check out [react-native-streamdown](https://github.com/software-mansion-labs/react-native-streamdown) — a streaming-ready markdown component built on top of `react-native-enriched-markdown`.
4
+
5
+ It combines [remend](https://www.npmjs.com/package/remend) for fixing incomplete markdown on the fly with [react-native-worklets](https://docs.swmansion.com/react-native-worklets/) **Bundle Mode** to run all processing off the JS thread, keeping your UI responsive while tokens arrive.
6
+
7
+ ```tsx
8
+ import { StreamdownText } from 'react-native-streamdown';
9
+
10
+ <StreamdownText markdown={partialMarkdown} />;
11
+ ```
12
+
13
+ `StreamdownText` accepts all props from `EnrichedMarkdownText` and adds a `remendConfig` prop for customizing the markdown repair pipeline. See the [react-native-streamdown README](https://github.com/software-mansion-labs/react-native-streamdown#readme) for full setup instructions including the required Babel and Metro configuration for Bundle Mode.
14
+
15
+ ## Table Streaming (GFM)
16
+
17
+ When using `flavor="github"` with streaming content, tables require special handling because they are block-level elements that can't be rendered until the parser has enough structure (at minimum a header row and separator line).
18
+
19
+ The `streamingConfig` prop controls this behavior:
20
+
21
+ ```tsx
22
+ <EnrichedMarkdownText
23
+ markdown={streamingMarkdown}
24
+ flavor="github"
25
+ streamingAnimation
26
+ streamingConfig={{ tableMode: 'hidden' }}
27
+ />
28
+ ```
29
+
30
+ ### Table Modes
31
+
32
+ | Mode | Behavior |
33
+ |---|---|
34
+ | `'progressive'` (default) | Renders the table row-by-row as content arrives. New rows fade in when `streamingAnimation` is enabled. Incomplete trailing rows are automatically trimmed. |
35
+ | `'hidden'` | The table is completely hidden until it is followed by a blank line, indicating the table is complete. Prevents visual jank from partially formed tables. |
36
+
@@ -0,0 +1,97 @@
1
+ # Mentions
2
+
3
+ `EnrichedMarkdownTextInput` supports mention flows — token-triggered inline entities (e.g. `@user`, `#channel`) rendered as styled links.
4
+
5
+ ## How It Works
6
+
7
+ 1. User types an indicator (`@`, `#`) or toolbar calls `startMention(indicator)`.
8
+ 2. `onStartMention` fires → show suggestion list.
9
+ 3. `onChangeMention` fires on each keystroke → filter suggestions by query.
10
+ 4. User picks a suggestion → call `insertMention(displayText, url)`.
11
+ 5. `onEndMention` fires → hide suggestions.
12
+
13
+ ## Example
14
+
15
+ ```tsx
16
+ <EnrichedMarkdownTextInput
17
+ ref={ref}
18
+ mentionIndicators={['@', '#']}
19
+ markdownStyle={{
20
+ link: { color: '#2563EB', underline: true },
21
+ linkVariants: {
22
+ '^user:': { color: '#1264A3', backgroundColor: '#E8F5FB', underline: false },
23
+ '^channel:': { color: '#065F46', backgroundColor: '#D1FAE5', underline: false },
24
+ },
25
+ }}
26
+ onStartMention={({ indicator }) => setShowSuggestions(true)}
27
+ onChangeMention={({ indicator, text }) => setQuery(text)}
28
+ onEndMention={() => setShowSuggestions(false)}
29
+ onCaretRectChange={setCaretRect} // for positioning the popup
30
+ />
31
+ ```
32
+
33
+ When the user selects a suggestion:
34
+
35
+ ```tsx
36
+ ref.current?.insertMention(`@${item.name}`, item.url);
37
+ // Markdown output: [@Alice](user://u_1)
38
+ ```
39
+
40
+ ## Props
41
+
42
+ | Prop | Type | Default | Description |
43
+ | ---- | ---- | ------- | ----------- |
44
+ | `mentionIndicators` | `string[]` | `[]` | Trigger strings that start a mention flow. |
45
+
46
+ ## Events
47
+
48
+ | Event | Payload | When |
49
+ | ----- | ------- | ---- |
50
+ | `onStartMention` | `{ indicator }` | Mention flow starts. |
51
+ | `onChangeMention` | `{ indicator, text }` | Query text changes (each keystroke). |
52
+ | `onEndMention` | `{ indicator }` | Mention flow ends (cancel, insert, or cursor moved away). |
53
+
54
+ ## Ref Methods
55
+
56
+ | Method | Description |
57
+ | ------ | ----------- |
58
+ | `startMention(indicator)` | Inserts the indicator at cursor and triggers the mention flow. Must be in `mentionIndicators`. |
59
+ | `insertMention(displayText, url)` | Replaces the active mention token with a styled link. Only works during an active flow. |
60
+
61
+ ## Link Variants (Styling)
62
+
63
+ Mentions are links — style them per URL pattern via `linkVariants` in `markdownStyle`:
64
+
65
+ ```tsx
66
+ linkVariants: {
67
+ '^user:': { color: '#1264A3', backgroundColor: '#E8F5FB', underline: false },
68
+ '^channel:': { color: '#065F46', backgroundColor: '#D1FAE5', underline: false },
69
+ }
70
+ ```
71
+
72
+ Each key is a regex tested against the link URL. First match wins. Unspecified properties inherit from the base `link` style. Patterns are auto-sorted longest-first.
73
+
74
+ ## Positioning the Suggestion List
75
+
76
+ Use `onCaretRectChange` to get the caret's `{ x, y, width, height }` relative to the input. Combine with the input's position (via `onLayout`) to place a floating popup:
77
+
78
+ ```tsx
79
+ <View
80
+ style={{
81
+ position: 'absolute',
82
+ left: inputLayout.x + caretRect.x,
83
+ top: inputLayout.y + caretRect.y + caretRect.height + 4,
84
+ }}
85
+ >
86
+ {/* suggestions */}
87
+ </View>
88
+ ```
89
+
90
+ For simpler layouts, just render the list adjacent to the input without caret tracking.
91
+
92
+ ## Behavior Notes
93
+
94
+ - **Atomic deletion**: Backspacing into a mention deletes it entirely (Slack-like).
95
+ - **Debounce**: `onChangeMention` fires every keystroke — debounce network requests.
96
+ - **Toolbar**: Call `focus()` before `startMention()` if the input isn't focused.
97
+ - **URL schemes**: Use custom schemes (`user://`, `channel://`) to distinguish mention types from regular links.
package/docs/RTL.md ADDED
@@ -0,0 +1,82 @@
1
+ # RTL Support
2
+
3
+ `react-native-enriched-markdown` resolves writing direction **per paragraph** on both platforms: each paragraph picks its own base direction from its first strong directional character. Arabic, Hebrew, and Persian content right-aligns automatically, even inside an LTR app and even when mixed with English paragraphs in the same document.
4
+
5
+ This document describes the read-only [`EnrichedMarkdownText`](TEXT.md) renderer. The rich [`EnrichedMarkdownTextInput`](INPUT.md#rtl-support) follows the same per-paragraph rules — see its [RTL section](INPUT.md#rtl-support) for input-specific caveats (placeholder, code blocks, etc.).
6
+
7
+ ## Platform Setup
8
+
9
+ No setup is required. Both platforms autodetect direction per paragraph out of the box.
10
+
11
+ ### Android
12
+
13
+ Uses Android's `TEXT_DIRECTION_FIRST_STRONG` heuristic on every `StaticLayout`. Paragraphs with no strong character fall back to the view's resolved layout direction (inherits ancestor `<View style={{ direction: 'rtl' }}>` and `I18nManager.isRTL`).
14
+
15
+ ### iOS
16
+
17
+ iOS TextKit's `NSWritingDirectionNatural` does **not** do per-paragraph first-strong — it follows the app's global UI layout direction. The library implements first-strong itself as a post-render pass, matching Android's behavior. The mode is controlled by the [`writingDirection`](#writingdirection-prop-ios) prop and defaults to `'first-strong'`.
18
+
19
+ > **Note:** Earlier versions documented `I18nManager.forceRTL(true)` as a requirement on iOS. That is no longer needed for content direction — `first-strong` resolves each paragraph from its content. `I18nManager.forceRTL` still affects the surrounding app layout (Yoga direction, navigation, etc.) and remains useful if you want the whole app in RTL mode, but it's no longer a precondition for Markdown to render right-aligned.
20
+
21
+ ## `writingDirection` prop (iOS)
22
+
23
+ | Value | Behavior |
24
+ |---|---|
25
+ | `'first-strong'` (default) | Per-paragraph autodetection. Neutral-only paragraphs fall back to the view's resolved layout direction. Matches Android. |
26
+ | `'auto'` | React Native parity. TextKit follows the app's `userInterfaceLayoutDirection`; mixed-direction documents do not auto-resolve. |
27
+ | `'ltr'` | Forces LTR on every paragraph. |
28
+ | `'rtl'` | Forces RTL on every paragraph. |
29
+
30
+ Code blocks always render LTR regardless of this prop.
31
+
32
+ Android ignores the prop; it always uses first-strong via the platform.
33
+
34
+ ```tsx
35
+ // Default — mixed Arabic/English document renders each paragraph correctly.
36
+ <EnrichedMarkdownText markdown={mixedContent} />
37
+
38
+ // Force RTL for the whole document (e.g. forms or admin UI in an Arabic locale).
39
+ <EnrichedMarkdownText writingDirection="rtl" markdown={content} />
40
+ ```
41
+
42
+ ## Element RTL Behavior
43
+
44
+ Each element follows the **paragraph it belongs to**, not a global flag — so a single document can mix sides cleanly.
45
+
46
+ | Element | Behavior |
47
+ |---|---|
48
+ | **Paragraphs & headings** | Base direction set per paragraph from first-strong (or forced by the prop). |
49
+ | **Unordered lists** | Bullet drawn on the side that matches the item's paragraph direction. |
50
+ | **Ordered lists** | Number drawn on the side that matches the item's paragraph direction. |
51
+ | **Task lists** | Checkbox drawn on the matching side; tap hit-test follows the same side. |
52
+ | **Blockquotes** | Border drawn on the side that matches the quoted paragraph. |
53
+ | **Tables** (`flavor="github"`) | Each cell resolves its own direction independently from cell content. |
54
+ | **Code blocks** | Always LTR. |
55
+ | **Inline code** | Inherits its paragraph's direction; characters flow correctly via TextKit Bidi. |
56
+
57
+ ## Yoga layout-direction inheritance
58
+
59
+ For paragraphs with no strong directional character (digits, punctuation, block spacers), the library falls back to the view's **Yoga-resolved layout direction**. That means:
60
+
61
+ ```tsx
62
+ // "123 456 789." has no strong character. Inside an RTL ancestor view,
63
+ // it right-aligns; otherwise it left-aligns.
64
+ <View style={{ direction: 'rtl' }}>
65
+ <EnrichedMarkdownText markdown="123 456 789." />
66
+ </View>
67
+ ```
68
+
69
+ This mirrors Android's first-strong fallback and lets you place an LTR app in an RTL screen (or vice versa) without surprises for neutral content.
70
+
71
+ ## Copy-as-HTML caveat
72
+
73
+ When you copy markdown content to the clipboard, the HTML representation carries a single `dir` attribute on `<html>` (or on `<table>` for table copies). That attribute is read from the document's first paragraph:
74
+
75
+ - `first-strong` document starting with Arabic → `<html dir="rtl">`
76
+ - `first-strong` document starting with English → `<html dir="ltr">`
77
+ - `'auto'` → `<html dir="auto">` (receivers do best-effort first-strong)
78
+ - `'ltr'` / `'rtl'` → forced
79
+
80
+ **Receivers (Gmail, Notes, Word, etc.) apply their own Bidi algorithm to the pasted HTML.** Mixed-direction documents may not visually reproduce the per-paragraph layout you see in-app — receivers can only honor what the markup encodes, and HTML's `dir` attribute is scoped to elements, not paragraphs within them. The plain-text and Markdown clipboard representations are unaffected and round-trip cleanly.
81
+
82
+ If you need precise mixed-direction output in a specific receiver, the rendered HTML carries no per-`<p>` `dir` attribute today — that would be a future enhancement.