react-native-nitro-markdown 0.4.3 → 0.5.1
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 +417 -25
- package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +46 -8
- package/android/src/main/java/com/nitromarkdown/NitroMarkdownPackage.kt +2 -1
- package/cpp/bindings/HybridMarkdownParser.cpp +216 -66
- package/cpp/bindings/HybridMarkdownParser.hpp +2 -0
- package/ios/HybridMarkdownSession.swift +51 -7
- package/lib/commonjs/MarkdownContext.js.map +1 -1
- package/lib/commonjs/headless.js +61 -5
- package/lib/commonjs/headless.js.map +1 -1
- package/lib/commonjs/index.js +9 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/markdown-stream.js +107 -13
- package/lib/commonjs/markdown-stream.js.map +1 -1
- package/lib/commonjs/markdown.js +191 -26
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/code.js +21 -2
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/table/cell-content.js +32 -0
- package/lib/commonjs/renderers/table/cell-content.js.map +1 -0
- package/lib/commonjs/renderers/table/index.js +310 -0
- package/lib/commonjs/renderers/table/index.js.map +1 -0
- package/lib/commonjs/renderers/table/table-reducer.js +29 -0
- package/lib/commonjs/renderers/table/table-reducer.js.map +1 -0
- package/lib/commonjs/renderers/table/table-utils.js +68 -0
- package/lib/commonjs/renderers/table/table-utils.js.map +1 -0
- package/lib/commonjs/renderers/table/types.js +6 -0
- package/lib/commonjs/renderers/table/types.js.map +1 -0
- package/lib/commonjs/renderers/table.js +6 -306
- package/lib/commonjs/renderers/table.js.map +1 -1
- package/lib/commonjs/theme.js +10 -1
- package/lib/commonjs/theme.js.map +1 -1
- package/lib/commonjs/use-markdown-stream.js +9 -1
- package/lib/commonjs/use-markdown-stream.js.map +1 -1
- package/lib/commonjs/utils/code-highlight.js +101 -0
- package/lib/commonjs/utils/code-highlight.js.map +1 -0
- package/lib/commonjs/utils/incremental-ast.js +153 -0
- package/lib/commonjs/utils/incremental-ast.js.map +1 -0
- package/lib/module/MarkdownContext.js.map +1 -1
- package/lib/module/headless.js +56 -4
- package/lib/module/headless.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/markdown-stream.js +108 -14
- package/lib/module/markdown-stream.js.map +1 -1
- package/lib/module/markdown.js +193 -28
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/code.js +21 -2
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/table/cell-content.js +27 -0
- package/lib/module/renderers/table/cell-content.js.map +1 -0
- package/lib/module/renderers/table/index.js +305 -0
- package/lib/module/renderers/table/index.js.map +1 -0
- package/lib/module/renderers/table/table-reducer.js +24 -0
- package/lib/module/renderers/table/table-reducer.js.map +1 -0
- package/lib/module/renderers/table/table-utils.js +62 -0
- package/lib/module/renderers/table/table-utils.js.map +1 -0
- package/lib/module/renderers/table/types.js +4 -0
- package/lib/module/renderers/table/types.js.map +1 -0
- package/lib/module/renderers/table.js +1 -305
- package/lib/module/renderers/table.js.map +1 -1
- package/lib/module/theme.js +10 -1
- package/lib/module/theme.js.map +1 -1
- package/lib/module/use-markdown-stream.js +9 -1
- package/lib/module/use-markdown-stream.js.map +1 -1
- package/lib/module/utils/code-highlight.js +97 -0
- package/lib/module/utils/code-highlight.js.map +1 -0
- package/lib/module/utils/incremental-ast.js +147 -0
- package/lib/module/utils/incremental-ast.js.map +1 -0
- package/lib/typescript/commonjs/Markdown.nitro.d.ts +2 -0
- package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/MarkdownContext.d.ts +6 -0
- package/lib/typescript/commonjs/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts +18 -0
- package/lib/typescript/commonjs/headless.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown-stream.d.ts +6 -1
- package/lib/typescript/commonjs/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown.d.ts +77 -1
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/table/cell-content.d.ts +15 -0
- package/lib/typescript/commonjs/renderers/table/cell-content.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table/index.d.ts +11 -0
- package/lib/typescript/commonjs/renderers/table/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table/table-reducer.d.ts +5 -0
- package/lib/typescript/commonjs/renderers/table/table-reducer.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table/table-utils.d.ts +10 -0
- package/lib/typescript/commonjs/renderers/table/table-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table/types.d.ts +24 -0
- package/lib/typescript/commonjs/renderers/table/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table.d.ts +1 -11
- package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/MarkdownSession.nitro.d.ts +14 -2
- package/lib/typescript/commonjs/specs/MarkdownSession.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme.d.ts +18 -2
- package/lib/typescript/commonjs/theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/use-markdown-stream.d.ts +4 -0
- package/lib/typescript/commonjs/use-markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/code-highlight.d.ts +8 -0
- package/lib/typescript/commonjs/utils/code-highlight.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/incremental-ast.d.ts +12 -0
- package/lib/typescript/commonjs/utils/incremental-ast.d.ts.map +1 -0
- package/lib/typescript/module/Markdown.nitro.d.ts +2 -0
- package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/module/MarkdownContext.d.ts +6 -0
- package/lib/typescript/module/MarkdownContext.d.ts.map +1 -1
- package/lib/typescript/module/headless.d.ts +18 -0
- package/lib/typescript/module/headless.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/markdown-stream.d.ts +6 -1
- package/lib/typescript/module/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/markdown.d.ts +77 -1
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.d.ts.map +1 -1
- package/lib/typescript/module/renderers/table/cell-content.d.ts +15 -0
- package/lib/typescript/module/renderers/table/cell-content.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table/index.d.ts +11 -0
- package/lib/typescript/module/renderers/table/index.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table/table-reducer.d.ts +5 -0
- package/lib/typescript/module/renderers/table/table-reducer.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table/table-utils.d.ts +10 -0
- package/lib/typescript/module/renderers/table/table-utils.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table/types.d.ts +24 -0
- package/lib/typescript/module/renderers/table/types.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table.d.ts +1 -11
- package/lib/typescript/module/renderers/table.d.ts.map +1 -1
- package/lib/typescript/module/specs/MarkdownSession.nitro.d.ts +14 -2
- package/lib/typescript/module/specs/MarkdownSession.nitro.d.ts.map +1 -1
- package/lib/typescript/module/theme.d.ts +18 -2
- package/lib/typescript/module/theme.d.ts.map +1 -1
- package/lib/typescript/module/use-markdown-stream.d.ts +4 -0
- package/lib/typescript/module/use-markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/utils/code-highlight.d.ts +8 -0
- package/lib/typescript/module/utils/code-highlight.d.ts.map +1 -0
- package/lib/typescript/module/utils/incremental-ast.d.ts +12 -0
- package/lib/typescript/module/utils/incremental-ast.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +38 -26
- package/nitrogen/generated/android/NitroMarkdownOnLoad.hpp +13 -4
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.cpp +49 -34
- package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.hpp +25 -24
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/Func_void_double_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSessionSpec.kt +34 -21
- package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.hpp +31 -0
- package/nitrogen/generated/ios/c++/HybridMarkdownSessionSpecSwift.hpp +34 -2
- package/nitrogen/generated/ios/swift/Func_void_double_double.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec.swift +6 -2
- package/nitrogen/generated/ios/swift/HybridMarkdownSessionSpec_cxx.swift +57 -9
- package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownSessionSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridMarkdownSessionSpec.hpp +6 -2
- package/package.json +9 -5
- package/react-native-nitro-markdown.podspec +1 -1
- package/src/Markdown.nitro.ts +2 -0
- package/src/MarkdownContext.ts +6 -0
- package/src/headless.ts +54 -4
- package/src/index.ts +10 -0
- package/src/markdown-stream.tsx +163 -15
- package/src/markdown.tsx +381 -26
- package/src/renderers/code.tsx +32 -3
- package/src/renderers/table/cell-content.tsx +38 -0
- package/src/renderers/table/index.tsx +419 -0
- package/src/renderers/table/table-reducer.ts +36 -0
- package/src/renderers/table/table-utils.ts +81 -0
- package/src/renderers/table/types.ts +24 -0
- package/src/renderers/table.tsx +1 -401
- package/src/specs/MarkdownSession.nitro.ts +16 -2
- package/src/theme.ts +29 -1
- package/src/use-markdown-stream.ts +10 -0
- package/src/utils/code-highlight.ts +102 -0
- package/src/utils/incremental-ast.ts +224 -0
package/README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="./readme/demo.gif" alt="react-native-nitro-markdown demo" width="
|
|
3
|
-
<img src="./readme/stream-demo.gif" alt="react-native-nitro-markdown stream demo" width="300" />
|
|
2
|
+
<img src="./readme/demo.gif" alt="react-native-nitro-markdown demo" width="400" />
|
|
4
3
|
</p>
|
|
5
4
|
|
|
6
5
|
# react-native-nitro-markdown
|
|
@@ -20,7 +19,7 @@ Native Markdown parsing and rendering for React Native.
|
|
|
20
19
|
## Requirements
|
|
21
20
|
|
|
22
21
|
- React Native `>=0.75.0`
|
|
23
|
-
- `react-native-nitro-modules`
|
|
22
|
+
- `react-native-nitro-modules >=0.35.0`
|
|
24
23
|
|
|
25
24
|
Optional for math rendering:
|
|
26
25
|
|
|
@@ -74,6 +73,79 @@ export function Example() {
|
|
|
74
73
|
}
|
|
75
74
|
```
|
|
76
75
|
|
|
76
|
+
## Demo App Tour
|
|
77
|
+
|
|
78
|
+
The example app in `apps/example` now maps each major feature to a screen:
|
|
79
|
+
|
|
80
|
+
- `Bench` (`app/index.tsx`)
|
|
81
|
+
- Nitro benchmark + JS parser comparisons
|
|
82
|
+
- `Default` (`app/render-default.tsx`)
|
|
83
|
+
- Built-in renderer defaults
|
|
84
|
+
- `Styles` (`app/render-default-styles.tsx`)
|
|
85
|
+
- `styles` prop and theme token overrides
|
|
86
|
+
- `Custom` (`app/render-custom.tsx`)
|
|
87
|
+
- `renderers` overrides + `astTransform`
|
|
88
|
+
- `Stream` (`app/render-stream.tsx`)
|
|
89
|
+
- streaming UX with live token append
|
|
90
|
+
|
|
91
|
+
## Runtime API Coverage (Demo + Docs)
|
|
92
|
+
|
|
93
|
+
This table maps each runtime API to where it is demonstrated.
|
|
94
|
+
|
|
95
|
+
| API | Purpose | Demo usage |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `Markdown` | Parse + render markdown component | `apps/example/app/render-default.tsx` |
|
|
98
|
+
| `Markdown` `options` | Enable parser flags (`gfm`, `math`) | `apps/example/app/render-default.tsx` |
|
|
99
|
+
| `Markdown` `styles` | Per-node style overrides | `apps/example/app/render-default-styles.tsx` |
|
|
100
|
+
| `Markdown` `renderers` | Custom node renderer overrides | `apps/example/app/render-custom.tsx` |
|
|
101
|
+
| `Markdown` `astTransform` | Post-parse AST transform hook | `apps/example/app/render-custom.tsx` |
|
|
102
|
+
| `Markdown` `virtualize` / `virtualization*` | Large-document block virtualization | README examples |
|
|
103
|
+
| `MarkdownStream` | Stream rendering from session text | `apps/example/app/render-stream.tsx` |
|
|
104
|
+
| `useMarkdownSession` | Own and reuse a native markdown session | `apps/example/app/render-stream.tsx` |
|
|
105
|
+
| `createMarkdownSession` | Create a manual session instance | README examples |
|
|
106
|
+
| `useStream` | Timed playback sync + highlighting | README examples |
|
|
107
|
+
| `parseMarkdown` | Headless parse/benchmark pipeline | `apps/example/app/index.tsx` |
|
|
108
|
+
| `parseMarkdownWithOptions` | Headless parse with parser flags | README examples |
|
|
109
|
+
| `getTextContent` | Extract raw text from AST subtree | README examples |
|
|
110
|
+
| `getFlattenedText` | Normalize AST text for indexing/search | README examples |
|
|
111
|
+
| `MarkdownParserModule` | Low-level Nitro parser access | README examples |
|
|
112
|
+
| `mergeThemes` / `defaultMarkdownTheme` / `minimalMarkdownTheme` | Theme composition and style presets | `apps/example/app/render-default-styles.tsx` + README examples |
|
|
113
|
+
| `useMarkdownContext` / `MarkdownContext` | Access theme/renderer/link handlers inside custom trees | README examples |
|
|
114
|
+
| Built-in renderer components (`CodeBlock`, `TableRenderer`, etc.) | Compose renderer overrides with built-ins | `apps/example/app/render-custom.tsx` |
|
|
115
|
+
| `onLinkPress`, `onParsingInProgress`, `onParseComplete`, `plugins`, `sourceAst` | Advanced lifecycle/link/pipeline control | README examples |
|
|
116
|
+
| `onError` | Structured error reporting for parse and plugin failures | README examples |
|
|
117
|
+
| `highlightCode` / `defaultHighlighter` | Opt-in syntax highlighting for code blocks | README examples |
|
|
118
|
+
| `tableOptions` | Per-instance table column and measurement tuning | README examples |
|
|
119
|
+
| `stripSourceOffsets` | Remove source position data from parsed AST | README examples |
|
|
120
|
+
| `MarkdownSession.reset` / `MarkdownSession.replace` | Full and partial buffer mutation | README examples |
|
|
121
|
+
|
|
122
|
+
## Feature Index
|
|
123
|
+
|
|
124
|
+
Use this table as a quick map from feature -> API -> demo usage.
|
|
125
|
+
|
|
126
|
+
| Feature | API | What it does | Demo |
|
|
127
|
+
| ------------------------ | --------------------------------------------------- | --------------------------------------------------------- | ------------------------------------- |
|
|
128
|
+
| Basic markdown rendering | `Markdown` | Parse and render markdown in one component | `app/render-default.tsx` |
|
|
129
|
+
| Parser flags | `options` (`gfm`, `math`) | Enable GFM and math parsing | `app/render-default.tsx` |
|
|
130
|
+
| Plugin pipeline | `plugins` (`beforeParse`, `afterParse`) | Rewrite markdown input or AST around parse | README examples |
|
|
131
|
+
| AST transform | `astTransform` | Post-parse AST rewrite before render | `app/render-custom.tsx` |
|
|
132
|
+
| Pre-parsed AST render | `sourceAst` | Skip parsing during render and render existing AST | README examples |
|
|
133
|
+
| Parse lifecycle | `onParsingInProgress`, `onParseComplete` | Observe parse start/finish and consume normalized text | README examples |
|
|
134
|
+
| Link interception | `onLinkPress` | Override default URL open behavior | README examples |
|
|
135
|
+
| Large doc virtualization | `virtualize`, `virtualizationMinBlocks` | Virtualizes top-level blocks for very large markdown docs | README examples |
|
|
136
|
+
| Streaming markdown | `MarkdownStream` + `createMarkdownSession` | Render incrementally appended markdown content | `app/render-stream.tsx` |
|
|
137
|
+
| Timed highlight sync | `useStream(timestamps)` | Sync highlight position to playback timeline | README examples |
|
|
138
|
+
| Headless parsing | `parseMarkdown`, `parseMarkdownWithOptions` | Parse markdown without built-in UI | `app/index.tsx` + README examples |
|
|
139
|
+
| Custom node rendering | `renderers` + built-in renderer components | Replace specific node UI while preserving parser behavior | `app/render-custom.tsx` |
|
|
140
|
+
| Styling and theme | `theme`, `styles`, `stylingStrategy`, `mergeThemes` | Control visual tokens and per-node styles | `app/render-default-styles.tsx` |
|
|
141
|
+
| Syntax highlighting | `highlightCode`, `defaultHighlighter`, `codeTokenColors` | Opt-in token-colored code block rendering | README examples |
|
|
142
|
+
| Plugin priority | `MarkdownPlugin.priority` | Control plugin execution order | README examples |
|
|
143
|
+
| Error reporting | `onError` | Observe parse and plugin failures without crashing | README examples |
|
|
144
|
+
| Table tuning | `tableOptions` | Configure column width and measurement debounce | README examples |
|
|
145
|
+
| AST cleanup | `stripSourceOffsets` | Remove source positions from AST for compact storage | README examples |
|
|
146
|
+
| Session mutation | `MarkdownSession.reset`, `MarkdownSession.replace` | Replace or partially edit the session text buffer | README examples |
|
|
147
|
+
| Low-level parser access | `MarkdownParserModule` | Direct access to Nitro parser methods | README examples |
|
|
148
|
+
|
|
77
149
|
## Package Exports
|
|
78
150
|
|
|
79
151
|
### Main Entry (`react-native-nitro-markdown`)
|
|
@@ -103,8 +175,12 @@ export function Example() {
|
|
|
103
175
|
- `CodeBlock`, `InlineCode`
|
|
104
176
|
- `List`, `ListItem`, `TaskListItem`
|
|
105
177
|
- `TableRenderer`, `Image`, `MathInline`, `MathBlock`
|
|
178
|
+
- Syntax highlighting:
|
|
179
|
+
- `defaultHighlighter`
|
|
180
|
+
- `CodeHighlighter`, `HighlightedToken`, `TokenType`
|
|
106
181
|
- Types:
|
|
107
182
|
- `MarkdownNode`, `ParserOptions`, `MarkdownParser`
|
|
183
|
+
- `MarkdownProps`, `AstTransform`, `MarkdownPlugin`, `MarkdownStreamProps`, `MarkdownVirtualizationOptions`
|
|
108
184
|
- `CustomRenderers`, `CustomRenderer`, `CustomRendererProps`
|
|
109
185
|
- `NodeRendererProps`, `BaseCustomRendererProps`, `EnhancedRendererProps`
|
|
110
186
|
- `HeadingRendererProps`, `LinkRendererProps`, `ImageRendererProps`
|
|
@@ -116,7 +192,7 @@ export function Example() {
|
|
|
116
192
|
|
|
117
193
|
### Headless Entry (`react-native-nitro-markdown/headless`)
|
|
118
194
|
|
|
119
|
-
Exports only parser-related API (`parseMarkdown`, `parseMarkdownWithOptions`, `getTextContent`, `getFlattenedText`, types). Use this when you do not need built-in UI rendering.
|
|
195
|
+
Exports only parser-related API (`parseMarkdown`, `parseMarkdownWithOptions`, `extractPlainText`, `extractPlainTextWithOptions`, `getTextContent`, `getFlattenedText`, `stripSourceOffsets`, types). Use this when you do not need built-in UI rendering.
|
|
120
196
|
|
|
121
197
|
## Component API
|
|
122
198
|
|
|
@@ -126,23 +202,187 @@ Exports only parser-related API (`parseMarkdown`, `parseMarkdownWithOptions`, `g
|
|
|
126
202
|
import { Markdown } from "react-native-nitro-markdown";
|
|
127
203
|
```
|
|
128
204
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
205
|
+
Demo usage:
|
|
206
|
+
|
|
207
|
+
- `apps/example/app/render-default.tsx`
|
|
208
|
+
- `apps/example/app/render-default-styles.tsx`
|
|
209
|
+
- `apps/example/app/render-custom.tsx`
|
|
210
|
+
|
|
211
|
+
| Prop | Type | Default | Description |
|
|
212
|
+
| --------------------- | ---------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
|
|
213
|
+
| `children` | `string` | required | Markdown input string. |
|
|
214
|
+
| `options` | `ParserOptions` | `undefined` | Parser flags (`gfm`, `math`). |
|
|
215
|
+
| `plugins` | `MarkdownPlugin[]` | `undefined` | Optional parser plugin hooks (`beforeParse`, `afterParse`). |
|
|
216
|
+
| `sourceAst` | `MarkdownNode` | `undefined` | Pre-parsed AST. When provided, native parse is skipped. |
|
|
217
|
+
| `astTransform` | `AstTransform` | `undefined` | Transform hook applied after plugins, before rendering and `onParseComplete`. |
|
|
218
|
+
| `renderers` | `CustomRenderers` | `{}` | Per-node custom renderers. |
|
|
219
|
+
| `theme` | `PartialMarkdownTheme` | `defaultMarkdownTheme` or `minimalMarkdownTheme` | Theme token overrides. |
|
|
220
|
+
| `styles` | `NodeStyleOverrides` | `undefined` | Per-node style overrides. |
|
|
221
|
+
| `stylingStrategy` | `"opinionated" \| "minimal"` | `"opinionated"` | Base styling preset. |
|
|
222
|
+
| `style` | `StyleProp<ViewStyle>` | `undefined` | Container style for the root `View`. |
|
|
223
|
+
| `onParsingInProgress` | `() => void` | `undefined` | Called when parse inputs change. |
|
|
224
|
+
| `onParseComplete` | `(result) => void` | `undefined` | Called with `{ raw, ast, text }` after successful parse. |
|
|
225
|
+
| `onLinkPress` | `LinkPressHandler` | `undefined` | Intercepts link press before default open behavior. Return `false` to block default open. |
|
|
226
|
+
| `onError` | `(error, phase, pluginName?) => void` | `undefined` | Called when a parse or plugin error occurs. `phase` is `'parse'`, `'before-plugin'`, or `'after-plugin'`. |
|
|
227
|
+
| `highlightCode` | `boolean \| CodeHighlighter` | `undefined` | Enables syntax highlighting for code blocks. Pass `true` for the built-in highlighter or a custom `CodeHighlighter` function. |
|
|
228
|
+
| `tableOptions` | `{ minColumnWidth?: number; measurementStabilizeMs?: number }` | `undefined` | Table layout tuning. `minColumnWidth` defaults to `60`; `measurementStabilizeMs` defaults to `140`. |
|
|
229
|
+
| `virtualize` | `boolean \| "auto"` | `false` | Enables top-level block virtualization. Use `"auto"` to activate by block threshold. |
|
|
230
|
+
| `virtualizationMinBlocks` | `number` | `40` | Minimum top-level block count before virtualization activates. |
|
|
231
|
+
| `virtualization` | `MarkdownVirtualizationOptions` | `undefined` | Optional FlatList tuning (`windowSize`, `initialNumToRender`, batching, clipping). |
|
|
141
232
|
|
|
142
233
|
Notes:
|
|
143
234
|
|
|
144
235
|
- Parse failures are caught and rendered as a fallback message (`Error parsing markdown`).
|
|
145
236
|
- `text` in `onParseComplete` is produced by `getFlattenedText(ast)`.
|
|
237
|
+
- `astTransform` should be wrapped with `useCallback` to avoid unnecessary re-parses.
|
|
238
|
+
- `astTransform` is a post-parse AST rewrite hook. It does not add parser syntax support and is not a markdown-it plugin API.
|
|
239
|
+
- Plugin pipeline order is: `beforeParse` plugins (sorted by `priority` desc) -> parse/sourceAst -> `afterParse` plugins (sorted by `priority` desc) -> `astTransform` -> render.
|
|
240
|
+
- `onError` is called per-failure; individual plugin failures do not abort the full pipeline.
|
|
241
|
+
- Tables render immediately with estimated column widths, then refine widths after layout measurement to improve reliability on slower layout cycles.
|
|
242
|
+
- For very large markdown content, enable `virtualize` to avoid mounting all top-level blocks at once.
|
|
243
|
+
- `virtualize="auto"` enables threshold-driven virtualization while keeping small markdown renders on plain `View` trees.
|
|
244
|
+
|
|
245
|
+
### Virtualization example (large docs)
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
<Markdown
|
|
249
|
+
virtualize="auto"
|
|
250
|
+
virtualizationMinBlocks={30}
|
|
251
|
+
virtualization={{
|
|
252
|
+
initialNumToRender: 10,
|
|
253
|
+
maxToRenderPerBatch: 10,
|
|
254
|
+
windowSize: 8,
|
|
255
|
+
updateCellsBatchingPeriod: 16,
|
|
256
|
+
removeClippedSubviews: true,
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
{content}
|
|
260
|
+
</Markdown>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Virtualization notes:
|
|
264
|
+
|
|
265
|
+
- Keep `Markdown` as the primary vertical scroller when `virtualize` is enabled.
|
|
266
|
+
- Avoid nesting it inside another vertical `ScrollView`, or virtualization effectiveness drops.
|
|
267
|
+
|
|
268
|
+
### AST transform example
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
import { useCallback } from "react";
|
|
272
|
+
import { Markdown, type AstTransform } from "react-native-nitro-markdown";
|
|
273
|
+
|
|
274
|
+
const astTransform = useCallback<AstTransform>((ast) => {
|
|
275
|
+
const transformNode = (node: Parameters<AstTransform>[0]) => ({
|
|
276
|
+
...node,
|
|
277
|
+
content:
|
|
278
|
+
node.type === "text"
|
|
279
|
+
? (node.content ?? "").replace(/:wink:/g, "😉")
|
|
280
|
+
: node.content,
|
|
281
|
+
children: node.children?.map(transformNode),
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
return transformNode(ast);
|
|
285
|
+
}, []);
|
|
286
|
+
|
|
287
|
+
<Markdown astTransform={astTransform}>{"Hello :wink:"}</Markdown>;
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Plugin pipeline example (`beforeParse` + `afterParse`)
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
import { Markdown, type MarkdownPlugin } from "react-native-nitro-markdown";
|
|
294
|
+
|
|
295
|
+
const plugins: MarkdownPlugin[] = [
|
|
296
|
+
{
|
|
297
|
+
name: "rewrite-before-parse",
|
|
298
|
+
priority: 10, // runs before lower-priority plugins
|
|
299
|
+
beforeParse: (input) => input.replace(/:rocket:/g, "ROCKET_TOKEN"),
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: "rewrite-after-parse",
|
|
303
|
+
afterParse: (ast) => {
|
|
304
|
+
const visit = (node: typeof ast): typeof ast => ({
|
|
305
|
+
...node,
|
|
306
|
+
content:
|
|
307
|
+
node.type === "text"
|
|
308
|
+
? (node.content ?? "").replace(/ROCKET_TOKEN/g, "🚀")
|
|
309
|
+
: node.content,
|
|
310
|
+
children: node.children?.map(visit),
|
|
311
|
+
});
|
|
312
|
+
return visit(ast);
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
<Markdown plugins={plugins}>{"Launch :rocket:"}</Markdown>;
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### `onError` example
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
324
|
+
|
|
325
|
+
<Markdown
|
|
326
|
+
onError={(error, phase, pluginName) => {
|
|
327
|
+
console.warn(`[markdown] ${phase} error`, { error, pluginName });
|
|
328
|
+
}}
|
|
329
|
+
plugins={plugins}
|
|
330
|
+
>
|
|
331
|
+
{content}
|
|
332
|
+
</Markdown>;
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Syntax highlighting example
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
339
|
+
|
|
340
|
+
// Built-in highlighter (JS/TS, Python, Bash)
|
|
341
|
+
<Markdown highlightCode>{"```typescript\nconst x: number = 42;\n```"}</Markdown>;
|
|
342
|
+
|
|
343
|
+
// Custom highlighter
|
|
344
|
+
import type { CodeHighlighter } from "react-native-nitro-markdown";
|
|
345
|
+
|
|
346
|
+
const myHighlighter: CodeHighlighter = (language, code) => {
|
|
347
|
+
// return an array of { text, type } tokens
|
|
348
|
+
return [{ text: code, type: "default" }];
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
<Markdown highlightCode={myHighlighter}>{content}</Markdown>;
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### `sourceAst` example (skip parsing in render)
|
|
355
|
+
|
|
356
|
+
```tsx
|
|
357
|
+
import {
|
|
358
|
+
Markdown,
|
|
359
|
+
parseMarkdownWithOptions,
|
|
360
|
+
} from "react-native-nitro-markdown";
|
|
361
|
+
|
|
362
|
+
const sourceAst = parseMarkdownWithOptions(content, { gfm: true, math: true });
|
|
363
|
+
|
|
364
|
+
<Markdown sourceAst={sourceAst}>
|
|
365
|
+
{"children is ignored when sourceAst is provided"}
|
|
366
|
+
</Markdown>;
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Parse lifecycle callbacks example
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
import { Markdown } from "react-native-nitro-markdown";
|
|
373
|
+
|
|
374
|
+
<Markdown
|
|
375
|
+
onParsingInProgress={() => setIsParsing(true)}
|
|
376
|
+
onParseComplete={({ raw, ast, text }) => {
|
|
377
|
+
setIsParsing(false);
|
|
378
|
+
setWordCount(text.trim().split(/\s+/).length);
|
|
379
|
+
setLastRaw(raw);
|
|
380
|
+
setLastAst(ast);
|
|
381
|
+
}}
|
|
382
|
+
>
|
|
383
|
+
{content}
|
|
384
|
+
</Markdown>;
|
|
385
|
+
```
|
|
146
386
|
|
|
147
387
|
## `MarkdownStream`
|
|
148
388
|
|
|
@@ -152,12 +392,22 @@ import { MarkdownStream } from "react-native-nitro-markdown";
|
|
|
152
392
|
|
|
153
393
|
`MarkdownStreamProps` extends `MarkdownProps` except `children`.
|
|
154
394
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
|
160
|
-
|
|
|
395
|
+
Demo usage:
|
|
396
|
+
|
|
397
|
+
- `apps/example/app/render-stream.tsx`
|
|
398
|
+
|
|
399
|
+
| Prop | Type | Default | Description |
|
|
400
|
+
| ---------------------- | --------------------- | ------------ | ---------------------------------------------------------------------------------------- |
|
|
401
|
+
| `session` | `MarkdownSession` | required | Session object that supplies streamed text chunks. |
|
|
402
|
+
| `updateIntervalMs` | `number` | `50` | Flush interval when `updateStrategy="interval"`. |
|
|
403
|
+
| `updateStrategy` | `"interval" \| "raf"` | `"interval"` | Update cadence (`setTimeout` vs `requestAnimationFrame`). |
|
|
404
|
+
| `useTransitionUpdates` | `boolean` | `false` | Applies `startTransition` to streamed UI updates. |
|
|
405
|
+
| `incrementalParsing` | `boolean` | `true` | Enables append-optimized incremental AST updates (falls back to full parse when unsafe). |
|
|
406
|
+
|
|
407
|
+
Notes:
|
|
408
|
+
|
|
409
|
+
- If any plugin defines `beforeParse`, `MarkdownStream` disables incremental AST mode for correctness.
|
|
410
|
+
- `MarkdownStream` consumes native session change ranges (`from`, `to`) and uses `getTextRange()` for contiguous appends to avoid full-buffer copies during token streams.
|
|
161
411
|
|
|
162
412
|
### Streaming Example
|
|
163
413
|
|
|
@@ -203,6 +453,39 @@ const session = createMarkdownSession();
|
|
|
203
453
|
session.append("hello");
|
|
204
454
|
```
|
|
205
455
|
|
|
456
|
+
`MarkdownSession` methods:
|
|
457
|
+
|
|
458
|
+
| Method | Signature | Description |
|
|
459
|
+
| --- | --- | --- |
|
|
460
|
+
| `append` | `(chunk: string) => number` | Appends text and returns new UTF-16 length. |
|
|
461
|
+
| `clear` | `() => void` | Clears buffer and emits a reset range event (`0, 0`). |
|
|
462
|
+
| `reset` | `(text: string) => void` | Replaces full buffer content and emits a full-range change event. |
|
|
463
|
+
| `replace` | `(from: number, to: number, text: string) => number` | Partial buffer mutation; returns new total UTF-16 length. |
|
|
464
|
+
| `getAllText` | `() => string` | Returns full session text. |
|
|
465
|
+
| `getLength` | `() => number` | Returns current UTF-16 text length without materializing a copy. |
|
|
466
|
+
| `getTextRange` | `(from: number, to: number) => string` | Returns a substring range for delta-driven streaming updates. |
|
|
467
|
+
| `addListener` | `(listener: (from: number, to: number) => void) => () => void` | Subscribes to mutation range events and returns an unsubscribe function. |
|
|
468
|
+
| `highlightPosition` | `number` | Mutable cursor used by stream highlight workflows. |
|
|
469
|
+
|
|
470
|
+
Demo usage:
|
|
471
|
+
|
|
472
|
+
- Referenced in `apps/example/app/render-stream.tsx` sample markdown content and used directly in README examples.
|
|
473
|
+
|
|
474
|
+
Manual session + stream rendering:
|
|
475
|
+
|
|
476
|
+
```tsx
|
|
477
|
+
import {
|
|
478
|
+
createMarkdownSession,
|
|
479
|
+
MarkdownStream,
|
|
480
|
+
} from "react-native-nitro-markdown";
|
|
481
|
+
|
|
482
|
+
const session = createMarkdownSession();
|
|
483
|
+
session.append("# Hello\n");
|
|
484
|
+
session.append("Streaming content...");
|
|
485
|
+
|
|
486
|
+
<MarkdownStream session={session} updateStrategy="raf" />;
|
|
487
|
+
```
|
|
488
|
+
|
|
206
489
|
## `useMarkdownSession()`
|
|
207
490
|
|
|
208
491
|
Creates and owns one `MarkdownSession` for a component lifecycle.
|
|
@@ -218,6 +501,10 @@ Returns:
|
|
|
218
501
|
| `clear` | `() => void` | Clears session content and resets `highlightPosition` to `0`. |
|
|
219
502
|
| `setHighlight` | `(position: number) => void` | Sets `session.highlightPosition`. |
|
|
220
503
|
|
|
504
|
+
Demo usage:
|
|
505
|
+
|
|
506
|
+
- `apps/example/app/render-stream.tsx`
|
|
507
|
+
|
|
221
508
|
## `useStream(timestamps?)`
|
|
222
509
|
|
|
223
510
|
Builds on `useMarkdownSession` and adds timeline sync helpers.
|
|
@@ -234,14 +521,36 @@ Additional returned fields:
|
|
|
234
521
|
| `setIsPlaying` | `(value: boolean) => void` | Setter for `isPlaying`. |
|
|
235
522
|
| `sync` | `(currentTimeMs: number) => void` | Applies timeline-based highlight updates. |
|
|
236
523
|
|
|
524
|
+
Example:
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
const stream = useStream({
|
|
528
|
+
0: 0,
|
|
529
|
+
1: 500,
|
|
530
|
+
2: 1000,
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// e.g. in media time update callback:
|
|
534
|
+
stream.sync(currentTimeMs);
|
|
535
|
+
|
|
536
|
+
<MarkdownStream session={stream.getSession()} />;
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Demo usage:
|
|
540
|
+
|
|
541
|
+
- README examples (timed playback scenario)
|
|
542
|
+
|
|
237
543
|
## Headless API
|
|
238
544
|
|
|
239
545
|
```tsx
|
|
240
546
|
import {
|
|
241
547
|
parseMarkdown,
|
|
242
548
|
parseMarkdownWithOptions,
|
|
549
|
+
extractPlainText,
|
|
550
|
+
extractPlainTextWithOptions,
|
|
243
551
|
getTextContent,
|
|
244
552
|
getFlattenedText,
|
|
553
|
+
stripSourceOffsets,
|
|
245
554
|
} from "react-native-nitro-markdown/headless";
|
|
246
555
|
```
|
|
247
556
|
|
|
@@ -249,8 +558,11 @@ import {
|
|
|
249
558
|
| -------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
250
559
|
| `parseMarkdown` | `(text: string) => MarkdownNode` | Parses markdown using default parser settings. |
|
|
251
560
|
| `parseMarkdownWithOptions` | `(text: string, options: ParserOptions) => MarkdownNode` | Parses markdown with `gfm` and/or `math` flags. |
|
|
561
|
+
| `extractPlainText` | `(text: string) => string` | Parses and returns normalized plain text directly from native parser. |
|
|
562
|
+
| `extractPlainTextWithOptions` | `(text: string, options: ParserOptions) => string` | Same as above with parser flags. |
|
|
252
563
|
| `getTextContent` | `(node: MarkdownNode) => string` | Concatenates text recursively without layout normalization. |
|
|
253
564
|
| `getFlattenedText` | `(node: MarkdownNode) => string` | Returns normalized plain text with paragraph and block separators. |
|
|
565
|
+
| `stripSourceOffsets` | `(node: MarkdownNode) => MarkdownNode` | Recursively removes `beg`/`end` source position fields. Useful for compact serialization or snapshot testing. |
|
|
254
566
|
|
|
255
567
|
### Parser Options
|
|
256
568
|
|
|
@@ -261,6 +573,31 @@ type ParserOptions = {
|
|
|
261
573
|
};
|
|
262
574
|
```
|
|
263
575
|
|
|
576
|
+
Example:
|
|
577
|
+
|
|
578
|
+
```tsx
|
|
579
|
+
const ast = parseMarkdownWithOptions(markdown, {
|
|
580
|
+
gfm: true, // tables, task lists, strikethrough
|
|
581
|
+
math: true, // inline/block LaTeX
|
|
582
|
+
});
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### `MarkdownParserModule` (low-level Nitro access)
|
|
586
|
+
|
|
587
|
+
Use this only when you want direct method access (`parse`, `parseWithOptions`, `extractPlainText`, `extractPlainTextWithOptions`).
|
|
588
|
+
|
|
589
|
+
```tsx
|
|
590
|
+
import {
|
|
591
|
+
MarkdownParserModule,
|
|
592
|
+
type ParserOptions,
|
|
593
|
+
} from "react-native-nitro-markdown/headless";
|
|
594
|
+
|
|
595
|
+
const options: ParserOptions = { gfm: true };
|
|
596
|
+
const jsonAst = JSON.parse(
|
|
597
|
+
MarkdownParserModule.parseWithOptions("# Hello", options),
|
|
598
|
+
);
|
|
599
|
+
```
|
|
600
|
+
|
|
264
601
|
## Custom Renderer API
|
|
265
602
|
|
|
266
603
|
## `renderers` prop contract
|
|
@@ -316,6 +653,32 @@ const renderers = {
|
|
|
316
653
|
<Markdown renderers={renderers}>{content}</Markdown>;
|
|
317
654
|
```
|
|
318
655
|
|
|
656
|
+
### `useMarkdownContext` example (inside custom renderer tree)
|
|
657
|
+
|
|
658
|
+
```tsx
|
|
659
|
+
import { Text } from "react-native";
|
|
660
|
+
import {
|
|
661
|
+
Markdown,
|
|
662
|
+
useMarkdownContext,
|
|
663
|
+
type CustomRendererProps,
|
|
664
|
+
} from "react-native-nitro-markdown";
|
|
665
|
+
|
|
666
|
+
function ThemedParagraph({ children }: Pick<CustomRendererProps, "children">) {
|
|
667
|
+
const { theme } = useMarkdownContext();
|
|
668
|
+
return <Text style={{ color: theme.colors.text }}>{children}</Text>;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
<Markdown
|
|
672
|
+
renderers={{
|
|
673
|
+
paragraph: ({ children }: CustomRendererProps) => (
|
|
674
|
+
<ThemedParagraph>{children}</ThemedParagraph>
|
|
675
|
+
),
|
|
676
|
+
}}
|
|
677
|
+
>
|
|
678
|
+
{content}
|
|
679
|
+
</Markdown>;
|
|
680
|
+
```
|
|
681
|
+
|
|
319
682
|
## Link Handling Behavior
|
|
320
683
|
|
|
321
684
|
Default link renderer behavior:
|
|
@@ -350,6 +713,7 @@ import type {
|
|
|
350
713
|
- `text`, `textMuted`, `heading`, `link`, `code`, `codeBackground`, `codeLanguage`
|
|
351
714
|
- `blockquote`, `border`, `surface`, `surfaceLight`, `accent`
|
|
352
715
|
- `tableBorder`, `tableHeader`, `tableHeaderText`, `tableRowEven`, `tableRowOdd`
|
|
716
|
+
- `codeTokenColors?`: `{ keyword?, string?, comment?, number?, operator?, punctuation?, type?, default? }` — per-token colors used by `highlightCode`
|
|
353
717
|
- `spacing`: `xs`, `s`, `m`, `l`, `xl`
|
|
354
718
|
- `fontSizes`: `xs`, `s`, `m`, `l`, `xl`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`
|
|
355
719
|
- `fontFamilies`: `regular`, `heading`, `mono`
|
|
@@ -363,14 +727,41 @@ Helpers:
|
|
|
363
727
|
- `minimalMarkdownTheme`
|
|
364
728
|
- `mergeThemes(base, partial)`
|
|
365
729
|
|
|
366
|
-
`NodeStyleOverrides` lets you override per-node styles:
|
|
730
|
+
`NodeStyleOverrides` lets you override per-node styles with type-safe shape checking:
|
|
367
731
|
|
|
368
732
|
```ts
|
|
733
|
+
// Text-type nodes accept TextStyle; view-type nodes accept ViewStyle.
|
|
734
|
+
// TypeScript will catch mismatched style shapes at compile time.
|
|
369
735
|
type NodeStyleOverrides = Partial<
|
|
370
|
-
|
|
736
|
+
{
|
|
737
|
+
text: TextStyle; bold: TextStyle; italic: TextStyle; /* ... */
|
|
738
|
+
document: ViewStyle; blockquote: ViewStyle; code_block: ViewStyle; /* ... */
|
|
739
|
+
}
|
|
371
740
|
>;
|
|
372
741
|
```
|
|
373
742
|
|
|
743
|
+
Example with `mergeThemes`:
|
|
744
|
+
|
|
745
|
+
```tsx
|
|
746
|
+
import {
|
|
747
|
+
Markdown,
|
|
748
|
+
defaultMarkdownTheme,
|
|
749
|
+
mergeThemes,
|
|
750
|
+
} from "react-native-nitro-markdown";
|
|
751
|
+
|
|
752
|
+
const theme = mergeThemes(defaultMarkdownTheme, {
|
|
753
|
+
colors: {
|
|
754
|
+
text: "#0f172a",
|
|
755
|
+
link: "#1d4ed8",
|
|
756
|
+
},
|
|
757
|
+
fontSizes: {
|
|
758
|
+
m: 16,
|
|
759
|
+
},
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
<Markdown theme={theme}>{content}</Markdown>;
|
|
763
|
+
```
|
|
764
|
+
|
|
374
765
|
## Built-in Renderer Components
|
|
375
766
|
|
|
376
767
|
Use these when composing custom renderer maps.
|
|
@@ -455,6 +846,7 @@ import { Markdown } from "react-native-nitro-markdown";
|
|
|
455
846
|
- For streaming text, prefer `updateStrategy="raf"`.
|
|
456
847
|
- If you use interval strategy, `updateIntervalMs` between `50` and `100` is a good baseline.
|
|
457
848
|
- Batch `session.append(...)` calls instead of appending one character at a time.
|
|
849
|
+
- For large markdown documents, enable `virtualize` and tune `virtualization.windowSize` / `maxToRenderPerBatch`.
|
|
458
850
|
- Use the headless API if you do not need built-in renderers.
|
|
459
851
|
|
|
460
852
|
## Troubleshooting
|
|
@@ -470,7 +862,7 @@ import { Markdown } from "react-native-nitro-markdown";
|
|
|
470
862
|
|
|
471
863
|
## Contributing
|
|
472
864
|
|
|
473
|
-
See
|
|
865
|
+
See `CONTRIBUTING.md`.
|
|
474
866
|
|
|
475
867
|
## License
|
|
476
868
|
|
|
@@ -2,7 +2,7 @@ package com.margelo.nitro.com.nitromarkdown
|
|
|
2
2
|
|
|
3
3
|
class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
4
4
|
private var buffer = StringBuilder()
|
|
5
|
-
private val listeners = mutableMapOf<Long, () -> Unit>()
|
|
5
|
+
private val listeners = mutableMapOf<Long, (Double, Double) -> Unit>()
|
|
6
6
|
private var nextListenerId = 0L
|
|
7
7
|
private val lock = Any()
|
|
8
8
|
|
|
@@ -17,11 +17,16 @@ class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
|
17
17
|
override val memorySize: Long
|
|
18
18
|
get() = buffer.length.toLong()
|
|
19
19
|
|
|
20
|
-
override fun append(chunk: String) {
|
|
20
|
+
override fun append(chunk: String): Double {
|
|
21
|
+
val from: Int
|
|
22
|
+
val to: Int
|
|
21
23
|
synchronized(lock) {
|
|
24
|
+
from = buffer.length
|
|
22
25
|
buffer.append(chunk)
|
|
26
|
+
to = buffer.length
|
|
23
27
|
}
|
|
24
|
-
notifyListeners()
|
|
28
|
+
notifyListeners(from.toDouble(), to.toDouble())
|
|
29
|
+
return to.toDouble()
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
override fun clear() {
|
|
@@ -29,7 +34,7 @@ class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
|
29
34
|
buffer.clear()
|
|
30
35
|
highlightPosition = 0.0
|
|
31
36
|
}
|
|
32
|
-
notifyListeners()
|
|
37
|
+
notifyListeners(0.0, 0.0)
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
override fun getAllText(): String {
|
|
@@ -38,7 +43,21 @@ class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
|
38
43
|
}
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
override fun
|
|
46
|
+
override fun getLength(): Double {
|
|
47
|
+
synchronized(lock) {
|
|
48
|
+
return buffer.length.toDouble()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override fun getTextRange(from: Double, to: Double): String {
|
|
53
|
+
synchronized(lock) {
|
|
54
|
+
val start = from.toInt().coerceIn(0, buffer.length)
|
|
55
|
+
val end = to.toInt().coerceIn(start, buffer.length)
|
|
56
|
+
return buffer.substring(start, end)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override fun addListener(listener: (Double, Double) -> Unit): () -> Unit {
|
|
42
61
|
val id: Long
|
|
43
62
|
synchronized(lock) {
|
|
44
63
|
id = nextListenerId++
|
|
@@ -51,11 +70,30 @@ class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
override fun reset(text: String) {
|
|
74
|
+
synchronized(lock) {
|
|
75
|
+
buffer.replace(0, buffer.length, text)
|
|
76
|
+
}
|
|
77
|
+
notifyListeners(0.0, text.length.toDouble())
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override fun replace(from: Double, to: Double, text: String): Double {
|
|
81
|
+
val newLength: Double
|
|
82
|
+
synchronized(lock) {
|
|
83
|
+
val start = from.toInt().coerceIn(0, buffer.length)
|
|
84
|
+
val end = to.toInt().coerceIn(start, buffer.length)
|
|
85
|
+
buffer.replace(start, end, text)
|
|
86
|
+
newLength = buffer.length.toDouble()
|
|
87
|
+
}
|
|
88
|
+
notifyListeners(from, from + text.length.toDouble())
|
|
89
|
+
return newLength
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private fun notifyListeners(from: Double, to: Double) {
|
|
93
|
+
val currentListeners: Collection<(Double, Double) -> Unit>
|
|
56
94
|
synchronized(lock) {
|
|
57
95
|
currentListeners = listeners.values.toList()
|
|
58
96
|
}
|
|
59
|
-
currentListeners.forEach { it() }
|
|
97
|
+
currentListeners.forEach { it(from, to) }
|
|
60
98
|
}
|
|
61
99
|
}
|