react-native-nitro-markdown 0.4.1 → 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 +329 -322
- 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 +32 -15
- 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 +33 -16
- 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 +74 -46
- 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
|
@@ -3,468 +3,475 @@
|
|
|
3
3
|
<img src="./readme/stream-demo.gif" alt="react-native-nitro-markdown stream demo" width="300" />
|
|
4
4
|
</p>
|
|
5
5
|
|
|
6
|
-
# react-native-nitro-markdown
|
|
6
|
+
# react-native-nitro-markdown
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Native Markdown parsing and rendering for React Native.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
[](https://opensource.org/licenses/MIT)
|
|
12
|
-
[](https://nitro.margelo.com)
|
|
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.
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
## Why use it
|
|
15
13
|
|
|
16
|
-
|
|
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
|
|
17
|
+
- GFM support (tables, strikethrough, task lists, autolinks)
|
|
18
|
+
- Optional math rendering with `react-native-mathjax-svg`
|
|
17
19
|
|
|
18
|
-
##
|
|
20
|
+
## Requirements
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
- React Native `>=0.75.0`
|
|
23
|
+
- `react-native-nitro-modules`
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
| :-------------------------- | :--------- | :---------------- | :-------------------- |
|
|
24
|
-
| **🚀 Nitro Markdown (C++)** | **~29 ms** | **1x (Baseline)** | **~1 frame** (Smooth) |
|
|
25
|
-
| 📋 CommonMark (JS) | ~82 ms | 2.8x slower | ~5 frames (Jank) |
|
|
26
|
-
| 🏗️ Markdown-It (JS) | ~118 ms | 4.0x slower | ~7 frames (Jank) |
|
|
27
|
-
| 💨 Marked (JS) | ~400 ms | 13.5x slower | ~24 frames (Freeze) |
|
|
25
|
+
Optional for math rendering:
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
- `react-native-mathjax-svg >=0.9.0`
|
|
28
|
+
- `react-native-svg >=13.0.0`
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
## Installation
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Choose your preferred package manager to install the package and its core dependency (`react-native-nitro-modules`).
|
|
36
|
-
|
|
37
|
-
### **1. Install Dependencies**
|
|
38
|
-
|
|
39
|
-
**npm**
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm install react-native-nitro-markdown react-native-nitro-modules
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
> **Note:** If you want to use **Math** (LaTeX) or certain **Image** features, you should also install the optional peer dependencies:
|
|
46
|
-
> `npm install react-native-svg react-native-mathjax-svg`
|
|
47
|
-
|
|
48
|
-
**Yarn**
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
yarn add react-native-nitro-markdown react-native-nitro-modules
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**Bun**
|
|
32
|
+
### React Native
|
|
55
33
|
|
|
56
34
|
```bash
|
|
57
35
|
bun add react-native-nitro-markdown react-native-nitro-modules
|
|
58
36
|
```
|
|
59
37
|
|
|
60
|
-
|
|
38
|
+
Optional math support:
|
|
61
39
|
|
|
62
40
|
```bash
|
|
63
|
-
|
|
41
|
+
bun add react-native-mathjax-svg react-native-svg
|
|
64
42
|
```
|
|
65
43
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
**Standard**
|
|
44
|
+
iOS pods:
|
|
69
45
|
|
|
70
46
|
```bash
|
|
71
47
|
cd ios && pod install
|
|
72
48
|
```
|
|
73
49
|
|
|
74
|
-
###
|
|
75
|
-
|
|
76
|
-
If you are using Expo, you must run a **Prebuild** (Development Build) because this package contains native C++ code.
|
|
50
|
+
### Expo (development build)
|
|
77
51
|
|
|
78
52
|
```bash
|
|
79
53
|
bunx expo install react-native-nitro-markdown react-native-nitro-modules
|
|
80
54
|
bunx expo prebuild
|
|
81
55
|
```
|
|
82
56
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
## 💻 Usage
|
|
57
|
+
Optional math support:
|
|
86
58
|
|
|
87
|
-
|
|
59
|
+
```bash
|
|
60
|
+
bunx expo install react-native-mathjax-svg react-native-svg
|
|
61
|
+
```
|
|
88
62
|
|
|
89
|
-
|
|
63
|
+
## Quick Start
|
|
90
64
|
|
|
91
65
|
```tsx
|
|
92
66
|
import { Markdown } from "react-native-nitro-markdown";
|
|
93
67
|
|
|
94
|
-
export function
|
|
68
|
+
export function Example() {
|
|
95
69
|
return (
|
|
96
70
|
<Markdown options={{ gfm: true }}>
|
|
97
|
-
{"# Hello
|
|
71
|
+
{"# Hello\nThis is **native** markdown."}
|
|
98
72
|
</Markdown>
|
|
99
73
|
);
|
|
100
74
|
}
|
|
101
75
|
```
|
|
102
76
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
###
|
|
106
|
-
|
|
107
|
-
|
|
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`
|
|
108
124
|
|
|
109
125
|
```tsx
|
|
110
|
-
|
|
111
|
-
styles={{
|
|
112
|
-
heading: { color: "#0ea5e9", fontWeight: "900" },
|
|
113
|
-
code_block: { backgroundColor: "#e2e8f0", borderRadius: 16 },
|
|
114
|
-
blockquote: { borderLeftColor: "#0ea5e9" },
|
|
115
|
-
}}
|
|
116
|
-
>
|
|
117
|
-
{markdown}
|
|
118
|
-
</Markdown>
|
|
126
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
119
127
|
```
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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. |
|
|
124
141
|
|
|
125
|
-
|
|
126
|
-
import {
|
|
127
|
-
Markdown,
|
|
128
|
-
CodeBlock,
|
|
129
|
-
type HeadingRendererProps,
|
|
130
|
-
type CodeBlockRendererProps,
|
|
131
|
-
} from "react-native-nitro-markdown";
|
|
142
|
+
Notes:
|
|
132
143
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
heading: ({ level, children }: HeadingRendererProps) => (
|
|
136
|
-
<MyHeading level={level}>{children}</MyHeading>
|
|
137
|
-
),
|
|
144
|
+
- Parse failures are caught and rendered as a fallback message (`Error parsing markdown`).
|
|
145
|
+
- `text` in `onParseComplete` is produced by `getFlattenedText(ast)`.
|
|
138
146
|
|
|
139
|
-
|
|
140
|
-
code_block: ({ content, language }: CodeBlockRendererProps) => (
|
|
141
|
-
<CodeBlock
|
|
142
|
-
content={content}
|
|
143
|
-
language={language}
|
|
144
|
-
style={{ borderWidth: 2 }}
|
|
145
|
-
/>
|
|
146
|
-
),
|
|
147
|
-
};
|
|
147
|
+
## `MarkdownStream`
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
</Markdown>;
|
|
149
|
+
```tsx
|
|
150
|
+
import { MarkdownStream } from "react-native-nitro-markdown";
|
|
152
151
|
```
|
|
153
152
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
- `heading` → `level` (1-6)
|
|
157
|
-
- `link` → `href`, `title`
|
|
158
|
-
- `image` → `url`, `alt`, `title`
|
|
159
|
-
- `code_block` → `content`, `language`
|
|
160
|
-
- `code_inline` → `content`
|
|
161
|
-
- `list` → `ordered`, `start`
|
|
162
|
-
- `task_list_item` → `checked`
|
|
153
|
+
`MarkdownStreamProps` extends `MarkdownProps` except `children`.
|
|
163
154
|
|
|
164
|
-
|
|
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. |
|
|
165
161
|
|
|
166
|
-
|
|
162
|
+
### Streaming Example
|
|
167
163
|
|
|
168
164
|
```tsx
|
|
169
|
-
import {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
heading: "#0f172a",
|
|
175
|
-
link: "#0ea5e9",
|
|
176
|
-
codeBackground: "#e2e8f0",
|
|
177
|
-
},
|
|
178
|
-
showCodeLanguage: false,
|
|
179
|
-
};
|
|
165
|
+
import { useEffect } from "react";
|
|
166
|
+
import {
|
|
167
|
+
MarkdownStream,
|
|
168
|
+
useMarkdownSession,
|
|
169
|
+
} from "react-native-nitro-markdown";
|
|
180
170
|
|
|
181
|
-
|
|
182
|
-
|
|
171
|
+
export function StreamingExample() {
|
|
172
|
+
const session = useMarkdownSession();
|
|
183
173
|
|
|
184
|
-
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
const s = session.getSession();
|
|
176
|
+
s.append("# Streaming\n");
|
|
177
|
+
s.append("This text arrives in chunks.");
|
|
185
178
|
|
|
186
|
-
|
|
179
|
+
return () => session.clear();
|
|
180
|
+
}, [session]);
|
|
187
181
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
182
|
+
return (
|
|
183
|
+
<MarkdownStream
|
|
184
|
+
session={session.getSession()}
|
|
185
|
+
options={{ gfm: true }}
|
|
186
|
+
updateStrategy="raf"
|
|
187
|
+
useTransitionUpdates
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
195
192
|
|
|
196
|
-
|
|
197
|
-
If you use a custom heading font on Android and don’t load a bold variant, set
|
|
198
|
-
`headingWeight: "normal"` (or use the `styles` prop) to avoid fallback to a
|
|
199
|
-
system serif font.
|
|
193
|
+
## Hooks and Session API
|
|
200
194
|
|
|
201
|
-
|
|
195
|
+
## `createMarkdownSession()`
|
|
202
196
|
|
|
203
|
-
|
|
197
|
+
Creates and returns a native `MarkdownSession` instance.
|
|
204
198
|
|
|
205
199
|
```tsx
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
import { createMarkdownSession } from "react-native-nitro-markdown";
|
|
201
|
+
|
|
202
|
+
const session = createMarkdownSession();
|
|
203
|
+
session.append("hello");
|
|
209
204
|
```
|
|
210
205
|
|
|
211
|
-
|
|
206
|
+
## `useMarkdownSession()`
|
|
212
207
|
|
|
213
|
-
|
|
208
|
+
Creates and owns one `MarkdownSession` for a component lifecycle.
|
|
214
209
|
|
|
215
|
-
|
|
210
|
+
Returns:
|
|
216
211
|
|
|
217
|
-
|
|
218
|
-
|
|
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`. |
|
|
219
220
|
|
|
220
|
-
|
|
221
|
-
<Heading level={1} style={{ color: "hotpink" }}>Title</Heading>
|
|
222
|
-
<CodeBlock content={code} style={{ borderRadius: 0 }} />
|
|
223
|
-
<InlineCode style={{ backgroundColor: "#ff0" }}>code</InlineCode>
|
|
224
|
-
```
|
|
221
|
+
## `useStream(timestamps?)`
|
|
225
222
|
|
|
226
|
-
|
|
223
|
+
Builds on `useMarkdownSession` and adds timeline sync helpers.
|
|
227
224
|
|
|
228
|
-
|
|
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.
|
|
229
228
|
|
|
230
|
-
|
|
231
|
-
// Before: Manual extraction required
|
|
232
|
-
code_block: ({ node }) => (
|
|
233
|
-
<CodeBlock content={getTextContent(node)} language={node.language} />
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
// After: Just pass the node
|
|
237
|
-
code_block: ({ node }) => <CodeBlock node={node} />;
|
|
238
|
-
|
|
239
|
-
// Or use the pre-mapped content prop (recommended)
|
|
240
|
-
code_block: ({ content, language }) => (
|
|
241
|
-
<CodeBlock content={content} language={language} />
|
|
242
|
-
);
|
|
243
|
-
```
|
|
229
|
+
Additional returned fields:
|
|
244
230
|
|
|
245
|
-
|
|
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. |
|
|
246
236
|
|
|
247
|
-
|
|
237
|
+
## Headless API
|
|
248
238
|
|
|
249
239
|
```tsx
|
|
250
240
|
import {
|
|
251
241
|
parseMarkdown,
|
|
242
|
+
parseMarkdownWithOptions,
|
|
252
243
|
getTextContent,
|
|
253
244
|
getFlattenedText,
|
|
254
245
|
} from "react-native-nitro-markdown/headless";
|
|
255
|
-
|
|
256
|
-
const ast = parseMarkdown("# Hello World");
|
|
257
|
-
const text = getTextContent(ast); // "Hello World"
|
|
258
|
-
const fullText = getFlattenedText(ast); // "Hello World\n\n" (Normalized with line breaks)
|
|
259
246
|
```
|
|
260
247
|
|
|
261
|
-
|
|
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. |
|
|
262
254
|
|
|
263
|
-
|
|
255
|
+
### Parser Options
|
|
264
256
|
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
257
|
+
```ts
|
|
258
|
+
type ParserOptions = {
|
|
259
|
+
gfm?: boolean;
|
|
260
|
+
math?: boolean;
|
|
261
|
+
};
|
|
262
|
+
```
|
|
270
263
|
|
|
271
|
-
|
|
272
|
-
const session = useMarkdownSession();
|
|
264
|
+
## Custom Renderer API
|
|
273
265
|
|
|
274
|
-
|
|
275
|
-
session.getSession().append("Hello **Nitro**!");
|
|
276
|
-
return () => session.clear();
|
|
277
|
-
}, [session]);
|
|
266
|
+
## `renderers` prop contract
|
|
278
267
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
updateIntervalMs={60}
|
|
284
|
-
updateStrategy="raf"
|
|
285
|
-
useTransitionUpdates
|
|
286
|
-
/>
|
|
287
|
-
);
|
|
288
|
-
}
|
|
268
|
+
`CustomRenderers` is:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
type CustomRenderers = Partial<Record<MarkdownNode["type"], CustomRenderer>>;
|
|
289
272
|
```
|
|
290
273
|
|
|
291
|
-
|
|
292
|
-
- `updateStrategy="raf"` for smooth, frame-aligned updates
|
|
293
|
-
- `updateIntervalMs={50–100}` when using `updateStrategy="interval"`
|
|
294
|
-
- Avoid per-token UI updates by batching token appends
|
|
274
|
+
`CustomRenderer` receives `EnhancedRendererProps` and returns:
|
|
295
275
|
|
|
296
|
-
|
|
276
|
+
- React node to override default rendering
|
|
277
|
+
- `undefined` to fallback to built-in renderer
|
|
278
|
+
- `null` to render nothing
|
|
297
279
|
|
|
298
|
-
|
|
299
|
-
| :-- | :-- | :-- | :-- |
|
|
300
|
-
| `updateIntervalMs` | `number` | `50` | Throttle UI updates when using interval strategy |
|
|
301
|
-
| `updateStrategy` | `"interval" \| "raf"` | `"interval"` | Interval batching or frame-aligned updates |
|
|
302
|
-
| `useTransitionUpdates` | `boolean` | `false` | Use React transitions to keep input responsive |
|
|
280
|
+
`EnhancedRendererProps` always includes:
|
|
303
281
|
|
|
304
|
-
|
|
282
|
+
- `node`: current `MarkdownNode`
|
|
283
|
+
- `children`: pre-rendered node children
|
|
284
|
+
- `Renderer`: recursive renderer component for nested custom rendering
|
|
305
285
|
|
|
306
|
-
|
|
286
|
+
And conditionally includes mapped fields by node type:
|
|
307
287
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
---
|
|
320
|
-
|
|
321
|
-
## 🎨 Using Context in Custom Renderers
|
|
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` |
|
|
322
297
|
|
|
323
|
-
|
|
298
|
+
### Example: Custom heading + code block
|
|
324
299
|
|
|
325
300
|
```tsx
|
|
326
301
|
import {
|
|
327
|
-
|
|
328
|
-
|
|
302
|
+
Markdown,
|
|
303
|
+
type HeadingRendererProps,
|
|
304
|
+
type CodeBlockRendererProps,
|
|
329
305
|
} from "react-native-nitro-markdown";
|
|
330
306
|
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
307
|
+
const renderers = {
|
|
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
|
+
),
|
|
335
314
|
};
|
|
315
|
+
|
|
316
|
+
<Markdown renderers={renderers}>{content}</Markdown>;
|
|
336
317
|
```
|
|
337
318
|
|
|
338
|
-
|
|
319
|
+
## Link Handling Behavior
|
|
339
320
|
|
|
340
|
-
|
|
321
|
+
Default link renderer behavior:
|
|
341
322
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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`.
|
|
350
333
|
|
|
351
|
-
|
|
352
|
-
export {
|
|
353
|
-
defaultMarkdownTheme,
|
|
354
|
-
minimalMarkdownTheme,
|
|
355
|
-
mergeThemes,
|
|
356
|
-
};
|
|
334
|
+
Relative URLs and anchors are ignored by default open behavior, but you can handle them in `onLinkPress`.
|
|
357
335
|
|
|
358
|
-
|
|
359
|
-
export { useMarkdownContext, MarkdownContext };
|
|
360
|
-
|
|
361
|
-
// Individual renderers
|
|
362
|
-
export {
|
|
363
|
-
Heading,
|
|
364
|
-
Paragraph,
|
|
365
|
-
Link,
|
|
366
|
-
Blockquote,
|
|
367
|
-
HorizontalRule,
|
|
368
|
-
CodeBlock,
|
|
369
|
-
InlineCode,
|
|
370
|
-
List,
|
|
371
|
-
ListItem,
|
|
372
|
-
TaskListItem,
|
|
373
|
-
TableRenderer,
|
|
374
|
-
Image,
|
|
375
|
-
MathInline,
|
|
376
|
-
MathBlock,
|
|
377
|
-
};
|
|
378
|
-
```
|
|
336
|
+
## Theme API
|
|
379
337
|
|
|
380
|
-
|
|
338
|
+
## `MarkdownTheme`
|
|
381
339
|
|
|
382
|
-
|
|
340
|
+
```tsx
|
|
341
|
+
import type {
|
|
342
|
+
MarkdownTheme,
|
|
343
|
+
PartialMarkdownTheme,
|
|
344
|
+
} from "react-native-nitro-markdown";
|
|
345
|
+
```
|
|
383
346
|
|
|
384
|
-
|
|
385
|
-
| :-------------- | :-------------------------- | :--------------------------------- |
|
|
386
|
-
| **Logic** | Raw C++ md4c Parser | Parser + Full UI Renderer |
|
|
387
|
-
| **Output** | JSON AST Tree | React Native Views |
|
|
388
|
-
| **Best For** | Search Indexing, Custom UIs | Fast Implementation, Documentation |
|
|
389
|
-
| **JS Overhead** | ~4 KB | ~60 KB |
|
|
347
|
+
`MarkdownTheme` fields:
|
|
390
348
|
|
|
391
|
-
|
|
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`
|
|
392
359
|
|
|
393
|
-
|
|
360
|
+
Helpers:
|
|
394
361
|
|
|
395
|
-
|
|
362
|
+
- `defaultMarkdownTheme`
|
|
363
|
+
- `minimalMarkdownTheme`
|
|
364
|
+
- `mergeThemes(base, partial)`
|
|
396
365
|
|
|
397
|
-
|
|
398
|
-
import { parseMarkdown } from "react-native-nitro-markdown/headless";
|
|
366
|
+
`NodeStyleOverrides` lets you override per-node styles:
|
|
399
367
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
368
|
+
```ts
|
|
369
|
+
type NodeStyleOverrides = Partial<
|
|
370
|
+
Record<MarkdownNode["type"], ViewStyle | TextStyle>
|
|
371
|
+
>;
|
|
404
372
|
```
|
|
405
373
|
|
|
406
|
-
|
|
374
|
+
## Built-in Renderer Components
|
|
407
375
|
|
|
408
|
-
|
|
409
|
-
| :----- | :-------- | :------ | :----------------------------------------------------------------------------- |
|
|
410
|
-
| `gfm` | `boolean` | `false` | Enable GitHub Flavored Markdown (Tables, Strikethrough, Autolinks, TaskLists). |
|
|
411
|
-
| `math` | `boolean` | `false` | Enable LaTeX Math support (`$` and `$$`). |
|
|
376
|
+
Use these when composing custom renderer maps.
|
|
412
377
|
|
|
413
|
-
|
|
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` |
|
|
414
394
|
|
|
415
|
-
##
|
|
395
|
+
## Supported AST Node Types
|
|
416
396
|
|
|
417
|
-
|
|
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`
|
|
418
398
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
href?: string;
|
|
426
|
-
checked?: boolean;
|
|
427
|
-
language?: string;
|
|
428
|
-
align?: "left" | "center" | "right";
|
|
429
|
-
isHeader?: boolean;
|
|
430
|
-
}
|
|
431
|
-
```
|
|
399
|
+
Notes:
|
|
400
|
+
|
|
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.
|
|
403
|
+
|
|
404
|
+
## Recipes
|
|
432
405
|
|
|
433
|
-
|
|
406
|
+
### Intercept links with `onLinkPress`
|
|
434
407
|
|
|
435
|
-
|
|
408
|
+
```tsx
|
|
409
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
436
410
|
|
|
437
|
-
|
|
411
|
+
<Markdown
|
|
412
|
+
onLinkPress={(href) => {
|
|
413
|
+
if (href.startsWith("/")) {
|
|
414
|
+
router.push(href);
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}}
|
|
418
|
+
>
|
|
419
|
+
{content}
|
|
420
|
+
</Markdown>;
|
|
421
|
+
```
|
|
438
422
|
|
|
439
|
-
|
|
423
|
+
### Use headless mode to build search index
|
|
440
424
|
|
|
441
425
|
```tsx
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
426
|
+
import {
|
|
427
|
+
parseMarkdown,
|
|
428
|
+
getFlattenedText,
|
|
429
|
+
} from "react-native-nitro-markdown/headless";
|
|
430
|
+
|
|
431
|
+
const ast = parseMarkdown(content);
|
|
432
|
+
const searchableText = getFlattenedText(ast);
|
|
446
433
|
```
|
|
447
434
|
|
|
448
|
-
|
|
435
|
+
### Minimal styling baseline
|
|
436
|
+
|
|
437
|
+
```tsx
|
|
438
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
449
439
|
|
|
450
|
-
|
|
440
|
+
<Markdown
|
|
441
|
+
stylingStrategy="minimal"
|
|
442
|
+
theme={{
|
|
443
|
+
colors: {
|
|
444
|
+
text: "#0f172a",
|
|
445
|
+
link: "#1d4ed8",
|
|
446
|
+
},
|
|
447
|
+
}}
|
|
448
|
+
>
|
|
449
|
+
{content}
|
|
450
|
+
</Markdown>;
|
|
451
|
+
```
|
|
451
452
|
|
|
452
|
-
|
|
453
|
-
| :------------------- | :------ |
|
|
454
|
-
| **Packed (tarball)** | ~75 kB |
|
|
455
|
-
| **Unpacked** | ~325 kB |
|
|
456
|
-
| **Total files** | 55 |
|
|
453
|
+
## Performance Guidance
|
|
457
454
|
|
|
458
|
-
|
|
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.
|
|
459
459
|
|
|
460
|
-
##
|
|
460
|
+
## Troubleshooting
|
|
461
461
|
|
|
462
|
-
|
|
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"`).
|
|
463
470
|
|
|
464
|
-
##
|
|
471
|
+
## Contributing
|
|
465
472
|
|
|
466
|
-
|
|
473
|
+
See `/Users/jota/Workspace/Projects/RN-Packages/react-native-nitro-markdown/CONTRIBUTING.md`.
|
|
467
474
|
|
|
468
|
-
|
|
475
|
+
## License
|
|
469
476
|
|
|
470
|
-
|
|
477
|
+
MIT
|