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.
- package/README.md +237 -103
- package/cpp/bindings/HybridMarkdownParser.cpp +2 -0
- package/cpp/core/MD4CParser.cpp +73 -39
- package/cpp/core/MarkdownTypes.hpp +6 -1
- package/cpp/md4c/md4c.c +79 -56
- package/cpp/md4c/md4c.h +7 -4
- 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 +49 -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 +100 -41
- 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 +39 -14
- 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 +12 -10
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/math.js +33 -47
- package/lib/commonjs/renderers/math.js.map +1 -1
- package/lib/commonjs/renderers/paragraph.js +0 -6
- 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 +46 -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 +101 -42
- 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 +40 -15
- 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 +12 -10
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/math.js +34 -48
- package/lib/module/renderers/math.js.map +1 -1
- package/lib/module/renderers/paragraph.js +0 -6
- 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 +12 -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 +33 -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/specs/MarkdownSession.nitro.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 +12 -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 +33 -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/specs/MarkdownSession.nitro.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/nitrogen/generated/ios/NitroMarkdownAutolinking.swift +8 -7
- package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec.swift +2 -2
- package/package.json +1 -1
- package/src/MarkdownContext.ts +66 -9
- package/src/default-markdown-renderer.tsx +1 -6
- package/src/headless.ts +67 -2
- package/src/index.ts +24 -19
- package/src/markdown.tsx +158 -52
- 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 +59 -15
- package/src/renderers/link.tsx +1 -6
- package/src/renderers/list.tsx +15 -15
- package/src/renderers/math.tsx +28 -45
- package/src/renderers/paragraph.tsx +1 -8
- package/src/renderers/table.tsx +185 -160
- package/src/specs/MarkdownSession.nitro.ts +4 -6
- 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,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?:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
|
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: "
|
|
211
|
+
alignItems: "center",
|
|
152
212
|
flexWrap: "wrap",
|
|
153
213
|
flexShrink: 1,
|
|
154
|
-
marginVertical: 0,
|
|
155
|
-
paddingVertical: 0,
|
|
156
214
|
}}
|
|
157
215
|
>
|
|
158
|
-
{currentInlineGroup.map((
|
|
216
|
+
{currentInlineGroup.map((n, idx) => (
|
|
159
217
|
<NodeRenderer
|
|
160
|
-
key={`${
|
|
161
|
-
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((
|
|
235
|
+
{currentInlineGroup.map((n, idx) => (
|
|
178
236
|
<NodeRenderer
|
|
179
|
-
key={`${
|
|
180
|
-
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
|
-
|
|
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
|
|
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
|
|
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
|
|
420
|
+
return (
|
|
421
|
+
<MathBlock content={getTextContent(node)} style={nodeStyleOverride} />
|
|
422
|
+
);
|
|
328
423
|
|
|
329
424
|
case "list":
|
|
330
425
|
return (
|
|
331
|
-
<List
|
|
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
|
|
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:
|
|
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
|
-
|