react-native-nitro-markdown 0.5.8 → 0.6.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.
@@ -14,35 +14,24 @@ import { getCachedStyles } from "./style-cache";
14
14
  import { useMarkdownContext } from "../MarkdownContext";
15
15
  import type { MarkdownTheme } from "../theme";
16
16
 
17
- let MathJaxComponent: ComponentType<{
17
+ let RaTeXViewComponent: ComponentType<{
18
+ latex: string;
18
19
  fontSize?: number;
20
+ displayMode?: boolean;
19
21
  color?: string;
20
- fontCache?: boolean;
21
22
  style?: StyleProp<ViewStyle>;
22
- width?: number;
23
- height?: number;
24
- children?: string;
23
+ onError?: (event: { nativeEvent: { error: string } }) => void;
25
24
  }> | null = null;
26
- let SvgFromXmlComponent: ComponentType<{
27
- xml: string;
28
- width?: number;
29
- height?: number;
30
- style?: StyleProp<ViewStyle>;
31
- }> | null = null;
32
- let texToSvg: ((textext: string, fontSize?: number) => string) | null = null;
33
25
 
34
26
  try {
35
27
  // eslint-disable-next-line @typescript-eslint/no-require-imports
36
- const mathJaxModule = require("react-native-mathjax-svg");
37
- MathJaxComponent = mathJaxModule.default || mathJaxModule;
38
- texToSvg = mathJaxModule.texToSvg ?? null;
39
- // eslint-disable-next-line @typescript-eslint/no-require-imports
40
- SvgFromXmlComponent = require("react-native-svg").SvgFromXml;
28
+ const ratexModule = require("ratex-react-native");
29
+ RaTeXViewComponent = ratexModule.RaTeXView ?? null;
41
30
  } catch {
42
31
  if (__DEV__) {
43
32
  // eslint-disable-next-line no-console
44
33
  console.warn(
45
- "[NitroMarkdown] react-native-mathjax-svg not found — math will render as plain text.",
34
+ "[NitroMarkdown] ratex-react-native not found — math will render as plain text.",
46
35
  );
47
36
  }
48
37
  }
@@ -55,34 +44,13 @@ type MathInlineProps = {
55
44
  type MathStyles = ReturnType<typeof createMathStyles>;
56
45
 
57
46
  const mathStylesCache = new WeakMap<MarkdownTheme, MathStyles>();
58
- const SVG_WIDTH_PATTERN = /<svg[^>]*\bwidth="([\d.]+)(?:ex|px)"/i;
59
- const SVG_HEIGHT_PATTERN = /<svg[^>]*\bheight="([\d.]+)(?:ex|px)"/i;
47
+ const INLINE_DISPLAY_MATH_PATTERN =
48
+ /\\(?:frac|dfrac|tfrac|sqrt|sum|prod|int|lim|begin|matrix|pmatrix|bmatrix|cases)\b/;
60
49
 
61
- function colorizeSvg(svg: string, color: string | undefined) {
62
- return color ? svg.replace(/currentColor/gim, color) : svg;
63
- }
64
-
65
- function createMathSvg(
66
- content: string,
67
- fontSize: number,
68
- color: string | undefined,
69
- ) {
70
- if (!texToSvg) return null;
71
-
72
- try {
73
- const xml = colorizeSvg(texToSvg(content, fontSize / 2), color);
74
- const widthMatch = SVG_WIDTH_PATTERN.exec(xml);
75
- const heightMatch = SVG_HEIGHT_PATTERN.exec(xml);
76
- if (!widthMatch || !heightMatch) return null;
77
-
78
- const width = Number.parseFloat(widthMatch[1]);
79
- const height = Number.parseFloat(heightMatch[1]);
80
- if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
81
-
82
- return { xml, width, height };
83
- } catch {
84
- return null;
85
- }
50
+ function getInlineMathFontSize(content: string, theme: MarkdownTheme) {
51
+ return INLINE_DISPLAY_MATH_PATTERN.test(content)
52
+ ? theme.fontSizes.xl + 2
53
+ : theme.fontSizes.l;
86
54
  }
87
55
 
88
56
  type HorizontalMathViewportProps = {
@@ -218,6 +186,10 @@ const createMathStyles = (theme: MarkdownTheme) =>
218
186
  color: theme.colors.code,
219
187
  ...(Platform.OS === "android" && { includeFontPadding: false }),
220
188
  },
189
+ ratexInline: {
190
+ backgroundColor: "transparent",
191
+ flexShrink: 0,
192
+ },
221
193
  mathBlockContainer: {
222
194
  width: "100%",
223
195
  maxWidth: "100%",
@@ -229,7 +201,6 @@ const createMathStyles = (theme: MarkdownTheme) =>
229
201
  borderRadius: theme.borderRadius.l,
230
202
  borderWidth: 1,
231
203
  borderColor: theme.colors.border,
232
- // Ensure we don't collapse if MathJax fails to report size immediately
233
204
  minHeight: 48,
234
205
  overflow: "hidden",
235
206
  },
@@ -244,10 +215,8 @@ const createMathStyles = (theme: MarkdownTheme) =>
244
215
  alignItems: "center",
245
216
  justifyContent: "center",
246
217
  },
247
- mathBlockSvg: {
218
+ ratexBlock: {
248
219
  backgroundColor: "transparent",
249
- },
250
- mathBlockSvgFrame: {
251
220
  flexShrink: 0,
252
221
  },
253
222
  mathBlockFallbackContainer: {
@@ -278,21 +247,23 @@ const createMathStyles = (theme: MarkdownTheme) =>
278
247
  export const MathInline: FC<MathInlineProps> = ({ content, style }) => {
279
248
  const { theme } = useMarkdownContext();
280
249
  const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
250
+ const [hasRenderError, setHasRenderError] = useState(false);
281
251
 
282
252
  if (!content) return null;
283
253
 
284
- if (MathJaxComponent) {
285
- const fontSize = theme.fontSizes.s;
254
+ if (RaTeXViewComponent && !hasRenderError) {
286
255
  return (
287
256
  <View style={[styles.mathInlineContainer, style]}>
288
- <MathJaxComponent
289
- fontSize={fontSize}
257
+ <RaTeXViewComponent
258
+ latex={content}
259
+ fontSize={getInlineMathFontSize(content, theme)}
260
+ displayMode={false}
290
261
  color={theme.colors.text}
291
- fontCache={false}
292
- style={{ backgroundColor: "transparent" }}
293
- >
294
- {content}
295
- </MathJaxComponent>
262
+ style={styles.ratexInline}
263
+ onError={() => {
264
+ setHasRenderError(true);
265
+ }}
266
+ />
296
267
  </View>
297
268
  );
298
269
  }
@@ -312,55 +283,27 @@ type MathBlockProps = {
312
283
  export const MathBlock: FC<MathBlockProps> = ({ content, style }) => {
313
284
  const { theme } = useMarkdownContext();
314
285
  const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
286
+ const [hasRenderError, setHasRenderError] = useState(false);
315
287
 
316
288
  if (!content) return null;
317
289
 
318
- const displayContent = `\\displaystyle ${content}`;
319
- const blockSvg =
320
- SvgFromXmlComponent &&
321
- createMathSvg(displayContent, theme.fontSizes.l, theme.colors.text);
322
-
323
- if (blockSvg && SvgFromXmlComponent) {
324
- return (
325
- <View style={[styles.mathBlockContainer, style]}>
326
- <HorizontalMathViewport
327
- style={styles.mathBlockScroll}
328
- contentStyle={styles.mathBlockScrollContent}
329
- contentWidth={blockSvg.width}
330
- >
331
- <View
332
- style={[
333
- styles.mathBlockSvgFrame,
334
- { width: blockSvg.width, height: blockSvg.height },
335
- ]}
336
- >
337
- <SvgFromXmlComponent
338
- xml={blockSvg.xml}
339
- width={blockSvg.width}
340
- height={blockSvg.height}
341
- style={styles.mathBlockSvg}
342
- />
343
- </View>
344
- </HorizontalMathViewport>
345
- </View>
346
- );
347
- }
348
-
349
- if (MathJaxComponent) {
290
+ if (RaTeXViewComponent && !hasRenderError) {
350
291
  return (
351
292
  <View style={[styles.mathBlockContainer, style]}>
352
293
  <HorizontalMathViewport
353
294
  style={styles.mathBlockScroll}
354
295
  contentStyle={styles.mathBlockScrollContent}
355
296
  >
356
- <MathJaxComponent
357
- fontSize={theme.fontSizes.l}
297
+ <RaTeXViewComponent
298
+ latex={content}
299
+ fontSize={theme.fontSizes.xl}
300
+ displayMode
358
301
  color={theme.colors.text}
359
- fontCache={false}
360
- style={{ backgroundColor: "transparent" }}
361
- >
362
- {displayContent}
363
- </MathJaxComponent>
302
+ style={styles.ratexBlock}
303
+ onError={() => {
304
+ setHasRenderError(true);
305
+ }}
306
+ />
364
307
  </HorizontalMathViewport>
365
308
  </View>
366
309
  );
@@ -1,2 +0,0 @@
1
- // Implementation is provided by the platform-specific HybridObject (iOS: Swift, Android: Kotlin)
2
- // via the Nitrogen-generated bridge. No C++ implementation is required here.
@@ -1,2 +0,0 @@
1
- // Implementation is provided by the platform-specific HybridObject (iOS: Swift, Android: Kotlin)
2
- // via the Nitrogen-generated bridge. No C++ implementation is required here.