react-native-nitro-markdown 0.5.2 → 0.5.4
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 +311 -669
- package/android/CMakeLists.txt +8 -1
- package/android/build.gradle +9 -2
- package/android/consumer-rules.pro +31 -0
- package/android/gradle.properties +2 -0
- package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +68 -22
- package/android/src/main/java/com/nitromarkdown/NitroMarkdownPackage.kt +6 -18
- package/cpp/bindings/HybridMarkdownParser.cpp +40 -12
- package/cpp/bindings/HybridMarkdownParser.hpp +4 -4
- package/cpp/bindings/HybridMarkdownSession.cpp +2 -0
- package/cpp/core/MD4CParser.cpp +147 -86
- package/cpp/core/MarkdownSessionCore.cpp +2 -0
- package/cpp/core/MarkdownTypes.hpp +1 -1
- package/ios/HybridMarkdownSession.swift +89 -46
- package/lib/commonjs/headless.js +34 -8
- package/lib/commonjs/headless.js.map +1 -1
- package/lib/commonjs/index.js +48 -38
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/markdown-stream.js +1 -1
- package/lib/commonjs/markdown-stream.js.map +1 -1
- package/lib/commonjs/markdown.js +57 -16
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/blockquote.js +15 -13
- package/lib/commonjs/renderers/blockquote.js.map +1 -1
- package/lib/commonjs/renderers/code.js +58 -54
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/heading.js +48 -46
- package/lib/commonjs/renderers/heading.js.map +1 -1
- package/lib/commonjs/renderers/horizontal-rule.js +10 -8
- package/lib/commonjs/renderers/horizontal-rule.js.map +1 -1
- package/lib/commonjs/renderers/image.js +18 -4
- package/lib/commonjs/renderers/image.js.map +1 -1
- package/lib/commonjs/renderers/link.js +7 -2
- package/lib/commonjs/renderers/link.js.map +1 -1
- package/lib/commonjs/renderers/list.js +75 -68
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/math.js +8 -5
- package/lib/commonjs/renderers/math.js.map +1 -1
- package/lib/commonjs/renderers/paragraph.js +15 -13
- package/lib/commonjs/renderers/paragraph.js.map +1 -1
- package/lib/commonjs/renderers/style-cache.js +14 -0
- package/lib/commonjs/renderers/style-cache.js.map +1 -0
- package/lib/commonjs/renderers/table/cell-content.js +1 -1
- package/lib/commonjs/renderers/table/cell-content.js.map +1 -1
- package/lib/commonjs/renderers/table/index.js +17 -6
- package/lib/commonjs/renderers/table/index.js.map +1 -1
- package/lib/commonjs/theme.js +7 -7
- package/lib/commonjs/theme.js.map +1 -1
- package/lib/commonjs/utils/code-highlight.js +24 -25
- package/lib/commonjs/utils/code-highlight.js.map +1 -1
- package/lib/module/headless.js +35 -7
- package/lib/module/headless.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/markdown-stream.js +1 -1
- package/lib/module/markdown-stream.js.map +1 -1
- package/lib/module/markdown.js +58 -17
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/blockquote.js +15 -13
- package/lib/module/renderers/blockquote.js.map +1 -1
- package/lib/module/renderers/code.js +58 -54
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/heading.js +48 -46
- package/lib/module/renderers/heading.js.map +1 -1
- package/lib/module/renderers/horizontal-rule.js +10 -8
- package/lib/module/renderers/horizontal-rule.js.map +1 -1
- package/lib/module/renderers/image.js +19 -5
- package/lib/module/renderers/image.js.map +1 -1
- package/lib/module/renderers/link.js +7 -2
- package/lib/module/renderers/link.js.map +1 -1
- package/lib/module/renderers/list.js +75 -68
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/math.js +8 -5
- package/lib/module/renderers/math.js.map +1 -1
- package/lib/module/renderers/paragraph.js +15 -13
- package/lib/module/renderers/paragraph.js.map +1 -1
- package/lib/module/renderers/style-cache.js +10 -0
- package/lib/module/renderers/style-cache.js.map +1 -0
- package/lib/module/renderers/table/cell-content.js +1 -1
- package/lib/module/renderers/table/cell-content.js.map +1 -1
- package/lib/module/renderers/table/index.js +17 -6
- package/lib/module/renderers/table/index.js.map +1 -1
- package/lib/module/theme.js +7 -7
- package/lib/module/theme.js.map +1 -1
- package/lib/module/utils/code-highlight.js +24 -25
- package/lib/module/utils/code-highlight.js.map +1 -1
- package/lib/typescript/commonjs/Markdown.nitro.d.ts +1 -0
- package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts +10 -2
- package/lib/typescript/commonjs/headless.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown.d.ts +8 -3
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
- 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 +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/style-cache.d.ts +3 -0
- package/lib/typescript/commonjs/renderers/style-cache.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table/cell-content.d.ts +4 -3
- package/lib/typescript/commonjs/renderers/table/cell-content.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/table/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/code-highlight.d.ts +1 -1
- package/lib/typescript/commonjs/utils/code-highlight.d.ts.map +1 -1
- package/lib/typescript/module/Markdown.nitro.d.ts +1 -0
- package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/module/headless.d.ts +10 -2
- package/lib/typescript/module/headless.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/markdown.d.ts +8 -3
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.d.ts.map +1 -1
- package/lib/typescript/module/renderers/heading.d.ts +1 -1
- package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
- 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 +1 -1
- package/lib/typescript/module/renderers/list.d.ts.map +1 -1
- package/lib/typescript/module/renderers/math.d.ts +1 -1
- package/lib/typescript/module/renderers/math.d.ts.map +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/module/renderers/style-cache.d.ts +3 -0
- package/lib/typescript/module/renderers/style-cache.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table/cell-content.d.ts +4 -3
- package/lib/typescript/module/renderers/table/cell-content.d.ts.map +1 -1
- package/lib/typescript/module/renderers/table/index.d.ts.map +1 -1
- package/lib/typescript/module/theme.d.ts.map +1 -1
- package/lib/typescript/module/utils/code-highlight.d.ts +1 -1
- package/lib/typescript/module/utils/code-highlight.d.ts.map +1 -1
- package/nitro.json +12 -3
- package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +2 -2
- package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.hpp +2 -2
- package/nitrogen/generated/ios/NitroMarkdown+autolinking.rb +2 -0
- package/nitrogen/generated/shared/c++/ParserOptions.hpp +6 -2
- package/package.json +8 -6
- package/react-native-nitro-markdown.podspec +3 -0
- package/src/Markdown.nitro.ts +1 -0
- package/src/headless.ts +58 -8
- package/src/index.ts +16 -2
- package/src/markdown-stream.tsx +1 -0
- package/src/markdown.tsx +108 -23
- package/src/renderers/blockquote.tsx +22 -17
- package/src/renderers/code.tsx +76 -57
- package/src/renderers/heading.tsx +60 -54
- package/src/renderers/horizontal-rule.tsx +17 -12
- package/src/renderers/image.tsx +24 -5
- package/src/renderers/link.tsx +8 -2
- package/src/renderers/list.tsx +93 -74
- package/src/renderers/math.tsx +14 -5
- package/src/renderers/paragraph.tsx +22 -17
- package/src/renderers/style-cache.ts +14 -0
- package/src/renderers/table/cell-content.tsx +15 -4
- package/src/renderers/table/index.tsx +30 -13
- package/src/theme.ts +34 -14
- package/src/utils/code-highlight.ts +133 -44
package/src/renderers/image.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useState,
|
|
3
|
-
|
|
3
|
+
useEffect,
|
|
4
4
|
useMemo,
|
|
5
5
|
type ReactNode,
|
|
6
6
|
type FC,
|
|
@@ -110,26 +110,45 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer, style }) => {
|
|
|
110
110
|
[theme, aspectRatio],
|
|
111
111
|
);
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
let isMounted = true;
|
|
115
|
+
setLoading(true);
|
|
116
|
+
setError(false);
|
|
117
|
+
setAspectRatio(undefined);
|
|
118
|
+
|
|
115
119
|
const picsumMatch = url.match(/picsum\.photos\/.*\/(\d+)\/(\d+)/);
|
|
116
120
|
if (picsumMatch) {
|
|
117
121
|
const w = parseInt(picsumMatch[1], 10);
|
|
118
122
|
const h = parseInt(picsumMatch[2], 10);
|
|
119
123
|
if (!isNaN(w) && !isNaN(h) && h !== 0) {
|
|
120
124
|
setAspectRatio(w / h);
|
|
125
|
+
return () => {
|
|
126
|
+
isMounted = false;
|
|
127
|
+
};
|
|
121
128
|
}
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
RNImage.getSize(
|
|
125
132
|
url,
|
|
126
133
|
(width, height) => {
|
|
127
|
-
if (width > 0 && height > 0) {
|
|
134
|
+
if (isMounted && width > 0 && height > 0) {
|
|
128
135
|
setAspectRatio(width / height);
|
|
129
136
|
}
|
|
130
137
|
},
|
|
131
|
-
() => {
|
|
138
|
+
(error) => {
|
|
139
|
+
if (__DEV__) {
|
|
140
|
+
// eslint-disable-next-line no-console
|
|
141
|
+
console.warn(
|
|
142
|
+
"[NitroMarkdown] Failed to get image dimensions:",
|
|
143
|
+
error,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
132
147
|
);
|
|
148
|
+
|
|
149
|
+
return () => {
|
|
150
|
+
isMounted = false;
|
|
151
|
+
};
|
|
133
152
|
}, [url]);
|
|
134
153
|
|
|
135
154
|
const altContent = useMemo(() => {
|
package/src/renderers/link.tsx
CHANGED
|
@@ -39,7 +39,8 @@ export const Link: FC<LinkProps> = ({ href, children, style }) => {
|
|
|
39
39
|
if (!normalizedHref) return;
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
|
-
const shouldOpen =
|
|
42
|
+
const shouldOpen =
|
|
43
|
+
(await Promise.resolve(onLinkPress?.(normalizedHref))) !== false;
|
|
43
44
|
if (!shouldOpen) return;
|
|
44
45
|
|
|
45
46
|
const allowedExternalHref = getAllowedExternalHref(normalizedHref);
|
|
@@ -49,7 +50,12 @@ export const Link: FC<LinkProps> = ({ href, children, style }) => {
|
|
|
49
50
|
if (!canOpen) return;
|
|
50
51
|
|
|
51
52
|
await Linking.openURL(allowedExternalHref);
|
|
52
|
-
} catch {
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (__DEV__) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.warn("[NitroMarkdown] Link press handler failed:", error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
53
59
|
};
|
|
54
60
|
|
|
55
61
|
return (
|
package/src/renderers/list.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC, ReactNode } from "react";
|
|
2
2
|
import { View, Text, StyleSheet, Platform, type ViewStyle } from "react-native";
|
|
3
|
+
import { getCachedStyles } from "./style-cache";
|
|
3
4
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
5
|
+
import type { MarkdownTheme } from "../theme";
|
|
4
6
|
|
|
5
7
|
type ListProps = {
|
|
6
8
|
ordered: boolean;
|
|
@@ -10,21 +12,10 @@ type ListProps = {
|
|
|
10
12
|
style?: ViewStyle;
|
|
11
13
|
};
|
|
12
14
|
|
|
15
|
+
// ordered/start stay on List for renderer API parity; markers render in ListItem.
|
|
13
16
|
export const List: FC<ListProps> = ({ depth, children, style }) => {
|
|
14
17
|
const { theme } = useMarkdownContext();
|
|
15
|
-
const styles =
|
|
16
|
-
() =>
|
|
17
|
-
StyleSheet.create({
|
|
18
|
-
list: {
|
|
19
|
-
marginBottom: theme.spacing.m,
|
|
20
|
-
},
|
|
21
|
-
listNested: {
|
|
22
|
-
marginLeft: theme.spacing.s,
|
|
23
|
-
marginBottom: 0,
|
|
24
|
-
},
|
|
25
|
-
}),
|
|
26
|
-
[theme],
|
|
27
|
-
);
|
|
18
|
+
const styles = getCachedStyles(listStylesCache, theme, createListStyles);
|
|
28
19
|
return (
|
|
29
20
|
<View style={[styles.list, depth > 0 && styles.listNested, style]}>
|
|
30
21
|
{children}
|
|
@@ -48,30 +39,10 @@ export const ListItem: FC<ListItemProps> = ({
|
|
|
48
39
|
style,
|
|
49
40
|
}) => {
|
|
50
41
|
const { theme } = useMarkdownContext();
|
|
51
|
-
const styles =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
flexDirection: "row",
|
|
56
|
-
marginBottom: theme.spacing.s,
|
|
57
|
-
alignItems: "flex-start",
|
|
58
|
-
},
|
|
59
|
-
listBullet: {
|
|
60
|
-
color: theme.colors.accent,
|
|
61
|
-
fontSize: theme.fontSizes.m,
|
|
62
|
-
lineHeight: theme.fontSizes.m * 1.6,
|
|
63
|
-
marginRight: theme.spacing.s,
|
|
64
|
-
minWidth: 22,
|
|
65
|
-
textAlign: "center",
|
|
66
|
-
fontFamily: theme.fontFamilies.regular,
|
|
67
|
-
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
68
|
-
},
|
|
69
|
-
listItemContent: {
|
|
70
|
-
flex: 1,
|
|
71
|
-
minWidth: 0,
|
|
72
|
-
},
|
|
73
|
-
}),
|
|
74
|
-
[theme],
|
|
42
|
+
const styles = getCachedStyles(
|
|
43
|
+
listItemStylesCache,
|
|
44
|
+
theme,
|
|
45
|
+
createListItemStyles,
|
|
75
46
|
);
|
|
76
47
|
const bullet = ordered ? `${start + index}.` : "•";
|
|
77
48
|
return (
|
|
@@ -94,42 +65,10 @@ export const TaskListItem: FC<TaskListItemProps> = ({
|
|
|
94
65
|
style,
|
|
95
66
|
}) => {
|
|
96
67
|
const { theme } = useMarkdownContext();
|
|
97
|
-
const styles =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
flexDirection: "row",
|
|
102
|
-
alignItems: "flex-start",
|
|
103
|
-
marginBottom: theme.spacing.s,
|
|
104
|
-
},
|
|
105
|
-
taskCheckbox: {
|
|
106
|
-
width: 18,
|
|
107
|
-
height: 18,
|
|
108
|
-
borderRadius: 4,
|
|
109
|
-
borderWidth: 2,
|
|
110
|
-
borderColor: theme.colors.accent,
|
|
111
|
-
alignItems: "center",
|
|
112
|
-
justifyContent: "center",
|
|
113
|
-
marginRight: theme.spacing.s,
|
|
114
|
-
marginTop: 2,
|
|
115
|
-
},
|
|
116
|
-
taskCheckboxChecked: {
|
|
117
|
-
backgroundColor: theme.colors.accent,
|
|
118
|
-
},
|
|
119
|
-
taskCheckboxText: {
|
|
120
|
-
color: theme.colors.surface,
|
|
121
|
-
fontSize: 12,
|
|
122
|
-
lineHeight: 12,
|
|
123
|
-
fontWeight: "700",
|
|
124
|
-
fontFamily: theme.fontFamilies.regular,
|
|
125
|
-
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
126
|
-
},
|
|
127
|
-
taskContent: {
|
|
128
|
-
flex: 1,
|
|
129
|
-
minWidth: 0,
|
|
130
|
-
},
|
|
131
|
-
}),
|
|
132
|
-
[theme],
|
|
68
|
+
const styles = getCachedStyles(
|
|
69
|
+
taskListItemStylesCache,
|
|
70
|
+
theme,
|
|
71
|
+
createTaskListItemStyles,
|
|
133
72
|
);
|
|
134
73
|
return (
|
|
135
74
|
<View style={[styles.taskListItem, style]}>
|
|
@@ -142,3 +81,83 @@ export const TaskListItem: FC<TaskListItemProps> = ({
|
|
|
142
81
|
</View>
|
|
143
82
|
);
|
|
144
83
|
};
|
|
84
|
+
|
|
85
|
+
type ListStyles = ReturnType<typeof createListStyles>;
|
|
86
|
+
type ListItemStyles = ReturnType<typeof createListItemStyles>;
|
|
87
|
+
type TaskListItemStyles = ReturnType<typeof createTaskListItemStyles>;
|
|
88
|
+
|
|
89
|
+
const listStylesCache = new WeakMap<MarkdownTheme, ListStyles>();
|
|
90
|
+
const listItemStylesCache = new WeakMap<MarkdownTheme, ListItemStyles>();
|
|
91
|
+
const taskListItemStylesCache = new WeakMap<
|
|
92
|
+
MarkdownTheme,
|
|
93
|
+
TaskListItemStyles
|
|
94
|
+
>();
|
|
95
|
+
|
|
96
|
+
const createListStyles = (theme: MarkdownTheme) =>
|
|
97
|
+
StyleSheet.create({
|
|
98
|
+
list: {
|
|
99
|
+
marginBottom: theme.spacing.m,
|
|
100
|
+
},
|
|
101
|
+
listNested: {
|
|
102
|
+
marginLeft: theme.spacing.s,
|
|
103
|
+
marginBottom: 0,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const createListItemStyles = (theme: MarkdownTheme) =>
|
|
108
|
+
StyleSheet.create({
|
|
109
|
+
listItem: {
|
|
110
|
+
flexDirection: "row",
|
|
111
|
+
marginBottom: theme.spacing.s,
|
|
112
|
+
alignItems: "flex-start",
|
|
113
|
+
},
|
|
114
|
+
listBullet: {
|
|
115
|
+
color: theme.colors.accent,
|
|
116
|
+
fontSize: theme.fontSizes.m,
|
|
117
|
+
lineHeight: theme.fontSizes.m * 1.6,
|
|
118
|
+
marginRight: theme.spacing.s,
|
|
119
|
+
minWidth: 22,
|
|
120
|
+
textAlign: "center",
|
|
121
|
+
fontFamily: theme.fontFamilies.regular,
|
|
122
|
+
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
123
|
+
},
|
|
124
|
+
listItemContent: {
|
|
125
|
+
flex: 1,
|
|
126
|
+
minWidth: 0,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const createTaskListItemStyles = (theme: MarkdownTheme) =>
|
|
131
|
+
StyleSheet.create({
|
|
132
|
+
taskListItem: {
|
|
133
|
+
flexDirection: "row",
|
|
134
|
+
alignItems: "flex-start",
|
|
135
|
+
marginBottom: theme.spacing.s,
|
|
136
|
+
},
|
|
137
|
+
taskCheckbox: {
|
|
138
|
+
width: 18,
|
|
139
|
+
height: 18,
|
|
140
|
+
borderRadius: 4,
|
|
141
|
+
borderWidth: 2,
|
|
142
|
+
borderColor: theme.colors.accent,
|
|
143
|
+
alignItems: "center",
|
|
144
|
+
justifyContent: "center",
|
|
145
|
+
marginRight: theme.spacing.s,
|
|
146
|
+
marginTop: 2,
|
|
147
|
+
},
|
|
148
|
+
taskCheckboxChecked: {
|
|
149
|
+
backgroundColor: theme.colors.accent,
|
|
150
|
+
},
|
|
151
|
+
taskCheckboxText: {
|
|
152
|
+
color: theme.colors.surface,
|
|
153
|
+
fontSize: 12,
|
|
154
|
+
lineHeight: 12,
|
|
155
|
+
fontWeight: "700",
|
|
156
|
+
fontFamily: theme.fontFamilies.regular,
|
|
157
|
+
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
158
|
+
},
|
|
159
|
+
taskContent: {
|
|
160
|
+
flex: 1,
|
|
161
|
+
minWidth: 0,
|
|
162
|
+
},
|
|
163
|
+
});
|
package/src/renderers/math.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC, ComponentType } from "react";
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
4
|
Text,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type StyleProp,
|
|
8
8
|
type ViewStyle,
|
|
9
9
|
} from "react-native";
|
|
10
|
+
import { getCachedStyles } from "./style-cache";
|
|
10
11
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
11
12
|
import type { MarkdownTheme } from "../theme";
|
|
12
13
|
|
|
@@ -23,7 +24,12 @@ try {
|
|
|
23
24
|
const mathJaxModule = require("react-native-mathjax-svg");
|
|
24
25
|
MathJaxComponent = mathJaxModule.default || mathJaxModule;
|
|
25
26
|
} catch {
|
|
26
|
-
|
|
27
|
+
if (__DEV__) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.warn(
|
|
30
|
+
"[NitroMarkdown] react-native-mathjax-svg not found — math will render as plain text.",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
type MathInlineProps = {
|
|
@@ -31,11 +37,14 @@ type MathInlineProps = {
|
|
|
31
37
|
style?: ViewStyle;
|
|
32
38
|
};
|
|
33
39
|
|
|
40
|
+
type MathStyles = ReturnType<typeof createMathStyles>;
|
|
41
|
+
|
|
42
|
+
const mathStylesCache = new WeakMap<MarkdownTheme, MathStyles>();
|
|
43
|
+
|
|
34
44
|
const createMathStyles = (theme: MarkdownTheme) =>
|
|
35
45
|
StyleSheet.create({
|
|
36
46
|
mathInlineContainer: {
|
|
37
47
|
marginHorizontal: 2,
|
|
38
|
-
// Ensure the inline view has layout alignment
|
|
39
48
|
justifyContent: "center",
|
|
40
49
|
},
|
|
41
50
|
mathInlineFallbackContainer: {
|
|
@@ -88,7 +97,7 @@ const createMathStyles = (theme: MarkdownTheme) =>
|
|
|
88
97
|
|
|
89
98
|
export const MathInline: FC<MathInlineProps> = ({ content, style }) => {
|
|
90
99
|
const { theme } = useMarkdownContext();
|
|
91
|
-
const styles =
|
|
100
|
+
const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
|
|
92
101
|
|
|
93
102
|
if (!content) return null;
|
|
94
103
|
|
|
@@ -122,7 +131,7 @@ type MathBlockProps = {
|
|
|
122
131
|
|
|
123
132
|
export const MathBlock: FC<MathBlockProps> = ({ content, style }) => {
|
|
124
133
|
const { theme } = useMarkdownContext();
|
|
125
|
-
const styles =
|
|
134
|
+
const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
|
|
126
135
|
|
|
127
136
|
if (!content) return null;
|
|
128
137
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC, ReactNode } from "react";
|
|
2
2
|
import { View, StyleSheet, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { getCachedStyles } from "./style-cache";
|
|
3
4
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
5
|
+
import type { MarkdownTheme } from "../theme";
|
|
4
6
|
|
|
5
7
|
type ParagraphProps = {
|
|
6
8
|
children: ReactNode;
|
|
@@ -14,22 +16,7 @@ export const Paragraph: FC<ParagraphProps> = ({
|
|
|
14
16
|
style,
|
|
15
17
|
}) => {
|
|
16
18
|
const { theme } = useMarkdownContext();
|
|
17
|
-
const styles =
|
|
18
|
-
() =>
|
|
19
|
-
StyleSheet.create({
|
|
20
|
-
paragraph: {
|
|
21
|
-
flexDirection: "row",
|
|
22
|
-
flexWrap: "wrap",
|
|
23
|
-
marginBottom: theme.spacing.l,
|
|
24
|
-
gap: 0,
|
|
25
|
-
},
|
|
26
|
-
paragraphInListItem: {
|
|
27
|
-
marginBottom: 0,
|
|
28
|
-
marginTop: 0,
|
|
29
|
-
},
|
|
30
|
-
}),
|
|
31
|
-
[theme],
|
|
32
|
-
);
|
|
19
|
+
const styles = getCachedStyles(stylesCache, theme, createStyles);
|
|
33
20
|
|
|
34
21
|
return (
|
|
35
22
|
<View
|
|
@@ -43,3 +30,21 @@ export const Paragraph: FC<ParagraphProps> = ({
|
|
|
43
30
|
</View>
|
|
44
31
|
);
|
|
45
32
|
};
|
|
33
|
+
|
|
34
|
+
type ParagraphStyles = ReturnType<typeof createStyles>;
|
|
35
|
+
|
|
36
|
+
const stylesCache = new WeakMap<MarkdownTheme, ParagraphStyles>();
|
|
37
|
+
|
|
38
|
+
const createStyles = (theme: MarkdownTheme) =>
|
|
39
|
+
StyleSheet.create({
|
|
40
|
+
paragraph: {
|
|
41
|
+
flexDirection: "row",
|
|
42
|
+
flexWrap: "wrap",
|
|
43
|
+
marginBottom: theme.spacing.l,
|
|
44
|
+
gap: 0,
|
|
45
|
+
},
|
|
46
|
+
paragraphInListItem: {
|
|
47
|
+
marginBottom: 0,
|
|
48
|
+
marginTop: 0,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MarkdownTheme } from "../theme";
|
|
2
|
+
|
|
3
|
+
export function getCachedStyles<T>(
|
|
4
|
+
cache: WeakMap<MarkdownTheme, T>,
|
|
5
|
+
theme: MarkdownTheme,
|
|
6
|
+
createStyles: (theme: MarkdownTheme) => T,
|
|
7
|
+
): T {
|
|
8
|
+
const cached = cache.get(theme);
|
|
9
|
+
if (cached) return cached;
|
|
10
|
+
|
|
11
|
+
const styles = createStyles(theme);
|
|
12
|
+
cache.set(theme, styles);
|
|
13
|
+
return styles;
|
|
14
|
+
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { FC, ComponentType } from "react";
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
type TextStyle,
|
|
7
|
+
type ViewStyle,
|
|
8
|
+
} from "react-native";
|
|
3
9
|
import type { MarkdownNode } from "../../headless";
|
|
4
10
|
import type { NodeRendererProps } from "../../MarkdownContext";
|
|
5
11
|
|
|
@@ -7,7 +13,8 @@ type CellContentProps = {
|
|
|
7
13
|
node: MarkdownNode;
|
|
8
14
|
Renderer: ComponentType<NodeRendererProps>;
|
|
9
15
|
styles: {
|
|
10
|
-
cellContentWrapper:
|
|
16
|
+
cellContentWrapper: StyleProp<ViewStyle>;
|
|
17
|
+
[key: string]: StyleProp<ViewStyle | TextStyle> | undefined;
|
|
11
18
|
};
|
|
12
19
|
textStyle?: StyleProp<TextStyle>;
|
|
13
20
|
};
|
|
@@ -26,7 +33,11 @@ export const CellContent: FC<CellContentProps> = ({
|
|
|
26
33
|
<View style={styles.cellContentWrapper}>
|
|
27
34
|
{node.children.map((child, idx) => (
|
|
28
35
|
<Renderer
|
|
29
|
-
key={
|
|
36
|
+
key={
|
|
37
|
+
child.beg != null
|
|
38
|
+
? `${child.type}-${child.beg}`
|
|
39
|
+
: `${child.type}-${idx}`
|
|
40
|
+
}
|
|
30
41
|
node={child}
|
|
31
42
|
depth={0}
|
|
32
43
|
inListItem={false}
|
|
@@ -15,15 +15,19 @@ import {
|
|
|
15
15
|
type ViewStyle,
|
|
16
16
|
type LayoutChangeEvent,
|
|
17
17
|
} from "react-native";
|
|
18
|
-
import { useMarkdownContext, type NodeRendererProps } from "../../MarkdownContext";
|
|
19
|
-
import type { MarkdownTheme } from "../../theme";
|
|
20
18
|
import { CellContent } from "./cell-content";
|
|
21
|
-
import { extractTableData, estimateColumnWidths } from "./table-utils";
|
|
22
19
|
import {
|
|
23
20
|
columnWidthsReducer,
|
|
24
21
|
DEFAULT_MIN_COLUMN_WIDTH,
|
|
25
22
|
DEFAULT_MEASUREMENT_STABILIZE_MS,
|
|
26
23
|
} from "./table-reducer";
|
|
24
|
+
import { extractTableData, estimateColumnWidths } from "./table-utils";
|
|
25
|
+
import {
|
|
26
|
+
useMarkdownContext,
|
|
27
|
+
type NodeRendererProps,
|
|
28
|
+
} from "../../MarkdownContext";
|
|
29
|
+
import { getCachedStyles } from "../style-cache";
|
|
30
|
+
import type { MarkdownTheme } from "../../theme";
|
|
27
31
|
|
|
28
32
|
type TableRendererProps = {
|
|
29
33
|
node: import("../../headless").MarkdownNode;
|
|
@@ -55,7 +59,7 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
55
59
|
);
|
|
56
60
|
|
|
57
61
|
const columnCount = headers.length;
|
|
58
|
-
const styles =
|
|
62
|
+
const styles = getCachedStyles(tableStylesCache, theme, createTableStyles);
|
|
59
63
|
const estimatedColumnWidths = useMemo(
|
|
60
64
|
() => estimateColumnWidths(headers, rows, columnCount, minColumnWidth),
|
|
61
65
|
[headers, rows, columnCount, minColumnWidth],
|
|
@@ -147,6 +151,15 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
147
151
|
};
|
|
148
152
|
}, [estimatedColumnWidths, expectedCellKeySignature, measurementStabilizeMs]);
|
|
149
153
|
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
const widthsMap = measuredWidths.current;
|
|
156
|
+
const cellsSet = measuredCells.current;
|
|
157
|
+
return () => {
|
|
158
|
+
widthsMap.clear();
|
|
159
|
+
cellsSet.clear();
|
|
160
|
+
};
|
|
161
|
+
}, []);
|
|
162
|
+
|
|
150
163
|
const onCellLayout = useCallback(
|
|
151
164
|
(cellKey: string, width: number) => {
|
|
152
165
|
if (width <= 0 || widthsCalculated.current || !needsMeasurement) return;
|
|
@@ -201,6 +214,14 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
201
214
|
return "flex-start";
|
|
202
215
|
};
|
|
203
216
|
|
|
217
|
+
const tableBackgroundStyle = useMemo(
|
|
218
|
+
() => ({
|
|
219
|
+
backgroundColor:
|
|
220
|
+
style?.backgroundColor ?? theme.colors.surface ?? "#111827",
|
|
221
|
+
}),
|
|
222
|
+
[style, theme.colors.surface],
|
|
223
|
+
);
|
|
224
|
+
|
|
204
225
|
if (columnCount === 0) return null;
|
|
205
226
|
|
|
206
227
|
const hasWidths = columnWidths.length === columnCount;
|
|
@@ -257,15 +278,7 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
257
278
|
style={styles.tableScroll}
|
|
258
279
|
bounces={false}
|
|
259
280
|
>
|
|
260
|
-
<View
|
|
261
|
-
style={[
|
|
262
|
-
styles.table,
|
|
263
|
-
{
|
|
264
|
-
backgroundColor:
|
|
265
|
-
style?.backgroundColor ?? theme.colors.surface ?? "#111827",
|
|
266
|
-
},
|
|
267
|
-
]}
|
|
268
|
-
>
|
|
281
|
+
<View style={[styles.table, tableBackgroundStyle]}>
|
|
269
282
|
<View style={styles.headerRow}>
|
|
270
283
|
{headers.map((cell, colIndex) => (
|
|
271
284
|
<View
|
|
@@ -326,6 +339,10 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
326
339
|
);
|
|
327
340
|
};
|
|
328
341
|
|
|
342
|
+
type TableStyles = ReturnType<typeof createTableStyles>;
|
|
343
|
+
|
|
344
|
+
const tableStylesCache = new WeakMap<MarkdownTheme, TableStyles>();
|
|
345
|
+
|
|
329
346
|
const createTableStyles = (theme: MarkdownTheme) => {
|
|
330
347
|
const colors = theme?.colors || {};
|
|
331
348
|
const borderRadius = theme?.borderRadius || { m: 8 };
|
package/src/theme.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Platform, type TextStyle, type ViewStyle } from "react-native";
|
|
2
|
-
import type { MarkdownNode } from "./headless";
|
|
3
2
|
|
|
4
3
|
export type MarkdownTheme = {
|
|
5
4
|
colors: {
|
|
@@ -85,13 +84,13 @@ export const defaultMarkdownTheme: MarkdownTheme = {
|
|
|
85
84
|
tableRowEven: "transparent",
|
|
86
85
|
tableRowOdd: "#f8fafc",
|
|
87
86
|
codeTokenColors: {
|
|
88
|
-
keyword:
|
|
89
|
-
string:
|
|
90
|
-
comment:
|
|
91
|
-
number:
|
|
92
|
-
operator:
|
|
93
|
-
punctuation:
|
|
94
|
-
type:
|
|
87
|
+
keyword: "#c792ea",
|
|
88
|
+
string: "#c3e88d",
|
|
89
|
+
comment: "#546e7a",
|
|
90
|
+
number: "#f78c6c",
|
|
91
|
+
operator: "#89ddff",
|
|
92
|
+
punctuation: "#89ddff",
|
|
93
|
+
type: "#ffcb6b",
|
|
95
94
|
},
|
|
96
95
|
},
|
|
97
96
|
spacing: {
|
|
@@ -149,13 +148,34 @@ export type PartialMarkdownTheme = {
|
|
|
149
148
|
};
|
|
150
149
|
|
|
151
150
|
type TextNodeType =
|
|
152
|
-
| "text"
|
|
153
|
-
| "
|
|
151
|
+
| "text"
|
|
152
|
+
| "bold"
|
|
153
|
+
| "italic"
|
|
154
|
+
| "strikethrough"
|
|
155
|
+
| "link"
|
|
156
|
+
| "code_inline"
|
|
157
|
+
| "heading"
|
|
158
|
+
| "paragraph"
|
|
159
|
+
| "math_inline"
|
|
160
|
+
| "html_inline";
|
|
154
161
|
type ViewNodeType =
|
|
155
|
-
| "document"
|
|
156
|
-
| "
|
|
157
|
-
| "
|
|
158
|
-
| "
|
|
162
|
+
| "document"
|
|
163
|
+
| "blockquote"
|
|
164
|
+
| "code_block"
|
|
165
|
+
| "horizontal_rule"
|
|
166
|
+
| "image"
|
|
167
|
+
| "list"
|
|
168
|
+
| "list_item"
|
|
169
|
+
| "task_list_item"
|
|
170
|
+
| "table"
|
|
171
|
+
| "table_head"
|
|
172
|
+
| "table_body"
|
|
173
|
+
| "table_row"
|
|
174
|
+
| "table_cell"
|
|
175
|
+
| "math_block"
|
|
176
|
+
| "html_block"
|
|
177
|
+
| "line_break"
|
|
178
|
+
| "soft_break";
|
|
159
179
|
|
|
160
180
|
export type NodeStyleOverrides = Partial<
|
|
161
181
|
{ [K in TextNodeType]: TextStyle } & { [K in ViewNodeType]: ViewStyle }
|