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.
- package/README.md +217 -101
- package/lib/commonjs/MarkdownContext.js +3 -7
- package/lib/commonjs/MarkdownContext.js.map +1 -1
- package/lib/commonjs/default-markdown-renderer.js +4 -7
- package/lib/commonjs/default-markdown-renderer.js.map +1 -1
- package/lib/commonjs/headless.js +12 -1
- package/lib/commonjs/headless.js.map +1 -1
- package/lib/commonjs/index.js +35 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/markdown.js +88 -37
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/blockquote.js +1 -1
- package/lib/commonjs/renderers/blockquote.js.map +1 -1
- package/lib/commonjs/renderers/code.js +15 -8
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/heading.js +2 -1
- package/lib/commonjs/renderers/heading.js.map +1 -1
- package/lib/commonjs/renderers/horizontal-rule.js +4 -2
- package/lib/commonjs/renderers/horizontal-rule.js.map +1 -1
- package/lib/commonjs/renderers/image.js +13 -9
- package/lib/commonjs/renderers/image.js.map +1 -1
- package/lib/commonjs/renderers/link.js +1 -3
- package/lib/commonjs/renderers/link.js.map +1 -1
- package/lib/commonjs/renderers/list.js +11 -7
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/math.js +23 -47
- package/lib/commonjs/renderers/math.js.map +1 -1
- package/lib/commonjs/renderers/paragraph.js +0 -5
- package/lib/commonjs/renderers/paragraph.js.map +1 -1
- package/lib/commonjs/renderers/table.js +127 -120
- package/lib/commonjs/renderers/table.js.map +1 -1
- package/lib/commonjs/theme.js +146 -13
- package/lib/commonjs/theme.js.map +1 -1
- package/lib/module/MarkdownContext.js +3 -8
- package/lib/module/MarkdownContext.js.map +1 -1
- package/lib/module/default-markdown-renderer.js +1 -4
- package/lib/module/default-markdown-renderer.js.map +1 -1
- package/lib/module/headless.js +10 -0
- package/lib/module/headless.js.map +1 -1
- package/lib/module/index.js +2 -20
- package/lib/module/index.js.map +1 -1
- package/lib/module/markdown.js +89 -38
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/blockquote.js +1 -1
- package/lib/module/renderers/blockquote.js.map +1 -1
- package/lib/module/renderers/code.js +15 -8
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/heading.js +2 -1
- package/lib/module/renderers/heading.js.map +1 -1
- package/lib/module/renderers/horizontal-rule.js +4 -2
- package/lib/module/renderers/horizontal-rule.js.map +1 -1
- package/lib/module/renderers/image.js +13 -9
- package/lib/module/renderers/image.js.map +1 -1
- package/lib/module/renderers/link.js +1 -3
- package/lib/module/renderers/link.js.map +1 -1
- package/lib/module/renderers/list.js +11 -7
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/math.js +24 -48
- package/lib/module/renderers/math.js.map +1 -1
- package/lib/module/renderers/paragraph.js +0 -5
- package/lib/module/renderers/paragraph.js.map +1 -1
- package/lib/module/renderers/table.js +128 -121
- package/lib/module/renderers/table.js.map +1 -1
- package/lib/module/theme.js +144 -12
- package/lib/module/theme.js.map +1 -1
- package/lib/typescript/commonjs/MarkdownContext.d.ts +45 -6
- package/lib/typescript/commonjs/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/default-markdown-renderer.d.ts +1 -1
- package/lib/typescript/commonjs/default-markdown-renderer.d.ts.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts +8 -1
- package/lib/typescript/commonjs/headless.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -16
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown.d.ts +20 -2
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.d.ts +6 -2
- package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +6 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/image.d.ts +2 -0
- package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts +4 -0
- package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts +3 -4
- package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts +0 -5
- package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/table.d.ts +2 -0
- package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme.d.ts +41 -28
- package/lib/typescript/commonjs/theme.d.ts.map +1 -1
- package/lib/typescript/module/MarkdownContext.d.ts +45 -6
- package/lib/typescript/module/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/module/default-markdown-renderer.d.ts +1 -1
- package/lib/typescript/module/default-markdown-renderer.d.ts.map +1 -1
- package/lib/typescript/module/headless.d.ts +8 -1
- package/lib/typescript/module/headless.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -16
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/markdown.d.ts +20 -2
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.d.ts +6 -2
- package/lib/typescript/module/renderers/code.d.ts.map +1 -1
- package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts +6 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/module/renderers/image.d.ts +2 -0
- package/lib/typescript/module/renderers/image.d.ts.map +1 -1
- package/lib/typescript/module/renderers/link.d.ts.map +1 -1
- package/lib/typescript/module/renderers/list.d.ts +4 -0
- package/lib/typescript/module/renderers/list.d.ts.map +1 -1
- package/lib/typescript/module/renderers/math.d.ts +3 -4
- package/lib/typescript/module/renderers/math.d.ts.map +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts +0 -5
- package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/module/renderers/table.d.ts +2 -0
- package/lib/typescript/module/renderers/table.d.ts.map +1 -1
- package/lib/typescript/module/theme.d.ts +41 -28
- package/lib/typescript/module/theme.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/MarkdownContext.ts +66 -9
- package/src/default-markdown-renderer.tsx +1 -6
- package/src/headless.ts +11 -1
- package/src/index.ts +24 -19
- package/src/markdown.tsx +125 -43
- package/src/renderers/blockquote.tsx +1 -2
- package/src/renderers/code.tsx +36 -12
- package/src/renderers/heading.tsx +1 -1
- package/src/renderers/horizontal-rule.tsx +7 -4
- package/src/renderers/image.tsx +24 -9
- package/src/renderers/link.tsx +1 -6
- package/src/renderers/list.tsx +11 -5
- package/src/renderers/math.tsx +20 -43
- package/src/renderers/paragraph.tsx +0 -6
- package/src/renderers/table.tsx +185 -160
- package/src/theme.ts +203 -12
package/src/markdown.tsx
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
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?:
|
|
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
|
-
|
|
78
|
-
|
|
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
|
|
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: "
|
|
180
|
+
alignItems: "center",
|
|
152
181
|
flexWrap: "wrap",
|
|
153
182
|
flexShrink: 1,
|
|
154
|
-
marginVertical: 0,
|
|
155
|
-
paddingVertical: 0,
|
|
156
183
|
}}
|
|
157
184
|
>
|
|
158
|
-
{currentInlineGroup.map((
|
|
185
|
+
{currentInlineGroup.map((n, idx) => (
|
|
159
186
|
<NodeRenderer
|
|
160
|
-
key={`${
|
|
161
|
-
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((
|
|
204
|
+
{currentInlineGroup.map((n, idx) => (
|
|
178
205
|
<NodeRenderer
|
|
179
|
-
key={`${
|
|
180
|
-
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
|
-
|
|
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
|
|
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
|
|
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
|
|
396
|
+
return (
|
|
397
|
+
<MathBlock content={getTextContent(node)} style={nodeStyleOverride} />
|
|
398
|
+
);
|
|
328
399
|
|
|
329
400
|
case "list":
|
|
330
401
|
return (
|
|
331
|
-
<List
|
|
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
|
|
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:
|
|
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
|
-
|
package/src/renderers/code.tsx
CHANGED
|
@@ -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
|
|
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:
|
|
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.
|
|
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:
|
|
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
|
-
{
|
|
67
|
+
{showLanguage && <Text style={styles.codeLanguage}>{language}</Text>}
|
|
56
68
|
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
|
57
|
-
<Text style={styles.codeBlockText}>{
|
|
69
|
+
<Text style={styles.codeBlockText}>{displayContent}</Text>
|
|
58
70
|
</ScrollView>
|
|
59
71
|
</View>
|
|
60
72
|
);
|
|
61
73
|
};
|
|
62
74
|
|
|
63
75
|
interface InlineCodeProps {
|
|
64
|
-
|
|
76
|
+
content?: string;
|
|
77
|
+
node?: MarkdownNode;
|
|
78
|
+
children?: ReactNode;
|
|
65
79
|
style?: TextStyle;
|
|
66
80
|
}
|
|
67
81
|
|
|
68
|
-
export const InlineCode: FC<InlineCodeProps> = ({
|
|
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:
|
|
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:
|
|
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]}>{
|
|
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
|
-
|
|
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
|
-
|
package/src/renderers/image.tsx
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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>
|
package/src/renderers/link.tsx
CHANGED
|
@@ -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
|
-
|