react-native-nitro-markdown 0.2.1 → 0.3.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 (148) hide show
  1. package/README.md +237 -103
  2. package/cpp/bindings/HybridMarkdownParser.cpp +2 -0
  3. package/cpp/core/MD4CParser.cpp +73 -39
  4. package/cpp/core/MarkdownTypes.hpp +6 -1
  5. package/cpp/md4c/md4c.c +79 -56
  6. package/cpp/md4c/md4c.h +7 -4
  7. package/lib/commonjs/MarkdownContext.js +3 -7
  8. package/lib/commonjs/MarkdownContext.js.map +1 -1
  9. package/lib/commonjs/default-markdown-renderer.js +4 -7
  10. package/lib/commonjs/default-markdown-renderer.js.map +1 -1
  11. package/lib/commonjs/headless.js +49 -1
  12. package/lib/commonjs/headless.js.map +1 -1
  13. package/lib/commonjs/index.js +35 -0
  14. package/lib/commonjs/index.js.map +1 -1
  15. package/lib/commonjs/markdown.js +100 -41
  16. package/lib/commonjs/markdown.js.map +1 -1
  17. package/lib/commonjs/renderers/blockquote.js +1 -1
  18. package/lib/commonjs/renderers/blockquote.js.map +1 -1
  19. package/lib/commonjs/renderers/code.js +15 -8
  20. package/lib/commonjs/renderers/code.js.map +1 -1
  21. package/lib/commonjs/renderers/heading.js +2 -1
  22. package/lib/commonjs/renderers/heading.js.map +1 -1
  23. package/lib/commonjs/renderers/horizontal-rule.js +4 -2
  24. package/lib/commonjs/renderers/horizontal-rule.js.map +1 -1
  25. package/lib/commonjs/renderers/image.js +39 -14
  26. package/lib/commonjs/renderers/image.js.map +1 -1
  27. package/lib/commonjs/renderers/link.js +1 -3
  28. package/lib/commonjs/renderers/link.js.map +1 -1
  29. package/lib/commonjs/renderers/list.js +12 -10
  30. package/lib/commonjs/renderers/list.js.map +1 -1
  31. package/lib/commonjs/renderers/math.js +33 -47
  32. package/lib/commonjs/renderers/math.js.map +1 -1
  33. package/lib/commonjs/renderers/paragraph.js +0 -6
  34. package/lib/commonjs/renderers/paragraph.js.map +1 -1
  35. package/lib/commonjs/renderers/table.js +127 -120
  36. package/lib/commonjs/renderers/table.js.map +1 -1
  37. package/lib/commonjs/theme.js +146 -13
  38. package/lib/commonjs/theme.js.map +1 -1
  39. package/lib/module/MarkdownContext.js +3 -8
  40. package/lib/module/MarkdownContext.js.map +1 -1
  41. package/lib/module/default-markdown-renderer.js +1 -4
  42. package/lib/module/default-markdown-renderer.js.map +1 -1
  43. package/lib/module/headless.js +46 -0
  44. package/lib/module/headless.js.map +1 -1
  45. package/lib/module/index.js +2 -20
  46. package/lib/module/index.js.map +1 -1
  47. package/lib/module/markdown.js +101 -42
  48. package/lib/module/markdown.js.map +1 -1
  49. package/lib/module/renderers/blockquote.js +1 -1
  50. package/lib/module/renderers/blockquote.js.map +1 -1
  51. package/lib/module/renderers/code.js +15 -8
  52. package/lib/module/renderers/code.js.map +1 -1
  53. package/lib/module/renderers/heading.js +2 -1
  54. package/lib/module/renderers/heading.js.map +1 -1
  55. package/lib/module/renderers/horizontal-rule.js +4 -2
  56. package/lib/module/renderers/horizontal-rule.js.map +1 -1
  57. package/lib/module/renderers/image.js +40 -15
  58. package/lib/module/renderers/image.js.map +1 -1
  59. package/lib/module/renderers/link.js +1 -3
  60. package/lib/module/renderers/link.js.map +1 -1
  61. package/lib/module/renderers/list.js +12 -10
  62. package/lib/module/renderers/list.js.map +1 -1
  63. package/lib/module/renderers/math.js +34 -48
  64. package/lib/module/renderers/math.js.map +1 -1
  65. package/lib/module/renderers/paragraph.js +0 -6
  66. package/lib/module/renderers/paragraph.js.map +1 -1
  67. package/lib/module/renderers/table.js +128 -121
  68. package/lib/module/renderers/table.js.map +1 -1
  69. package/lib/module/theme.js +144 -12
  70. package/lib/module/theme.js.map +1 -1
  71. package/lib/typescript/commonjs/MarkdownContext.d.ts +45 -6
  72. package/lib/typescript/commonjs/MarkdownContext.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/default-markdown-renderer.d.ts +1 -1
  74. package/lib/typescript/commonjs/default-markdown-renderer.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/headless.d.ts +12 -1
  76. package/lib/typescript/commonjs/headless.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/index.d.ts +4 -16
  78. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/markdown.d.ts +33 -2
  80. package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/renderers/code.d.ts +6 -2
  82. package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +6 -1
  85. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/renderers/image.d.ts +2 -0
  87. package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -1
  89. package/lib/typescript/commonjs/renderers/list.d.ts +4 -0
  90. package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/renderers/math.d.ts +3 -4
  92. package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/renderers/paragraph.d.ts +0 -5
  94. package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/renderers/table.d.ts +2 -0
  96. package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/specs/MarkdownSession.nitro.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/theme.d.ts +41 -28
  99. package/lib/typescript/commonjs/theme.d.ts.map +1 -1
  100. package/lib/typescript/module/MarkdownContext.d.ts +45 -6
  101. package/lib/typescript/module/MarkdownContext.d.ts.map +1 -1
  102. package/lib/typescript/module/default-markdown-renderer.d.ts +1 -1
  103. package/lib/typescript/module/default-markdown-renderer.d.ts.map +1 -1
  104. package/lib/typescript/module/headless.d.ts +12 -1
  105. package/lib/typescript/module/headless.d.ts.map +1 -1
  106. package/lib/typescript/module/index.d.ts +4 -16
  107. package/lib/typescript/module/index.d.ts.map +1 -1
  108. package/lib/typescript/module/markdown.d.ts +33 -2
  109. package/lib/typescript/module/markdown.d.ts.map +1 -1
  110. package/lib/typescript/module/renderers/code.d.ts +6 -2
  111. package/lib/typescript/module/renderers/code.d.ts.map +1 -1
  112. package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
  113. package/lib/typescript/module/renderers/horizontal-rule.d.ts +6 -1
  114. package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
  115. package/lib/typescript/module/renderers/image.d.ts +2 -0
  116. package/lib/typescript/module/renderers/image.d.ts.map +1 -1
  117. package/lib/typescript/module/renderers/link.d.ts.map +1 -1
  118. package/lib/typescript/module/renderers/list.d.ts +4 -0
  119. package/lib/typescript/module/renderers/list.d.ts.map +1 -1
  120. package/lib/typescript/module/renderers/math.d.ts +3 -4
  121. package/lib/typescript/module/renderers/math.d.ts.map +1 -1
  122. package/lib/typescript/module/renderers/paragraph.d.ts +0 -5
  123. package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
  124. package/lib/typescript/module/renderers/table.d.ts +2 -0
  125. package/lib/typescript/module/renderers/table.d.ts.map +1 -1
  126. package/lib/typescript/module/specs/MarkdownSession.nitro.d.ts.map +1 -1
  127. package/lib/typescript/module/theme.d.ts +41 -28
  128. package/lib/typescript/module/theme.d.ts.map +1 -1
  129. package/nitrogen/generated/ios/NitroMarkdownAutolinking.swift +8 -7
  130. package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec.swift +2 -2
  131. package/package.json +1 -1
  132. package/src/MarkdownContext.ts +66 -9
  133. package/src/default-markdown-renderer.tsx +1 -6
  134. package/src/headless.ts +67 -2
  135. package/src/index.ts +24 -19
  136. package/src/markdown.tsx +158 -52
  137. package/src/renderers/blockquote.tsx +1 -2
  138. package/src/renderers/code.tsx +36 -12
  139. package/src/renderers/heading.tsx +1 -1
  140. package/src/renderers/horizontal-rule.tsx +7 -4
  141. package/src/renderers/image.tsx +59 -15
  142. package/src/renderers/link.tsx +1 -6
  143. package/src/renderers/list.tsx +15 -15
  144. package/src/renderers/math.tsx +28 -45
  145. package/src/renderers/paragraph.tsx +1 -8
  146. package/src/renderers/table.tsx +185 -160
  147. package/src/specs/MarkdownSession.nitro.ts +4 -6
  148. package/src/theme.ts +203 -12
