react-native-nitro-markdown 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -84
  3. package/android/src/main/cpp/cpp-adapter.cpp +1 -1
  4. package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +61 -0
  5. package/android/src/main/java/com/nitromarkdown/NitroMarkdownPackage.kt +5 -1
  6. package/cpp/bindings/HybridMarkdownParser.cpp +2 -2
  7. package/cpp/bindings/HybridMarkdownParser.hpp +2 -2
  8. package/cpp/bindings/HybridMarkdownSession.cpp +0 -0
  9. package/cpp/core/MarkdownSessionCore.cpp +0 -0
  10. package/ios/HybridMarkdownSession.swift +64 -0
  11. package/lib/commonjs/MarkdownContext.js +21 -0
  12. package/lib/commonjs/MarkdownContext.js.map +1 -0
  13. package/lib/commonjs/MarkdownSession.js +11 -0
  14. package/lib/commonjs/MarkdownSession.js.map +1 -0
  15. package/lib/commonjs/default-markdown-renderer.js +220 -0
  16. package/lib/commonjs/default-markdown-renderer.js.map +1 -0
  17. package/lib/commonjs/headless.js +50 -0
  18. package/lib/commonjs/headless.js.map +1 -0
  19. package/lib/commonjs/index.js +185 -13
  20. package/lib/commonjs/index.js.map +1 -1
  21. package/lib/commonjs/markdown-stream.js +32 -0
  22. package/lib/commonjs/markdown-stream.js.map +1 -0
  23. package/lib/commonjs/markdown.js +326 -0
  24. package/lib/commonjs/markdown.js.map +1 -0
  25. package/lib/commonjs/package.json +1 -0
  26. package/lib/commonjs/renderers/blockquote.js +36 -0
  27. package/lib/commonjs/renderers/blockquote.js.map +1 -0
  28. package/lib/commonjs/renderers/code.js +92 -0
  29. package/lib/commonjs/renderers/code.js.map +1 -0
  30. package/lib/commonjs/renderers/heading.js +62 -0
  31. package/lib/commonjs/renderers/heading.js.map +1 -0
  32. package/lib/commonjs/renderers/horizontal-rule.js +27 -0
  33. package/lib/commonjs/renderers/horizontal-rule.js.map +1 -0
  34. package/lib/commonjs/renderers/image.js +159 -0
  35. package/lib/commonjs/renderers/image.js.map +1 -0
  36. package/lib/commonjs/renderers/link.js +37 -0
  37. package/lib/commonjs/renderers/link.js.map +1 -0
  38. package/lib/commonjs/renderers/list.js +114 -0
  39. package/lib/commonjs/renderers/list.js.map +1 -0
  40. package/lib/commonjs/renderers/math.js +151 -0
  41. package/lib/commonjs/renderers/math.js.map +1 -0
  42. package/lib/commonjs/renderers/paragraph.js +44 -0
  43. package/lib/commonjs/renderers/paragraph.js.map +1 -0
  44. package/lib/commonjs/renderers/table.js +283 -0
  45. package/lib/commonjs/renderers/table.js.map +1 -0
  46. package/lib/commonjs/specs/MarkdownSession.nitro.js +6 -0
  47. package/lib/commonjs/specs/MarkdownSession.nitro.js.map +1 -0
  48. package/lib/commonjs/theme.js +58 -0
  49. package/lib/commonjs/theme.js.map +1 -0
  50. package/lib/commonjs/use-markdown-stream.js +71 -0
  51. package/lib/commonjs/use-markdown-stream.js.map +1 -0
  52. package/lib/module/MarkdownContext.js +17 -0
  53. package/lib/module/MarkdownContext.js.map +1 -0
  54. package/lib/module/MarkdownSession.js +7 -0
  55. package/lib/module/MarkdownSession.js.map +1 -0
  56. package/lib/module/default-markdown-renderer.js +215 -0
  57. package/lib/module/default-markdown-renderer.js.map +1 -0
  58. package/lib/module/headless.js +44 -0
  59. package/lib/module/headless.js.map +1 -0
  60. package/lib/module/index.js +36 -10
  61. package/lib/module/index.js.map +1 -1
  62. package/lib/module/markdown-stream.js +27 -0
  63. package/lib/module/markdown-stream.js.map +1 -0
  64. package/lib/module/markdown.js +321 -0
  65. package/lib/module/markdown.js.map +1 -0
  66. package/lib/module/package.json +1 -0
  67. package/lib/module/renderers/blockquote.js +31 -0
  68. package/lib/module/renderers/blockquote.js.map +1 -0
  69. package/lib/module/renderers/code.js +86 -0
  70. package/lib/module/renderers/code.js.map +1 -0
  71. package/lib/module/renderers/heading.js +57 -0
  72. package/lib/module/renderers/heading.js.map +1 -0
  73. package/lib/module/renderers/horizontal-rule.js +22 -0
  74. package/lib/module/renderers/horizontal-rule.js.map +1 -0
  75. package/lib/module/renderers/image.js +154 -0
  76. package/lib/module/renderers/image.js.map +1 -0
  77. package/lib/module/renderers/link.js +32 -0
  78. package/lib/module/renderers/link.js.map +1 -0
  79. package/lib/module/renderers/list.js +107 -0
  80. package/lib/module/renderers/list.js.map +1 -0
  81. package/lib/module/renderers/math.js +145 -0
  82. package/lib/module/renderers/math.js.map +1 -0
  83. package/lib/module/renderers/paragraph.js +39 -0
  84. package/lib/module/renderers/paragraph.js.map +1 -0
  85. package/lib/module/renderers/table.js +278 -0
  86. package/lib/module/renderers/table.js.map +1 -0
  87. package/lib/module/specs/MarkdownSession.nitro.js +4 -0
  88. package/lib/module/specs/MarkdownSession.nitro.js.map +1 -0
  89. package/lib/module/theme.js +54 -0
  90. package/lib/module/theme.js.map +1 -0
  91. package/lib/module/use-markdown-stream.js +66 -0
  92. package/lib/module/use-markdown-stream.js.map +1 -0
  93. package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -0
  94. package/lib/typescript/commonjs/MarkdownContext.d.ts +26 -0
  95. package/lib/typescript/commonjs/MarkdownContext.d.ts.map +1 -0
  96. package/lib/typescript/commonjs/MarkdownSession.d.ts +4 -0
  97. package/lib/typescript/commonjs/MarkdownSession.d.ts.map +1 -0
  98. package/lib/typescript/commonjs/default-markdown-renderer.d.ts +10 -0
  99. package/lib/typescript/commonjs/default-markdown-renderer.d.ts.map +1 -0
  100. package/lib/typescript/commonjs/headless.d.ts +50 -0
  101. package/lib/typescript/commonjs/headless.d.ts.map +1 -0
  102. package/lib/typescript/commonjs/index.d.ts +34 -0
  103. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  104. package/lib/typescript/commonjs/markdown-stream.d.ts +15 -0
  105. package/lib/typescript/commonjs/markdown-stream.d.ts.map +1 -0
  106. package/lib/typescript/commonjs/markdown.d.ts +29 -0
  107. package/lib/typescript/commonjs/markdown.d.ts.map +1 -0
  108. package/lib/typescript/commonjs/package.json +1 -0
  109. package/lib/typescript/commonjs/renderers/blockquote.d.ts +9 -0
  110. package/lib/typescript/commonjs/renderers/blockquote.d.ts.map +1 -0
  111. package/lib/typescript/commonjs/renderers/code.d.ts +15 -0
  112. package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -0
  113. package/lib/typescript/commonjs/renderers/heading.d.ts +10 -0
  114. package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +3 -0
  116. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/renderers/image.d.ts +11 -0
  118. package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/renderers/link.d.ts +10 -0
  120. package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -0
  121. package/lib/typescript/commonjs/renderers/list.d.ts +22 -0
  122. package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -0
  123. package/lib/typescript/commonjs/renderers/math.d.ts +15 -0
  124. package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/renderers/paragraph.d.ts +15 -0
  126. package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -0
  127. package/lib/typescript/commonjs/renderers/table.d.ts +10 -0
  128. package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/specs/MarkdownSession.nitro.d.ts +12 -0
  130. package/lib/typescript/commonjs/specs/MarkdownSession.nitro.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/theme.d.ts +52 -0
  132. package/lib/typescript/commonjs/theme.d.ts.map +1 -0
  133. package/lib/typescript/commonjs/use-markdown-stream.d.ts +22 -0
  134. package/lib/typescript/commonjs/use-markdown-stream.d.ts.map +1 -0
  135. package/lib/typescript/module/Markdown.nitro.d.ts +13 -0
  136. package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -0
  137. package/lib/typescript/module/MarkdownContext.d.ts +26 -0
  138. package/lib/typescript/module/MarkdownContext.d.ts.map +1 -0
  139. package/lib/typescript/module/MarkdownSession.d.ts +4 -0
  140. package/lib/typescript/module/MarkdownSession.d.ts.map +1 -0
  141. package/lib/typescript/module/default-markdown-renderer.d.ts +10 -0
  142. package/lib/typescript/module/default-markdown-renderer.d.ts.map +1 -0
  143. package/lib/typescript/module/headless.d.ts +50 -0
  144. package/lib/typescript/module/headless.d.ts.map +1 -0
  145. package/lib/typescript/module/index.d.ts +34 -0
  146. package/lib/typescript/module/index.d.ts.map +1 -0
  147. package/lib/typescript/module/markdown-stream.d.ts +15 -0
  148. package/lib/typescript/module/markdown-stream.d.ts.map +1 -0
  149. package/lib/typescript/module/markdown.d.ts +29 -0
  150. package/lib/typescript/module/markdown.d.ts.map +1 -0
  151. package/lib/typescript/module/package.json +1 -0
  152. package/lib/typescript/module/renderers/blockquote.d.ts +9 -0
  153. package/lib/typescript/module/renderers/blockquote.d.ts.map +1 -0
  154. package/lib/typescript/module/renderers/code.d.ts +15 -0
  155. package/lib/typescript/module/renderers/code.d.ts.map +1 -0
  156. package/lib/typescript/module/renderers/heading.d.ts +10 -0
  157. package/lib/typescript/module/renderers/heading.d.ts.map +1 -0
  158. package/lib/typescript/module/renderers/horizontal-rule.d.ts +3 -0
  159. package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -0
  160. package/lib/typescript/module/renderers/image.d.ts +11 -0
  161. package/lib/typescript/module/renderers/image.d.ts.map +1 -0
  162. package/lib/typescript/module/renderers/link.d.ts +10 -0
  163. package/lib/typescript/module/renderers/link.d.ts.map +1 -0
  164. package/lib/typescript/module/renderers/list.d.ts +22 -0
  165. package/lib/typescript/module/renderers/list.d.ts.map +1 -0
  166. package/lib/typescript/module/renderers/math.d.ts +15 -0
  167. package/lib/typescript/module/renderers/math.d.ts.map +1 -0
  168. package/lib/typescript/module/renderers/paragraph.d.ts +15 -0
  169. package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -0
  170. package/lib/typescript/module/renderers/table.d.ts +10 -0
  171. package/lib/typescript/module/renderers/table.d.ts.map +1 -0
  172. package/lib/typescript/module/specs/MarkdownSession.nitro.d.ts +12 -0
  173. package/lib/typescript/module/specs/MarkdownSession.nitro.d.ts.map +1 -0
  174. package/lib/typescript/module/theme.d.ts +52 -0
  175. package/lib/typescript/module/theme.d.ts.map +1 -0
  176. package/lib/typescript/module/use-markdown-stream.d.ts +22 -0
  177. package/lib/typescript/module/use-markdown-stream.d.ts.map +1 -0
  178. package/nitro.json +5 -2
  179. package/nitrogen/generated/android/NitroMarkdown+autolinking.cmake +3 -2
  180. package/nitrogen/generated/android/NitroMarkdown+autolinking.gradle +1 -1
  181. package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +17 -5
  182. package/nitrogen/generated/android/NitroMarkdownOnLoad.hpp +4 -4
  183. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  184. package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.cpp +91 -0
  185. package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.hpp +70 -0
  186. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/Func_void.kt +80 -0
  187. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSessionSpec.kt +78 -0
  188. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/NitroMarkdownOnLoad.kt +1 -1
  189. package/nitrogen/generated/ios/NitroMarkdown+autolinking.rb +2 -2
  190. package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.cpp +28 -4
  191. package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.hpp +72 -6
  192. package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Umbrella.hpp +11 -4
  193. package/nitrogen/generated/ios/NitroMarkdownAutolinking.mm +11 -3
  194. package/nitrogen/generated/ios/NitroMarkdownAutolinking.swift +16 -3
  195. package/nitrogen/generated/ios/c++/HybridMarkdownSessionSpecSwift.cpp +11 -0
  196. package/nitrogen/generated/ios/c++/HybridMarkdownSessionSpecSwift.hpp +108 -0
  197. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  198. package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec.swift +59 -0
  199. package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec_cxx.swift +190 -0
  200. package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.cpp +3 -3
  201. package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.hpp +4 -4
  202. package/nitrogen/generated/shared/c++/HybridMarkdownSessionSpec.cpp +26 -0
  203. package/nitrogen/generated/shared/c++/HybridMarkdownSessionSpec.hpp +67 -0
  204. package/nitrogen/generated/shared/c++/ParserOptions.hpp +22 -14
  205. package/package.json +45 -7
  206. package/react-native-nitro-markdown.podspec +5 -5
  207. package/src/MarkdownContext.ts +41 -0
  208. package/src/MarkdownSession.ts +8 -0
  209. package/src/default-markdown-renderer.tsx +266 -0
  210. package/src/headless.ts +106 -0
  211. package/src/index.ts +41 -59
  212. package/src/markdown-stream.tsx +32 -0
  213. package/src/markdown.tsx +415 -0
  214. package/src/renderers/blockquote.tsx +31 -0
  215. package/src/renderers/code.tsx +88 -0
  216. package/src/renderers/heading.tsx +66 -0
  217. package/src/renderers/horizontal-rule.tsx +20 -0
  218. package/src/renderers/image.tsx +160 -0
  219. package/src/renderers/link.tsx +38 -0
  220. package/src/renderers/list.tsx +125 -0
  221. package/src/renderers/math.tsx +164 -0
  222. package/src/renderers/paragraph.tsx +53 -0
  223. package/src/renderers/table.tsx +345 -0
  224. package/src/specs/MarkdownSession.nitro.ts +16 -0
  225. package/src/theme.ts +52 -0
  226. package/src/use-markdown-stream.ts +83 -0
  227. package/ios/NitroMarkdown-Bridging-Header.h +0 -14
  228. package/lib/commonjs/MarkdownJS.reference.js +0 -114
  229. package/lib/commonjs/MarkdownJS.reference.js.map +0 -1
  230. package/lib/module/MarkdownJS.reference.js +0 -107
  231. package/lib/module/MarkdownJS.reference.js.map +0 -1
  232. package/lib/typescript/Markdown.nitro.d.ts.map +0 -1
  233. package/lib/typescript/MarkdownJS.reference.d.ts +0 -33
  234. package/lib/typescript/MarkdownJS.reference.d.ts.map +0 -1
  235. package/lib/typescript/index.d.ts +0 -22
  236. package/lib/typescript/index.d.ts.map +0 -1
  237. package/src/MarkdownJS.reference.ts +0 -122
  238. /package/lib/typescript/{Markdown.nitro.d.ts → commonjs/Markdown.nitro.d.ts} +0 -0
