react-native-nitro-markdown 0.1.1 → 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 -15
  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,160 @@
1
+ import { useState, useMemo, type ReactNode, type FC, type ComponentType } from "react";
2
+ import { View, Text, Image as RNImage, StyleSheet } from "react-native";
3
+
4
+ import { parseMarkdownWithOptions, type MarkdownNode } from "../headless";
5
+ import type { NodeRendererProps } from "../MarkdownContext";
6
+ import { useMarkdownContext } from "../MarkdownContext";
7
+
8
+ const renderInlineContent = (
9
+ node: MarkdownNode,
10
+ Renderer: ComponentType<NodeRendererProps>
11
+ ): ReactNode => {
12
+ if (node.type === "paragraph" && node.children) {
13
+ return (
14
+ <>
15
+ {node.children.map((child, idx) => (
16
+ <Renderer key={idx} node={child} depth={0} inListItem={false} />
17
+ ))}
18
+ </>
19
+ );
20
+ }
21
+ return null;
22
+ };
23
+
24
+ interface ImageProps {
25
+ url: string;
26
+ title?: string;
27
+ alt?: string;
28
+
29
+ Renderer?: ComponentType<NodeRendererProps>;
30
+ }
31
+
32
+ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
33
+ const [loading, setLoading] = useState(true);
34
+ const [error, setError] = useState(false);
35
+ const { theme } = useMarkdownContext();
36
+
37
+ const styles = useMemo(
38
+ () =>
39
+ StyleSheet.create({
40
+ imageContainer: {
41
+ marginVertical: theme.spacing.m,
42
+ alignItems: "center",
43
+ },
44
+ image: {
45
+ width: "100%",
46
+ height: 200,
47
+ borderRadius: 8,
48
+ backgroundColor: theme.colors.surface,
49
+ },
50
+ imageHidden: {
51
+ opacity: 0,
52
+ position: "absolute",
53
+ },
54
+ imageLoading: {
55
+ width: "100%",
56
+ height: 200,
57
+ borderRadius: 8,
58
+ backgroundColor: theme.colors.surface,
59
+ justifyContent: "center",
60
+ alignItems: "center",
61
+ },
62
+ imageLoadingText: {
63
+ color: theme.colors.textMuted,
64
+ fontSize: theme.fontSizes.s,
65
+ },
66
+ imageError: {
67
+ width: "100%",
68
+ padding: theme.spacing.l,
69
+ borderRadius: 8,
70
+ backgroundColor: theme.colors.surface,
71
+ alignItems: "center",
72
+ marginVertical: theme.spacing.m,
73
+ },
74
+ imageErrorText: {
75
+ color: theme.colors.textMuted,
76
+ fontSize: theme.fontSizes.s,
77
+ },
78
+ imageCaption: {
79
+ color: theme.colors.textMuted,
80
+ fontSize: theme.fontSizes.xs,
81
+ marginTop: theme.spacing.s,
82
+ fontStyle: "italic",
83
+ textAlign: "center",
84
+ },
85
+ }),
86
+ [theme]
87
+ );
88
+
89
+ const altContent = useMemo(() => {
90
+ if (!alt || !Renderer) return null;
91
+ if (
92
+ alt.includes("$") ||
93
+ alt.includes("*") ||
94
+ alt.includes("_") ||
95
+ alt.includes("`") ||
96
+ alt.includes("[")
97
+ ) {
98
+ try {
99
+ const ast = parseMarkdownWithOptions(alt, { math: true, gfm: true });
100
+ if (
101
+ ast?.type === "document" &&
102
+ ast.children?.[0]?.type === "paragraph"
103
+ ) {
104
+ const paragraph = ast.children[0];
105
+ const inlineContent = renderInlineContent(paragraph, Renderer);
106
+ if (inlineContent) {
107
+ return inlineContent;
108
+ }
109
+ }
110
+ return <Text style={styles.imageErrorText}>{alt}</Text>;
111
+ } catch {
112
+ return <Text style={styles.imageErrorText}>{alt}</Text>;
113
+ }
114
+ }
115
+ return <Text style={styles.imageErrorText}>{alt}</Text>;
116
+ }, [alt, Renderer, styles.imageErrorText]);
117
+
118
+ if (error) {
119
+ return (
120
+ <View style={styles.imageError}>
121
+ <View
122
+ style={{
123
+ flexDirection: "row",
124
+ alignItems: "baseline",
125
+ flexWrap: "wrap",
126
+ justifyContent: "center",
127
+ }}
128
+ >
129
+ <Text style={styles.imageErrorText}>🖼️ </Text>
130
+ {altContent || (
131
+ <Text style={styles.imageErrorText}>
132
+ {title || "Image failed to load"}
133
+ </Text>
134
+ )}
135
+ </View>
136
+ </View>
137
+ );
138
+ }
139
+
140
+ return (
141
+ <View style={styles.imageContainer}>
142
+ {loading && (
143
+ <View style={styles.imageLoading}>
144
+ <Text style={styles.imageLoadingText}>Loading image...</Text>
145
+ </View>
146
+ )}
147
+ <RNImage
148
+ source={{ uri: url }}
149
+ style={[styles.image, loading && styles.imageHidden]}
150
+ resizeMode="contain"
151
+ onLoad={() => setLoading(false)}
152
+ onError={() => {
153
+ setLoading(false);
154
+ setError(true);
155
+ }}
156
+ />
157
+ {title && !loading && <Text style={styles.imageCaption}>{title}</Text>}
158
+ </View>
159
+ );
160
+ };
@@ -0,0 +1,38 @@
1
+ import { ReactNode, useMemo, type FC } from "react";
2
+ import { Text, StyleSheet, Linking, type TextStyle } from "react-native";
3
+ import { useMarkdownContext } from "../MarkdownContext";
4
+
5
+ interface LinkProps {
6
+ href: string;
7
+ children: ReactNode;
8
+ style?: TextStyle;
9
+ }
10
+
11
+ export const Link: FC<LinkProps> = ({ href, children, style }) => {
12
+ const { theme } = useMarkdownContext();
13
+ const styles = useMemo(
14
+ () =>
15
+ StyleSheet.create({
16
+ link: {
17
+ color: theme.colors.link,
18
+ textDecorationLine: "underline",
19
+ },
20
+ }),
21
+ [theme]
22
+ );
23
+
24
+ const handlePress = () => {
25
+ if (href) {
26
+ Linking.openURL(href).catch((err) =>
27
+ console.error("Failed to open URL:", err)
28
+ );
29
+ }
30
+ };
31
+
32
+ return (
33
+ <Text style={[styles.link, style]} onPress={handlePress}>
34
+ {children}
35
+ </Text>
36
+ );
37
+ };
38
+
@@ -0,0 +1,125 @@
1
+ import { ReactNode, useMemo, type FC } from "react";
2
+ import { View, Text, StyleSheet } from "react-native";
3
+ import { useMarkdownContext } from "../MarkdownContext";
4
+
5
+ interface ListProps {
6
+ ordered: boolean;
7
+ start?: number;
8
+ depth: number;
9
+ children: ReactNode;
10
+ }
11
+
12
+ export const List: FC<ListProps> = ({
13
+ ordered,
14
+ start = 1,
15
+ depth,
16
+ children,
17
+ }) => {
18
+ const { theme } = useMarkdownContext();
19
+ const styles = useMemo(
20
+ () =>
21
+ StyleSheet.create({
22
+ list: {
23
+ marginBottom: theme.spacing.m,
24
+ },
25
+ listNested: {
26
+ marginLeft: theme.spacing.l,
27
+ marginBottom: 0,
28
+ },
29
+ }),
30
+ [theme]
31
+ );
32
+ return (
33
+ <View style={[styles.list, depth > 0 && styles.listNested]}>
34
+ {children}
35
+ </View>
36
+ );
37
+ };
38
+
39
+ interface ListItemProps {
40
+ children: ReactNode;
41
+ index: number;
42
+ ordered: boolean;
43
+ start: number;
44
+ }
45
+
46
+ export const ListItem: FC<ListItemProps> = ({
47
+ children,
48
+ index,
49
+ ordered,
50
+ start,
51
+ }) => {
52
+ const { theme } = useMarkdownContext();
53
+ const styles = useMemo(
54
+ () =>
55
+ StyleSheet.create({
56
+ listItem: {
57
+ flexDirection: "row",
58
+ marginBottom: theme.spacing.s,
59
+ alignItems: "flex-start",
60
+ },
61
+ listBullet: {
62
+ color: theme.colors.accent,
63
+ fontSize: theme.fontSizes.m,
64
+ lineHeight: theme.fontSizes.m * 1.6,
65
+ marginRight: theme.spacing.s,
66
+ minWidth: 20,
67
+ textAlign: "center",
68
+ },
69
+ listItemContent: {
70
+ flex: 1,
71
+ flexShrink: 1,
72
+ minWidth: 0,
73
+ },
74
+ }),
75
+ [theme]
76
+ );
77
+ const bullet = ordered ? `${start + index}.` : "•";
78
+ return (
79
+ <View style={styles.listItem}>
80
+ <Text style={styles.listBullet}>{bullet}</Text>
81
+ <View style={styles.listItemContent}>{children}</View>
82
+ </View>
83
+ );
84
+ };
85
+
86
+ interface TaskListItemProps {
87
+ children: ReactNode;
88
+ checked: boolean;
89
+ }
90
+
91
+ export const TaskListItem: FC<TaskListItemProps> = ({
92
+ children,
93
+ checked,
94
+ }) => {
95
+ const { theme } = useMarkdownContext();
96
+ const styles = useMemo(
97
+ () =>
98
+ StyleSheet.create({
99
+ taskListItem: {
100
+ flexDirection: "row",
101
+ alignItems: "flex-start",
102
+ marginBottom: theme.spacing.s,
103
+ },
104
+ taskCheckbox: {
105
+ fontSize: theme.fontSizes.l,
106
+ lineHeight: theme.fontSizes.m * 1.6,
107
+ marginRight: theme.spacing.s,
108
+ color: theme.colors.accent,
109
+ },
110
+ taskContent: {
111
+ flex: 1,
112
+ flexShrink: 1,
113
+ minWidth: 0,
114
+ },
115
+ }),
116
+ [theme]
117
+ );
118
+ return (
119
+ <View style={styles.taskListItem}>
120
+ <Text style={styles.taskCheckbox}>{checked ? "☑" : "☐"}</Text>
121
+ <View style={styles.taskContent}>{children}</View>
122
+ </View>
123
+ );
124
+ };
125
+
@@ -0,0 +1,164 @@
1
+ import { useMemo, type FC, type ComponentType } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ type StyleProp,
7
+ type ViewStyle,
8
+ } from "react-native";
9
+ import { useMarkdownContext } from "../MarkdownContext";
10
+ import type { MarkdownTheme } from "../theme";
11
+
12
+ let MathJaxComponent: ComponentType<{
13
+ fontSize?: number;
14
+ color?: string;
15
+ fontCache?: boolean;
16
+ style?: StyleProp<ViewStyle>;
17
+ children?: string;
18
+ }> | null = null;
19
+
20
+ try {
21
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
22
+ const mathJaxModule = require("react-native-mathjax-svg");
23
+ MathJaxComponent = mathJaxModule.default || mathJaxModule;
24
+ } catch {
25
+ // ignored
26
+ }
27
+
28
+ interface MathInlineProps {
29
+ content?: string;
30
+ }
31
+
32
+ /**
33
+ * Inline math renderer.
34
+ * Uses react-native-mathjax-svg if installed, otherwise shows raw LaTeX.
35
+ * Note: Renders as a View since SVG can't be nested in Text.
36
+ */
37
+
38
+ const createMathStyles = (theme: MarkdownTheme) =>
39
+ StyleSheet.create({
40
+ mathInlineContainer: {
41
+ flexDirection: "row",
42
+ alignItems: "baseline",
43
+ alignSelf: "baseline",
44
+ justifyContent: "flex-start",
45
+ flexShrink: 1,
46
+ marginHorizontal: 2,
47
+ top: -theme.fontSizes.m,
48
+ marginBottom: 0,
49
+ paddingVertical: 0,
50
+ },
51
+ mathJaxInline: {
52
+ marginTop: 0,
53
+ marginBottom: 0,
54
+ },
55
+ mathInlineFallbackContainer: {
56
+ backgroundColor: theme.colors.codeBackground,
57
+ paddingHorizontal: 4,
58
+ paddingVertical: 2,
59
+ borderRadius: 4,
60
+ alignSelf: "baseline",
61
+ marginHorizontal: 2,
62
+ },
63
+ mathInlineFallback: {
64
+ fontFamily: "monospace",
65
+ fontSize: theme.fontSizes.s,
66
+ color: theme.colors.code,
67
+ },
68
+ mathBlockContainer: {
69
+ marginVertical: theme.spacing.m,
70
+ paddingVertical: theme.spacing.l,
71
+ paddingHorizontal: theme.spacing.l,
72
+ backgroundColor: theme.colors.surface,
73
+ borderRadius: 12,
74
+ alignItems: "center",
75
+ borderWidth: 1,
76
+ borderColor: theme.colors.border,
77
+ shadowColor: "#000",
78
+ shadowOffset: { width: 0, height: 1 },
79
+ shadowOpacity: 0.05,
80
+ shadowRadius: 2,
81
+ elevation: 1,
82
+ },
83
+ mathBlockFallbackContainer: {
84
+ marginVertical: theme.spacing.m,
85
+ paddingVertical: theme.spacing.m,
86
+ paddingHorizontal: theme.spacing.l,
87
+ backgroundColor: theme.colors.codeBackground,
88
+ borderRadius: 8,
89
+ alignItems: "center",
90
+ borderWidth: 1,
91
+ borderColor: theme.colors.border,
92
+ },
93
+ mathBlockFallback: {
94
+ fontFamily: "monospace",
95
+ fontSize: theme.fontSizes.m,
96
+ color: theme.colors.code,
97
+ textAlign: "center",
98
+ },
99
+ });
100
+
101
+ export const MathInline: FC<MathInlineProps> = ({ content }) => {
102
+ const { theme } = useMarkdownContext();
103
+ const styles = useMemo(() => createMathStyles(theme), [theme]);
104
+
105
+ if (!content) return null;
106
+
107
+ if (MathJaxComponent) {
108
+ const fontSize = theme.fontSizes.m;
109
+ return (
110
+ <View style={styles.mathInlineContainer}>
111
+ <MathJaxComponent
112
+ fontSize={fontSize}
113
+ color={theme.colors.text}
114
+ fontCache={true}
115
+ style={styles.mathJaxInline}
116
+ >
117
+ {content}
118
+ </MathJaxComponent>
119
+ </View>
120
+ );
121
+ }
122
+
123
+ return (
124
+ <View style={styles.mathInlineFallbackContainer}>
125
+ <Text style={styles.mathInlineFallback}>{content}</Text>
126
+ </View>
127
+ );
128
+ };
129
+
130
+ interface MathBlockProps {
131
+ content?: string;
132
+ }
133
+
134
+ /**
135
+ * Block math renderer.
136
+ * Uses react-native-mathjax-svg if installed, otherwise shows raw LaTeX.
137
+ */
138
+ export const MathBlock: FC<MathBlockProps> = ({ content }) => {
139
+ const { theme } = useMarkdownContext();
140
+ const styles = useMemo(() => createMathStyles(theme), [theme]);
141
+
142
+ if (!content) return null;
143
+
144
+ if (MathJaxComponent) {
145
+ return (
146
+ <View style={styles.mathBlockContainer}>
147
+ <MathJaxComponent
148
+ fontSize={theme.fontSizes.l}
149
+ color={theme.colors.text}
150
+ fontCache={true}
151
+ >
152
+ {`\\displaystyle ${content}`}
153
+ </MathJaxComponent>
154
+ </View>
155
+ );
156
+ }
157
+
158
+ return (
159
+ <View style={styles.mathBlockFallbackContainer}>
160
+ <Text style={styles.mathBlockFallback}>{content}</Text>
161
+ </View>
162
+ );
163
+ };
164
+
@@ -0,0 +1,53 @@
1
+ import { ReactNode, useMemo, type FC } from "react";
2
+ import { View, StyleSheet, type StyleProp, type ViewStyle } from "react-native";
3
+ import { useMarkdownContext } from "../MarkdownContext";
4
+
5
+ interface ParagraphProps {
6
+ children: ReactNode;
7
+ inListItem?: boolean;
8
+ style?: StyleProp<ViewStyle>;
9
+ }
10
+
11
+ /**
12
+ * Paragraph component that supports mixed content (Text and View elements).
13
+ * Uses View with flexDirection: 'row' and flexWrap: 'wrap' to allow inline flow
14
+ * of both text and non-text elements (like inline math).
15
+ */
16
+ export const Paragraph: FC<ParagraphProps> = ({
17
+ children,
18
+ inListItem,
19
+ style,
20
+ }) => {
21
+ const { theme } = useMarkdownContext();
22
+ const styles = useMemo(
23
+ () =>
24
+ StyleSheet.create({
25
+ paragraph: {
26
+ flexDirection: "row",
27
+ flexWrap: "wrap",
28
+ alignItems: "baseline",
29
+ marginBottom: theme.spacing.l,
30
+ gap: 0,
31
+ },
32
+ paragraphInListItem: {
33
+ marginBottom: 0,
34
+ marginTop: 0,
35
+ flexShrink: 1,
36
+ },
37
+ }),
38
+ [theme]
39
+ );
40
+
41
+ return (
42
+ <View
43
+ style={[
44
+ styles.paragraph,
45
+ inListItem && styles.paragraphInListItem,
46
+ style,
47
+ ]}
48
+ >
49
+ {children}
50
+ </View>
51
+ );
52
+ };
53
+