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,47 @@
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
+ export const Paragraph: FC<ParagraphProps> = ({
12
+ children,
13
+ inListItem,
14
+ style,
15
+ }) => {
16
+ const { theme } = useMarkdownContext();
17
+ const styles = useMemo(
18
+ () =>
19
+ StyleSheet.create({
20
+ paragraph: {
21
+ flexDirection: "row",
22
+ flexWrap: "wrap",
23
+ alignItems: "baseline",
24
+ marginBottom: theme.spacing.l,
25
+ gap: 0,
26
+ },
27
+ paragraphInListItem: {
28
+ marginBottom: 0,
29
+ marginTop: 0,
30
+ flexShrink: 1,
31
+ },
32
+ }),
33
+ [theme]
34
+ );
35
+
36
+ return (
37
+ <View
38
+ style={[
39
+ styles.paragraph,
40
+ inListItem && styles.paragraphInListItem,
41
+ style,
42
+ ]}
43
+ >
44
+ {children}
45
+ </View>
46
+ );
47
+ };
@@ -0,0 +1,370 @@
1
+ import {
2
+ useMemo,
3
+ useRef,
4
+ useReducer,
5
+ useCallback,
6
+ type FC,
7
+ type ComponentType,
8
+ type ReactNode,
9
+ } from "react";
10
+ import {
11
+ View,
12
+ Text,
13
+ StyleSheet,
14
+ ScrollView,
15
+ type StyleProp,
16
+ type TextStyle,
17
+ type ViewStyle,
18
+ type LayoutChangeEvent,
19
+ } from "react-native";
20
+
21
+ import type { MarkdownNode } from "../headless";
22
+ import { useMarkdownContext, type NodeRendererProps } from "../MarkdownContext";
23
+ import { MarkdownTheme } from "../theme";
24
+
25
+ type TableData = {
26
+ headers: MarkdownNode[];
27
+ rows: MarkdownNode[][];
28
+ alignments: (string | undefined)[];
29
+ };
30
+
31
+ const extractTableData = (node: MarkdownNode): TableData => {
32
+ const headers: MarkdownNode[] = [];
33
+ const rows: MarkdownNode[][] = [];
34
+ const alignments: (string | undefined)[] = [];
35
+
36
+ for (const child of node.children ?? []) {
37
+ if (child.type === "table_head") {
38
+ for (const row of child.children ?? []) {
39
+ if (row.type === "table_row") {
40
+ for (const cell of row.children ?? []) {
41
+ headers.push(cell);
42
+ alignments.push(cell.align);
43
+ }
44
+ }
45
+ }
46
+ } else if (child.type === "table_body") {
47
+ for (const row of child.children ?? []) {
48
+ if (row.type === "table_row") {
49
+ const rowCells: MarkdownNode[] = [];
50
+ for (const cell of row.children ?? []) {
51
+ rowCells.push(cell);
52
+ }
53
+ rows.push(rowCells);
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ return { headers, rows, alignments };
60
+ };
61
+
62
+ interface TableRendererProps {
63
+ node: MarkdownNode;
64
+ Renderer: ComponentType<NodeRendererProps>;
65
+ style?: ViewStyle;
66
+ }
67
+
68
+ type ColumnWidthsAction = {
69
+ type: "SET_WIDTHS";
70
+ payload: number[];
71
+ };
72
+
73
+ const columnWidthsReducer = (state: number[], action: ColumnWidthsAction) => {
74
+ if (action.type === "SET_WIDTHS") return action.payload;
75
+ return state;
76
+ };
77
+
78
+ export const TableRenderer: FC<TableRendererProps> = ({
79
+ node,
80
+ Renderer,
81
+ style,
82
+ }) => {
83
+ const { theme } = useMarkdownContext();
84
+ const { headers, rows, alignments } = useMemo(
85
+ () => extractTableData(node),
86
+ [node]
87
+ );
88
+
89
+ const columnCount = headers.length;
90
+ const styles = useMemo(() => createTableStyles(theme), [theme]);
91
+
92
+ const [columnWidths, dispatch] = useReducer(columnWidthsReducer, []);
93
+ const measuredWidths = useRef<Map<string, number>>(new Map());
94
+ const measuredCells = useRef<Set<string>>(new Set());
95
+ const widthsCalculated = useRef(false);
96
+
97
+ const onCellLayout = useCallback(
98
+ (cellKey: string, width: number) => {
99
+ measuredWidths.current.set(cellKey, width);
100
+ measuredCells.current.add(cellKey);
101
+
102
+ const expectedCells = new Set<string>();
103
+ for (let col = 0; col < columnCount; col++) {
104
+ expectedCells.add(`header-${col}`);
105
+ }
106
+ for (let row = 0; row < rows.length; row++) {
107
+ for (let col = 0; col < columnCount; col++) {
108
+ expectedCells.add(`cell-${row}-${col}`);
109
+ }
110
+ }
111
+
112
+ const allCellsMeasured = [...expectedCells].every((key) =>
113
+ measuredCells.current.has(key)
114
+ );
115
+ if (!allCellsMeasured || widthsCalculated.current) return;
116
+
117
+ const maxWidths: number[] = new Array(columnCount).fill(0);
118
+
119
+ for (let col = 0; col < columnCount; col++) {
120
+ const headerWidth = measuredWidths.current.get(`header-${col}`);
121
+ if (headerWidth && headerWidth > 0) {
122
+ maxWidths[col] = Math.max(maxWidths[col], headerWidth);
123
+ }
124
+
125
+ for (let row = 0; row < rows.length; row++) {
126
+ const cellWidth = measuredWidths.current.get(`cell-${row}-${col}`);
127
+ if (cellWidth && cellWidth > 0) {
128
+ maxWidths[col] = Math.max(maxWidths[col], cellWidth);
129
+ }
130
+ }
131
+
132
+ maxWidths[col] = Math.max(maxWidths[col] + 8, 60);
133
+ }
134
+
135
+ widthsCalculated.current = true;
136
+ dispatch({ type: "SET_WIDTHS", payload: maxWidths });
137
+ },
138
+ [columnCount, rows.length]
139
+ );
140
+
141
+ const getAlignment = (
142
+ index: number
143
+ ): "flex-start" | "center" | "flex-end" => {
144
+ const align = alignments[index];
145
+ if (align === "center") return "center";
146
+ if (align === "right") return "flex-end";
147
+ return "flex-start";
148
+ };
149
+
150
+ if (columnCount === 0) return null;
151
+
152
+ const hasWidths = columnWidths.length === columnCount;
153
+
154
+ return (
155
+ <View style={[styles.container, style]}>
156
+ <View style={styles.measurementContainer}>
157
+ <View style={styles.measurementRow}>
158
+ {headers.map((cell, colIndex) => (
159
+ <View
160
+ key={`measure-header-${colIndex}`}
161
+ style={styles.measurementCell}
162
+ onLayout={(e: LayoutChangeEvent) => {
163
+ onCellLayout(`header-${colIndex}`, e.nativeEvent.layout.width);
164
+ }}
165
+ >
166
+ <CellContent node={cell} Renderer={Renderer} styles={styles} />
167
+ </View>
168
+ ))}
169
+ </View>
170
+ {rows.map((row, rowIndex) => (
171
+ <View key={`measure-row-${rowIndex}`} style={styles.measurementRow}>
172
+ {row.map((cell, colIndex) => (
173
+ <View
174
+ key={`measure-cell-${rowIndex}-${colIndex}`}
175
+ style={styles.measurementCell}
176
+ onLayout={(e: LayoutChangeEvent) => {
177
+ onCellLayout(
178
+ `cell-${rowIndex}-${colIndex}`,
179
+ e.nativeEvent.layout.width
180
+ );
181
+ }}
182
+ >
183
+ <CellContent node={cell} Renderer={Renderer} styles={styles} />
184
+ </View>
185
+ ))}
186
+ </View>
187
+ ))}
188
+ </View>
189
+
190
+ {hasWidths && (
191
+ <ScrollView
192
+ horizontal
193
+ showsHorizontalScrollIndicator
194
+ style={styles.tableScroll}
195
+ bounces={false}
196
+ >
197
+ <View style={styles.table}>
198
+ <View style={styles.headerRow}>
199
+ {headers.map((cell, colIndex) => (
200
+ <View
201
+ key={`header-${colIndex}`}
202
+ style={[
203
+ styles.headerCell,
204
+ {
205
+ width: columnWidths[colIndex],
206
+ alignItems: getAlignment(colIndex),
207
+ },
208
+ colIndex === columnCount - 1 && styles.lastCell,
209
+ ]}
210
+ >
211
+ <CellContent
212
+ node={cell}
213
+ Renderer={Renderer}
214
+ styles={styles}
215
+ textStyle={styles.headerText}
216
+ />
217
+ </View>
218
+ ))}
219
+ </View>
220
+
221
+ {rows.map((row, rowIndex) => (
222
+ <View
223
+ key={`row-${rowIndex}`}
224
+ style={[
225
+ styles.bodyRow,
226
+ rowIndex === rows.length - 1 && styles.lastRow,
227
+ rowIndex % 2 === 1 && styles.oddRow,
228
+ ]}
229
+ >
230
+ {row.map((cell, colIndex) => (
231
+ <View
232
+ key={`cell-${rowIndex}-${colIndex}`}
233
+ style={[
234
+ styles.bodyCell,
235
+ {
236
+ width: columnWidths[colIndex],
237
+ alignItems: getAlignment(colIndex),
238
+ },
239
+ colIndex === columnCount - 1 && styles.lastCell,
240
+ ]}
241
+ >
242
+ <CellContent
243
+ node={cell}
244
+ Renderer={Renderer}
245
+ styles={styles}
246
+ textStyle={styles.cellText}
247
+ />
248
+ </View>
249
+ ))}
250
+ </View>
251
+ ))}
252
+ </View>
253
+ </ScrollView>
254
+ )}
255
+ </View>
256
+ );
257
+ };
258
+
259
+ const CellContent: FC<{
260
+ node: MarkdownNode;
261
+ Renderer: ComponentType<NodeRendererProps>;
262
+ styles: ReturnType<typeof createTableStyles>;
263
+ textStyle?: StyleProp<TextStyle>;
264
+ }> = ({ node, Renderer, styles, textStyle }) => {
265
+ if (!node.children || node.children.length === 0) {
266
+ return <Text style={textStyle}>{node.content ?? ""}</Text>;
267
+ }
268
+
269
+ return (
270
+ <View style={styles.cellContentWrapper}>
271
+ {node.children.map((child, idx) => (
272
+ <Renderer
273
+ key={idx}
274
+ node={child}
275
+ depth={0}
276
+ inListItem={false}
277
+ parentIsText={false}
278
+ />
279
+ ))}
280
+ </View>
281
+ );
282
+ };
283
+
284
+ const createTableStyles = (theme: MarkdownTheme) => {
285
+ const colors = theme?.colors || {};
286
+ const borderRadius = theme?.borderRadius || { m: 8 };
287
+
288
+ return StyleSheet.create({
289
+ container: {
290
+ marginVertical: theme.spacing.s,
291
+ },
292
+ measurementContainer: {
293
+ position: "absolute",
294
+ opacity: 0,
295
+ pointerEvents: "none",
296
+ left: -9999,
297
+ },
298
+ measurementRow: {
299
+ flexDirection: "row",
300
+ },
301
+ measurementCell: {
302
+ paddingVertical: 10,
303
+ paddingHorizontal: 12,
304
+ },
305
+ tableScroll: {
306
+ flexGrow: 0,
307
+ },
308
+ table: {
309
+ borderRadius: borderRadius.m,
310
+ overflow: "hidden",
311
+ borderWidth: 1,
312
+ borderColor: colors.tableBorder || "#374151",
313
+ backgroundColor: colors.surface || "#111827",
314
+ },
315
+ headerRow: {
316
+ flexDirection: "row",
317
+ backgroundColor: colors.tableHeader || "#1f2937",
318
+ borderBottomWidth: 1,
319
+ borderBottomColor: colors.tableBorder || "#374151",
320
+ },
321
+ bodyRow: {
322
+ flexDirection: "row",
323
+ borderBottomWidth: 1,
324
+ borderBottomColor: colors.tableBorder || "#374151",
325
+ },
326
+ oddRow: {
327
+ backgroundColor: colors.tableRowOdd || "rgba(255,255,255,0.02)",
328
+ },
329
+ lastRow: {
330
+ borderBottomWidth: 0,
331
+ },
332
+ headerCell: {
333
+ flexShrink: 0,
334
+ paddingVertical: 10,
335
+ paddingHorizontal: 12,
336
+ minWidth: 60,
337
+ borderRightWidth: 1,
338
+ borderRightColor: colors.tableBorder || "#374151",
339
+ },
340
+ bodyCell: {
341
+ flexShrink: 0,
342
+ paddingVertical: 10,
343
+ paddingHorizontal: 12,
344
+ minWidth: 60,
345
+ borderRightWidth: 1,
346
+ borderRightColor: colors.tableBorder || "#374151",
347
+ justifyContent: "center",
348
+ },
349
+ lastCell: {
350
+ borderRightWidth: 0,
351
+ },
352
+ headerText: {
353
+ color: colors.tableHeaderText || "#9ca3af",
354
+ fontSize: 12,
355
+ fontWeight: "600",
356
+ fontFamily: theme.fontFamilies?.regular,
357
+ },
358
+ cellText: {
359
+ color: colors.text || "#e5e7eb",
360
+ fontSize: 14,
361
+ lineHeight: 20,
362
+ fontFamily: theme.fontFamilies?.regular,
363
+ },
364
+ cellContentWrapper: {
365
+ flexDirection: "row",
366
+ flexWrap: "wrap",
367
+ alignItems: "center",
368
+ },
369
+ });
370
+ };
@@ -0,0 +1,16 @@
1
+ import type { HybridObject } from "react-native-nitro-modules";
2
+
3
+ export interface MarkdownSession
4
+ extends HybridObject<{ ios: "swift"; android: "kotlin" }> {
5
+ // Buffer operations
6
+ append(chunk: string): void;
7
+ clear(): void;
8
+ getAllText(): string;
9
+
10
+ // Karaoke highlighting (native rendering)
11
+ // Nitro generates getter/setter for properties automatically
12
+ highlightPosition: number;
13
+
14
+ // Listener for view updates
15
+ addListener(listener: () => void): () => void;
16
+ }
package/src/theme.ts ADDED
@@ -0,0 +1,243 @@
1
+ import type { TextStyle, ViewStyle } from "react-native";
2
+ import type { MarkdownNode } from "./headless";
3
+
4
+ export interface MarkdownTheme {
5
+ colors: {
6
+ text: string | undefined;
7
+ textMuted: string | undefined;
8
+ heading: string | undefined;
9
+ link: string | undefined;
10
+ code: string | undefined;
11
+ codeBackground: string | undefined;
12
+ codeLanguage: string | undefined;
13
+ blockquote: string | undefined;
14
+ border: string | undefined;
15
+ surface: string | undefined;
16
+ surfaceLight: string | undefined;
17
+ accent: string | undefined;
18
+ tableBorder: string | undefined;
19
+ tableHeader: string | undefined;
20
+ tableHeaderText: string | undefined;
21
+ tableRowEven: string | undefined;
22
+ tableRowOdd: string | undefined;
23
+ };
24
+ spacing: {
25
+ xs: number;
26
+ s: number;
27
+ m: number;
28
+ l: number;
29
+ xl: number;
30
+ };
31
+ fontSizes: {
32
+ xs: number;
33
+ s: number;
34
+ m: number;
35
+ l: number;
36
+ xl: number;
37
+ h1: number;
38
+ h2: number;
39
+ h3: number;
40
+ h4: number;
41
+ h5: number;
42
+ h6: number;
43
+ };
44
+ fontFamilies: {
45
+ regular: string | undefined;
46
+ heading: string | undefined;
47
+ mono: string | undefined;
48
+ };
49
+ borderRadius: {
50
+ s: number;
51
+ m: number;
52
+ l: number;
53
+ };
54
+ showCodeLanguage: boolean;
55
+ }
56
+
57
+ export const defaultMarkdownTheme: MarkdownTheme = {
58
+ colors: {
59
+ text: "#e0e0e0",
60
+ textMuted: "#888",
61
+ heading: "#f0f0f0",
62
+ link: "#60a5fa",
63
+ code: "#fbbf24",
64
+ codeBackground: "#1a1a2e",
65
+ codeLanguage: "#4ade80",
66
+ blockquote: "#3b82f6",
67
+ border: "#252525",
68
+ surface: "#151515",
69
+ surfaceLight: "#1a1a1a",
70
+ accent: "#4ade80",
71
+ tableBorder: "#334155",
72
+ tableHeader: "#0f172a",
73
+ tableHeaderText: "#94a3b8",
74
+ tableRowEven: "#0f172a",
75
+ tableRowOdd: "#1e293b",
76
+ },
77
+ spacing: {
78
+ xs: 4,
79
+ s: 8,
80
+ m: 12,
81
+ l: 16,
82
+ xl: 24,
83
+ },
84
+ fontSizes: {
85
+ xs: 12,
86
+ s: 14,
87
+ m: 16,
88
+ l: 18,
89
+ xl: 22,
90
+ h1: 32,
91
+ h2: 26,
92
+ h3: 22,
93
+ h4: 18,
94
+ h5: 16,
95
+ h6: 14,
96
+ },
97
+ fontFamilies: {
98
+ regular: undefined,
99
+ heading: undefined,
100
+ mono: undefined,
101
+ },
102
+ borderRadius: {
103
+ s: 4,
104
+ m: 8,
105
+ l: 12,
106
+ },
107
+ showCodeLanguage: true,
108
+ };
109
+
110
+ export type PartialMarkdownTheme = {
111
+ [K in keyof MarkdownTheme]?: K extends "showCodeLanguage"
112
+ ? MarkdownTheme[K]
113
+ : Partial<MarkdownTheme[K]>;
114
+ };
115
+
116
+ export type NodeStyleOverrides = Partial<
117
+ Record<MarkdownNode["type"], ViewStyle | TextStyle>
118
+ >;
119
+
120
+ export type StylingStrategy = "opinionated" | "minimal";
121
+
122
+ export const minimalMarkdownTheme: MarkdownTheme = {
123
+ colors: {
124
+ text: undefined,
125
+ textMuted: undefined,
126
+ heading: undefined,
127
+ link: "#0066cc",
128
+ code: undefined,
129
+ codeBackground: "transparent",
130
+ codeLanguage: "#888888",
131
+ blockquote: "#cccccc",
132
+ border: "#cccccc",
133
+ surface: "transparent",
134
+ surfaceLight: "transparent",
135
+ accent: "#0066cc",
136
+ tableBorder: "#cccccc",
137
+ tableHeader: "transparent",
138
+ tableHeaderText: undefined,
139
+ tableRowEven: "transparent",
140
+ tableRowOdd: "transparent",
141
+ },
142
+ spacing: {
143
+ xs: 0,
144
+ s: 0,
145
+ m: 0,
146
+ l: 0,
147
+ xl: 0,
148
+ },
149
+ fontSizes: {
150
+ xs: 12,
151
+ s: 14,
152
+ m: 16,
153
+ l: 18,
154
+ xl: 22,
155
+ h1: 32,
156
+ h2: 26,
157
+ h3: 22,
158
+ h4: 18,
159
+ h5: 16,
160
+ h6: 14,
161
+ },
162
+ fontFamilies: {
163
+ regular: undefined,
164
+ heading: undefined,
165
+ mono: undefined,
166
+ },
167
+ borderRadius: {
168
+ s: 0,
169
+ m: 0,
170
+ l: 0,
171
+ },
172
+ showCodeLanguage: false,
173
+ };
174
+
175
+ export const lightMarkdownTheme: MarkdownTheme = {
176
+ colors: {
177
+ text: "#1a1a1a",
178
+ textMuted: "#6b7280",
179
+ heading: "#000000",
180
+ link: "#2563eb",
181
+ code: "#ea580c",
182
+ codeBackground: "#f3f4f6",
183
+ codeLanguage: "#10b981",
184
+ blockquote: "#3b82f6",
185
+ border: "#e5e7eb",
186
+ surface: "#ffffff",
187
+ surfaceLight: "#f9fafb",
188
+ accent: "#10b981",
189
+ tableBorder: "#e5e7eb",
190
+ tableHeader: "#f3f4f6",
191
+ tableHeaderText: "#6b7280",
192
+ tableRowEven: "#ffffff",
193
+ tableRowOdd: "#f9fafb",
194
+ },
195
+ spacing: {
196
+ xs: 4,
197
+ s: 8,
198
+ m: 12,
199
+ l: 16,
200
+ xl: 24,
201
+ },
202
+ fontSizes: {
203
+ xs: 12,
204
+ s: 14,
205
+ m: 16,
206
+ l: 18,
207
+ xl: 22,
208
+ h1: 32,
209
+ h2: 26,
210
+ h3: 22,
211
+ h4: 18,
212
+ h5: 16,
213
+ h6: 14,
214
+ },
215
+ fontFamilies: {
216
+ regular: undefined,
217
+ heading: undefined,
218
+ mono: undefined,
219
+ },
220
+ borderRadius: {
221
+ s: 4,
222
+ m: 8,
223
+ l: 12,
224
+ },
225
+ showCodeLanguage: true,
226
+ };
227
+
228
+ export const darkMarkdownTheme = defaultMarkdownTheme;
229
+
230
+ export const mergeThemes = (
231
+ base: MarkdownTheme,
232
+ partial?: PartialMarkdownTheme
233
+ ): MarkdownTheme => {
234
+ if (!partial) return base;
235
+ return {
236
+ colors: { ...base.colors, ...partial.colors },
237
+ spacing: { ...base.spacing, ...partial.spacing },
238
+ fontSizes: { ...base.fontSizes, ...partial.fontSizes },
239
+ fontFamilies: { ...base.fontFamilies, ...partial.fontFamilies },
240
+ borderRadius: { ...base.borderRadius, ...partial.borderRadius },
241
+ showCodeLanguage: partial.showCodeLanguage ?? base.showCodeLanguage,
242
+ };
243
+ };