react-native-nitro-markdown 0.2.1 → 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 (138) hide show
  1. package/README.md +217 -101
  2. package/lib/commonjs/MarkdownContext.js +3 -7
  3. package/lib/commonjs/MarkdownContext.js.map +1 -1
  4. package/lib/commonjs/default-markdown-renderer.js +4 -7
  5. package/lib/commonjs/default-markdown-renderer.js.map +1 -1
  6. package/lib/commonjs/headless.js +12 -1
  7. package/lib/commonjs/headless.js.map +1 -1
  8. package/lib/commonjs/index.js +35 -0
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/markdown.js +88 -37
  11. package/lib/commonjs/markdown.js.map +1 -1
  12. package/lib/commonjs/renderers/blockquote.js +1 -1
  13. package/lib/commonjs/renderers/blockquote.js.map +1 -1
  14. package/lib/commonjs/renderers/code.js +15 -8
  15. package/lib/commonjs/renderers/code.js.map +1 -1
  16. package/lib/commonjs/renderers/heading.js +2 -1
  17. package/lib/commonjs/renderers/heading.js.map +1 -1
  18. package/lib/commonjs/renderers/horizontal-rule.js +4 -2
  19. package/lib/commonjs/renderers/horizontal-rule.js.map +1 -1
  20. package/lib/commonjs/renderers/image.js +13 -9
  21. package/lib/commonjs/renderers/image.js.map +1 -1
  22. package/lib/commonjs/renderers/link.js +1 -3
  23. package/lib/commonjs/renderers/link.js.map +1 -1
  24. package/lib/commonjs/renderers/list.js +11 -7
  25. package/lib/commonjs/renderers/list.js.map +1 -1
  26. package/lib/commonjs/renderers/math.js +23 -47
  27. package/lib/commonjs/renderers/math.js.map +1 -1
  28. package/lib/commonjs/renderers/paragraph.js +0 -5
  29. package/lib/commonjs/renderers/paragraph.js.map +1 -1
  30. package/lib/commonjs/renderers/table.js +127 -120
  31. package/lib/commonjs/renderers/table.js.map +1 -1
  32. package/lib/commonjs/theme.js +146 -13
  33. package/lib/commonjs/theme.js.map +1 -1
  34. package/lib/module/MarkdownContext.js +3 -8
  35. package/lib/module/MarkdownContext.js.map +1 -1
  36. package/lib/module/default-markdown-renderer.js +1 -4
  37. package/lib/module/default-markdown-renderer.js.map +1 -1
  38. package/lib/module/headless.js +10 -0
  39. package/lib/module/headless.js.map +1 -1
  40. package/lib/module/index.js +2 -20
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/markdown.js +89 -38
  43. package/lib/module/markdown.js.map +1 -1
  44. package/lib/module/renderers/blockquote.js +1 -1
  45. package/lib/module/renderers/blockquote.js.map +1 -1
  46. package/lib/module/renderers/code.js +15 -8
  47. package/lib/module/renderers/code.js.map +1 -1
  48. package/lib/module/renderers/heading.js +2 -1
  49. package/lib/module/renderers/heading.js.map +1 -1
  50. package/lib/module/renderers/horizontal-rule.js +4 -2
  51. package/lib/module/renderers/horizontal-rule.js.map +1 -1
  52. package/lib/module/renderers/image.js +13 -9
  53. package/lib/module/renderers/image.js.map +1 -1
  54. package/lib/module/renderers/link.js +1 -3
  55. package/lib/module/renderers/link.js.map +1 -1
  56. package/lib/module/renderers/list.js +11 -7
  57. package/lib/module/renderers/list.js.map +1 -1
  58. package/lib/module/renderers/math.js +24 -48
  59. package/lib/module/renderers/math.js.map +1 -1
  60. package/lib/module/renderers/paragraph.js +0 -5
  61. package/lib/module/renderers/paragraph.js.map +1 -1
  62. package/lib/module/renderers/table.js +128 -121
  63. package/lib/module/renderers/table.js.map +1 -1
  64. package/lib/module/theme.js +144 -12
  65. package/lib/module/theme.js.map +1 -1
  66. package/lib/typescript/commonjs/MarkdownContext.d.ts +45 -6
  67. package/lib/typescript/commonjs/MarkdownContext.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/default-markdown-renderer.d.ts +1 -1
  69. package/lib/typescript/commonjs/default-markdown-renderer.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/headless.d.ts +8 -1
  71. package/lib/typescript/commonjs/headless.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/index.d.ts +4 -16
  73. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  74. package/lib/typescript/commonjs/markdown.d.ts +20 -2
  75. package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
  76. package/lib/typescript/commonjs/renderers/code.d.ts +6 -2
  77. package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +6 -1
  80. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/renderers/image.d.ts +2 -0
  82. package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/renderers/list.d.ts +4 -0
  85. package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/renderers/math.d.ts +3 -4
  87. package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/renderers/paragraph.d.ts +0 -5
  89. package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
  90. package/lib/typescript/commonjs/renderers/table.d.ts +2 -0
  91. package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/theme.d.ts +41 -28
  93. package/lib/typescript/commonjs/theme.d.ts.map +1 -1
  94. package/lib/typescript/module/MarkdownContext.d.ts +45 -6
  95. package/lib/typescript/module/MarkdownContext.d.ts.map +1 -1
  96. package/lib/typescript/module/default-markdown-renderer.d.ts +1 -1
  97. package/lib/typescript/module/default-markdown-renderer.d.ts.map +1 -1
  98. package/lib/typescript/module/headless.d.ts +8 -1
  99. package/lib/typescript/module/headless.d.ts.map +1 -1
  100. package/lib/typescript/module/index.d.ts +4 -16
  101. package/lib/typescript/module/index.d.ts.map +1 -1
  102. package/lib/typescript/module/markdown.d.ts +20 -2
  103. package/lib/typescript/module/markdown.d.ts.map +1 -1
  104. package/lib/typescript/module/renderers/code.d.ts +6 -2
  105. package/lib/typescript/module/renderers/code.d.ts.map +1 -1
  106. package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
  107. package/lib/typescript/module/renderers/horizontal-rule.d.ts +6 -1
  108. package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
  109. package/lib/typescript/module/renderers/image.d.ts +2 -0
  110. package/lib/typescript/module/renderers/image.d.ts.map +1 -1
  111. package/lib/typescript/module/renderers/link.d.ts.map +1 -1
  112. package/lib/typescript/module/renderers/list.d.ts +4 -0
  113. package/lib/typescript/module/renderers/list.d.ts.map +1 -1
  114. package/lib/typescript/module/renderers/math.d.ts +3 -4
  115. package/lib/typescript/module/renderers/math.d.ts.map +1 -1
  116. package/lib/typescript/module/renderers/paragraph.d.ts +0 -5
  117. package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
  118. package/lib/typescript/module/renderers/table.d.ts +2 -0
  119. package/lib/typescript/module/renderers/table.d.ts.map +1 -1
  120. package/lib/typescript/module/theme.d.ts +41 -28
  121. package/lib/typescript/module/theme.d.ts.map +1 -1
  122. package/package.json +1 -1
  123. package/src/MarkdownContext.ts +66 -9
  124. package/src/default-markdown-renderer.tsx +1 -6
  125. package/src/headless.ts +11 -1
  126. package/src/index.ts +24 -19
  127. package/src/markdown.tsx +125 -43
  128. package/src/renderers/blockquote.tsx +1 -2
  129. package/src/renderers/code.tsx +36 -12
  130. package/src/renderers/heading.tsx +1 -1
  131. package/src/renderers/horizontal-rule.tsx +7 -4
  132. package/src/renderers/image.tsx +24 -9
  133. package/src/renderers/link.tsx +1 -6
  134. package/src/renderers/list.tsx +11 -5
  135. package/src/renderers/math.tsx +20 -43
  136. package/src/renderers/paragraph.tsx +0 -6
  137. package/src/renderers/table.tsx +185 -160
  138. 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,7 @@ import {
10
18
  import {
11
19
  parseMarkdown,
12
20
  parseMarkdownWithOptions,
21
+ getTextContent,
13
22
  type MarkdownNode,
14
23
  } from "./headless";
15
24
  import type { ParserOptions } from "./Markdown.nitro";
@@ -42,12 +51,30 @@ export interface MarkdownProps {
42
51
  options?: ParserOptions;
43
52
  /**
44
53
  * Custom renderers for specific markdown node types.
54
+ * Each renderer receives { node, children, Renderer } plus type-specific props.
45
55
  */
46
56
  renderers?: CustomRenderers;
47
57
  /**
48
58
  * Custom theme to override default styles.
49
59
  */
50
- theme?: Partial<MarkdownTheme>;
60
+ theme?: PartialMarkdownTheme;
61
+ /**
62
+ * Style overrides for specific node types.
63
+ * Applied after internal styles, allowing fine-grained customization.
64
+ * @example
65
+ * ```tsx
66
+ * <Markdown styles={{ heading: { color: 'red' }, code_block: { borderRadius: 0 } }}>
67
+ * {content}
68
+ * </Markdown>
69
+ * ```
70
+ */
71
+ styles?: NodeStyleOverrides;
72
+ /**
73
+ * Styling strategy for the component.
74
+ * - "opinionated": Full styling with colors, spacing, and visual effects (default)
75
+ * - "minimal": Bare minimum styling for a clean slate
76
+ */
77
+ stylingStrategy?: StylingStrategy;
51
78
  /**
52
79
  * Optional style for the container view.
53
80
  */
@@ -59,6 +86,8 @@ export const Markdown: FC<MarkdownProps> = ({
59
86
  options,
60
87
  renderers = {},
61
88
  theme: userTheme,
89
+ styles: nodeStyles,
90
+ stylingStrategy = "opinionated",
62
91
  style,
63
92
  }) => {
64
93
  const ast = useMemo(() => {
@@ -73,10 +102,13 @@ export const Markdown: FC<MarkdownProps> = ({
73
102
  }
74
103
  }, [children, options]);
75
104
 
76
- const theme = useMemo(
77
- () => ({ ...defaultMarkdownTheme, ...userTheme }),
78
- [userTheme]
79
- );
105
+ const theme = useMemo(() => {
106
+ const base =
107
+ stylingStrategy === "minimal"
108
+ ? minimalMarkdownTheme
109
+ : defaultMarkdownTheme;
110
+ return mergeThemes(base, userTheme);
111
+ }, [userTheme, stylingStrategy]);
80
112
 
81
113
  const baseStyles = useMemo(() => createBaseStyles(theme), [theme]);
82
114
 
@@ -89,7 +121,9 @@ export const Markdown: FC<MarkdownProps> = ({
89
121
  }
90
122
 
91
123
  return (
92
- <MarkdownContext.Provider value={{ renderers, theme }}>
124
+ <MarkdownContext.Provider
125
+ value={{ renderers, theme, styles: nodeStyles, stylingStrategy }}
126
+ >
93
127
  <View style={[baseStyles.container, style]}>
94
128
  <NodeRenderer node={ast} depth={0} inListItem={false} />
95
129
  </View>
@@ -112,18 +146,13 @@ const isInline = (type: MarkdownNode["type"]): boolean => {
112
146
  );
113
147
  };
114
148
 
115
- const getTextContent = (node: MarkdownNode): string => {
116
- if (node.content) return node.content;
117
- return node.children?.map(getTextContent).join("") ?? "";
118
- };
119
-
120
149
  const NodeRenderer: FC<NodeRendererProps> = ({
121
150
  node,
122
151
  depth,
123
152
  inListItem,
124
153
  parentIsText = false,
125
154
  }) => {
126
- const { renderers, theme } = useMarkdownContext();
155
+ const { renderers, theme, styles: nodeStyles } = useMarkdownContext();
127
156
  const baseStyles = useMemo(() => createBaseStyles(theme), [theme]);
128
157
 
129
158
  const renderChildren = (
@@ -148,17 +177,15 @@ const NodeRenderer: FC<NodeRendererProps> = ({
148
177
  key={`inline-group-${elements.length}`}
149
178
  style={{
150
179
  flexDirection: "row",
151
- alignItems: "baseline",
180
+ alignItems: "center",
152
181
  flexWrap: "wrap",
153
182
  flexShrink: 1,
154
- marginVertical: 0,
155
- paddingVertical: 0,
156
183
  }}
157
184
  >
158
- {currentInlineGroup.map((node, idx) => (
185
+ {currentInlineGroup.map((n, idx) => (
159
186
  <NodeRenderer
160
- key={`${node.type}-${idx}`}
161
- node={node}
187
+ key={`${n.type}-${idx}`}
188
+ node={n}
162
189
  depth={depth + 1}
163
190
  inListItem={childInListItem}
164
191
  parentIsText={false}
@@ -174,10 +201,10 @@ const NodeRenderer: FC<NodeRendererProps> = ({
174
201
 
175
202
  elements.push(
176
203
  <Wrapper key={`inline-group-${elements.length}`} {...wrapperProps}>
177
- {currentInlineGroup.map((node, idx) => (
204
+ {currentInlineGroup.map((n, idx) => (
178
205
  <NodeRenderer
179
- key={`${node.type}-${idx}`}
180
- node={node}
206
+ key={`${n.type}-${idx}`}
207
+ node={n}
181
208
  depth={depth + 1}
182
209
  inListItem={childInListItem}
183
210
  parentIsText={true}
@@ -218,32 +245,67 @@ const NodeRenderer: FC<NodeRendererProps> = ({
218
245
  inListItem,
219
246
  parentIsText
220
247
  );
221
- const result = customRenderer({
248
+
249
+ const baseProps = {
222
250
  node,
223
251
  children: childrenRendered,
224
252
  Renderer: NodeRenderer,
225
- });
253
+ };
254
+
255
+ const enhancedProps = {
256
+ ...baseProps,
257
+ // Heading
258
+ ...(node.type === "heading" && {
259
+ level: (node.level ?? 1) as 1 | 2 | 3 | 4 | 5 | 6,
260
+ }),
261
+ // Link
262
+ ...(node.type === "link" && { href: node.href ?? "", title: node.title }),
263
+ // Image
264
+ ...(node.type === "image" && {
265
+ url: node.href ?? "",
266
+ alt: node.alt,
267
+ title: node.title,
268
+ }),
269
+ // Code block
270
+ ...(node.type === "code_block" && {
271
+ content: getTextContent(node),
272
+ language: node.language,
273
+ }),
274
+ // Inline code
275
+ ...(node.type === "code_inline" && { content: node.content ?? "" }),
276
+ // List
277
+ ...(node.type === "list" && {
278
+ ordered: node.ordered ?? false,
279
+ start: node.start,
280
+ }),
281
+ // Task list item
282
+ ...(node.type === "task_list_item" && { checked: node.checked ?? false }),
283
+ };
284
+
285
+ const result = customRenderer(enhancedProps);
226
286
  if (result !== undefined) return <>{result}</>;
227
287
  }
228
288
 
289
+ const nodeStyleOverride = nodeStyles?.[node.type];
290
+
229
291
  switch (node.type) {
230
292
  case "document":
231
293
  return (
232
- <View style={baseStyles.document}>
294
+ <View style={[baseStyles.document, nodeStyleOverride]}>
233
295
  {renderChildren(node.children, false, false)}
234
296
  </View>
235
297
  );
236
298
 
237
299
  case "heading":
238
300
  return (
239
- <Heading level={node.level ?? 1}>
301
+ <Heading level={node.level ?? 1} style={nodeStyleOverride}>
240
302
  {renderChildren(node.children, inListItem, true)}
241
303
  </Heading>
242
304
  );
243
305
 
244
306
  case "paragraph":
245
307
  return (
246
- <Paragraph inListItem={inListItem}>
308
+ <Paragraph inListItem={inListItem} style={nodeStyleOverride}>
247
309
  {renderChildren(node.children, inListItem, false)}
248
310
  </Paragraph>
249
311
  );
@@ -252,32 +314,34 @@ const NodeRenderer: FC<NodeRendererProps> = ({
252
314
  if (parentIsText) {
253
315
  return <Text>{node.content}</Text>;
254
316
  }
255
- return <Text style={baseStyles.text}>{node.content}</Text>;
317
+ return (
318
+ <Text style={[baseStyles.text, nodeStyleOverride]}>{node.content}</Text>
319
+ );
256
320
 
257
321
  case "bold":
258
322
  return (
259
- <Text style={baseStyles.bold}>
323
+ <Text style={[baseStyles.bold, nodeStyleOverride]}>
260
324
  {renderChildren(node.children, inListItem, true)}
261
325
  </Text>
262
326
  );
263
327
 
264
328
  case "italic":
265
329
  return (
266
- <Text style={baseStyles.italic}>
330
+ <Text style={[baseStyles.italic, nodeStyleOverride]}>
267
331
  {renderChildren(node.children, inListItem, true)}
268
332
  </Text>
269
333
  );
270
334
 
271
335
  case "strikethrough":
272
336
  return (
273
- <Text style={baseStyles.strikethrough}>
337
+ <Text style={[baseStyles.strikethrough, nodeStyleOverride]}>
274
338
  {renderChildren(node.children, inListItem, true)}
275
339
  </Text>
276
340
  );
277
341
 
278
342
  case "link":
279
343
  return (
280
- <Link href={node.href ?? ""}>
344
+ <Link href={node.href ?? ""} style={nodeStyleOverride}>
281
345
  {renderChildren(node.children, inListItem, true)}
282
346
  </Link>
283
347
  );
@@ -289,26 +353,31 @@ const NodeRenderer: FC<NodeRendererProps> = ({
289
353
  title={node.title}
290
354
  alt={node.alt}
291
355
  Renderer={NodeRenderer}
356
+ style={nodeStyleOverride}
292
357
  />
293
358
  );
294
359
 
295
360
  case "code_inline":
296
- return <InlineCode>{node.content}</InlineCode>;
361
+ return <InlineCode style={nodeStyleOverride}>{node.content}</InlineCode>;
297
362
 
298
363
  case "code_block":
299
364
  return (
300
- <CodeBlock language={node.language} content={getTextContent(node)} />
365
+ <CodeBlock
366
+ language={node.language}
367
+ content={getTextContent(node)}
368
+ style={nodeStyleOverride}
369
+ />
301
370
  );
302
371
 
303
372
  case "blockquote":
304
373
  return (
305
- <Blockquote>
374
+ <Blockquote style={nodeStyleOverride}>
306
375
  {renderChildren(node.children, inListItem, false)}
307
376
  </Blockquote>
308
377
  );
309
378
 
310
379
  case "horizontal_rule":
311
- return <HorizontalRule />;
380
+ return <HorizontalRule style={nodeStyleOverride} />;
312
381
 
313
382
  case "line_break":
314
383
  return <Text>{"\n"}</Text>;
@@ -320,15 +389,22 @@ const NodeRenderer: FC<NodeRendererProps> = ({
320
389
  let mathContent = getTextContent(node);
321
390
  if (!mathContent) return null;
322
391
  mathContent = mathContent.replace(/^\$+|\$+$/g, "").trim();
323
- return <MathInline content={mathContent} />;
392
+ return <MathInline content={mathContent} style={nodeStyleOverride} />;
324
393
  }
325
394
 
326
395
  case "math_block":
327
- return <MathBlock content={getTextContent(node)} />;
396
+ return (
397
+ <MathBlock content={getTextContent(node)} style={nodeStyleOverride} />
398
+ );
328
399
 
329
400
  case "list":
330
401
  return (
331
- <List ordered={node.ordered ?? false} start={node.start} depth={depth}>
402
+ <List
403
+ ordered={node.ordered ?? false}
404
+ start={node.start}
405
+ depth={depth}
406
+ style={nodeStyleOverride}
407
+ >
332
408
  {node.children?.map((child, index) => {
333
409
  if (child.type === "task_list_item") {
334
410
  return (
@@ -365,19 +441,24 @@ const NodeRenderer: FC<NodeRendererProps> = ({
365
441
 
366
442
  case "task_list_item":
367
443
  return (
368
- <TaskListItem checked={node.checked ?? false}>
444
+ <TaskListItem checked={node.checked ?? false} style={nodeStyleOverride}>
369
445
  {renderChildren(node.children, true, false)}
370
446
  </TaskListItem>
371
447
  );
372
448
 
373
449
  case "table":
374
- return <TableRenderer node={node} Renderer={NodeRenderer} />;
450
+ return (
451
+ <TableRenderer
452
+ node={node}
453
+ Renderer={NodeRenderer}
454
+ style={nodeStyleOverride}
455
+ />
456
+ );
375
457
 
376
458
  case "table_head":
377
459
  case "table_body":
378
460
  case "table_row":
379
461
  case "table_cell":
380
- // Handled by TableRenderer
381
462
  return null;
382
463
 
383
464
  default:
@@ -396,12 +477,13 @@ const createBaseStyles = (theme: MarkdownTheme) =>
396
477
  errorText: {
397
478
  color: "#f87171",
398
479
  fontSize: 14,
399
- fontFamily: "monospace",
480
+ fontFamily: theme.fontFamilies.mono ?? "monospace",
400
481
  },
401
482
  text: {
402
483
  color: theme.colors.text,
403
484
  fontSize: theme.fontSizes.m,
404
485
  lineHeight: theme.fontSizes.m * 1.6,
486
+ fontFamily: theme.fontFamilies.regular,
405
487
  },
406
488
  bold: {
407
489
  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
-
@@ -1,5 +1,17 @@
1
- import { useState, useMemo, type ReactNode, type FC, type ComponentType } from "react";
2
- import { View, Text, Image as RNImage, StyleSheet } from "react-native";
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";
3
15
 
4
16
  import { parseMarkdownWithOptions, type MarkdownNode } from "../headless";
5
17
  import type { NodeRendererProps } from "../MarkdownContext";
@@ -25,11 +37,11 @@ interface ImageProps {
25
37
  url: string;
26
38
  title?: string;
27
39
  alt?: string;
28
-
29
40
  Renderer?: ComponentType<NodeRendererProps>;
41
+ style?: ViewStyle;
30
42
  }
31
43
 
32
- export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
44
+ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer, style }) => {
33
45
  const [loading, setLoading] = useState(true);
34
46
  const [error, setError] = useState(false);
35
47
  const { theme } = useMarkdownContext();
@@ -44,7 +56,7 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
44
56
  image: {
45
57
  width: "100%",
46
58
  height: 200,
47
- borderRadius: 8,
59
+ borderRadius: theme.borderRadius.m,
48
60
  backgroundColor: theme.colors.surface,
49
61
  },
50
62
  imageHidden: {
@@ -54,7 +66,7 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
54
66
  imageLoading: {
55
67
  width: "100%",
56
68
  height: 200,
57
- borderRadius: 8,
69
+ borderRadius: theme.borderRadius.m,
58
70
  backgroundColor: theme.colors.surface,
59
71
  justifyContent: "center",
60
72
  alignItems: "center",
@@ -62,11 +74,12 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
62
74
  imageLoadingText: {
63
75
  color: theme.colors.textMuted,
64
76
  fontSize: theme.fontSizes.s,
77
+ fontFamily: theme.fontFamilies.regular,
65
78
  },
66
79
  imageError: {
67
80
  width: "100%",
68
81
  padding: theme.spacing.l,
69
- borderRadius: 8,
82
+ borderRadius: theme.borderRadius.m,
70
83
  backgroundColor: theme.colors.surface,
71
84
  alignItems: "center",
72
85
  marginVertical: theme.spacing.m,
@@ -74,6 +87,7 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
74
87
  imageErrorText: {
75
88
  color: theme.colors.textMuted,
76
89
  fontSize: theme.fontSizes.s,
90
+ fontFamily: theme.fontFamilies.regular,
77
91
  },
78
92
  imageCaption: {
79
93
  color: theme.colors.textMuted,
@@ -81,6 +95,7 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
81
95
  marginTop: theme.spacing.s,
82
96
  fontStyle: "italic",
83
97
  textAlign: "center",
98
+ fontFamily: theme.fontFamilies.regular,
84
99
  },
85
100
  }),
86
101
  [theme]
@@ -117,7 +132,7 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
117
132
 
118
133
  if (error) {
119
134
  return (
120
- <View style={styles.imageError}>
135
+ <View style={[styles.imageError, style]}>
121
136
  <View
122
137
  style={{
123
138
  flexDirection: "row",
@@ -138,7 +153,7 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer }) => {
138
153
  }
139
154
 
140
155
  return (
141
- <View style={styles.imageContainer}>
156
+ <View style={[styles.imageContainer, style]}>
142
157
  {loading && (
143
158
  <View style={styles.imageLoading}>
144
159
  <Text style={styles.imageLoadingText}>Loading image...</Text>
@@ -22,11 +22,7 @@ export const Link: FC<LinkProps> = ({ href, children, style }) => {
22
22
  );
23
23
 
24
24
  const handlePress = () => {
25
- if (href) {
26
- Linking.openURL(href).catch((err) =>
27
- console.error("Failed to open URL:", err)
28
- );
29
- }
25
+ if (href) Linking.openURL(href);
30
26
  };
31
27
 
32
28
  return (
@@ -35,4 +31,3 @@ export const Link: FC<LinkProps> = ({ href, children, style }) => {
35
31
  </Text>
36
32
  );
37
33
  };
38
-