react-native-nitro-markdown 0.5.3 → 0.5.5

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 (125) hide show
  1. package/README.md +89 -10
  2. package/android/CMakeLists.txt +1 -1
  3. package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +9 -3
  4. package/cpp/CMakeLists.txt +2 -1
  5. package/cpp/bindings/HybridMarkdownParser.cpp +4 -2
  6. package/cpp/core/MD4CParser.cpp +69 -2
  7. package/cpp/core/MD4CParser.hpp +17 -0
  8. package/cpp/core/MarkdownTypes.hpp +1 -1
  9. package/lib/commonjs/headless.js +2 -2
  10. package/lib/commonjs/markdown.js +56 -44
  11. package/lib/commonjs/markdown.js.map +1 -1
  12. package/lib/commonjs/renderers/blockquote.js +15 -13
  13. package/lib/commonjs/renderers/blockquote.js.map +1 -1
  14. package/lib/commonjs/renderers/code.js +57 -53
  15. package/lib/commonjs/renderers/code.js.map +1 -1
  16. package/lib/commonjs/renderers/heading.js +48 -46
  17. package/lib/commonjs/renderers/heading.js.map +1 -1
  18. package/lib/commonjs/renderers/horizontal-rule.js +10 -8
  19. package/lib/commonjs/renderers/horizontal-rule.js.map +1 -1
  20. package/lib/commonjs/renderers/image.js +12 -3
  21. package/lib/commonjs/renderers/image.js.map +1 -1
  22. package/lib/commonjs/renderers/list.js +75 -70
  23. package/lib/commonjs/renderers/list.js.map +1 -1
  24. package/lib/commonjs/renderers/math.js +4 -3
  25. package/lib/commonjs/renderers/math.js.map +1 -1
  26. package/lib/commonjs/renderers/paragraph.js +15 -13
  27. package/lib/commonjs/renderers/paragraph.js.map +1 -1
  28. package/lib/commonjs/renderers/style-cache.js +14 -0
  29. package/lib/commonjs/renderers/style-cache.js.map +1 -0
  30. package/lib/commonjs/renderers/table/index.js +7 -4
  31. package/lib/commonjs/renderers/table/index.js.map +1 -1
  32. package/lib/module/headless.js +2 -2
  33. package/lib/module/markdown.js +56 -44
  34. package/lib/module/markdown.js.map +1 -1
  35. package/lib/module/renderers/blockquote.js +15 -13
  36. package/lib/module/renderers/blockquote.js.map +1 -1
  37. package/lib/module/renderers/code.js +57 -53
  38. package/lib/module/renderers/code.js.map +1 -1
  39. package/lib/module/renderers/heading.js +48 -46
  40. package/lib/module/renderers/heading.js.map +1 -1
  41. package/lib/module/renderers/horizontal-rule.js +10 -8
  42. package/lib/module/renderers/horizontal-rule.js.map +1 -1
  43. package/lib/module/renderers/image.js +13 -4
  44. package/lib/module/renderers/image.js.map +1 -1
  45. package/lib/module/renderers/list.js +75 -70
  46. package/lib/module/renderers/list.js.map +1 -1
  47. package/lib/module/renderers/math.js +4 -3
  48. package/lib/module/renderers/math.js.map +1 -1
  49. package/lib/module/renderers/paragraph.js +15 -13
  50. package/lib/module/renderers/paragraph.js.map +1 -1
  51. package/lib/module/renderers/style-cache.js +10 -0
  52. package/lib/module/renderers/style-cache.js.map +1 -0
  53. package/lib/module/renderers/table/index.js +7 -4
  54. package/lib/module/renderers/table/index.js.map +1 -1
  55. package/lib/typescript/commonjs/Markdown.nitro.d.ts +1 -0
  56. package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/headless.d.ts +2 -2
  58. package/lib/typescript/commonjs/markdown.d.ts +7 -1
  59. package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/renderers/blockquote.d.ts +1 -1
  61. package/lib/typescript/commonjs/renderers/blockquote.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
  63. package/lib/typescript/commonjs/renderers/heading.d.ts +1 -1
  64. package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +1 -1
  66. package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/renderers/list.d.ts +1 -1
  69. package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/renderers/math.d.ts +1 -1
  71. package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/renderers/paragraph.d.ts +1 -1
  73. package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
  74. package/lib/typescript/commonjs/renderers/style-cache.d.ts +3 -0
  75. package/lib/typescript/commonjs/renderers/style-cache.d.ts.map +1 -0
  76. package/lib/typescript/commonjs/renderers/table/index.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/theme.d.ts +2 -2
  78. package/lib/typescript/commonjs/theme.d.ts.map +1 -1
  79. package/lib/typescript/module/Markdown.nitro.d.ts +1 -0
  80. package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -1
  81. package/lib/typescript/module/headless.d.ts +2 -2
  82. package/lib/typescript/module/markdown.d.ts +7 -1
  83. package/lib/typescript/module/markdown.d.ts.map +1 -1
  84. package/lib/typescript/module/renderers/blockquote.d.ts +1 -1
  85. package/lib/typescript/module/renderers/blockquote.d.ts.map +1 -1
  86. package/lib/typescript/module/renderers/code.d.ts.map +1 -1
  87. package/lib/typescript/module/renderers/heading.d.ts +1 -1
  88. package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
  89. package/lib/typescript/module/renderers/horizontal-rule.d.ts +1 -1
  90. package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
  91. package/lib/typescript/module/renderers/image.d.ts.map +1 -1
  92. package/lib/typescript/module/renderers/list.d.ts +1 -1
  93. package/lib/typescript/module/renderers/list.d.ts.map +1 -1
  94. package/lib/typescript/module/renderers/math.d.ts +1 -1
  95. package/lib/typescript/module/renderers/math.d.ts.map +1 -1
  96. package/lib/typescript/module/renderers/paragraph.d.ts +1 -1
  97. package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
  98. package/lib/typescript/module/renderers/style-cache.d.ts +3 -0
  99. package/lib/typescript/module/renderers/style-cache.d.ts.map +1 -0
  100. package/lib/typescript/module/renderers/table/index.d.ts.map +1 -1
  101. package/lib/typescript/module/theme.d.ts +2 -2
  102. package/lib/typescript/module/theme.d.ts.map +1 -1
  103. package/nitro.json +12 -3
  104. package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +2 -2
  105. package/nitrogen/generated/android/c++/JFunc_void.hpp +2 -2
  106. package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +2 -2
  107. package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.hpp +2 -2
  108. package/nitrogen/generated/ios/NitroMarkdown+autolinking.rb +2 -0
  109. package/nitrogen/generated/shared/c++/ParserOptions.hpp +6 -2
  110. package/package.json +6 -5
  111. package/react-native-nitro-markdown.podspec +3 -0
  112. package/src/Markdown.nitro.ts +1 -0
  113. package/src/headless.ts +2 -2
  114. package/src/markdown.tsx +102 -58
  115. package/src/renderers/blockquote.tsx +22 -17
  116. package/src/renderers/code.tsx +75 -63
  117. package/src/renderers/heading.tsx +60 -54
  118. package/src/renderers/horizontal-rule.tsx +17 -12
  119. package/src/renderers/image.tsx +15 -4
  120. package/src/renderers/list.tsx +93 -76
  121. package/src/renderers/math.tsx +8 -3
  122. package/src/renderers/paragraph.tsx +22 -17
  123. package/src/renderers/style-cache.ts +14 -0
  124. package/src/renderers/table/index.tsx +15 -10
  125. package/src/theme.ts +2 -2
