react-native-nitro-markdown 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +293 -119
  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 +17 -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 +217 -0
  16. package/lib/commonjs/default-markdown-renderer.js.map +1 -0
  17. package/lib/commonjs/headless.js +61 -0
  18. package/lib/commonjs/headless.js.map +1 -0
  19. package/lib/commonjs/index.js +220 -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 +377 -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 +99 -0
  29. package/lib/commonjs/renderers/code.js.map +1 -0
  30. package/lib/commonjs/renderers/heading.js +63 -0
  31. package/lib/commonjs/renderers/heading.js.map +1 -0
  32. package/lib/commonjs/renderers/horizontal-rule.js +29 -0
  33. package/lib/commonjs/renderers/horizontal-rule.js.map +1 -0
  34. package/lib/commonjs/renderers/image.js +163 -0
  35. package/lib/commonjs/renderers/image.js.map +1 -0
  36. package/lib/commonjs/renderers/link.js +35 -0
  37. package/lib/commonjs/renderers/link.js.map +1 -0
  38. package/lib/commonjs/renderers/list.js +118 -0
  39. package/lib/commonjs/renderers/list.js.map +1 -0
  40. package/lib/commonjs/renderers/math.js +127 -0
  41. package/lib/commonjs/renderers/math.js.map +1 -0
  42. package/lib/commonjs/renderers/paragraph.js +39 -0
  43. package/lib/commonjs/renderers/paragraph.js.map +1 -0
  44. package/lib/commonjs/renderers/table.js +290 -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 +191 -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 +12 -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 +212 -0
  57. package/lib/module/default-markdown-renderer.js.map +1 -0
  58. package/lib/module/headless.js +54 -0
  59. package/lib/module/headless.js.map +1 -0
  60. package/lib/module/index.js +18 -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 +372 -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 +93 -0
  70. package/lib/module/renderers/code.js.map +1 -0
  71. package/lib/module/renderers/heading.js +58 -0
  72. package/lib/module/renderers/heading.js.map +1 -0
  73. package/lib/module/renderers/horizontal-rule.js +24 -0
  74. package/lib/module/renderers/horizontal-rule.js.map +1 -0
  75. package/lib/module/renderers/image.js +158 -0
  76. package/lib/module/renderers/image.js.map +1 -0
  77. package/lib/module/renderers/link.js +30 -0
  78. package/lib/module/renderers/link.js.map +1 -0
  79. package/lib/module/renderers/list.js +111 -0
  80. package/lib/module/renderers/list.js.map +1 -0
  81. package/lib/module/renderers/math.js +121 -0
  82. package/lib/module/renderers/math.js.map +1 -0
  83. package/lib/module/renderers/paragraph.js +34 -0
  84. package/lib/module/renderers/paragraph.js.map +1 -0
  85. package/lib/module/renderers/table.js +285 -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 +186 -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 +65 -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 +57 -0
  101. package/lib/typescript/commonjs/headless.d.ts.map +1 -0
  102. package/lib/typescript/commonjs/index.d.ts +22 -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 +47 -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 +19 -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 +8 -0
  116. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/renderers/image.d.ts +13 -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 +26 -0
  122. package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -0
  123. package/lib/typescript/commonjs/renderers/math.d.ts +14 -0
  124. package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/renderers/paragraph.d.ts +10 -0
  126. package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -0
  127. package/lib/typescript/commonjs/renderers/table.d.ts +12 -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 +65 -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 +65 -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 +57 -0
  144. package/lib/typescript/module/headless.d.ts.map +1 -0
  145. package/lib/typescript/module/index.d.ts +22 -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 +47 -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 +19 -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 +8 -0
  159. package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -0
  160. package/lib/typescript/module/renderers/image.d.ts +13 -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 +26 -0
  165. package/lib/typescript/module/renderers/list.d.ts.map +1 -0
  166. package/lib/typescript/module/renderers/math.d.ts +14 -0
  167. package/lib/typescript/module/renderers/math.d.ts.map +1 -0
  168. package/lib/typescript/module/renderers/paragraph.d.ts +10 -0
  169. package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -0
  170. package/lib/typescript/module/renderers/table.d.ts +12 -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 +65 -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 +98 -0
  208. package/src/MarkdownSession.ts +8 -0
  209. package/src/default-markdown-renderer.tsx +261 -0
  210. package/src/headless.ts +116 -0
  211. package/src/index.ts +47 -60
  212. package/src/markdown-stream.tsx +32 -0
  213. package/src/markdown.tsx +497 -0
  214. package/src/renderers/blockquote.tsx +30 -0
  215. package/src/renderers/code.tsx +112 -0
  216. package/src/renderers/heading.tsx +66 -0
  217. package/src/renderers/horizontal-rule.tsx +23 -0
  218. package/src/renderers/image.tsx +175 -0
  219. package/src/renderers/link.tsx +33 -0
  220. package/src/renderers/list.tsx +131 -0
  221. package/src/renderers/math.tsx +141 -0
  222. package/src/renderers/paragraph.tsx +47 -0
  223. package/src/renderers/table.tsx +370 -0
  224. package/src/specs/MarkdownSession.nitro.ts +16 -0
  225. package/src/theme.ts +243 -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,112 @@
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
+ import type { MarkdownNode } from "../headless";
13
+ import { getTextContent } from "../headless";
14
+
15
+ interface CodeBlockProps {
16
+ language?: string;
17
+ content?: string;
18
+ node?: MarkdownNode;
19
+ style?: ViewStyle;
20
+ }
21
+
22
+ export const CodeBlock: FC<CodeBlockProps> = ({
23
+ language,
24
+ content,
25
+ node,
26
+ style,
27
+ }) => {
28
+ const { theme } = useMarkdownContext();
29
+
30
+ const displayContent = content ?? (node ? getTextContent(node) : "");
31
+
32
+ const styles = useMemo(
33
+ () =>
34
+ StyleSheet.create({
35
+ codeBlock: {
36
+ backgroundColor: theme.colors.codeBackground,
37
+ borderRadius: theme.borderRadius.m,
38
+ padding: theme.spacing.l,
39
+ marginVertical: theme.spacing.m,
40
+ borderWidth: 1,
41
+ borderColor: theme.colors.border,
42
+ },
43
+ codeLanguage: {
44
+ color: theme.colors.codeLanguage,
45
+ fontSize: theme.fontSizes.xs,
46
+ fontWeight: "600",
47
+ marginBottom: theme.spacing.s,
48
+ textTransform: "uppercase",
49
+ letterSpacing: 0.5,
50
+ },
51
+ codeBlockText: {
52
+ fontFamily:
53
+ theme.fontFamilies.mono ??
54
+ Platform.select({ ios: "Courier", android: "monospace" }),
55
+ fontSize: theme.fontSizes.s,
56
+ color: theme.colors.text,
57
+ lineHeight: theme.fontSizes.s * 1.5,
58
+ },
59
+ }),
60
+ [theme]
61
+ );
62
+
63
+ const showLanguage = theme.showCodeLanguage && language;
64
+
65
+ return (
66
+ <View style={[styles.codeBlock, style]}>
67
+ {showLanguage && <Text style={styles.codeLanguage}>{language}</Text>}
68
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
69
+ <Text style={styles.codeBlockText}>{displayContent}</Text>
70
+ </ScrollView>
71
+ </View>
72
+ );
73
+ };
74
+
75
+ interface InlineCodeProps {
76
+ content?: string;
77
+ node?: MarkdownNode;
78
+ children?: ReactNode;
79
+ style?: TextStyle;
80
+ }
81
+
82
+ export const InlineCode: FC<InlineCodeProps> = ({
83
+ content,
84
+ node,
85
+ children,
86
+ style,
87
+ }) => {
88
+ const { theme } = useMarkdownContext();
89
+
90
+ const displayContent =
91
+ content ?? children ?? (node ? getTextContent(node) : "");
92
+
93
+ const styles = useMemo(
94
+ () =>
95
+ StyleSheet.create({
96
+ codeInline: {
97
+ fontFamily:
98
+ theme.fontFamilies.mono ??
99
+ Platform.select({ ios: "Courier", android: "monospace" }),
100
+ fontSize: theme.fontSizes.s,
101
+ color: theme.colors.code,
102
+ backgroundColor: theme.colors.codeBackground,
103
+ paddingHorizontal: theme.spacing.xs,
104
+ paddingVertical: 2,
105
+ borderRadius: theme.borderRadius.s,
106
+ ...(Platform.OS === "android" && { includeFontPadding: false }),
107
+ },
108
+ }),
109
+ [theme]
110
+ );
111
+ return <Text style={[styles.codeInline, style]}>{displayContent}</Text>;
112
+ };
@@ -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
+ fontFamily: theme.fontFamilies.heading,
22
+ },
23
+ h1: {
24
+ fontSize: theme.fontSizes.h1,
25
+ lineHeight: theme.fontSizes.h1 * 1.3,
26
+ borderBottomWidth: 1,
27
+ borderBottomColor: theme.colors.border,
28
+ paddingBottom: theme.spacing.s,
29
+ },
30
+ h2: {
31
+ fontSize: theme.fontSizes.h2,
32
+ lineHeight: theme.fontSizes.h2 * 1.3,
33
+ },
34
+ h3: {
35
+ fontSize: theme.fontSizes.h3,
36
+ lineHeight: theme.fontSizes.h3 * 1.3,
37
+ },
38
+ h4: {
39
+ fontSize: theme.fontSizes.h4,
40
+ lineHeight: theme.fontSizes.h4 * 1.3,
41
+ },
42
+ h5: {
43
+ fontSize: theme.fontSizes.h5,
44
+ lineHeight: theme.fontSizes.h5 * 1.3,
45
+ },
46
+ h6: {
47
+ fontSize: theme.fontSizes.h6,
48
+ lineHeight: theme.fontSizes.h6 * 1.3,
49
+ color: theme.colors.textMuted,
50
+ },
51
+ }),
52
+ [theme]
53
+ );
54
+
55
+ const headingStyles = [
56
+ styles.heading,
57
+ level === 1 && styles.h1,
58
+ level === 2 && styles.h2,
59
+ level === 3 && styles.h3,
60
+ level === 4 && styles.h4,
61
+ level === 5 && styles.h5,
62
+ level === 6 && styles.h6,
63
+ ];
64
+
65
+ return <Text style={[...headingStyles, style]}>{children}</Text>;
66
+ };
@@ -0,0 +1,23 @@
1
+ import { useMemo, type FC } from "react";
2
+ import { View, StyleSheet, type ViewStyle } from "react-native";
3
+ import { useMarkdownContext } from "../MarkdownContext";
4
+
5
+ interface HorizontalRuleProps {
6
+ style?: ViewStyle;
7
+ }
8
+
9
+ export const HorizontalRule: FC<HorizontalRuleProps> = ({ style }) => {
10
+ const { theme } = useMarkdownContext();
11
+ const styles = useMemo(
12
+ () =>
13
+ StyleSheet.create({
14
+ horizontalRule: {
15
+ height: 1,
16
+ backgroundColor: theme.colors.border,
17
+ marginVertical: theme.spacing.xl,
18
+ },
19
+ }),
20
+ [theme]
21
+ );
22
+ return <View style={[styles.horizontalRule, style]} />;
23
+ };
@@ -0,0 +1,175 @@
1
+ import {
2
+ useState,
3
+ useMemo,
4
+ type ReactNode,
5
+ type FC,
6
+ type ComponentType,
7
+ } from "react";
8
+ import {
9
+ View,
10
+ Text,
11
+ Image as RNImage,
12
+ StyleSheet,
13
+ type ViewStyle,
14
+ } from "react-native";
15
+
16
+ import { parseMarkdownWithOptions, type MarkdownNode } from "../headless";
17
+ import type { NodeRendererProps } from "../MarkdownContext";
18
+ import { useMarkdownContext } from "../MarkdownContext";
19
+
20
+ const renderInlineContent = (
21
+ node: MarkdownNode,
22
+ Renderer: ComponentType<NodeRendererProps>
23
+ ): ReactNode => {
24
+ if (node.type === "paragraph" && node.children) {
25
+ return (
26
+ <>
27
+ {node.children.map((child, idx) => (
28
+ <Renderer key={idx} node={child} depth={0} inListItem={false} />
29
+ ))}
30
+ </>
31
+ );
32
+ }
33
+ return null;
34
+ };
35
+
36
+ interface ImageProps {
37
+ url: string;
38
+ title?: string;
39
+ alt?: string;
40
+ Renderer?: ComponentType<NodeRendererProps>;
41
+ style?: ViewStyle;
42
+ }
43
+
44
+ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer, style }) => {
45
+ const [loading, setLoading] = useState(true);
46
+ const [error, setError] = useState(false);
47
+ const { theme } = useMarkdownContext();
48
+
49
+ const styles = useMemo(
50
+ () =>
51
+ StyleSheet.create({
52
+ imageContainer: {
53
+ marginVertical: theme.spacing.m,
54
+ alignItems: "center",
55
+ },
56
+ image: {
57
+ width: "100%",
58
+ height: 200,
59
+ borderRadius: theme.borderRadius.m,
60
+ backgroundColor: theme.colors.surface,
61
+ },
62
+ imageHidden: {
63
+ opacity: 0,
64
+ position: "absolute",
65
+ },
66
+ imageLoading: {
67
+ width: "100%",
68
+ height: 200,
69
+ borderRadius: theme.borderRadius.m,
70
+ backgroundColor: theme.colors.surface,
71
+ justifyContent: "center",
72
+ alignItems: "center",
73
+ },
74
+ imageLoadingText: {
75
+ color: theme.colors.textMuted,
76
+ fontSize: theme.fontSizes.s,
77
+ fontFamily: theme.fontFamilies.regular,
78
+ },
79
+ imageError: {
80
+ width: "100%",
81
+ padding: theme.spacing.l,
82
+ borderRadius: theme.borderRadius.m,
83
+ backgroundColor: theme.colors.surface,
84
+ alignItems: "center",
85
+ marginVertical: theme.spacing.m,
86
+ },
87
+ imageErrorText: {
88
+ color: theme.colors.textMuted,
89
+ fontSize: theme.fontSizes.s,
90
+ fontFamily: theme.fontFamilies.regular,
91
+ },
92
+ imageCaption: {
93
+ color: theme.colors.textMuted,
94
+ fontSize: theme.fontSizes.xs,
95
+ marginTop: theme.spacing.s,
96
+ fontStyle: "italic",
97
+ textAlign: "center",
98
+ fontFamily: theme.fontFamilies.regular,
99
+ },
100
+ }),
101
+ [theme]
102
+ );
103
+
104
+ const altContent = useMemo(() => {
105
+ if (!alt || !Renderer) return null;
106
+ if (
107
+ alt.includes("$") ||
108
+ alt.includes("*") ||
109
+ alt.includes("_") ||
110
+ alt.includes("`") ||
111
+ alt.includes("[")
112
+ ) {
113
+ try {
114
+ const ast = parseMarkdownWithOptions(alt, { math: true, gfm: true });
115
+ if (
116
+ ast?.type === "document" &&
117
+ ast.children?.[0]?.type === "paragraph"
118
+ ) {
119
+ const paragraph = ast.children[0];
120
+ const inlineContent = renderInlineContent(paragraph, Renderer);
121
+ if (inlineContent) {
122
+ return inlineContent;
123
+ }
124
+ }
125
+ return <Text style={styles.imageErrorText}>{alt}</Text>;
126
+ } catch {
127
+ return <Text style={styles.imageErrorText}>{alt}</Text>;
128
+ }
129
+ }
130
+ return <Text style={styles.imageErrorText}>{alt}</Text>;
131
+ }, [alt, Renderer, styles.imageErrorText]);
132
+
133
+ if (error) {
134
+ return (
135
+ <View style={[styles.imageError, style]}>
136
+ <View
137
+ style={{
138
+ flexDirection: "row",
139
+ alignItems: "baseline",
140
+ flexWrap: "wrap",
141
+ justifyContent: "center",
142
+ }}
143
+ >
144
+ <Text style={styles.imageErrorText}>🖼️ </Text>
145
+ {altContent || (
146
+ <Text style={styles.imageErrorText}>
147
+ {title || "Image failed to load"}
148
+ </Text>
149
+ )}
150
+ </View>
151
+ </View>
152
+ );
153
+ }
154
+
155
+ return (
156
+ <View style={[styles.imageContainer, style]}>
157
+ {loading && (
158
+ <View style={styles.imageLoading}>
159
+ <Text style={styles.imageLoadingText}>Loading image...</Text>
160
+ </View>
161
+ )}
162
+ <RNImage
163
+ source={{ uri: url }}
164
+ style={[styles.image, loading && styles.imageHidden]}
165
+ resizeMode="contain"
166
+ onLoad={() => setLoading(false)}
167
+ onError={() => {
168
+ setLoading(false);
169
+ setError(true);
170
+ }}
171
+ />
172
+ {title && !loading && <Text style={styles.imageCaption}>{title}</Text>}
173
+ </View>
174
+ );
175
+ };
@@ -0,0 +1,33 @@
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) Linking.openURL(href);
26
+ };
27
+
28
+ return (
29
+ <Text style={[styles.link, style]} onPress={handlePress}>
30
+ {children}
31
+ </Text>
32
+ );
33
+ };
@@ -0,0 +1,131 @@
1
+ import { ReactNode, useMemo, type FC } from "react";
2
+ import { View, Text, StyleSheet, type ViewStyle } 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
+ style?: ViewStyle;
11
+ }
12
+
13
+ export const List: FC<ListProps> = ({
14
+ ordered,
15
+ start = 1,
16
+ depth,
17
+ children,
18
+ style,
19
+ }) => {
20
+ const { theme } = useMarkdownContext();
21
+ const styles = useMemo(
22
+ () =>
23
+ StyleSheet.create({
24
+ list: {
25
+ marginBottom: theme.spacing.m,
26
+ },
27
+ listNested: {
28
+ marginLeft: theme.spacing.l,
29
+ marginBottom: 0,
30
+ },
31
+ }),
32
+ [theme]
33
+ );
34
+ return (
35
+ <View style={[styles.list, depth > 0 && styles.listNested, style]}>
36
+ {children}
37
+ </View>
38
+ );
39
+ };
40
+
41
+ interface ListItemProps {
42
+ children: ReactNode;
43
+ index: number;
44
+ ordered: boolean;
45
+ start: number;
46
+ style?: ViewStyle;
47
+ }
48
+
49
+ export const ListItem: FC<ListItemProps> = ({
50
+ children,
51
+ index,
52
+ ordered,
53
+ start,
54
+ style,
55
+ }) => {
56
+ const { theme } = useMarkdownContext();
57
+ const styles = useMemo(
58
+ () =>
59
+ StyleSheet.create({
60
+ listItem: {
61
+ flexDirection: "row",
62
+ marginBottom: theme.spacing.s,
63
+ alignItems: "flex-start",
64
+ },
65
+ listBullet: {
66
+ color: theme.colors.accent,
67
+ fontSize: theme.fontSizes.m,
68
+ lineHeight: theme.fontSizes.m * 1.6,
69
+ marginRight: theme.spacing.s,
70
+ minWidth: 20,
71
+ textAlign: "center",
72
+ fontFamily: theme.fontFamilies.regular,
73
+ },
74
+ listItemContent: {
75
+ flex: 1,
76
+ flexShrink: 1,
77
+ minWidth: 0,
78
+ },
79
+ }),
80
+ [theme]
81
+ );
82
+ const bullet = ordered ? `${start + index}.` : "•";
83
+ return (
84
+ <View style={[styles.listItem, style]}>
85
+ <Text style={styles.listBullet}>{bullet}</Text>
86
+ <View style={styles.listItemContent}>{children}</View>
87
+ </View>
88
+ );
89
+ };
90
+
91
+ interface TaskListItemProps {
92
+ children: ReactNode;
93
+ checked: boolean;
94
+ style?: ViewStyle;
95
+ }
96
+
97
+ export const TaskListItem: FC<TaskListItemProps> = ({
98
+ children,
99
+ checked,
100
+ style,
101
+ }) => {
102
+ const { theme } = useMarkdownContext();
103
+ const styles = useMemo(
104
+ () =>
105
+ StyleSheet.create({
106
+ taskListItem: {
107
+ flexDirection: "row",
108
+ alignItems: "flex-start",
109
+ marginBottom: theme.spacing.s,
110
+ },
111
+ taskCheckbox: {
112
+ fontSize: theme.fontSizes.l,
113
+ lineHeight: theme.fontSizes.m * 1.6,
114
+ marginRight: theme.spacing.s,
115
+ color: theme.colors.accent,
116
+ },
117
+ taskContent: {
118
+ flex: 1,
119
+ flexShrink: 1,
120
+ minWidth: 0,
121
+ },
122
+ }),
123
+ [theme]
124
+ );
125
+ return (
126
+ <View style={[styles.taskListItem, style]}>
127
+ <Text style={styles.taskCheckbox}>{checked ? "☑" : "☐"}</Text>
128
+ <View style={styles.taskContent}>{children}</View>
129
+ </View>
130
+ );
131
+ };
@@ -0,0 +1,141 @@
1
+ import { useMemo, type FC, type ComponentType } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ Platform,
7
+ type StyleProp,
8
+ type ViewStyle,
9
+ } from "react-native";
10
+ import { useMarkdownContext } from "../MarkdownContext";
11
+ import type { MarkdownTheme } from "../theme";
12
+
13
+ let MathJaxComponent: ComponentType<{
14
+ fontSize?: number;
15
+ color?: string;
16
+ fontCache?: boolean;
17
+ style?: StyleProp<ViewStyle>;
18
+ children?: string;
19
+ }> | null = null;
20
+
21
+ try {
22
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
23
+ const mathJaxModule = require("react-native-mathjax-svg");
24
+ MathJaxComponent = mathJaxModule.default || mathJaxModule;
25
+ } catch {
26
+ // ignored
27
+ }
28
+
29
+ interface MathInlineProps {
30
+ content?: string;
31
+ style?: ViewStyle;
32
+ }
33
+
34
+ const createMathStyles = (theme: MarkdownTheme) =>
35
+ StyleSheet.create({
36
+ mathInlineContainer: {
37
+ marginHorizontal: 2,
38
+ },
39
+ mathInlineFallbackContainer: {
40
+ backgroundColor: theme.colors.codeBackground,
41
+ paddingHorizontal: theme.spacing.xs,
42
+ paddingVertical: 2,
43
+ borderRadius: theme.borderRadius.s,
44
+ marginHorizontal: 2,
45
+ },
46
+ mathInlineFallback: {
47
+ fontFamily:
48
+ theme.fontFamilies.mono ??
49
+ Platform.select({ ios: "Courier", android: "monospace" }),
50
+ fontSize: theme.fontSizes.s,
51
+ color: theme.colors.code,
52
+ },
53
+ mathBlockContainer: {
54
+ marginVertical: theme.spacing.m,
55
+ paddingVertical: theme.spacing.l,
56
+ paddingHorizontal: theme.spacing.l,
57
+ backgroundColor: theme.colors.surface,
58
+ borderRadius: theme.borderRadius.l,
59
+ alignItems: "center",
60
+ borderWidth: 1,
61
+ borderColor: theme.colors.border,
62
+ },
63
+ mathBlockFallbackContainer: {
64
+ marginVertical: theme.spacing.m,
65
+ paddingVertical: theme.spacing.m,
66
+ paddingHorizontal: theme.spacing.l,
67
+ backgroundColor: theme.colors.codeBackground,
68
+ borderRadius: theme.borderRadius.m,
69
+ alignItems: "center",
70
+ borderWidth: 1,
71
+ borderColor: theme.colors.border,
72
+ },
73
+ mathBlockFallback: {
74
+ fontFamily:
75
+ theme.fontFamilies.mono ??
76
+ Platform.select({ ios: "Courier", android: "monospace" }),
77
+ fontSize: theme.fontSizes.m,
78
+ color: theme.colors.code,
79
+ textAlign: "center",
80
+ },
81
+ });
82
+
83
+ export const MathInline: FC<MathInlineProps> = ({ content, style }) => {
84
+ const { theme } = useMarkdownContext();
85
+ const styles = useMemo(() => createMathStyles(theme), [theme]);
86
+
87
+ if (!content) return null;
88
+
89
+ if (MathJaxComponent) {
90
+ const fontSize = theme.fontSizes.s;
91
+ return (
92
+ <View style={[styles.mathInlineContainer, style]}>
93
+ <MathJaxComponent
94
+ fontSize={fontSize}
95
+ color={theme.colors.text}
96
+ fontCache={true}
97
+ >
98
+ {content}
99
+ </MathJaxComponent>
100
+ </View>
101
+ );
102
+ }
103
+
104
+ return (
105
+ <View style={[styles.mathInlineFallbackContainer, style]}>
106
+ <Text style={styles.mathInlineFallback}>{content}</Text>
107
+ </View>
108
+ );
109
+ };
110
+
111
+ interface MathBlockProps {
112
+ content?: string;
113
+ style?: ViewStyle;
114
+ }
115
+
116
+ export const MathBlock: FC<MathBlockProps> = ({ content, style }) => {
117
+ const { theme } = useMarkdownContext();
118
+ const styles = useMemo(() => createMathStyles(theme), [theme]);
119
+
120
+ if (!content) return null;
121
+
122
+ if (MathJaxComponent) {
123
+ return (
124
+ <View style={[styles.mathBlockContainer, style]}>
125
+ <MathJaxComponent
126
+ fontSize={theme.fontSizes.l}
127
+ color={theme.colors.text}
128
+ fontCache={true}
129
+ >
130
+ {`\\displaystyle ${content}`}
131
+ </MathJaxComponent>
132
+ </View>
133
+ );
134
+ }
135
+
136
+ return (
137
+ <View style={[styles.mathBlockFallbackContainer, style]}>
138
+ <Text style={styles.mathBlockFallback}>{content}</Text>
139
+ </View>
140
+ );
141
+ };