package/src/markdown.tsx CHANGED
@@ -1,4 +1,12 @@
1
- import { defaultMarkdownTheme, type MarkdownTheme } from "./theme";
1
+ import {
2
+ defaultMarkdownTheme,
3
+ minimalMarkdownTheme,
4
+ mergeThemes,
5
+ type MarkdownTheme,
6
+ type PartialMarkdownTheme,
7
+ type NodeStyleOverrides,
8
+ type StylingStrategy,
9
+ } from "./theme";
2
10
  import { useMemo, type ReactNode, type FC, Fragment } from "react";
3
11
  import {
4
12
  StyleSheet,
@@ -10,6 +18,8 @@ import {
10
18
  import {
11
19
  parseMarkdown,
12
20
  parseMarkdownWithOptions,
21
+ getFlattenedText,
22
+ getTextContent,
13
23
  type MarkdownNode,
14
24
  } from "./headless";
15
25
  import type { ParserOptions } from "./Markdown.nitro";
@@ -40,14 +50,44 @@ export interface MarkdownProps {
40
50
  * Parser options to enable GFM or Math support.
41
51
  */
42
52
  options?: ParserOptions;
53
+ /**
54
+ * Callback fired when parsing begins.
55
+ */
56
+ onParsingInProgress?: () => void;
57
+ /**
58
+ * Callback fired when parsing completes.
59
+ */
60
+ onParseComplete?: (result: {
61
+ raw: string;
62
+ ast: MarkdownNode;
63
+ text: string;
64
+ }) => void;
43
65
  /**
44
66
  * Custom renderers for specific markdown node types.
67
+ * Each renderer receives { node, children, Renderer } plus type-specific props.
45
68
  */
46
69
  renderers?: CustomRenderers;
47
70
  /**
48
71
  * Custom theme to override default styles.
49
72
  */
50
- theme?: Partial<MarkdownTheme>;
73
+ theme?: PartialMarkdownTheme;
74
+ /**
75
+ * Style overrides for specific node types.
76
+ * Applied after internal styles, allowing fine-grained customization.
77
+ * @example
78
+ * ```tsx
79
+ * <Markdown styles={{ heading: { color: 'red' }, code_block: { borderRadius: 0 } }}>
80
+ * {content}
81
+ * </Markdown>
82
+ * ```
83
+ */
84
+ styles?: NodeStyleOverrides;
85
+ /**
86
+ * Styling strategy for the component.
87
+ * - "opinionated": Full styling with colors, spacing, and visual effects (default)
88
+ * - "minimal": Bare minimum styling for a clean slate
89
+ */
90
+ stylingStrategy?: StylingStrategy;
51
91
  /**
52
92
  * Optional style for the container view.
53
93
  */
@@ -59,24 +99,47 @@ export const Markdown: FC<MarkdownProps> = ({
59
99
  options,
60
100
  renderers = {},
61
101
  theme: userTheme,
102
+ styles: nodeStyles,
103
+ stylingStrategy = "opinionated",
62
104
  style,
105
+ onParsingInProgress,
106
+ onParseComplete,
63
107
  }) => {
64
108
  const ast = useMemo(() => {
65
109
  try {
110
+ if (onParsingInProgress) {
111
+ onParsingInProgress();
112
+ }
113
+
114
+ let result: MarkdownNode;
66
115
  if (options) {
67
- return parseMarkdownWithOptions(children, options);
116
+ result = parseMarkdownWithOptions(children, options);
117
+ } else {
118
+ result = parseMarkdown(children);
119
+ }
120
+
121
+ if (onParseComplete) {
122
+ onParseComplete({
123
+ raw: children,
124
+ ast: result,
125
+ text: getFlattenedText(result),
126
+ });
68
127
  }
69
- return parseMarkdown(children);
128
+
129
+ return result;
70
130
  } catch (error) {
71
131
  console.error("Failed to parse markdown:", error);
72
132
  return null;
73
133
  }
74
- }, [children, options]);
134
+ }, [children, options, onParsingInProgress, onParseComplete]);
75
135
 
76
- const theme = useMemo(
77
- () => ({ ...defaultMarkdownTheme, ...userTheme }),
78
- [userTheme]
79
- );
136
+ const theme = useMemo(() => {
137
+ const base =
138
+ stylingStrategy === "minimal"
139
+ ? minimalMarkdownTheme
140
+ : defaultMarkdownTheme;
141
+ return mergeThemes(base, userTheme);
142
+ }, [userTheme, stylingStrategy]);
80
143
 
81
144
  const baseStyles = useMemo(() => createBaseStyles(theme), [theme]);
82
145
 
@@ -89,7 +152,9 @@ export const Markdown: FC<MarkdownProps> = ({
89
152
  }
90
153
 
91
154
  return (
92
- <MarkdownContext.Provider value={{ renderers, theme }}>
155
+ <MarkdownContext.Provider
156
+ value={{ renderers, theme, styles: nodeStyles, stylingStrategy }}
157
+ >
93
158
  <View style={[baseStyles.container, style]}>
94
159
  <NodeRenderer node={ast} depth={0} inListItem={false} />
95
160
  </View>
@@ -112,24 +177,19 @@ const isInline = (type: MarkdownNode["type"]): boolean => {
112
177
  );
113
178
  };
114
179
 
115
- const getTextContent = (node: MarkdownNode): string => {
116
- if (node.content) return node.content;
117
- return node.children?.map(getTextContent).join("") ?? "";
118
- };
119
-
120
180
  const NodeRenderer: FC<NodeRendererProps> = ({
121
181
  node,
122
182
  depth,
123
183
  inListItem,
124
184
  parentIsText = false,
125
185
  }) => {
126
- const { renderers, theme } = useMarkdownContext();
186
+ const { renderers, theme, styles: nodeStyles } = useMarkdownContext();
127
187
  const baseStyles = useMemo(() => createBaseStyles(theme), [theme]);
128
188
 
129
189
  const renderChildren = (
130
190
  children?: MarkdownNode[],
131
191
  childInListItem = false,
132
- childParentIsText = false
192
+ childParentIsText = false,
133
193
  ): ReactNode => {
134
194
  if (!children || children.length === 0) return null;
135
195
 
@@ -139,7 +199,7 @@ const NodeRenderer: FC<NodeRendererProps> = ({
139
199
  const flushInlineGroup = () => {
140
200
  if (currentInlineGroup.length > 0) {
141
201
  const hasMath = currentInlineGroup.some(
142
- (child) => child.type === "math_inline"
202
+ (child) => child.type === "math_inline",
143
203
  );
144
204
 
145
205
  if (hasMath && !childParentIsText) {
@@ -148,23 +208,21 @@ const NodeRenderer: FC<NodeRendererProps> = ({
148
208
  key={`inline-group-${elements.length}`}
149
209
  style={{
150
210
  flexDirection: "row",
151
- alignItems: "baseline",
211
+ alignItems: "center",
152
212
  flexWrap: "wrap",
153
213
  flexShrink: 1,
154
- marginVertical: 0,
155
- paddingVertical: 0,
156
214
  }}
157
215
  >
158
- {currentInlineGroup.map((node, idx) => (
216
+ {currentInlineGroup.map((n, idx) => (
159
217
  <NodeRenderer
160
- key={`${node.type}-${idx}`}
161
- node={node}
218
+ key={`${n.type}-${idx}`}
219
+ node={n}
162
220
  depth={depth + 1}
163
221
  inListItem={childInListItem}
164
222
  parentIsText={false}
165
223
  />
166
224
  ))}
167
- </View>
225
+ </View>,
168
226
  );
169
227
  } else {
170
228
  const Wrapper = childParentIsText ? Fragment : Text;
@@ -174,16 +232,16 @@ const NodeRenderer: FC<NodeRendererProps> = ({
174
232
 
175
233
  elements.push(
176
234
  <Wrapper key={`inline-group-${elements.length}`} {...wrapperProps}>
177
- {currentInlineGroup.map((node, idx) => (
235
+ {currentInlineGroup.map((n, idx) => (
178
236
  <NodeRenderer
179
- key={`${node.type}-${idx}`}
180
- node={node}
237
+ key={`${n.type}-${idx}`}
238
+ node={n}
181
239
  depth={depth + 1}
182
240
  inListItem={childInListItem}
183
241
  parentIsText={true}
184
242
  />
185
243
  ))}
186
- </Wrapper>
244
+ </Wrapper>,
187
245
  );
188
246
  }
189
247
  currentInlineGroup = [];
@@ -202,7 +260,7 @@ const NodeRenderer: FC<NodeRendererProps> = ({
202
260
  depth={depth + 1}
203
261
  inListItem={childInListItem}
204
262
  parentIsText={childParentIsText}
205
- />
263
+ />,
206
264
  );
207
265
  }
208
266
  });
@@ -216,34 +274,62 @@ const NodeRenderer: FC<NodeRendererProps> = ({
216
274
  const childrenRendered = renderChildren(
217
275
  node.children,
218
276
  inListItem,
219
- parentIsText
277
+ parentIsText,
220
278
  );
221
- const result = customRenderer({
279
+
280
+ const baseProps = {
222
281
  node,
223
282
  children: childrenRendered,
224
283
  Renderer: NodeRenderer,
225
- });
284
+ };
285
+
286
+ const enhancedProps = {
287
+ ...baseProps,
288
+ ...(node.type === "heading" && {
289
+ level: (node.level ?? 1) as 1 | 2 | 3 | 4 | 5 | 6,
290
+ }),
291
+ ...(node.type === "link" && { href: node.href ?? "", title: node.title }),
292
+ ...(node.type === "image" && {
293
+ url: node.href ?? "",
294
+ alt: node.alt,
295
+ title: node.title,
296
+ }),
297
+ ...(node.type === "code_block" && {
298
+ content: getTextContent(node),
299
+ language: node.language,
300
+ }),
301
+ ...(node.type === "code_inline" && { content: node.content ?? "" }),
302
+ ...(node.type === "list" && {
303
+ ordered: node.ordered ?? false,
304
+ start: node.start,
305
+ }),
306
+ ...(node.type === "task_list_item" && { checked: node.checked ?? false }),
307
+ };
308
+
309
+ const result = customRenderer(enhancedProps);
226
310
  if (result !== undefined) return <>{result}</>;
227
311
  }
228
312
 
313
+ const nodeStyleOverride = nodeStyles?.[node.type];
314
+
229
315
  switch (node.type) {
230
316
  case "document":
231
317
  return (
232
- <View style={baseStyles.document}>
318
+ <View style={[baseStyles.document, nodeStyleOverride]}>
233
319
  {renderChildren(node.children, false, false)}
234
320
  </View>
235
321
  );
236
322
 
237
323
  case "heading":
238
324
  return (
239
- <Heading level={node.level ?? 1}>
325
+ <Heading level={node.level ?? 1} style={nodeStyleOverride}>
240
326
  {renderChildren(node.children, inListItem, true)}
241
327
  </Heading>
242
328
  );
243
329
 
244
330
  case "paragraph":
245
331
  return (
246
- <Paragraph inListItem={inListItem}>
332
+ <Paragraph inListItem={inListItem} style={nodeStyleOverride}>
247
333
  {renderChildren(node.children, inListItem, false)}
248
334
  </Paragraph>
249
335
  );
@@ -252,32 +338,34 @@ const NodeRenderer: FC<NodeRendererProps> = ({
252
338
  if (parentIsText) {
253
339
  return <Text>{node.content}</Text>;
254
340
  }
255
- return <Text style={baseStyles.text}>{node.content}</Text>;
341
+ return (
342
+ <Text style={[baseStyles.text, nodeStyleOverride]}>{node.content}</Text>
343
+ );
256
344
 
257
345
  case "bold":
258
346
  return (
259
- <Text style={baseStyles.bold}>
347
+ <Text style={[baseStyles.bold, nodeStyleOverride]}>
260
348
  {renderChildren(node.children, inListItem, true)}
261
349
  </Text>
262
350
  );
263
351
 
264
352
  case "italic":
265
353
  return (
266
- <Text style={baseStyles.italic}>
354
+ <Text style={[baseStyles.italic, nodeStyleOverride]}>
267
355
  {renderChildren(node.children, inListItem, true)}
268
356
  </Text>
269
357
  );
270
358
 
271
359
  case "strikethrough":
272
360
  return (
273
- <Text style={baseStyles.strikethrough}>
361
+ <Text style={[baseStyles.strikethrough, nodeStyleOverride]}>
274
362
  {renderChildren(node.children, inListItem, true)}
275
363
  </Text>
276
364
  );
277
365
 
278
366
  case "link":
279
367
  return (
280
- <Link href={node.href ?? ""}>
368
+ <Link href={node.href ?? ""} style={nodeStyleOverride}>
281
369
  {renderChildren(node.children, inListItem, true)}
282
370
  </Link>
283
371
  );
@@ -289,26 +377,31 @@ const NodeRenderer: FC<NodeRendererProps> = ({
289
377
  title={node.title}
290
378
  alt={node.alt}
291
379
  Renderer={NodeRenderer}
380
+ style={nodeStyleOverride}
292
381
  />
293
382
  );
294
383
 
295
384
  case "code_inline":
296
- return <InlineCode>{node.content}</InlineCode>;
385
+ return <InlineCode style={nodeStyleOverride}>{node.content}</InlineCode>;
297
386
 
298
387
  case "code_block":
299
388
  return (
300
- <CodeBlock language={node.language} content={getTextContent(node)} />
389
+ <CodeBlock
390
+ language={node.language}
391
+ content={getTextContent(node)}
392
+ style={nodeStyleOverride}
393
+ />
301
394
  );
302
395
 
303
396
  case "blockquote":
304
397
  return (
305
- <Blockquote>
398
+ <Blockquote style={nodeStyleOverride}>
306
399
  {renderChildren(node.children, inListItem, false)}
307
400
  </Blockquote>
308
401
  );
309
402
 
310
403
  case "horizontal_rule":
311
- return <HorizontalRule />;
404
+ return <HorizontalRule style={nodeStyleOverride} />;
312
405
 
313
406
  case "line_break":
314
407
  return <Text>{"\n"}</Text>;
@@ -320,15 +413,22 @@ const NodeRenderer: FC<NodeRendererProps> = ({
320
413
  let mathContent = getTextContent(node);
321
414
  if (!mathContent) return null;
322
415
  mathContent = mathContent.replace(/^\$+|\$+$/g, "").trim();
323
- return <MathInline content={mathContent} />;
416
+ return <MathInline content={mathContent} style={nodeStyleOverride} />;
324
417
  }
325
418
 
326
419
  case "math_block":
327
- return <MathBlock content={getTextContent(node)} />;
420
+ return (
421
+ <MathBlock content={getTextContent(node)} style={nodeStyleOverride} />
422
+ );
328
423
 
329
424
  case "list":
330
425
  return (
331
- <List ordered={node.ordered ?? false} start={node.start} depth={depth}>
426
+ <List
427
+ ordered={node.ordered ?? false}
428
+ start={node.start}
429
+ depth={depth}
430
+ style={nodeStyleOverride}
431
+ >
332
432
  {node.children?.map((child, index) => {
333
433
  if (child.type === "task_list_item") {
334
434
  return (
@@ -365,19 +465,24 @@ const NodeRenderer: FC<NodeRendererProps> = ({
365
465
 
366
466
  case "task_list_item":
367
467
  return (
368
- <TaskListItem checked={node.checked ?? false}>
468
+ <TaskListItem checked={node.checked ?? false} style={nodeStyleOverride}>
369
469
  {renderChildren(node.children, true, false)}
370
470
  </TaskListItem>
371
471
  );
372
472
 
373
473
  case "table":
374
- return <TableRenderer node={node} Renderer={NodeRenderer} />;
474
+ return (
475
+ <TableRenderer
476
+ node={node}
477
+ Renderer={NodeRenderer}
478
+ style={nodeStyleOverride}
479
+ />
480
+ );
375
481
 
376
482
  case "table_head":
377
483
  case "table_body":
378
484
  case "table_row":
379
485
  case "table_cell":
380
- // Handled by TableRenderer
381
486
  return null;
382
487
 
383
488
  default:
@@ -396,12 +501,13 @@ const createBaseStyles = (theme: MarkdownTheme) =>
396
501
  errorText: {
397
502
  color: "#f87171",
398
503
  fontSize: 14,
399
- fontFamily: "monospace",
504
+ fontFamily: theme.fontFamilies.mono ?? "monospace",
400
505
  },
401
506
  text: {
402
507
  color: theme.colors.text,
403
508
  fontSize: theme.fontSizes.m,
404
509
  lineHeight: theme.fontSizes.m * 1.6,
510
+ fontFamily: theme.fontFamilies.regular,
405
511
  },
406
512
  bold: {
407
513
  fontWeight: "700",
@@ -20,7 +20,7 @@ export const Blockquote: FC<BlockquoteProps> = ({ children, style }) => {
20
20
  backgroundColor: theme.colors.surfaceLight,
21
21
  paddingVertical: theme.spacing.m,
22
22
  paddingRight: theme.spacing.m,
23
- borderRadius: 4,
23
+ borderRadius: theme.borderRadius.s,
24
24
  },
25
25
  }),
26
26
  [theme]
@@ -28,4 +28,3 @@ export const Blockquote: FC<BlockquoteProps> = ({ children, style }) => {
28
28
 
29
29
  return <View style={[styles.blockquote, style]}>{children}</View>;
30
30
  };
31
-
@@ -9,32 +9,39 @@ import {
9
9
  type TextStyle,
10
10
  } from "react-native";
11
11
  import { useMarkdownContext } from "../MarkdownContext";
12
+ import type { MarkdownNode } from "../headless";
13
+ import { getTextContent } from "../headless";
12
14
 
13
15
  interface CodeBlockProps {
14
16
  language?: string;
15
- content: string;
17
+ content?: string;
18
+ node?: MarkdownNode;
16
19
  style?: ViewStyle;
17
20
  }
18
21
 
19
22
  export const CodeBlock: FC<CodeBlockProps> = ({
20
23
  language,
21
24
  content,
25
+ node,
22
26
  style,
23
27
  }) => {
24
28
  const { theme } = useMarkdownContext();
29
+
30
+ const displayContent = content ?? (node ? getTextContent(node) : "");
31
+
25
32
  const styles = useMemo(
26
33
  () =>
27
34
  StyleSheet.create({
28
35
  codeBlock: {
29
36
  backgroundColor: theme.colors.codeBackground,
30
- borderRadius: 8,
37
+ borderRadius: theme.borderRadius.m,
31
38
  padding: theme.spacing.l,
32
39
  marginVertical: theme.spacing.m,
33
40
  borderWidth: 1,
34
41
  borderColor: theme.colors.border,
35
42
  },
36
43
  codeLanguage: {
37
- color: theme.colors.accent,
44
+ color: theme.colors.codeLanguage,
38
45
  fontSize: theme.fontSizes.xs,
39
46
  fontWeight: "600",
40
47
  marginBottom: theme.spacing.s,
@@ -42,7 +49,9 @@ export const CodeBlock: FC<CodeBlockProps> = ({
42
49
  letterSpacing: 0.5,
43
50
  },
44
51
  codeBlockText: {
45
- fontFamily: Platform.select({ ios: "Courier", android: "monospace" }),
52
+ fontFamily:
53
+ theme.fontFamilies.mono ??
54
+ Platform.select({ ios: "Courier", android: "monospace" }),
46
55
  fontSize: theme.fontSizes.s,
47
56
  color: theme.colors.text,
48
57
  lineHeight: theme.fontSizes.s * 1.5,
@@ -50,39 +59,54 @@ export const CodeBlock: FC<CodeBlockProps> = ({
50
59
  }),
51
60
  [theme]
52
61
  );
62
+
63
+ const showLanguage = theme.showCodeLanguage && language;
64
+
53
65
  return (
54
66
  <View style={[styles.codeBlock, style]}>
55
- {language && <Text style={styles.codeLanguage}>{language}</Text>}
67
+ {showLanguage && <Text style={styles.codeLanguage}>{language}</Text>}
56
68
  <ScrollView horizontal showsHorizontalScrollIndicator={false}>
57
- <Text style={styles.codeBlockText}>{content}</Text>
69
+ <Text style={styles.codeBlockText}>{displayContent}</Text>
58
70
  </ScrollView>
59
71
  </View>
60
72
  );
61
73
  };
62
74
 
63
75
  interface InlineCodeProps {
64
- children: ReactNode;
76
+ content?: string;
77
+ node?: MarkdownNode;
78
+ children?: ReactNode;
65
79
  style?: TextStyle;
66
80
  }
67
81
 
68
- export const InlineCode: FC<InlineCodeProps> = ({ children, style }) => {
82
+ export const InlineCode: FC<InlineCodeProps> = ({
83
+ content,
84
+ node,
85
+ children,
86
+ style,
87
+ }) => {
69
88
  const { theme } = useMarkdownContext();
89
+
90
+ const displayContent =
91
+ content ?? children ?? (node ? getTextContent(node) : "");
92
+
70
93
  const styles = useMemo(
71
94
  () =>
72
95
  StyleSheet.create({
73
96
  codeInline: {
74
- fontFamily: Platform.select({ ios: "Courier", android: "monospace" }),
97
+ fontFamily:
98
+ theme.fontFamilies.mono ??
99
+ Platform.select({ ios: "Courier", android: "monospace" }),
75
100
  fontSize: theme.fontSizes.s,
76
101
  color: theme.colors.code,
77
102
  backgroundColor: theme.colors.codeBackground,
78
103
  paddingHorizontal: theme.spacing.xs,
79
104
  paddingVertical: 2,
80
- borderRadius: 4,
105
+ borderRadius: theme.borderRadius.s,
81
106
  ...(Platform.OS === "android" && { includeFontPadding: false }),
82
107
  },
83
108
  }),
84
109
  [theme]
85
110
  );
86
- return <Text style={[styles.codeInline, style]}>{children}</Text>;
111
+ return <Text style={[styles.codeInline, style]}>{displayContent}</Text>;
87
112
  };
88
-
@@ -18,6 +18,7 @@ export const Heading: FC<HeadingProps> = ({ level, children, style }) => {
18
18
  fontWeight: "700",
19
19
  marginTop: theme.spacing.xl,
20
20
  marginBottom: theme.spacing.m,
21
+ fontFamily: theme.fontFamilies.heading,
21
22
  },
22
23
  h1: {
23
24
  fontSize: theme.fontSizes.h1,
@@ -63,4 +64,3 @@ export const Heading: FC<HeadingProps> = ({ level, children, style }) => {
63
64
 
64
65
  return <Text style={[...headingStyles, style]}>{children}</Text>;
65
66
  };
66
-
@@ -1,8 +1,12 @@
1
1
  import { useMemo, type FC } from "react";
2
- import { View, StyleSheet } from "react-native";
2
+ import { View, StyleSheet, type ViewStyle } from "react-native";
3
3
  import { useMarkdownContext } from "../MarkdownContext";
4
4
 
5
- export const HorizontalRule: FC = () => {
5
+ interface HorizontalRuleProps {
6
+ style?: ViewStyle;
7
+ }
8
+
9
+ export const HorizontalRule: FC<HorizontalRuleProps> = ({ style }) => {
6
10
  const { theme } = useMarkdownContext();
7
11
  const styles = useMemo(
8
12
  () =>
@@ -15,6 +19,5 @@ export const HorizontalRule: FC = () => {
15
19
  }),
16
20
  [theme]
17
21
  );
18
- return <View style={styles.horizontalRule} />;
22
+ return <View style={[styles.horizontalRule, style]} />;
19
23
  };
20
-