@@ -1,6 +1,8 @@
1
- import { useMemo, type FC, type ReactNode } from "react";
1
+ import type { FC, ReactNode } from "react";
2
2
  import { Text, StyleSheet, Platform, type TextStyle } 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 HeadingProps = {
6
8
  level: number;
@@ -20,59 +22,7 @@ const ANDROID_SYSTEM_FONTS = new Set([
20
22
 
21
23
  export const Heading: FC<HeadingProps> = ({ level, children, style }) => {
22
24
  const { theme } = useMarkdownContext();
23
- const headingWeight =
24
- theme.headingWeight ??
25
- (Platform.OS === "android" &&
26
- theme.fontFamilies.heading &&
27
- !ANDROID_SYSTEM_FONTS.has(theme.fontFamilies.heading)
28
- ? "normal"
29
- : "700");
30
- const styles = useMemo(
31
- () =>
32
- StyleSheet.create({
33
- heading: {
34
- color: theme.colors.heading,
35
- fontWeight: headingWeight,
36
- marginTop: theme.spacing.xl,
37
- marginBottom: theme.spacing.m,
38
- fontFamily: theme.fontFamilies.heading,
39
- letterSpacing: -0.2,
40
- ...(Platform.OS === "android" && { includeFontPadding: false }),
41
- },
42
- h1: {
43
- fontSize: theme.fontSizes.h1,
44
- lineHeight: theme.fontSizes.h1 * 1.3,
45
- borderBottomWidth: 1,
46
- borderBottomColor: theme.colors.border,
47
- paddingBottom: theme.spacing.s,
48
- letterSpacing: -0.6,
49
- },
50
- h2: {
51
- fontSize: theme.fontSizes.h2,
52
- lineHeight: theme.fontSizes.h2 * 1.3,
53
- letterSpacing: -0.4,
54
- },
55
- h3: {
56
- fontSize: theme.fontSizes.h3,
57
- lineHeight: theme.fontSizes.h3 * 1.3,
58
- letterSpacing: -0.2,
59
- },
60
- h4: {
61
- fontSize: theme.fontSizes.h4,
62
- lineHeight: theme.fontSizes.h4 * 1.3,
63
- },
64
- h5: {
65
- fontSize: theme.fontSizes.h5,
66
- lineHeight: theme.fontSizes.h5 * 1.3,
67
- },
68
- h6: {
69
- fontSize: theme.fontSizes.h6,
70
- lineHeight: theme.fontSizes.h6 * 1.3,
71
- color: theme.colors.textMuted,
72
- },
73
- }),
74
- [headingWeight, theme],
75
- );
25
+ const styles = getCachedStyles(stylesCache, theme, createStyles);
76
26
 
77
27
  const headingStyles = [
78
28
  styles.heading,
@@ -86,3 +36,59 @@ export const Heading: FC<HeadingProps> = ({ level, children, style }) => {
86
36
 
87
37
  return <Text style={[...headingStyles, style]}>{children}</Text>;
88
38
  };
39
+
40
+ type HeadingStyles = ReturnType<typeof createStyles>;
41
+
42
+ const stylesCache = new WeakMap<MarkdownTheme, HeadingStyles>();
43
+
44
+ const getHeadingWeight = (theme: MarkdownTheme) =>
45
+ theme.headingWeight ??
46
+ (Platform.OS === "android" &&
47
+ theme.fontFamilies.heading &&
48
+ !ANDROID_SYSTEM_FONTS.has(theme.fontFamilies.heading)
49
+ ? "normal"
50
+ : "700");
51
+
52
+ const createStyles = (theme: MarkdownTheme) =>
53
+ StyleSheet.create({
54
+ heading: {
55
+ color: theme.colors.heading,
56
+ fontWeight: getHeadingWeight(theme),
57
+ marginTop: theme.spacing.xl,
58
+ marginBottom: theme.spacing.m,
59
+ fontFamily: theme.fontFamilies.heading,
60
+ letterSpacing: -0.2,
61
+ ...(Platform.OS === "android" && { includeFontPadding: false }),
62
+ },
63
+ h1: {
64
+ fontSize: theme.fontSizes.h1,
65
+ lineHeight: theme.fontSizes.h1 * 1.3,
66
+ borderBottomWidth: 1,
67
+ borderBottomColor: theme.colors.border,
68
+ paddingBottom: theme.spacing.s,
69
+ letterSpacing: -0.6,
70
+ },
71
+ h2: {
72
+ fontSize: theme.fontSizes.h2,
73
+ lineHeight: theme.fontSizes.h2 * 1.3,
74
+ letterSpacing: -0.4,
75
+ },
76
+ h3: {
77
+ fontSize: theme.fontSizes.h3,
78
+ lineHeight: theme.fontSizes.h3 * 1.3,
79
+ letterSpacing: -0.2,
80
+ },
81
+ h4: {
82
+ fontSize: theme.fontSizes.h4,
83
+ lineHeight: theme.fontSizes.h4 * 1.3,
84
+ },
85
+ h5: {
86
+ fontSize: theme.fontSizes.h5,
87
+ lineHeight: theme.fontSizes.h5 * 1.3,
88
+ },
89
+ h6: {
90
+ fontSize: theme.fontSizes.h6,
91
+ lineHeight: theme.fontSizes.h6 * 1.3,
92
+ color: theme.colors.textMuted,
93
+ },
94
+ });
@@ -1,6 +1,8 @@
1
- import { useMemo, type FC } from "react";
1
+ import type { FC } from "react";
2
2
  import { View, StyleSheet, 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 HorizontalRuleProps = {
6
8
  style?: ViewStyle;
@@ -8,16 +10,19 @@ type HorizontalRuleProps = {
8
10
 
9
11
  export const HorizontalRule: FC<HorizontalRuleProps> = ({ style }) => {
10
12
  const { theme } = useMarkdownContext();
11
- const styles = useMemo(
12
- () =>
13
- StyleSheet.create({
14
- horizontalRule: {
15
- height: 1,
16
- backgroundColor: theme.colors.border,
17
- marginVertical: theme.spacing.xl,
18
- },
19
- }),
20
- [theme],
21
- );
13
+ const styles = getCachedStyles(stylesCache, theme, createStyles);
22
14
  return <View style={[styles.horizontalRule, style]} />;
23
15
  };
16
+
17
+ type HorizontalRuleStyles = ReturnType<typeof createStyles>;
18
+
19
+ const stylesCache = new WeakMap<MarkdownTheme, HorizontalRuleStyles>();
20
+
21
+ const createStyles = (theme: MarkdownTheme) =>
22
+ StyleSheet.create({
23
+ horizontalRule: {
24
+ height: 1,
25
+ backgroundColor: theme.colors.border,
26
+ marginVertical: theme.spacing.xl,
27
+ },
28
+ });
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useState,
3
- useLayoutEffect,
3
+ useEffect,
4
4
  useMemo,
5
5
  type ReactNode,
6
6
  type FC,
@@ -110,21 +110,28 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer, style }) => {
110
110
  [theme, aspectRatio],
111
111
  );
112
112
 
113
- useLayoutEffect(() => {
114
- // Fast path for consistent aspect ratios if checking picsum
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
  },
@@ -138,6 +145,10 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer, style }) => {
138
145
  }
139
146
  },
140
147
  );
148
+
149
+ return () => {
150
+ isMounted = false;
151
+ };
141
152
  }, [url]);
142
153
 
143
154
  const altContent = useMemo(() => {
@@ -1,6 +1,8 @@
1
- import { useMemo, type FC, type ReactNode } from "react";
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,23 +12,10 @@ type ListProps = {
10
12
  style?: ViewStyle;
11
13
  };
12
14
 
13
- // Note: ordered and start are accepted for API consistency but layout is
14
- // handled at the ListItem level — the List container is style-only.
15
+ // ordered/start stay on List for renderer API parity; markers render in ListItem.
15
16
  export const List: FC<ListProps> = ({ depth, children, style }) => {
16
17
  const { theme } = useMarkdownContext();
17
- const styles = useMemo(
18
- () =>
19
- StyleSheet.create({
20
- list: {
21
- marginBottom: theme.spacing.m,
22
- },
23
- listNested: {
24
- marginLeft: theme.spacing.s,
25
- marginBottom: 0,
26
- },
27
- }),
28
- [theme],
29
- );
18
+ const styles = getCachedStyles(listStylesCache, theme, createListStyles);
30
19
  return (
31
20
  <View style={[styles.list, depth > 0 && styles.listNested, style]}>
32
21
  {children}
@@ -50,30 +39,10 @@ export const ListItem: FC<ListItemProps> = ({
50
39
  style,
51
40
  }) => {
52
41
  const { theme } = useMarkdownContext();
53
- const styles = useMemo(
54
- () =>
55
- StyleSheet.create({
56
- listItem: {
57
- flexDirection: "row",
58
- marginBottom: theme.spacing.s,
59
- alignItems: "flex-start",
60
- },
61
- listBullet: {
62
- color: theme.colors.accent,
63
- fontSize: theme.fontSizes.m,
64
- lineHeight: theme.fontSizes.m * 1.6,
65
- marginRight: theme.spacing.s,
66
- minWidth: 22,
67
- textAlign: "center",
68
- fontFamily: theme.fontFamilies.regular,
69
- ...(Platform.OS === "android" && { includeFontPadding: false }),
70
- },
71
- listItemContent: {
72
- flex: 1,
73
- minWidth: 0,
74
- },
75
- }),
76
- [theme],
42
+ const styles = getCachedStyles(
43
+ listItemStylesCache,
44
+ theme,
45
+ createListItemStyles,
77
46
  );
78
47
  const bullet = ordered ? `${start + index}.` : "•";
79
48
  return (
@@ -96,42 +65,10 @@ export const TaskListItem: FC<TaskListItemProps> = ({
96
65
  style,
97
66
  }) => {
98
67
  const { theme } = useMarkdownContext();
99
- const styles = useMemo(
100
- () =>
101
- StyleSheet.create({
102
- taskListItem: {
103
- flexDirection: "row",
104
- alignItems: "flex-start",
105
- marginBottom: theme.spacing.s,
106
- },
107
- taskCheckbox: {
108
- width: 18,
109
- height: 18,
110
- borderRadius: 4,
111
- borderWidth: 2,
112
- borderColor: theme.colors.accent,
113
- alignItems: "center",
114
- justifyContent: "center",
115
- marginRight: theme.spacing.s,
116
- marginTop: 2,
117
- },
118
- taskCheckboxChecked: {
119
- backgroundColor: theme.colors.accent,
120
- },
121
- taskCheckboxText: {
122
- color: theme.colors.surface,
123
- fontSize: 12,
124
- lineHeight: 12,
125
- fontWeight: "700",
126
- fontFamily: theme.fontFamilies.regular,
127
- ...(Platform.OS === "android" && { includeFontPadding: false }),
128
- },
129
- taskContent: {
130
- flex: 1,
131
- minWidth: 0,
132
- },
133
- }),
134
- [theme],
68
+ const styles = getCachedStyles(
69
+ taskListItemStylesCache,
70
+ theme,
71
+ createTaskListItemStyles,
135
72
  );
136
73
  return (
137
74
  <View style={[styles.taskListItem, style]}>
@@ -144,3 +81,83 @@ export const TaskListItem: FC<TaskListItemProps> = ({
144
81
  </View>
145
82
  );
146
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
+ });
@@ -1,4 +1,4 @@
1
- import { useMemo, type FC, type ComponentType } from "react";
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
 
@@ -36,6 +37,10 @@ type MathInlineProps = {
36
37
  style?: ViewStyle;
37
38
  };
38
39
 
40
+ type MathStyles = ReturnType<typeof createMathStyles>;
41
+
42
+ const mathStylesCache = new WeakMap<MarkdownTheme, MathStyles>();
43
+
39
44
  const createMathStyles = (theme: MarkdownTheme) =>
40
45
  StyleSheet.create({
41
46
  mathInlineContainer: {
@@ -92,7 +97,7 @@ const createMathStyles = (theme: MarkdownTheme) =>
92
97
 
93
98
  export const MathInline: FC<MathInlineProps> = ({ content, style }) => {
94
99
  const { theme } = useMarkdownContext();
95
- const styles = useMemo(() => createMathStyles(theme), [theme]);
100
+ const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
96
101
 
97
102
  if (!content) return null;
98
103
 
@@ -126,7 +131,7 @@ type MathBlockProps = {
126
131
 
127
132
  export const MathBlock: FC<MathBlockProps> = ({ content, style }) => {
128
133
  const { theme } = useMarkdownContext();
129
- const styles = useMemo(() => createMathStyles(theme), [theme]);
134
+ const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
130
135
 
131
136
  if (!content) return null;
132
137
 
@@ -1,6 +1,8 @@
1
- import { useMemo, type FC, type ReactNode } from "react";
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 = useMemo(
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
+ }
@@ -26,6 +26,7 @@ import {
26
26
  useMarkdownContext,
27
27
  type NodeRendererProps,
28
28
  } from "../../MarkdownContext";
29
+ import { getCachedStyles } from "../style-cache";
29
30
  import type { MarkdownTheme } from "../../theme";
30
31
 
31
32
  type TableRendererProps = {
@@ -58,7 +59,7 @@ export const TableRenderer: FC<TableRendererProps> = ({
58
59
  );
59
60
 
60
61
  const columnCount = headers.length;
61
- const styles = useMemo(() => createTableStyles(theme), [theme]);
62
+ const styles = getCachedStyles(tableStylesCache, theme, createTableStyles);
62
63
  const estimatedColumnWidths = useMemo(
63
64
  () => estimateColumnWidths(headers, rows, columnCount, minColumnWidth),
64
65
  [headers, rows, columnCount, minColumnWidth],
@@ -213,6 +214,14 @@ export const TableRenderer: FC<TableRendererProps> = ({
213
214
  return "flex-start";
214
215
  };
215
216
 
217
+ const tableBackgroundStyle = useMemo(
218
+ () => ({
219
+ backgroundColor:
220
+ style?.backgroundColor ?? theme.colors.surface ?? "#111827",
221
+ }),
222
+ [style, theme.colors.surface],
223
+ );
224
+
216
225
  if (columnCount === 0) return null;
217
226
 
218
227
  const hasWidths = columnWidths.length === columnCount;
@@ -269,15 +278,7 @@ export const TableRenderer: FC<TableRendererProps> = ({
269
278
  style={styles.tableScroll}
270
279
  bounces={false}
271
280
  >
272
- <View
273
- style={[
274
- styles.table,
275
- {
276
- backgroundColor:
277
- style?.backgroundColor ?? theme.colors.surface ?? "#111827",
278
- },
279
- ]}
280
- >
281
+ <View style={[styles.table, tableBackgroundStyle]}>
281
282
  <View style={styles.headerRow}>
282
283
  {headers.map((cell, colIndex) => (
283
284
  <View
@@ -338,6 +339,10 @@ export const TableRenderer: FC<TableRendererProps> = ({
338
339
  );
339
340
  };
340
341
 
342
+ type TableStyles = ReturnType<typeof createTableStyles>;
343
+
344
+ const tableStylesCache = new WeakMap<MarkdownTheme, TableStyles>();
345
+
341
346
  const createTableStyles = (theme: MarkdownTheme) => {
342
347
  const colors = theme?.colors || {};
343
348
  const borderRadius = theme?.borderRadius || { m: 8 };
package/src/theme.ts CHANGED
@@ -155,8 +155,6 @@ type TextNodeType =
155
155
  | "link"
156
156
  | "code_inline"
157
157
  | "heading"
158
- | "paragraph"
159
- | "math_inline"
160
158
  | "html_inline";
161
159
  type ViewNodeType =
162
160
  | "document"
@@ -172,6 +170,8 @@ type ViewNodeType =
172
170
  | "table_body"
173
171
  | "table_row"
174
172
  | "table_cell"
173
+ | "paragraph"
174
+ | "math_inline"
175
175
  | "math_block"
176
176
  | "html_block"
177
177
  | "line_break"