react-native-nitro-markdown 0.4.2 → 0.4.3
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 +310 -352
- package/lib/commonjs/MarkdownContext.js +2 -1
- package/lib/commonjs/MarkdownContext.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/markdown-stream.js +3 -1
- package/lib/commonjs/markdown-stream.js.map +1 -1
- package/lib/commonjs/markdown.js +51 -35
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/code.js +3 -3
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/heading.js +1 -1
- package/lib/commonjs/renderers/heading.js.map +1 -1
- package/lib/commonjs/renderers/image.js +7 -5
- package/lib/commonjs/renderers/image.js.map +1 -1
- package/lib/commonjs/renderers/link.js +15 -3
- package/lib/commonjs/renderers/link.js.map +1 -1
- package/lib/commonjs/renderers/list.js +2 -2
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/table.js +36 -23
- package/lib/commonjs/renderers/table.js.map +1 -1
- package/lib/commonjs/use-markdown-stream.js +16 -14
- package/lib/commonjs/use-markdown-stream.js.map +1 -1
- package/lib/commonjs/utils/link-security.js +21 -0
- package/lib/commonjs/utils/link-security.js.map +1 -0
- package/lib/commonjs/utils/stream-timeline.js +62 -0
- package/lib/commonjs/utils/stream-timeline.js.map +1 -0
- package/lib/module/MarkdownContext.js +2 -1
- package/lib/module/MarkdownContext.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/markdown-stream.js +3 -1
- package/lib/module/markdown-stream.js.map +1 -1
- package/lib/module/markdown.js +52 -36
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/blockquote.js.map +1 -1
- package/lib/module/renderers/code.js +3 -3
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/heading.js +1 -1
- package/lib/module/renderers/heading.js.map +1 -1
- package/lib/module/renderers/image.js +7 -5
- package/lib/module/renderers/image.js.map +1 -1
- package/lib/module/renderers/link.js +15 -3
- package/lib/module/renderers/link.js.map +1 -1
- package/lib/module/renderers/list.js +2 -2
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/paragraph.js.map +1 -1
- package/lib/module/renderers/table.js +37 -24
- package/lib/module/renderers/table.js.map +1 -1
- package/lib/module/use-markdown-stream.js +16 -14
- package/lib/module/use-markdown-stream.js.map +1 -1
- package/lib/module/utils/link-security.js +15 -0
- package/lib/module/utils/link-security.js.map +1 -0
- package/lib/module/utils/stream-timeline.js +56 -0
- package/lib/module/utils/stream-timeline.js.map +1 -0
- package/lib/typescript/commonjs/Markdown.nitro.d.ts +3 -3
- package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/MarkdownContext.d.ts +26 -25
- package/lib/typescript/commonjs/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts +2 -2
- package/lib/typescript/commonjs/headless.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown-stream.d.ts +2 -2
- package/lib/typescript/commonjs/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown.d.ts +9 -4
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.d.ts +5 -5
- package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +2 -2
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/image.d.ts +2 -2
- package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/link.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts +7 -7
- package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts +4 -4
- package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/table.d.ts +3 -3
- package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme.d.ts +2 -2
- package/lib/typescript/commonjs/theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/use-markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/link-security.d.ts +3 -0
- package/lib/typescript/commonjs/utils/link-security.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/stream-timeline.d.ts +11 -0
- package/lib/typescript/commonjs/utils/stream-timeline.d.ts.map +1 -0
- package/lib/typescript/module/Markdown.nitro.d.ts +3 -3
- package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/module/MarkdownContext.d.ts +26 -25
- package/lib/typescript/module/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/module/headless.d.ts +2 -2
- package/lib/typescript/module/headless.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/markdown-stream.d.ts +2 -2
- package/lib/typescript/module/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/markdown.d.ts +9 -4
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts +3 -3
- package/lib/typescript/module/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.d.ts +5 -5
- package/lib/typescript/module/renderers/code.d.ts.map +1 -1
- package/lib/typescript/module/renderers/heading.d.ts +3 -3
- package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts +2 -2
- package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/module/renderers/image.d.ts +2 -2
- package/lib/typescript/module/renderers/image.d.ts.map +1 -1
- package/lib/typescript/module/renderers/link.d.ts +3 -3
- package/lib/typescript/module/renderers/link.d.ts.map +1 -1
- package/lib/typescript/module/renderers/list.d.ts +7 -7
- package/lib/typescript/module/renderers/list.d.ts.map +1 -1
- package/lib/typescript/module/renderers/math.d.ts +4 -4
- package/lib/typescript/module/renderers/math.d.ts.map +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts +3 -3
- package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/module/renderers/table.d.ts +3 -3
- package/lib/typescript/module/renderers/table.d.ts.map +1 -1
- package/lib/typescript/module/theme.d.ts +2 -2
- package/lib/typescript/module/theme.d.ts.map +1 -1
- package/lib/typescript/module/use-markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/utils/link-security.d.ts +3 -0
- package/lib/typescript/module/utils/link-security.d.ts.map +1 -0
- package/lib/typescript/module/utils/stream-timeline.d.ts +11 -0
- package/lib/typescript/module/utils/stream-timeline.d.ts.map +1 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +0 -1
- package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec.swift +0 -1
- package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec_cxx.swift +0 -1
- package/package.json +4 -3
- package/src/Markdown.nitro.ts +5 -3
- package/src/MarkdownContext.ts +31 -25
- package/src/headless.ts +2 -2
- package/src/index.ts +1 -0
- package/src/markdown-stream.tsx +6 -10
- package/src/markdown.tsx +86 -45
- package/src/renderers/blockquote.tsx +4 -4
- package/src/renderers/code.tsx +11 -9
- package/src/renderers/heading.tsx +4 -4
- package/src/renderers/horizontal-rule.tsx +3 -3
- package/src/renderers/image.tsx +11 -9
- package/src/renderers/link.tsx +25 -7
- package/src/renderers/list.tsx +9 -12
- package/src/renderers/math.tsx +4 -4
- package/src/renderers/paragraph.tsx +3 -3
- package/src/renderers/table.tsx +77 -51
- package/src/theme.ts +3 -3
- package/src/use-markdown-stream.ts +22 -16
- package/src/utils/link-security.ts +22 -0
- package/src/utils/stream-timeline.ts +72 -0
package/README.md
CHANGED
|
@@ -5,54 +5,37 @@
|
|
|
5
5
|
|
|
6
6
|
# react-native-nitro-markdown
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Native Markdown parsing and rendering for React Native.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`react-native-nitro-markdown` uses `md4c` (C++) through Nitro Modules (JSI) to parse Markdown synchronously into a typed AST, then render it with customizable React Native components.
|
|
11
11
|
|
|
12
|
-
## Why
|
|
12
|
+
## Why use it
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Markdown string -> md4c C++ parser -> JSON AST -> React Native renderers
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- Native C++ parser with JSI access (fast, synchronous parsing)
|
|
25
|
-
- Full renderer included (Markdown component)
|
|
26
|
-
- Headless API for custom renderers or processing
|
|
14
|
+
- Native parser (`md4c`) for lower JS thread overhead on large documents
|
|
15
|
+
- End-to-end solution: parser + renderer + streaming session API
|
|
16
|
+
- Headless API for custom rendering and text processing
|
|
27
17
|
- GFM support (tables, strikethrough, task lists, autolinks)
|
|
28
|
-
-
|
|
29
|
-
- Streaming support for token-by-token updates
|
|
30
|
-
- Theming and per-node style overrides
|
|
31
|
-
- Built-in renderers exposed for reuse
|
|
32
|
-
|
|
33
|
-
---
|
|
18
|
+
- Optional math rendering with `react-native-mathjax-svg`
|
|
34
19
|
|
|
35
20
|
## Requirements
|
|
36
21
|
|
|
37
|
-
- React Native
|
|
38
|
-
- react-native-nitro-modules
|
|
22
|
+
- React Native `>=0.75.0`
|
|
23
|
+
- `react-native-nitro-modules`
|
|
39
24
|
|
|
40
|
-
Optional
|
|
25
|
+
Optional for math rendering:
|
|
41
26
|
|
|
42
|
-
- react-native-mathjax-svg
|
|
43
|
-
- react-native-svg
|
|
44
|
-
|
|
45
|
-
---
|
|
27
|
+
- `react-native-mathjax-svg >=0.9.0`
|
|
28
|
+
- `react-native-svg >=13.0.0`
|
|
46
29
|
|
|
47
30
|
## Installation
|
|
48
31
|
|
|
49
|
-
|
|
32
|
+
### React Native
|
|
50
33
|
|
|
51
34
|
```bash
|
|
52
35
|
bun add react-native-nitro-markdown react-native-nitro-modules
|
|
53
36
|
```
|
|
54
37
|
|
|
55
|
-
Optional math
|
|
38
|
+
Optional math support:
|
|
56
39
|
|
|
57
40
|
```bash
|
|
58
41
|
bun add react-native-mathjax-svg react-native-svg
|
|
@@ -64,168 +47,135 @@ iOS pods:
|
|
|
64
47
|
cd ios && pod install
|
|
65
48
|
```
|
|
66
49
|
|
|
67
|
-
Expo (
|
|
50
|
+
### Expo (development build)
|
|
68
51
|
|
|
69
52
|
```bash
|
|
70
53
|
bunx expo install react-native-nitro-markdown react-native-nitro-modules
|
|
71
54
|
bunx expo prebuild
|
|
72
55
|
```
|
|
73
56
|
|
|
74
|
-
|
|
57
|
+
Optional math support:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bunx expo install react-native-mathjax-svg react-native-svg
|
|
61
|
+
```
|
|
75
62
|
|
|
76
63
|
## Quick Start
|
|
77
64
|
|
|
78
65
|
```tsx
|
|
79
66
|
import { Markdown } from "react-native-nitro-markdown";
|
|
80
67
|
|
|
81
|
-
export function
|
|
68
|
+
export function Example() {
|
|
82
69
|
return (
|
|
83
70
|
<Markdown options={{ gfm: true }}>
|
|
84
|
-
{"# Hello
|
|
71
|
+
{"# Hello\nThis is **native** markdown."}
|
|
85
72
|
</Markdown>
|
|
86
73
|
);
|
|
87
74
|
}
|
|
88
75
|
```
|
|
89
76
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
- `
|
|
97
|
-
- `
|
|
98
|
-
- `
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
- `
|
|
109
|
-
- `
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
77
|
+
## Package Exports
|
|
78
|
+
|
|
79
|
+
### Main Entry (`react-native-nitro-markdown`)
|
|
80
|
+
|
|
81
|
+
- Parser and headless helpers:
|
|
82
|
+
- `parseMarkdown`
|
|
83
|
+
- `parseMarkdownWithOptions`
|
|
84
|
+
- `getTextContent`
|
|
85
|
+
- `getFlattenedText`
|
|
86
|
+
- `MarkdownParserModule`
|
|
87
|
+
- Components:
|
|
88
|
+
- `Markdown`
|
|
89
|
+
- `MarkdownStream`
|
|
90
|
+
- Hooks and sessions:
|
|
91
|
+
- `useMarkdownSession`
|
|
92
|
+
- `useStream`
|
|
93
|
+
- `createMarkdownSession`
|
|
94
|
+
- Context:
|
|
95
|
+
- `MarkdownContext`
|
|
96
|
+
- `useMarkdownContext`
|
|
97
|
+
- Theme:
|
|
98
|
+
- `defaultMarkdownTheme`
|
|
99
|
+
- `minimalMarkdownTheme`
|
|
100
|
+
- `mergeThemes`
|
|
101
|
+
- Built-in renderers:
|
|
102
|
+
- `Heading`, `Paragraph`, `Link`, `Blockquote`, `HorizontalRule`
|
|
103
|
+
- `CodeBlock`, `InlineCode`
|
|
104
|
+
- `List`, `ListItem`, `TaskListItem`
|
|
105
|
+
- `TableRenderer`, `Image`, `MathInline`, `MathBlock`
|
|
106
|
+
- Types:
|
|
107
|
+
- `MarkdownNode`, `ParserOptions`, `MarkdownParser`
|
|
108
|
+
- `CustomRenderers`, `CustomRenderer`, `CustomRendererProps`
|
|
109
|
+
- `NodeRendererProps`, `BaseCustomRendererProps`, `EnhancedRendererProps`
|
|
110
|
+
- `HeadingRendererProps`, `LinkRendererProps`, `ImageRendererProps`
|
|
111
|
+
- `CodeBlockRendererProps`, `InlineCodeRendererProps`
|
|
112
|
+
- `ListRendererProps`, `TaskListItemRendererProps`
|
|
113
|
+
- `LinkPressHandler`, `MarkdownContextValue`
|
|
114
|
+
- `MarkdownTheme`, `PartialMarkdownTheme`, `NodeStyleOverrides`, `StylingStrategy`
|
|
115
|
+
- `MarkdownSession`
|
|
116
|
+
|
|
117
|
+
### Headless Entry (`react-native-nitro-markdown/headless`)
|
|
118
|
+
|
|
119
|
+
Exports only parser-related API (`parseMarkdown`, `parseMarkdownWithOptions`, `getTextContent`, `getFlattenedText`, types). Use this when you do not need built-in UI rendering.
|
|
120
|
+
|
|
121
|
+
## Component API
|
|
122
|
+
|
|
123
|
+
## `Markdown`
|
|
114
124
|
|
|
115
125
|
```tsx
|
|
116
126
|
import { Markdown } from "react-native-nitro-markdown";
|
|
117
|
-
|
|
118
|
-
const theme = {
|
|
119
|
-
colors: {
|
|
120
|
-
text: "#0f172a",
|
|
121
|
-
heading: "#0f172a",
|
|
122
|
-
link: "#2563eb",
|
|
123
|
-
codeBackground: "#f1f5f9",
|
|
124
|
-
},
|
|
125
|
-
showCodeLanguage: true,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
<Markdown theme={theme}>{content}</Markdown>;
|
|
129
127
|
```
|
|
130
128
|
|
|
131
|
-
|
|
129
|
+
| Prop | Type | Default | Description |
|
|
130
|
+
| --------------------- | ---------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------- | -------------------- |
|
|
131
|
+
| `children` | `string` | required | Markdown input string. |
|
|
132
|
+
| `options` | `ParserOptions` | `undefined` | Parser flags (`gfm`, `math`). |
|
|
133
|
+
| `renderers` | `CustomRenderers` | `{}` | Per-node custom renderers. |
|
|
134
|
+
| `theme` | `PartialMarkdownTheme` | `defaultMarkdownTheme` or `minimalMarkdownTheme` | Theme token overrides. |
|
|
135
|
+
| `styles` | `NodeStyleOverrides` | `undefined` | Per-node style overrides. |
|
|
136
|
+
| `stylingStrategy` | `"opinionated" | "minimal"` | `"opinionated"` | Base styling preset. |
|
|
137
|
+
| `style` | `StyleProp<ViewStyle>` | `undefined` | Container style for the root `View`. |
|
|
138
|
+
| `onParsingInProgress` | `() => void` | `undefined` | Called when parse inputs change. |
|
|
139
|
+
| `onParseComplete` | `(result) => void` | `undefined` | Called with `{ raw, ast, text }` after successful parse. |
|
|
140
|
+
| `onLinkPress` | `LinkPressHandler` | `undefined` | Intercepts link press before default open behavior. Return `false` to block default open. |
|
|
132
141
|
|
|
133
|
-
|
|
142
|
+
Notes:
|
|
134
143
|
|
|
135
|
-
|
|
144
|
+
- Parse failures are caught and rendered as a fallback message (`Error parsing markdown`).
|
|
145
|
+
- `text` in `onParseComplete` is produced by `getFlattenedText(ast)`.
|
|
136
146
|
|
|
137
|
-
|
|
138
|
-
<Markdown
|
|
139
|
-
styles={{
|
|
140
|
-
heading: { color: "#0ea5e9", fontWeight: "900" },
|
|
141
|
-
code_block: { backgroundColor: "#e2e8f0", borderRadius: 16 },
|
|
142
|
-
blockquote: { borderLeftColor: "#0ea5e9" },
|
|
143
|
-
}}
|
|
144
|
-
>
|
|
145
|
-
{content}
|
|
146
|
-
</Markdown>
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### 4) Custom renderers
|
|
150
|
-
|
|
151
|
-
Provide a custom renderer for any node type. You get pre-mapped props for common values.
|
|
147
|
+
## `MarkdownStream`
|
|
152
148
|
|
|
153
149
|
```tsx
|
|
154
|
-
import {
|
|
155
|
-
Markdown,
|
|
156
|
-
CodeBlock,
|
|
157
|
-
type HeadingRendererProps,
|
|
158
|
-
type CodeBlockRendererProps,
|
|
159
|
-
} from "react-native-nitro-markdown";
|
|
160
|
-
|
|
161
|
-
const renderers = {
|
|
162
|
-
heading: ({ level, children }: HeadingRendererProps) => (
|
|
163
|
-
<MyHeading level={level}>{children}</MyHeading>
|
|
164
|
-
),
|
|
165
|
-
code_block: ({ content, language }: CodeBlockRendererProps) => (
|
|
166
|
-
<CodeBlock content={content} language={language} />
|
|
167
|
-
),
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
<Markdown renderers={renderers}>{content}</Markdown>;
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Custom renderer behavior:
|
|
174
|
-
|
|
175
|
-
- Return `undefined` to fall back to the built-in renderer.
|
|
176
|
-
- Return `null` to render nothing for that node.
|
|
177
|
-
- The `Renderer` prop lets you render nested children the same way the default renderer does.
|
|
178
|
-
|
|
179
|
-
Pre-mapped props by node type:
|
|
180
|
-
|
|
181
|
-
| Node type | Extra props |
|
|
182
|
-
| --- | --- |
|
|
183
|
-
| `heading` | `level` |
|
|
184
|
-
| `link` | `href`, `title` |
|
|
185
|
-
| `image` | `url`, `alt`, `title` |
|
|
186
|
-
| `code_block` | `content`, `language` |
|
|
187
|
-
| `code_inline` | `content` |
|
|
188
|
-
| `list` | `ordered`, `start` |
|
|
189
|
-
| `task_list_item` | `checked` |
|
|
190
|
-
|
|
191
|
-
### 5) Built-in renderers
|
|
192
|
-
|
|
193
|
-
All built-in renderers are exported so you can reuse them in custom renderers.
|
|
194
|
-
|
|
195
|
-
```tsx
|
|
196
|
-
import { Heading, CodeBlock, InlineCode } from "react-native-nitro-markdown";
|
|
150
|
+
import { MarkdownStream } from "react-native-nitro-markdown";
|
|
197
151
|
```
|
|
198
152
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
- `Heading`
|
|
202
|
-
- `Paragraph`
|
|
203
|
-
- `Link`
|
|
204
|
-
- `Blockquote`
|
|
205
|
-
- `HorizontalRule`
|
|
206
|
-
- `CodeBlock`
|
|
207
|
-
- `InlineCode`
|
|
208
|
-
- `List`
|
|
209
|
-
- `ListItem`
|
|
210
|
-
- `TaskListItem`
|
|
211
|
-
- `TableRenderer`
|
|
212
|
-
- `Image`
|
|
213
|
-
- `MathInline`
|
|
214
|
-
- `MathBlock`
|
|
153
|
+
`MarkdownStreamProps` extends `MarkdownProps` except `children`.
|
|
215
154
|
|
|
216
|
-
|
|
155
|
+
| Prop | Type | Default | Description |
|
|
156
|
+
| ---------------------- | ----------------- | -------- | -------------------------------------------------- | --------------------------------------------------------- |
|
|
157
|
+
| `session` | `MarkdownSession` | required | Session object that supplies streamed text chunks. |
|
|
158
|
+
| `updateIntervalMs` | `number` | `50` | Flush interval when `updateStrategy="interval"`. |
|
|
159
|
+
| `updateStrategy` | `"interval" | "raf"` | `"interval"` | Update cadence (`setTimeout` vs `requestAnimationFrame`). |
|
|
160
|
+
| `useTransitionUpdates` | `boolean` | `false` | Applies `startTransition` to streamed UI updates. |
|
|
217
161
|
|
|
218
|
-
|
|
162
|
+
### Streaming Example
|
|
219
163
|
|
|
220
164
|
```tsx
|
|
221
165
|
import { useEffect } from "react";
|
|
222
|
-
import {
|
|
166
|
+
import {
|
|
167
|
+
MarkdownStream,
|
|
168
|
+
useMarkdownSession,
|
|
169
|
+
} from "react-native-nitro-markdown";
|
|
223
170
|
|
|
224
|
-
export function
|
|
171
|
+
export function StreamingExample() {
|
|
225
172
|
const session = useMarkdownSession();
|
|
226
173
|
|
|
227
174
|
useEffect(() => {
|
|
228
|
-
session.getSession()
|
|
175
|
+
const s = session.getSession();
|
|
176
|
+
s.append("# Streaming\n");
|
|
177
|
+
s.append("This text arrives in chunks.");
|
|
178
|
+
|
|
229
179
|
return () => session.clear();
|
|
230
180
|
}, [session]);
|
|
231
181
|
|
|
@@ -240,15 +190,51 @@ export function AIResponseStream() {
|
|
|
240
190
|
}
|
|
241
191
|
```
|
|
242
192
|
|
|
243
|
-
|
|
193
|
+
## Hooks and Session API
|
|
194
|
+
|
|
195
|
+
## `createMarkdownSession()`
|
|
196
|
+
|
|
197
|
+
Creates and returns a native `MarkdownSession` instance.
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { createMarkdownSession } from "react-native-nitro-markdown";
|
|
201
|
+
|
|
202
|
+
const session = createMarkdownSession();
|
|
203
|
+
session.append("hello");
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## `useMarkdownSession()`
|
|
207
|
+
|
|
208
|
+
Creates and owns one `MarkdownSession` for a component lifecycle.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
|
|
212
|
+
| Field | Type | Description |
|
|
213
|
+
| ---------------- | ---------------------------- | ------------------------------------------------------------- |
|
|
214
|
+
| `getSession` | `() => MarkdownSession` | Returns the stable native session instance. |
|
|
215
|
+
| `isStreaming` | `boolean` | Stateful flag for app-level streaming UI. |
|
|
216
|
+
| `setIsStreaming` | `(value: boolean) => void` | Setter for `isStreaming`. |
|
|
217
|
+
| `stop` | `() => void` | Sets `isStreaming` to `false`. |
|
|
218
|
+
| `clear` | `() => void` | Clears session content and resets `highlightPosition` to `0`. |
|
|
219
|
+
| `setHighlight` | `(position: number) => void` | Sets `session.highlightPosition`. |
|
|
244
220
|
|
|
245
|
-
|
|
246
|
-
- `updateIntervalMs` between `50` and `100` when using interval strategy
|
|
247
|
-
- Avoid per-token UI updates by batching appends
|
|
221
|
+
## `useStream(timestamps?)`
|
|
248
222
|
|
|
249
|
-
|
|
223
|
+
Builds on `useMarkdownSession` and adds timeline sync helpers.
|
|
250
224
|
|
|
251
|
-
|
|
225
|
+
- `timestamps` type: `Record<number, number>` where key = word/token index, value = timestamp in ms.
|
|
226
|
+
- `sync(currentTimeMs)` computes highlight position from timestamp map.
|
|
227
|
+
- Uses optimized lookup for monotonic timelines and handles non-monotonic maps safely.
|
|
228
|
+
|
|
229
|
+
Additional returned fields:
|
|
230
|
+
|
|
231
|
+
| Field | Type | Description |
|
|
232
|
+
| -------------- | --------------------------------- | ----------------------------------------- |
|
|
233
|
+
| `isPlaying` | `boolean` | Playback state for timed streaming. |
|
|
234
|
+
| `setIsPlaying` | `(value: boolean) => void` | Setter for `isPlaying`. |
|
|
235
|
+
| `sync` | `(currentTimeMs: number) => void` | Applies timeline-based highlight updates. |
|
|
236
|
+
|
|
237
|
+
## Headless API
|
|
252
238
|
|
|
253
239
|
```tsx
|
|
254
240
|
import {
|
|
@@ -257,262 +243,234 @@ import {
|
|
|
257
243
|
getTextContent,
|
|
258
244
|
getFlattenedText,
|
|
259
245
|
} from "react-native-nitro-markdown/headless";
|
|
260
|
-
|
|
261
|
-
const ast = parseMarkdown("# Hello World");
|
|
262
|
-
const text = getTextContent(ast);
|
|
263
|
-
const normalized = getFlattenedText(ast);
|
|
264
246
|
```
|
|
265
247
|
|
|
266
|
-
|
|
248
|
+
| Function | Signature | Description |
|
|
249
|
+
| -------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
250
|
+
| `parseMarkdown` | `(text: string) => MarkdownNode` | Parses markdown using default parser settings. |
|
|
251
|
+
| `parseMarkdownWithOptions` | `(text: string, options: ParserOptions) => MarkdownNode` | Parses markdown with `gfm` and/or `math` flags. |
|
|
252
|
+
| `getTextContent` | `(node: MarkdownNode) => string` | Concatenates text recursively without layout normalization. |
|
|
253
|
+
| `getFlattenedText` | `(node: MarkdownNode) => string` | Returns normalized plain text with paragraph and block separators. |
|
|
267
254
|
|
|
268
|
-
|
|
255
|
+
### Parser Options
|
|
269
256
|
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
>
|
|
276
|
-
{content}
|
|
277
|
-
</Markdown>
|
|
257
|
+
```ts
|
|
258
|
+
type ParserOptions = {
|
|
259
|
+
gfm?: boolean;
|
|
260
|
+
math?: boolean;
|
|
261
|
+
};
|
|
278
262
|
```
|
|
279
263
|
|
|
280
|
-
|
|
264
|
+
## Custom Renderer API
|
|
281
265
|
|
|
282
|
-
|
|
283
|
-
- Images use React Native `Image` and try to preserve the real aspect ratio.
|
|
284
|
-
- Math nodes render with `react-native-mathjax-svg` if installed; otherwise they fall back to a code-style look.
|
|
266
|
+
## `renderers` prop contract
|
|
285
267
|
|
|
286
|
-
|
|
268
|
+
`CustomRenderers` is:
|
|
287
269
|
|
|
288
|
-
|
|
270
|
+
```ts
|
|
271
|
+
type CustomRenderers = Partial<Record<MarkdownNode["type"], CustomRenderer>>;
|
|
272
|
+
```
|
|
289
273
|
|
|
290
|
-
|
|
274
|
+
`CustomRenderer` receives `EnhancedRendererProps` and returns:
|
|
291
275
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
276
|
+
- React node to override default rendering
|
|
277
|
+
- `undefined` to fallback to built-in renderer
|
|
278
|
+
- `null` to render nothing
|
|
295
279
|
|
|
296
|
-
|
|
297
|
-
link: ({ href, children }: LinkRendererProps) => (
|
|
298
|
-
<Text
|
|
299
|
-
style={{ textDecorationLine: "underline" }}
|
|
300
|
-
onPress={async () => {
|
|
301
|
-
if (href && (await Linking.canOpenURL(href))) {
|
|
302
|
-
Linking.openURL(href);
|
|
303
|
-
}
|
|
304
|
-
}}
|
|
305
|
-
>
|
|
306
|
-
{children}
|
|
307
|
-
</Text>
|
|
308
|
-
),
|
|
309
|
-
};
|
|
280
|
+
`EnhancedRendererProps` always includes:
|
|
310
281
|
|
|
311
|
-
|
|
312
|
-
|
|
282
|
+
- `node`: current `MarkdownNode`
|
|
283
|
+
- `children`: pre-rendered node children
|
|
284
|
+
- `Renderer`: recursive renderer component for nested custom rendering
|
|
313
285
|
|
|
314
|
-
|
|
286
|
+
And conditionally includes mapped fields by node type:
|
|
315
287
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
288
|
+
| Node type | Extra mapped fields |
|
|
289
|
+
| ---------------- | --------------------- |
|
|
290
|
+
| `heading` | `level` |
|
|
291
|
+
| `link` | `href`, `title` |
|
|
292
|
+
| `image` | `url`, `alt`, `title` |
|
|
293
|
+
| `code_block` | `content`, `language` |
|
|
294
|
+
| `code_inline` | `content` |
|
|
295
|
+
| `list` | `ordered`, `start` |
|
|
296
|
+
| `task_list_item` | `checked` |
|
|
319
297
|
|
|
320
|
-
|
|
321
|
-
image: ({ url, title, alt }: ImageRendererProps) => (
|
|
322
|
-
<View>
|
|
323
|
-
<Image source={{ uri: url }} style={{ height: 220, borderRadius: 12 }} />
|
|
324
|
-
{(title || alt) && (
|
|
325
|
-
<Text style={{ marginTop: 6, opacity: 0.6 }}>{title || alt}</Text>
|
|
326
|
-
)}
|
|
327
|
-
</View>
|
|
328
|
-
),
|
|
329
|
-
};
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### Render HTML nodes as code (opt-in)
|
|
298
|
+
### Example: Custom heading + code block
|
|
333
299
|
|
|
334
300
|
```tsx
|
|
335
|
-
import {
|
|
301
|
+
import {
|
|
302
|
+
Markdown,
|
|
303
|
+
type HeadingRendererProps,
|
|
304
|
+
type CodeBlockRendererProps,
|
|
305
|
+
} from "react-native-nitro-markdown";
|
|
336
306
|
|
|
337
307
|
const renderers = {
|
|
338
|
-
|
|
339
|
-
|
|
308
|
+
heading: ({ level, children }: HeadingRendererProps) => (
|
|
309
|
+
<MyHeading level={level}>{children}</MyHeading>
|
|
310
|
+
),
|
|
311
|
+
code_block: ({ language, content }: CodeBlockRendererProps) => (
|
|
312
|
+
<MyCode language={language} content={content} />
|
|
313
|
+
),
|
|
340
314
|
};
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### Minimal styling + custom palette
|
|
344
315
|
|
|
345
|
-
|
|
346
|
-
import { Markdown } from "react-native-nitro-markdown";
|
|
347
|
-
|
|
348
|
-
<Markdown
|
|
349
|
-
stylingStrategy="minimal"
|
|
350
|
-
theme={{ colors: { text: "#e2e8f0", link: "#38bdf8" } }}
|
|
351
|
-
>
|
|
352
|
-
{content}
|
|
353
|
-
</Markdown>;
|
|
316
|
+
<Markdown renderers={renderers}>{content}</Markdown>;
|
|
354
317
|
```
|
|
355
318
|
|
|
356
|
-
|
|
319
|
+
## Link Handling Behavior
|
|
357
320
|
|
|
358
|
-
|
|
359
|
-
import { parseMarkdown, getFlattenedText } from "react-native-nitro-markdown/headless";
|
|
321
|
+
Default link renderer behavior:
|
|
360
322
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
323
|
+
1. Trims incoming href.
|
|
324
|
+
2. Calls `onLinkPress(href)` if provided.
|
|
325
|
+
3. Stops if handler returns `false`.
|
|
326
|
+
4. Allows only protocol-based links with these schemes:
|
|
327
|
+
- `http:`
|
|
328
|
+
- `https:`
|
|
329
|
+
- `mailto:`
|
|
330
|
+
- `tel:`
|
|
331
|
+
- `sms:`
|
|
332
|
+
5. Uses `Linking.canOpenURL` before `Linking.openURL`.
|
|
364
333
|
|
|
365
|
-
|
|
334
|
+
Relative URLs and anchors are ignored by default open behavior, but you can handle them in `onLinkPress`.
|
|
366
335
|
|
|
367
|
-
## API
|
|
336
|
+
## Theme API
|
|
368
337
|
|
|
369
|
-
|
|
338
|
+
## `MarkdownTheme`
|
|
370
339
|
|
|
371
340
|
```tsx
|
|
372
|
-
import {
|
|
341
|
+
import type {
|
|
342
|
+
MarkdownTheme,
|
|
343
|
+
PartialMarkdownTheme,
|
|
344
|
+
} from "react-native-nitro-markdown";
|
|
373
345
|
```
|
|
374
346
|
|
|
375
|
-
|
|
347
|
+
`MarkdownTheme` fields:
|
|
376
348
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
| `onParseComplete` | `(result) => void` | `undefined` | Called with `{ raw, ast, text }` |
|
|
349
|
+
- `colors`
|
|
350
|
+
- `text`, `textMuted`, `heading`, `link`, `code`, `codeBackground`, `codeLanguage`
|
|
351
|
+
- `blockquote`, `border`, `surface`, `surfaceLight`, `accent`
|
|
352
|
+
- `tableBorder`, `tableHeader`, `tableHeaderText`, `tableRowEven`, `tableRowOdd`
|
|
353
|
+
- `spacing`: `xs`, `s`, `m`, `l`, `xl`
|
|
354
|
+
- `fontSizes`: `xs`, `s`, `m`, `l`, `xl`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`
|
|
355
|
+
- `fontFamilies`: `regular`, `heading`, `mono`
|
|
356
|
+
- `headingWeight?`
|
|
357
|
+
- `borderRadius`: `s`, `m`, `l`
|
|
358
|
+
- `showCodeLanguage`
|
|
388
359
|
|
|
389
|
-
|
|
360
|
+
Helpers:
|
|
390
361
|
|
|
391
|
-
|
|
392
|
-
|
|
362
|
+
- `defaultMarkdownTheme`
|
|
363
|
+
- `minimalMarkdownTheme`
|
|
364
|
+
- `mergeThemes(base, partial)`
|
|
365
|
+
|
|
366
|
+
`NodeStyleOverrides` lets you override per-node styles:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
type NodeStyleOverrides = Partial<
|
|
370
|
+
Record<MarkdownNode["type"], ViewStyle | TextStyle>
|
|
371
|
+
>;
|
|
393
372
|
```
|
|
394
373
|
|
|
395
|
-
|
|
374
|
+
## Built-in Renderer Components
|
|
375
|
+
|
|
376
|
+
Use these when composing custom renderer maps.
|
|
377
|
+
|
|
378
|
+
| Component | Key props |
|
|
379
|
+
| ---------------- | ------------------------------------------------ |
|
|
380
|
+
| `Heading` | `level`, `children`, `style` |
|
|
381
|
+
| `Paragraph` | `children`, `inListItem`, `style` |
|
|
382
|
+
| `Link` | `href`, `children`, `style` |
|
|
383
|
+
| `Blockquote` | `children`, `style` |
|
|
384
|
+
| `HorizontalRule` | `style` |
|
|
385
|
+
| `CodeBlock` | `language`, `content`, `node`, `style` |
|
|
386
|
+
| `InlineCode` | `content`, `node`, `children`, `style` |
|
|
387
|
+
| `List` | `ordered`, `start`, `depth`, `children`, `style` |
|
|
388
|
+
| `ListItem` | `children`, `index`, `ordered`, `start`, `style` |
|
|
389
|
+
| `TaskListItem` | `children`, `checked`, `style` |
|
|
390
|
+
| `TableRenderer` | `node`, `Renderer`, `style` |
|
|
391
|
+
| `Image` | `url`, `title`, `alt`, `Renderer`, `style` |
|
|
392
|
+
| `MathInline` | `content`, `style` |
|
|
393
|
+
| `MathBlock` | `content`, `style` |
|
|
394
|
+
|
|
395
|
+
## Supported AST Node Types
|
|
396
396
|
|
|
397
|
-
|
|
398
|
-
| --- | --- | --- | --- |
|
|
399
|
-
| `session` | `MarkdownSession` | required | Active session for streaming text |
|
|
400
|
-
| `updateIntervalMs` | `number` | `50` | Throttle interval for updates |
|
|
401
|
-
| `updateStrategy` | `"interval" \| "raf"` | `"interval"` | Update strategy |
|
|
402
|
-
| `useTransitionUpdates` | `boolean` | `false` | Use React transitions |
|
|
397
|
+
`document`, `heading`, `paragraph`, `text`, `bold`, `italic`, `strikethrough`, `link`, `image`, `code_inline`, `code_block`, `blockquote`, `horizontal_rule`, `line_break`, `soft_break`, `table`, `table_head`, `table_body`, `table_row`, `table_cell`, `list`, `list_item`, `task_list_item`, `math_inline`, `math_block`, `html_block`, `html_inline`
|
|
403
398
|
|
|
404
|
-
|
|
399
|
+
Notes:
|
|
405
400
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
```
|
|
401
|
+
- `html_inline` and `html_block` are parsed but not rendered by default.
|
|
402
|
+
- Table internals (`table_head`, `table_body`, `table_row`, `table_cell`) are renderer internals; override `table` for custom table UI.
|
|
409
403
|
|
|
410
|
-
|
|
411
|
-
- `createMarkdownSession()` creates a session without React hooks.
|
|
412
|
-
- `useStream(timestamps)` adds a simple sync helper for time-based streaming.
|
|
404
|
+
## Recipes
|
|
413
405
|
|
|
414
|
-
###
|
|
406
|
+
### Intercept links with `onLinkPress`
|
|
415
407
|
|
|
416
408
|
```tsx
|
|
417
|
-
import {
|
|
409
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
410
|
+
|
|
411
|
+
<Markdown
|
|
412
|
+
onLinkPress={(href) => {
|
|
413
|
+
if (href.startsWith("/")) {
|
|
414
|
+
router.push(href);
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}}
|
|
418
|
+
>
|
|
419
|
+
{content}
|
|
420
|
+
</Markdown>;
|
|
418
421
|
```
|
|
419
422
|
|
|
420
|
-
###
|
|
423
|
+
### Use headless mode to build search index
|
|
421
424
|
|
|
422
425
|
```tsx
|
|
423
426
|
import {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
} from "react-native-nitro-markdown";
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
### Types
|
|
427
|
+
parseMarkdown,
|
|
428
|
+
getFlattenedText,
|
|
429
|
+
} from "react-native-nitro-markdown/headless";
|
|
431
430
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
CustomRenderers,
|
|
435
|
-
NodeStyleOverrides,
|
|
436
|
-
MarkdownTheme,
|
|
437
|
-
PartialMarkdownTheme,
|
|
438
|
-
} from "react-native-nitro-markdown";
|
|
431
|
+
const ast = parseMarkdown(content);
|
|
432
|
+
const searchableText = getFlattenedText(ast);
|
|
439
433
|
```
|
|
440
434
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
## Supported Node Types
|
|
444
|
-
|
|
445
|
-
You can customize any of these node types with `renderers` or `styles`.
|
|
446
|
-
|
|
447
|
-
`document`, `heading`, `paragraph`, `text`, `bold`, `italic`, `strikethrough`, `link`, `image`, `code_inline`, `code_block`, `blockquote`, `horizontal_rule`, `line_break`, `soft_break`, `table`, `table_head`, `table_body`, `table_row`, `table_cell`, `list`, `list_item`, `task_list_item`, `math_inline`, `math_block`, `html_block`, `html_inline`
|
|
435
|
+
### Minimal styling baseline
|
|
448
436
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
---
|
|
452
|
-
|
|
453
|
-
## AST Shape
|
|
454
|
-
|
|
455
|
-
The headless API returns a typed AST. This is the core shape used by the renderer.
|
|
437
|
+
```tsx
|
|
438
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
456
439
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
| "image"
|
|
469
|
-
| "code_inline"
|
|
470
|
-
| "code_block"
|
|
471
|
-
| "blockquote"
|
|
472
|
-
| "horizontal_rule"
|
|
473
|
-
| "line_break"
|
|
474
|
-
| "soft_break"
|
|
475
|
-
| "table"
|
|
476
|
-
| "table_head"
|
|
477
|
-
| "table_body"
|
|
478
|
-
| "table_row"
|
|
479
|
-
| "table_cell"
|
|
480
|
-
| "list"
|
|
481
|
-
| "list_item"
|
|
482
|
-
| "task_list_item"
|
|
483
|
-
| "math_inline"
|
|
484
|
-
| "math_block"
|
|
485
|
-
| "html_block"
|
|
486
|
-
| "html_inline";
|
|
487
|
-
content?: string;
|
|
488
|
-
children?: MarkdownNode[];
|
|
489
|
-
level?: number;
|
|
490
|
-
href?: string;
|
|
491
|
-
title?: string;
|
|
492
|
-
alt?: string;
|
|
493
|
-
language?: string;
|
|
494
|
-
ordered?: boolean;
|
|
495
|
-
start?: number;
|
|
496
|
-
checked?: boolean;
|
|
497
|
-
align?: string;
|
|
498
|
-
isHeader?: boolean;
|
|
499
|
-
}
|
|
440
|
+
<Markdown
|
|
441
|
+
stylingStrategy="minimal"
|
|
442
|
+
theme={{
|
|
443
|
+
colors: {
|
|
444
|
+
text: "#0f172a",
|
|
445
|
+
link: "#1d4ed8",
|
|
446
|
+
},
|
|
447
|
+
}}
|
|
448
|
+
>
|
|
449
|
+
{content}
|
|
450
|
+
</Markdown>;
|
|
500
451
|
```
|
|
501
452
|
|
|
502
|
-
|
|
453
|
+
## Performance Guidance
|
|
503
454
|
|
|
504
|
-
|
|
455
|
+
- For streaming text, prefer `updateStrategy="raf"`.
|
|
456
|
+
- If you use interval strategy, `updateIntervalMs` between `50` and `100` is a good baseline.
|
|
457
|
+
- Batch `session.append(...)` calls instead of appending one character at a time.
|
|
458
|
+
- Use the headless API if you do not need built-in renderers.
|
|
505
459
|
|
|
506
|
-
|
|
507
|
-
- iOS build errors: run `pod install` after installing dependencies.
|
|
508
|
-
- Expo: you must use a development build (`expo prebuild` + `expo run`), not Expo Go.
|
|
509
|
-
- Android heading font looks wrong: set `headingWeight: "normal"` when your font has no bold variant.
|
|
460
|
+
## Troubleshooting
|
|
510
461
|
|
|
511
|
-
|
|
462
|
+
- Math appears as plain code-style fallback:
|
|
463
|
+
- Install `react-native-mathjax-svg` and `react-native-svg`.
|
|
464
|
+
- iOS native build issues after install:
|
|
465
|
+
- Run `pod install` in your iOS project.
|
|
466
|
+
- Expo app does not load native module:
|
|
467
|
+
- Use a development build (`expo prebuild` + `expo run`), not Expo Go.
|
|
468
|
+
- Android heading font weight looks wrong:
|
|
469
|
+
- Set `theme.headingWeight` explicitly (for custom fonts without bold variants, use `"normal"`).
|
|
512
470
|
|
|
513
471
|
## Contributing
|
|
514
472
|
|
|
515
|
-
See
|
|
473
|
+
See `/Users/jota/Workspace/Projects/RN-Packages/react-native-nitro-markdown/CONTRIBUTING.md`.
|
|
516
474
|
|
|
517
475
|
## License
|
|
518
476
|
|