@@ -0,0 +1,415 @@
1
+ import { defaultMarkdownTheme, type MarkdownTheme } from "./theme";
2
+ import { useMemo, type ReactNode, type FC, Fragment } from "react";
3
+ import {
4
+ StyleSheet,
5
+ View,
6
+ Text,
7
+ type StyleProp,
8
+ type ViewStyle,
9
+ } from "react-native";
10
+ import {
11
+ parseMarkdown,
12
+ parseMarkdownWithOptions,
13
+ type MarkdownNode,
14
+ } from "./headless";
15
+ import type { ParserOptions } from "./Markdown.nitro";
16
+ import {
17
+ MarkdownContext,
18
+ useMarkdownContext,
19
+ type CustomRenderers,
20
+ type NodeRendererProps,
21
+ } from "./MarkdownContext";
22
+
23
+ import { Heading } from "./renderers/heading";
24
+ import { Paragraph } from "./renderers/paragraph";
25
+ import { Link } from "./renderers/link";
26
+ import { Blockquote } from "./renderers/blockquote";
27
+ import { HorizontalRule } from "./renderers/horizontal-rule";
28
+ import { CodeBlock, InlineCode } from "./renderers/code";
29
+ import { List, ListItem, TaskListItem } from "./renderers/list";
30
+ import { TableRenderer } from "./renderers/table";
31
+ import { Image } from "./renderers/image";
32
+ import { MathInline, MathBlock } from "./renderers/math";
33
+
34
+ export interface MarkdownProps {
35
+ /**
36
+ * The markdown string to parse and render.
37
+ */
38
+ children: string;
39
+ /**
40
+ * Parser options to enable GFM or Math support.
41
+ */
42
+ options?: ParserOptions;
43
+ /**
44
+ * Custom renderers for specific markdown node types.
45
+ */
46
+ renderers?: CustomRenderers;
47
+ /**
48
+ * Custom theme to override default styles.
49
+ */
50
+ theme?: Partial<MarkdownTheme>;
51
+ /**
52
+ * Optional style for the container view.
53
+ */
54
+ style?: StyleProp<ViewStyle>;
55
+ }
56
+
57
+ export const Markdown: FC<MarkdownProps> = ({
58
+ children,
59
+ options,
60
+ renderers = {},
61
+ theme: userTheme,
62
+ style,
63
+ }) => {
64
+ const ast = useMemo(() => {
65
+ try {
66
+ if (options) {
67
+ return parseMarkdownWithOptions(children, options);
68
+ }
69
+ return parseMarkdown(children);
70
+ } catch (error) {
71
+ console.error("Failed to parse markdown:", error);
72
+ return null;
73
+ }
74
+ }, [children, options]);
75
+
76
+ const theme = useMemo(
77
+ () => ({ ...defaultMarkdownTheme, ...userTheme }),
78
+ [userTheme]
79
+ );
80
+
81
+ const baseStyles = useMemo(() => createBaseStyles(theme), [theme]);
82
+
83
+ if (!ast) {
84
+ return (
85
+ <View style={[baseStyles.container, style]}>
86
+ <Text style={baseStyles.errorText}>Error parsing markdown</Text>
87
+ </View>
88
+ );
89
+ }
90
+
91
+ return (
92
+ <MarkdownContext.Provider value={{ renderers, theme }}>
93
+ <View style={[baseStyles.container, style]}>
94
+ <NodeRenderer node={ast} depth={0} inListItem={false} />
95
+ </View>
96
+ </MarkdownContext.Provider>
97
+ );
98
+ };
99
+
100
+ const isInline = (type: MarkdownNode["type"]): boolean => {
101
+ return (
102
+ type === "text" ||
103
+ type === "bold" ||
104
+ type === "italic" ||
105
+ type === "strikethrough" ||
106
+ type === "link" ||
107
+ type === "code_inline" ||
108
+ type === "soft_break" ||
109
+ type === "line_break" ||
110
+ type === "html_inline" ||
111
+ type === "math_inline"
112
+ );
113
+ };
114
+
115
+ const getTextContent = (node: MarkdownNode): string => {
116
+ if (node.content) return node.content;
117
+ return node.children?.map(getTextContent).join("") ?? "";
118
+ };
119
+
120
+ const NodeRenderer: FC<NodeRendererProps> = ({
121
+ node,
122
+ depth,
123
+ inListItem,
124
+ parentIsText = false,
125
+ }) => {
126
+ const { renderers, theme } = useMarkdownContext();
127
+ const baseStyles = useMemo(() => createBaseStyles(theme), [theme]);
128
+
129
+ const renderChildren = (
130
+ children?: MarkdownNode[],
131
+ childInListItem = false,
132
+ childParentIsText = false
133
+ ): ReactNode => {
134
+ if (!children || children.length === 0) return null;
135
+
136
+ const elements: ReactNode[] = [];
137
+ let currentInlineGroup: MarkdownNode[] = [];
138
+
139
+ const flushInlineGroup = () => {
140
+ if (currentInlineGroup.length > 0) {
141
+ const hasMath = currentInlineGroup.some(
142
+ (child) => child.type === "math_inline"
143
+ );
144
+
145
+ if (hasMath && !childParentIsText) {
146
+ elements.push(
147
+ <View
148
+ key={`inline-group-${elements.length}`}
149
+ style={{
150
+ flexDirection: "row",
151
+ alignItems: "baseline",
152
+ flexWrap: "wrap",
153
+ flexShrink: 1,
154
+ marginVertical: 0,
155
+ paddingVertical: 0,
156
+ }}
157
+ >
158
+ {currentInlineGroup.map((node, idx) => (
159
+ <NodeRenderer
160
+ key={`${node.type}-${idx}`}
161
+ node={node}
162
+ depth={depth + 1}
163
+ inListItem={childInListItem}
164
+ parentIsText={false}
165
+ />
166
+ ))}
167
+ </View>
168
+ );
169
+ } else {
170
+ const Wrapper = childParentIsText ? Fragment : Text;
171
+ const wrapperProps = childParentIsText
172
+ ? {}
173
+ : { style: baseStyles.text };
174
+
175
+ elements.push(
176
+ <Wrapper key={`inline-group-${elements.length}`} {...wrapperProps}>
177
+ {currentInlineGroup.map((node, idx) => (
178
+ <NodeRenderer
179
+ key={`${node.type}-${idx}`}
180
+ node={node}
181
+ depth={depth + 1}
182
+ inListItem={childInListItem}
183
+ parentIsText={true}
184
+ />
185
+ ))}
186
+ </Wrapper>
187
+ );
188
+ }
189
+ currentInlineGroup = [];
190
+ }
191
+ };
192
+
193
+ children.forEach((child, index) => {
194
+ if (isInline(child.type)) {
195
+ currentInlineGroup.push(child);
196
+ } else {
197
+ flushInlineGroup();
198
+ elements.push(
199
+ <NodeRenderer
200
+ key={`${child.type}-${index}`}
201
+ node={child}
202
+ depth={depth + 1}
203
+ inListItem={childInListItem}
204
+ parentIsText={childParentIsText}
205
+ />
206
+ );
207
+ }
208
+ });
209
+
210
+ flushInlineGroup();
211
+ return elements;
212
+ };
213
+
214
+ const customRenderer = renderers[node.type];
215
+ if (customRenderer) {
216
+ const childrenRendered = renderChildren(
217
+ node.children,
218
+ inListItem,
219
+ parentIsText
220
+ );
221
+ const result = customRenderer({
222
+ node,
223
+ children: childrenRendered,
224
+ Renderer: NodeRenderer,
225
+ });
226
+ if (result !== undefined) return <>{result}</>;
227
+ }
228
+
229
+ switch (node.type) {
230
+ case "document":
231
+ return (
232
+ <View style={baseStyles.document}>
233
+ {renderChildren(node.children, false, false)}
234
+ </View>
235
+ );
236
+
237
+ case "heading":
238
+ return (
239
+ <Heading level={node.level ?? 1}>
240
+ {renderChildren(node.children, inListItem, true)}
241
+ </Heading>
242
+ );
243
+
244
+ case "paragraph":
245
+ return (
246
+ <Paragraph inListItem={inListItem}>
247
+ {renderChildren(node.children, inListItem, false)}
248
+ </Paragraph>
249
+ );
250
+
251
+ case "text":
252
+ if (parentIsText) {
253
+ return <Text>{node.content}</Text>;
254
+ }
255
+ return <Text style={baseStyles.text}>{node.content}</Text>;
256
+
257
+ case "bold":
258
+ return (
259
+ <Text style={baseStyles.bold}>
260
+ {renderChildren(node.children, inListItem, true)}
261
+ </Text>
262
+ );
263
+
264
+ case "italic":
265
+ return (
266
+ <Text style={baseStyles.italic}>
267
+ {renderChildren(node.children, inListItem, true)}
268
+ </Text>
269
+ );
270
+
271
+ case "strikethrough":
272
+ return (
273
+ <Text style={baseStyles.strikethrough}>
274
+ {renderChildren(node.children, inListItem, true)}
275
+ </Text>
276
+ );
277
+
278
+ case "link":
279
+ return (
280
+ <Link href={node.href ?? ""}>
281
+ {renderChildren(node.children, inListItem, true)}
282
+ </Link>
283
+ );
284
+
285
+ case "image":
286
+ return (
287
+ <Image
288
+ url={node.href ?? ""}
289
+ title={node.title}
290
+ alt={node.alt}
291
+ Renderer={NodeRenderer}
292
+ />
293
+ );
294
+
295
+ case "code_inline":
296
+ return <InlineCode>{node.content}</InlineCode>;
297
+
298
+ case "code_block":
299
+ return (
300
+ <CodeBlock language={node.language} content={getTextContent(node)} />
301
+ );
302
+
303
+ case "blockquote":
304
+ return (
305
+ <Blockquote>
306
+ {renderChildren(node.children, inListItem, false)}
307
+ </Blockquote>
308
+ );
309
+
310
+ case "horizontal_rule":
311
+ return <HorizontalRule />;
312
+
313
+ case "line_break":
314
+ return <Text>{"\n"}</Text>;
315
+
316
+ case "soft_break":
317
+ return <Text> </Text>;
318
+
319
+ case "math_inline": {
320
+ let mathContent = getTextContent(node);
321
+ if (!mathContent) return null;
322
+ mathContent = mathContent.replace(/^\$+|\$+$/g, "").trim();
323
+ return <MathInline content={mathContent} />;
324
+ }
325
+
326
+ case "math_block":
327
+ return <MathBlock content={getTextContent(node)} />;
328
+
329
+ case "list":
330
+ return (
331
+ <List ordered={node.ordered ?? false} start={node.start} depth={depth}>
332
+ {node.children?.map((child, index) => {
333
+ if (child.type === "task_list_item") {
334
+ return (
335
+ <NodeRenderer
336
+ key={index}
337
+ node={child}
338
+ depth={depth + 1}
339
+ inListItem={true}
340
+ parentIsText={false}
341
+ />
342
+ );
343
+ }
344
+ return (
345
+ <ListItem
346
+ key={index}
347
+ index={index}
348
+ ordered={node.ordered ?? false}
349
+ start={node.start ?? 1}
350
+ >
351
+ <NodeRenderer
352
+ node={child}
353
+ depth={depth + 1}
354
+ inListItem={true}
355
+ parentIsText={false}
356
+ />
357
+ </ListItem>
358
+ );
359
+ })}
360
+ </List>
361
+ );
362
+
363
+ case "list_item":
364
+ return <>{renderChildren(node.children, true, false)}</>;
365
+
366
+ case "task_list_item":
367
+ return (
368
+ <TaskListItem checked={node.checked ?? false}>
369
+ {renderChildren(node.children, true, false)}
370
+ </TaskListItem>
371
+ );
372
+
373
+ case "table":
374
+ return <TableRenderer node={node} Renderer={NodeRenderer} />;
375
+
376
+ case "table_head":
377
+ case "table_body":
378
+ case "table_row":
379
+ case "table_cell":
380
+ // Handled by TableRenderer
381
+ return null;
382
+
383
+ default:
384
+ return null;
385
+ }
386
+ };
387
+
388
+ const createBaseStyles = (theme: MarkdownTheme) =>
389
+ StyleSheet.create({
390
+ container: {
391
+ flex: 1,
392
+ },
393
+ document: {
394
+ flex: 1,
395
+ },
396
+ errorText: {
397
+ color: "#f87171",
398
+ fontSize: 14,
399
+ fontFamily: "monospace",
400
+ },
401
+ text: {
402
+ color: theme.colors.text,
403
+ fontSize: theme.fontSizes.m,
404
+ lineHeight: theme.fontSizes.m * 1.6,
405
+ },
406
+ bold: {
407
+ fontWeight: "700",
408
+ },
409
+ italic: {
410
+ fontStyle: "italic",
411
+ },
412
+ strikethrough: {
413
+ textDecorationLine: "line-through",
414
+ },
415
+ });
@@ -0,0 +1,31 @@
1
+ import { ReactNode, useMemo, type FC } from "react";
2
+ import { View, StyleSheet, type ViewStyle } from "react-native";
3
+ import { useMarkdownContext } from "../MarkdownContext";
4
+
5
+ interface BlockquoteProps {
6
+ children: ReactNode;
7
+ style?: ViewStyle;
8
+ }
9
+
10
+ export const Blockquote: FC<BlockquoteProps> = ({ children, style }) => {
11
+ const { theme } = useMarkdownContext();
12
+ const styles = useMemo(
13
+ () =>
14
+ StyleSheet.create({
15
+ blockquote: {
16
+ borderLeftWidth: 4,
17
+ borderLeftColor: theme.colors.blockquote,
18
+ paddingLeft: theme.spacing.l,
19
+ marginVertical: theme.spacing.m,
20
+ backgroundColor: theme.colors.surfaceLight,
21
+ paddingVertical: theme.spacing.m,
22
+ paddingRight: theme.spacing.m,
23
+ borderRadius: 4,
24
+ },
25
+ }),
26
+ [theme]
27
+ );
28
+
29
+ return <View style={[styles.blockquote, style]}>{children}</View>;
30
+ };
31
+
@@ -0,0 +1,88 @@
1
+ import { ReactNode, useMemo, type FC } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ ScrollView,
7
+ Platform,
8
+ type ViewStyle,
9
+ type TextStyle,
10
+ } from "react-native";
11
+ import { useMarkdownContext } from "../MarkdownContext";
12
+
13
+ interface CodeBlockProps {
14
+ language?: string;
15
+ content: string;
16
+ style?: ViewStyle;
17
+ }
18
+
19
+ export const CodeBlock: FC<CodeBlockProps> = ({
20
+ language,
21
+ content,
22
+ style,
23
+ }) => {
24
+ const { theme } = useMarkdownContext();
25
+ const styles = useMemo(
26
+ () =>
27
+ StyleSheet.create({
28
+ codeBlock: {
29
+ backgroundColor: theme.colors.codeBackground,
30
+ borderRadius: 8,
31
+ padding: theme.spacing.l,
32
+ marginVertical: theme.spacing.m,
33
+ borderWidth: 1,
34
+ borderColor: theme.colors.border,
35
+ },
36
+ codeLanguage: {
37
+ color: theme.colors.accent,
38
+ fontSize: theme.fontSizes.xs,
39
+ fontWeight: "600",
40
+ marginBottom: theme.spacing.s,
41
+ textTransform: "uppercase",
42
+ letterSpacing: 0.5,
43
+ },
44
+ codeBlockText: {
45
+ fontFamily: Platform.select({ ios: "Courier", android: "monospace" }),
46
+ fontSize: theme.fontSizes.s,
47
+ color: theme.colors.text,
48
+ lineHeight: theme.fontSizes.s * 1.5,
49
+ },
50
+ }),
51
+ [theme]
52
+ );
53
+ return (
54
+ <View style={[styles.codeBlock, style]}>
55
+ {language && <Text style={styles.codeLanguage}>{language}</Text>}
56
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
57
+ <Text style={styles.codeBlockText}>{content}</Text>
58
+ </ScrollView>
59
+ </View>
60
+ );
61
+ };
62
+
63
+ interface InlineCodeProps {
64
+ children: ReactNode;
65
+ style?: TextStyle;
66
+ }
67
+
68
+ export const InlineCode: FC<InlineCodeProps> = ({ children, style }) => {
69
+ const { theme } = useMarkdownContext();
70
+ const styles = useMemo(
71
+ () =>
72
+ StyleSheet.create({
73
+ codeInline: {
74
+ fontFamily: Platform.select({ ios: "Courier", android: "monospace" }),
75
+ fontSize: theme.fontSizes.s,
76
+ color: theme.colors.code,
77
+ backgroundColor: theme.colors.codeBackground,
78
+ paddingHorizontal: theme.spacing.xs,
79
+ paddingVertical: 2,
80
+ borderRadius: 4,
81
+ ...(Platform.OS === "android" && { includeFontPadding: false }),
82
+ },
83
+ }),
84
+ [theme]
85
+ );
86
+ return <Text style={[styles.codeInline, style]}>{children}</Text>;
87
+ };
88
+
@@ -0,0 +1,66 @@
1
+ import { ReactNode, useMemo, type FC } from "react";
2
+ import { Text, StyleSheet, type TextStyle } from "react-native";
3
+ import { useMarkdownContext } from "../MarkdownContext";
4
+
5
+ interface HeadingProps {
6
+ level: number;
7
+ children: ReactNode;
8
+ style?: TextStyle;
9
+ }
10
+
11
+ export const Heading: FC<HeadingProps> = ({ level, children, style }) => {
12
+ const { theme } = useMarkdownContext();
13
+ const styles = useMemo(
14
+ () =>
15
+ StyleSheet.create({
16
+ heading: {
17
+ color: theme.colors.heading,
18
+ fontWeight: "700",
19
+ marginTop: theme.spacing.xl,
20
+ marginBottom: theme.spacing.m,
21
+ },
22
+ h1: {
23
+ fontSize: theme.fontSizes.h1,
24
+ lineHeight: theme.fontSizes.h1 * 1.3,
25
+ borderBottomWidth: 1,
26
+ borderBottomColor: theme.colors.border,
27
+ paddingBottom: theme.spacing.s,
28
+ },
29
+ h2: {
30
+ fontSize: theme.fontSizes.h2,
31
+ lineHeight: theme.fontSizes.h2 * 1.3,
32
+ },
33
+ h3: {
34
+ fontSize: theme.fontSizes.h3,
35
+ lineHeight: theme.fontSizes.h3 * 1.3,
36
+ },
37
+ h4: {
38
+ fontSize: theme.fontSizes.h4,
39
+ lineHeight: theme.fontSizes.h4 * 1.3,
40
+ },
41
+ h5: {
42
+ fontSize: theme.fontSizes.h5,
43
+ lineHeight: theme.fontSizes.h5 * 1.3,
44
+ },
45
+ h6: {
46
+ fontSize: theme.fontSizes.h6,
47
+ lineHeight: theme.fontSizes.h6 * 1.3,
48
+ color: theme.colors.textMuted,
49
+ },
50
+ }),
51
+ [theme]
52
+ );
53
+
54
+ const headingStyles = [
55
+ styles.heading,
56
+ level === 1 && styles.h1,
57
+ level === 2 && styles.h2,
58
+ level === 3 && styles.h3,
59
+ level === 4 && styles.h4,
60
+ level === 5 && styles.h5,
61
+ level === 6 && styles.h6,
62
+ ];
63
+
64
+ return <Text style={[...headingStyles, style]}>{children}</Text>;
65
+ };
66
+
@@ -0,0 +1,20 @@
1
+ import { useMemo, type FC } from "react";
2
+ import { View, StyleSheet } from "react-native";
3
+ import { useMarkdownContext } from "../MarkdownContext";
4
+
5
+ export const HorizontalRule: FC = () => {
6
+ const { theme } = useMarkdownContext();
7
+ const styles = useMemo(
8
+ () =>
9
+ StyleSheet.create({
10
+ horizontalRule: {
11
+ height: 1,
12
+ backgroundColor: theme.colors.border,
13
+ marginVertical: theme.spacing.xl,
14
+ },
15
+ }),
16
+ [theme]
17
+ );
18
+ return <View style={styles.horizontalRule} />;
19
+ };
20
+