react-native-nitro-markdown 0.3.2 → 0.4.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.
Files changed (99) hide show
  1. package/README.md +84 -82
  2. package/cpp/core/MD4CParser.cpp +60 -25
  3. package/cpp/core/MD4CParser.hpp +13 -1
  4. package/cpp/md4c/md4c.c +1 -1
  5. package/lib/commonjs/index.js +0 -22
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/commonjs/markdown-stream.js +53 -4
  8. package/lib/commonjs/markdown-stream.js.map +1 -1
  9. package/lib/commonjs/markdown.js +20 -5
  10. package/lib/commonjs/markdown.js.map +1 -1
  11. package/lib/commonjs/renderers/code.js +9 -2
  12. package/lib/commonjs/renderers/code.js.map +1 -1
  13. package/lib/commonjs/renderers/heading.js +14 -5
  14. package/lib/commonjs/renderers/heading.js.map +1 -1
  15. package/lib/commonjs/renderers/image.js +12 -3
  16. package/lib/commonjs/renderers/image.js.map +1 -1
  17. package/lib/commonjs/renderers/link.js +6 -1
  18. package/lib/commonjs/renderers/link.js.map +1 -1
  19. package/lib/commonjs/renderers/list.js +32 -8
  20. package/lib/commonjs/renderers/list.js.map +1 -1
  21. package/lib/commonjs/renderers/math.js +8 -2
  22. package/lib/commonjs/renderers/math.js.map +1 -1
  23. package/lib/commonjs/renderers/table.js +8 -2
  24. package/lib/commonjs/renderers/table.js.map +1 -1
  25. package/lib/commonjs/theme.js +47 -84
  26. package/lib/commonjs/theme.js.map +1 -1
  27. package/lib/module/index.js +1 -2
  28. package/lib/module/index.js.map +1 -1
  29. package/lib/module/markdown-stream.js +54 -5
  30. package/lib/module/markdown-stream.js.map +1 -1
  31. package/lib/module/markdown.js +21 -6
  32. package/lib/module/markdown.js.map +1 -1
  33. package/lib/module/renderers/code.js +9 -2
  34. package/lib/module/renderers/code.js.map +1 -1
  35. package/lib/module/renderers/heading.js +15 -6
  36. package/lib/module/renderers/heading.js.map +1 -1
  37. package/lib/module/renderers/image.js +13 -4
  38. package/lib/module/renderers/image.js.map +1 -1
  39. package/lib/module/renderers/link.js +7 -2
  40. package/lib/module/renderers/link.js.map +1 -1
  41. package/lib/module/renderers/list.js +33 -9
  42. package/lib/module/renderers/list.js.map +1 -1
  43. package/lib/module/renderers/math.js +8 -2
  44. package/lib/module/renderers/math.js.map +1 -1
  45. package/lib/module/renderers/table.js +9 -3
  46. package/lib/module/renderers/table.js.map +1 -1
  47. package/lib/module/theme.js +46 -83
  48. package/lib/module/theme.js.map +1 -1
  49. package/lib/typescript/commonjs/index.d.ts +1 -2
  50. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/markdown-stream.d.ts +16 -0
  52. package/lib/typescript/commonjs/markdown-stream.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/markdown.d.ts +2 -2
  54. package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/renderers/table.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/theme.d.ts +3 -4
  63. package/lib/typescript/commonjs/theme.d.ts.map +1 -1
  64. package/lib/typescript/module/index.d.ts +1 -2
  65. package/lib/typescript/module/index.d.ts.map +1 -1
  66. package/lib/typescript/module/markdown-stream.d.ts +16 -0
  67. package/lib/typescript/module/markdown-stream.d.ts.map +1 -1
  68. package/lib/typescript/module/markdown.d.ts +2 -2
  69. package/lib/typescript/module/markdown.d.ts.map +1 -1
  70. package/lib/typescript/module/renderers/code.d.ts.map +1 -1
  71. package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
  72. package/lib/typescript/module/renderers/image.d.ts.map +1 -1
  73. package/lib/typescript/module/renderers/link.d.ts.map +1 -1
  74. package/lib/typescript/module/renderers/list.d.ts.map +1 -1
  75. package/lib/typescript/module/renderers/math.d.ts.map +1 -1
  76. package/lib/typescript/module/renderers/table.d.ts.map +1 -1
  77. package/lib/typescript/module/theme.d.ts +3 -4
  78. package/lib/typescript/module/theme.d.ts.map +1 -1
  79. package/package.json +2 -2
  80. package/src/index.ts +0 -3
  81. package/src/markdown-stream.tsx +82 -5
  82. package/src/markdown.tsx +8 -2
  83. package/src/renderers/code.tsx +3 -0
  84. package/src/renderers/heading.tsx +24 -2
  85. package/src/renderers/image.tsx +4 -0
  86. package/src/renderers/link.tsx +10 -1
  87. package/src/renderers/list.tsx +30 -6
  88. package/src/renderers/math.tsx +2 -0
  89. package/src/renderers/table.tsx +3 -0
  90. package/src/theme.ts +51 -88
  91. package/lib/commonjs/default-markdown-renderer.js +0 -217
  92. package/lib/commonjs/default-markdown-renderer.js.map +0 -1
  93. package/lib/module/default-markdown-renderer.js +0 -212
  94. package/lib/module/default-markdown-renderer.js.map +0 -1
  95. package/lib/typescript/commonjs/default-markdown-renderer.d.ts +0 -10
  96. package/lib/typescript/commonjs/default-markdown-renderer.d.ts.map +0 -1
  97. package/lib/typescript/module/default-markdown-renderer.d.ts +0 -10
  98. package/lib/typescript/module/default-markdown-renderer.d.ts.map +0 -1
  99. package/src/default-markdown-renderer.tsx +0 -261
package/README.md CHANGED
@@ -86,7 +86,7 @@ bunx expo prebuild
86
86
 
87
87
  ### Option 1: Batteries Included (Simplest)
88
88
 
89
- Use the `Markdown` component with built-in premium dark-mode styling:
89
+ Use the `Markdown` component with clean, neutral styling that stays out of the way:
90
90
 
91
91
  ```tsx
92
92
  import { Markdown } from "react-native-nitro-markdown";
@@ -100,91 +100,27 @@ export function MyComponent() {
100
100
  }
101
101
  ```
102
102
 
103
- ### Option 2: Light Theme / Theme Presets
103
+ If you're rendering on a dark surface, override `theme.colors.text` (or use the `styles` prop) to match your app's palette.
104
104
 
105
- The default theme is optimized for dark mode. For light backgrounds, use the provided `lightMarkdownTheme`:
106
-
107
- ```tsx
108
- import { Markdown, lightMarkdownTheme } from "react-native-nitro-markdown";
109
-
110
- <Markdown theme={lightMarkdownTheme}>{"# Light Mode Markdown"}</Markdown>;
111
- ```
112
-
113
- Available presets:
114
-
115
- - `defaultMarkdownTheme` / `darkMarkdownTheme` - Modern dark theme
116
- - `lightMarkdownTheme` - Clean light theme
117
- - `minimalMarkdownTheme` - Bare minimum styling for a clean slate
118
-
119
- ### Option 3: Custom Theming
120
-
121
- Customize the look and feel by passing a partial `theme` object:
122
-
123
- ```tsx
124
- import { Markdown } from "react-native-nitro-markdown";
125
-
126
- const myTheme = {
127
- colors: {
128
- text: "#2D3748",
129
- heading: "#1A202C",
130
- link: "#3182CE",
131
- },
132
- fontFamilies: {
133
- regular: "Inter",
134
- heading: "Inter-Bold",
135
- mono: "JetBrainsMono",
136
- },
137
- borderRadius: {
138
- s: 4,
139
- m: 8,
140
- l: 16,
141
- },
142
- showCodeLanguage: true, // Toggle code language labels
143
- };
144
-
145
- <Markdown theme={myTheme}>{"# Custom Themed Markdown"}</Markdown>;
146
- ```
147
-
148
- **Theme Properties:**
149
-
150
- - `colors` - All color tokens (text, heading, link, code, codeBackground, codeLanguage, etc.)
151
- - `spacing` - Spacing tokens (xs, s, m, l, xl)
152
- - `fontSizes` - Font sizes (xs, s, m, l, xl, h1-h6)
153
- - `fontFamilies` - Font families for regular, heading, and mono text
154
- - `borderRadius` - Border radius tokens (s, m, l)
155
- - `showCodeLanguage` - Show/hide code block language labels
156
-
157
- ### Option 4: Style Overrides per Node Type
105
+ ### Option 2: Style Overrides per Node Type
158
106
 
159
107
  Apply quick style overrides to specific node types without writing custom renderers:
160
108
 
161
109
  ```tsx
162
110
  <Markdown
163
111
  styles={{
164
- heading: { color: "red", fontWeight: "900" },
165
- code_block: { backgroundColor: "#1a1a2e", borderRadius: 16 },
166
- blockquote: { borderLeftColor: "#ff6b6b" },
112
+ heading: { color: "#0ea5e9", fontWeight: "900" },
113
+ code_block: { backgroundColor: "#e2e8f0", borderRadius: 16 },
114
+ blockquote: { borderLeftColor: "#0ea5e9" },
167
115
  }}
168
116
  >
169
117
  {markdown}
170
118
  </Markdown>
171
119
  ```
172
120
 
173
- ### Option 5: Minimal Styling Strategy
121
+ ### Option 3: Custom Renderers
174
122
 
175
- Start with a clean slate using the `stylingStrategy` prop:
176
-
177
- ```tsx
178
- <Markdown stylingStrategy="minimal" theme={myLightTheme}>
179
- {content}
180
- </Markdown>
181
- ```
182
-
183
- This zeros out all spacing and removes opinionated colors, letting you build up from scratch.
184
-
185
- ### Option 6: Custom Renderers
186
-
187
- Override specific node types with full control. Custom renderers now receive **pre-mapped props** for common values:
123
+ Override specific node types with full control. Custom renderers receive **pre-mapped props** for common values:
188
124
 
189
125
  ```tsx
190
126
  import {
@@ -225,7 +161,56 @@ const renderers = {
225
161
  - `list` → `ordered`, `start`
226
162
  - `task_list_item` → `checked`
227
163
 
228
- ### Option 7: Style Props on Individual Renderers
164
+ ### Option 4: Token Overrides (Theme)
165
+
166
+ Customize the look and feel by passing a partial `theme` object:
167
+
168
+ ```tsx
169
+ import { Markdown } from "react-native-nitro-markdown";
170
+
171
+ const myTheme = {
172
+ colors: {
173
+ text: "#0f172a",
174
+ heading: "#0f172a",
175
+ link: "#0ea5e9",
176
+ codeBackground: "#e2e8f0",
177
+ },
178
+ showCodeLanguage: false,
179
+ };
180
+
181
+ <Markdown theme={myTheme}>{"# Custom Themed Markdown"}</Markdown>;
182
+ ```
183
+
184
+ Defaults live in `defaultMarkdownTheme` and are intentionally neutral so you can layer your own palette on top.
185
+
186
+ **Theme Properties:**
187
+
188
+ - `colors` - All color tokens (text, heading, link, code, codeBackground, codeLanguage, etc.)
189
+ - `spacing` - Spacing tokens (xs, s, m, l, xl)
190
+ - `fontSizes` - Font sizes (xs, s, m, l, xl, h1-h6)
191
+ - `fontFamilies` - Font families for regular, heading, and mono text
192
+ - `headingWeight` - Optional weight for headings (useful for Android custom fonts)
193
+ - `borderRadius` - Border radius tokens (s, m, l)
194
+ - `showCodeLanguage` - Show/hide code block language labels
195
+
196
+ **Android custom fonts note:**
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.
200
+
201
+ ### Option 5: Minimal Styling Strategy
202
+
203
+ Start with a clean slate using the `stylingStrategy` prop:
204
+
205
+ ```tsx
206
+ <Markdown stylingStrategy="minimal" theme={myLightTheme}>
207
+ {content}
208
+ </Markdown>
209
+ ```
210
+
211
+ This zeros out all spacing and removes opinionated colors, letting you build up from scratch.
212
+
213
+ ### Option 6: Style Props on Individual Renderers
229
214
 
230
215
  All built-in renderers accept a `style` prop for fine-grained overrides:
231
216
 
@@ -238,7 +223,7 @@ import { Heading, CodeBlock, InlineCode } from "react-native-nitro-markdown";
238
223
  <InlineCode style={{ backgroundColor: "#ff0" }}>code</InlineCode>
239
224
  ```
240
225
 
241
- ### Option 8: Auto Content Extraction for Code
226
+ ### Option 7: Auto Content Extraction for Code
242
227
 
243
228
  The `CodeBlock` and `InlineCode` components now accept a `node` prop for automatic content extraction:
244
229
 
@@ -257,7 +242,7 @@ code_block: ({ content, language }) => (
257
242
  );
258
243
  ```
259
244
 
260
- ### Option 9: Headless (Minimal Bundle)
245
+ ### Option 8: Headless (Minimal Bundle)
261
246
 
262
247
  For maximum control, data processing, or minimal JS overhead:
263
248
 
@@ -273,9 +258,9 @@ const text = getTextContent(ast); // "Hello World"
273
258
  const fullText = getFlattenedText(ast); // "Hello World\n\n" (Normalized with line breaks)
274
259
  ```
275
260
 
276
- ### Option 10: High-Performance Streaming (LLMs)
261
+ ### Option 9: High-Performance Streaming (LLMs)
277
262
 
278
- When streaming text token-by-token (e.g., from ChatGPT or Gemini):
263
+ When streaming text token-by-token (e.g., from ChatGPT or Gemini), you should batch UI updates to avoid re-rendering on every token:
279
264
 
280
265
  ```tsx
281
266
  import {
@@ -292,12 +277,31 @@ export function AIResponseStream() {
292
277
  }, [session]);
293
278
 
294
279
  return (
295
- <MarkdownStream session={session.getSession()} options={{ gfm: true }} />
280
+ <MarkdownStream
281
+ session={session.getSession()}
282
+ options={{ gfm: true }}
283
+ updateIntervalMs={60}
284
+ updateStrategy="raf"
285
+ useTransitionUpdates
286
+ />
296
287
  );
297
288
  }
298
289
  ```
299
290
 
300
- ### Option 11: Extracting Plain Text
291
+ **Recommended defaults for token streaming:**
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
295
+
296
+ **Streaming props:**
297
+
298
+ | Prop | Type | Default | Description |
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 |
303
+
304
+ ### Option 10: Extracting Plain Text
301
305
 
302
306
  You can extract the plain text representation (with proper line breaks) using the `onParseComplete` callback. This is useful for "Copy All" buttons or TTS.
303
307
 
@@ -344,11 +348,9 @@ export {
344
348
  getFlattenedText,
345
349
  } from "./headless";
346
350
 
347
- // Theme presets
351
+ // Theme tokens
348
352
  export {
349
353
  defaultMarkdownTheme,
350
- lightMarkdownTheme,
351
- darkMarkdownTheme,
352
354
  minimalMarkdownTheme,
353
355
  mergeThemes,
354
356
  };
@@ -3,9 +3,20 @@
3
3
 
4
4
  #include <stack>
5
5
  #include <cstring>
6
+ #include <limits>
6
7
 
7
8
  namespace NitroMarkdown {
8
9
 
10
+ namespace {
11
+ size_t clampInputSize(size_t inputSize) {
12
+ size_t maxSize = static_cast<size_t>(std::numeric_limits<MD_SIZE>::max());
13
+ if (inputSize > maxSize) {
14
+ return maxSize;
15
+ }
16
+ return inputSize;
17
+ }
18
+ } // namespace
19
+
9
20
  class MD4CParser::Impl {
10
21
  public:
11
22
  std::shared_ptr<MarkdownNode> root;
@@ -55,25 +66,42 @@ public:
55
66
  }
56
67
 
57
68
  std::string getAttributeText(const MD_ATTRIBUTE* attr) {
58
- if (!attr || attr->size == 0) return "";
69
+ if (!attr || attr->size == 0 || !attr->text) return "";
70
+ if (!attr->substr_types || !attr->substr_offsets) {
71
+ return std::string(attr->text, attr->size);
72
+ }
59
73
 
60
74
  std::string result;
61
75
  result.reserve(attr->size);
62
-
63
- for (unsigned i = 0; i < attr->size; i++) {
76
+
77
+ for (unsigned i = 0; ; i++) {
78
+ size_t start = static_cast<size_t>(attr->substr_offsets[i]);
79
+ size_t end = static_cast<size_t>(attr->substr_offsets[i + 1]);
80
+
81
+ if (end > attr->size) {
82
+ end = static_cast<size_t>(attr->size);
83
+ }
84
+ if (start > end) {
85
+ break;
86
+ }
87
+
64
88
  if (attr->substr_types[i] == MD_TEXT_NORMAL ||
65
89
  attr->substr_types[i] == MD_TEXT_ENTITY ||
66
90
  attr->substr_types[i] == MD_TEXT_NULLCHAR) {
67
- result.append(attr->substr_offsets[i],
68
- i + 1 < attr->size ? attr->substr_offsets[i + 1] - attr->substr_offsets[i]
69
- : attr->size - attr->substr_offsets[i]);
91
+ if (end > start) {
92
+ result.append(attr->text + start, end - start);
93
+ }
94
+ }
95
+
96
+ if (end >= attr->size) {
97
+ break;
70
98
  }
71
99
  }
72
-
73
- if (result.empty() && attr->text && attr->size > 0) {
100
+
101
+ if (result.empty() && attr->size > 0) {
74
102
  result.assign(attr->text, attr->size);
75
103
  }
76
-
104
+
77
105
  return result;
78
106
  }
79
107
 
@@ -133,8 +161,8 @@ public:
133
161
  case MD_BLOCK_CODE: {
134
162
  auto* d = static_cast<MD_BLOCK_CODE_DETAIL*>(detail);
135
163
  auto node = std::make_shared<MarkdownNode>(NodeType::CodeBlock);
136
- if (d->lang.text && d->lang.size > 0) {
137
- node->language = std::string(d->lang.text, d->lang.size);
164
+ if (d->lang.size > 0) {
165
+ node->language = impl->getAttributeText(&d->lang);
138
166
  }
139
167
  impl->pushNode(node, off);
140
168
  break;
@@ -243,11 +271,11 @@ public:
243
271
  case MD_SPAN_A: {
244
272
  auto* d = static_cast<MD_SPAN_A_DETAIL*>(detail);
245
273
  auto node = std::make_shared<MarkdownNode>(NodeType::Link);
246
- if (d->href.text && d->href.size > 0) {
247
- node->href = std::string(d->href.text, d->href.size);
274
+ if (d->href.size > 0) {
275
+ node->href = impl->getAttributeText(&d->href);
248
276
  }
249
- if (d->title.text && d->title.size > 0) {
250
- node->title = std::string(d->title.text, d->title.size);
277
+ if (d->title.size > 0) {
278
+ node->title = impl->getAttributeText(&d->title);
251
279
  }
252
280
  impl->pushNode(node, off);
253
281
  break;
@@ -256,11 +284,11 @@ public:
256
284
  case MD_SPAN_IMG: {
257
285
  auto* d = static_cast<MD_SPAN_IMG_DETAIL*>(detail);
258
286
  auto node = std::make_shared<MarkdownNode>(NodeType::Image);
259
- if (d->src.text && d->src.size > 0) {
260
- node->href = std::string(d->src.text, d->src.size);
287
+ if (d->src.size > 0) {
288
+ node->href = impl->getAttributeText(&d->src);
261
289
  }
262
- if (d->title.text && d->title.size > 0) {
263
- node->title = std::string(d->title.text, d->title.size);
290
+ if (d->title.size > 0) {
291
+ node->title = impl->getAttributeText(&d->title);
264
292
  }
265
293
  impl->pushNode(node, off);
266
294
  break;
@@ -329,10 +357,17 @@ public:
329
357
  if (!text || size == 0) return 0;
330
358
 
331
359
  switch (type) {
332
- case MD_TEXT_NULLCHAR:
333
- if (impl->currentText.empty()) impl->currentTextBeg = impl->lastTextEnd;
360
+ case MD_TEXT_NULLCHAR: {
361
+ MD_OFFSET off = impl->lastTextEnd;
362
+ ptrdiff_t diff = text - impl->inputText;
363
+ if (diff >= 0 && static_cast<size_t>(diff) <= impl->inputTextSize) {
364
+ off = static_cast<MD_OFFSET>(diff);
365
+ }
366
+ if (impl->currentText.empty()) impl->currentTextBeg = off;
334
367
  impl->currentText += '\0';
368
+ impl->lastTextEnd = off + 1;
335
369
  break;
370
+ }
336
371
 
337
372
  case MD_TEXT_BR:
338
373
  impl->flushText();
@@ -400,7 +435,8 @@ MD4CParser::~MD4CParser() = default;
400
435
  std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, const ParserOptions& options) {
401
436
  impl_->reset();
402
437
  impl_->inputText = markdown.c_str();
403
- impl_->inputTextSize = markdown.size();
438
+ size_t inputSize = clampInputSize(markdown.size());
439
+ impl_->inputTextSize = inputSize;
404
440
 
405
441
  unsigned int flags = MD_FLAG_NOHTML;
406
442
 
@@ -427,8 +463,8 @@ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, con
427
463
  nullptr
428
464
  };
429
465
 
430
- md_parse(markdown.c_str(),
431
- static_cast<MD_SIZE>(markdown.size()),
466
+ md_parse(markdown.c_str(),
467
+ static_cast<MD_SIZE>(inputSize),
432
468
  &parser,
433
469
  impl_.get());
434
470
 
@@ -437,4 +473,3 @@ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, con
437
473
  }
438
474
 
439
475
  } // namespace NitroMarkdown
440
-
@@ -3,6 +3,12 @@
3
3
  #include "MarkdownTypes.hpp"
4
4
  #include <string>
5
5
  #include <memory>
6
+ #include <cstddef>
7
+
8
+ #ifdef NITRO_MARKDOWN_TESTING
9
+ #include "../md4c/md4c.h"
10
+ #include <limits>
11
+ #endif
6
12
 
7
13
  namespace NitroMarkdown {
8
14
 
@@ -11,6 +17,13 @@ public:
11
17
  MD4CParser();
12
18
  ~MD4CParser();
13
19
  std::shared_ptr<MarkdownNode> parse(const std::string& markdown, const ParserOptions& options);
20
+
21
+ #ifdef NITRO_MARKDOWN_TESTING
22
+ static size_t clampInputSizeForTest(size_t inputSize) {
23
+ size_t maxSize = static_cast<size_t>(std::numeric_limits<MD_SIZE>::max());
24
+ return inputSize > maxSize ? maxSize : inputSize;
25
+ }
26
+ #endif
14
27
 
15
28
  private:
16
29
  class Impl;
@@ -18,4 +31,3 @@ private:
18
31
  };
19
32
 
20
33
  } // namespace NitroMarkdown
21
-
package/cpp/md4c/md4c.c CHANGED
@@ -6489,7 +6489,7 @@ md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userd
6489
6489
  ctx.code_indent_offset = (ctx.parser.flags & MD_FLAG_NOINDENTEDCODEBLOCKS) ? (OFF)(-1) : 4;
6490
6490
  md_build_mark_char_map(&ctx);
6491
6491
  ctx.doc_ends_with_newline = (size > 0 && ISNEWLINE_(text[size-1]));
6492
- ctx.max_ref_def_output = MIN(MIN(16 * (uint64_t)size, (uint64_t)(1024 * 1024)), (uint64_t)SZ_MAX);
6492
+ ctx.max_ref_def_output = (SZ) MIN(MIN(16 * (uint64_t)size, (uint64_t)(1024 * 1024)), (uint64_t)SZ_MAX);
6493
6493
 
6494
6494
  /* Reset all mark stacks and lists. */
6495
6495
  for(i = 0; i < (int) SIZEOF_ARRAY(ctx.opener_stacks); i++)
@@ -4,14 +4,11 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  var _exportNames = {
7
- DefaultMarkdownRenderer: true,
8
7
  Markdown: true,
9
8
  MarkdownStream: true,
10
9
  useMarkdownContext: true,
11
10
  MarkdownContext: true,
12
11
  defaultMarkdownTheme: true,
13
- lightMarkdownTheme: true,
14
- darkMarkdownTheme: true,
15
12
  minimalMarkdownTheme: true,
16
13
  mergeThemes: true,
17
14
  Heading: true,
@@ -44,12 +41,6 @@ Object.defineProperty(exports, "CodeBlock", {
44
41
  return _code.CodeBlock;
45
42
  }
46
43
  });
47
- Object.defineProperty(exports, "DefaultMarkdownRenderer", {
48
- enumerable: true,
49
- get: function () {
50
- return _defaultMarkdownRenderer.DefaultMarkdownRenderer;
51
- }
52
- });
53
44
  Object.defineProperty(exports, "Heading", {
54
45
  enumerable: true,
55
46
  get: function () {
@@ -146,24 +137,12 @@ Object.defineProperty(exports, "createMarkdownSession", {
146
137
  return _MarkdownSession.createMarkdownSession;
147
138
  }
148
139
  });
149
- Object.defineProperty(exports, "darkMarkdownTheme", {
150
- enumerable: true,
151
- get: function () {
152
- return _theme.darkMarkdownTheme;
153
- }
154
- });
155
140
  Object.defineProperty(exports, "defaultMarkdownTheme", {
156
141
  enumerable: true,
157
142
  get: function () {
158
143
  return _theme.defaultMarkdownTheme;
159
144
  }
160
145
  });
161
- Object.defineProperty(exports, "lightMarkdownTheme", {
162
- enumerable: true,
163
- get: function () {
164
- return _theme.lightMarkdownTheme;
165
- }
166
- });
167
146
  Object.defineProperty(exports, "mergeThemes", {
168
147
  enumerable: true,
169
148
  get: function () {
@@ -206,7 +185,6 @@ Object.keys(_headless).forEach(function (key) {
206
185
  }
207
186
  });
208
187
  });
209
- var _defaultMarkdownRenderer = require("./default-markdown-renderer.js");
210
188
  var _markdown = require("./markdown.js");
211
189
  var _markdownStream = require("./markdown-stream.js");
212
190
  var _MarkdownContext = require("./MarkdownContext.js");
@@ -1 +1 @@
1
- {"version":3,"names":["_headless","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_defaultMarkdownRenderer","_markdown","_markdownStream","_MarkdownContext","_theme","_heading","_paragraph","_link","_blockquote","_horizontalRule","_code","_list","_table","_image","_math","_MarkdownSession","_useMarkdownStream"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,SAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAL,SAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAb,SAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AAEA,IAAAS,wBAAA,GAAAb,OAAA;AACA,IAAAc,SAAA,GAAAd,OAAA;AACA,IAAAe,eAAA,GAAAf,OAAA;AAEA,IAAAgB,gBAAA,GAAAhB,OAAA;AAkBA,IAAAiB,MAAA,GAAAjB,OAAA;AAcA,IAAAkB,QAAA,GAAAlB,OAAA;AACA,IAAAmB,UAAA,GAAAnB,OAAA;AACA,IAAAoB,KAAA,GAAApB,OAAA;AACA,IAAAqB,WAAA,GAAArB,OAAA;AACA,IAAAsB,eAAA,GAAAtB,OAAA;AACA,IAAAuB,KAAA,GAAAvB,OAAA;AACA,IAAAwB,KAAA,GAAAxB,OAAA;AACA,IAAAyB,MAAA,GAAAzB,OAAA;AACA,IAAA0B,MAAA,GAAA1B,OAAA;AACA,IAAA2B,KAAA,GAAA3B,OAAA;AAEA,IAAA4B,gBAAA,GAAA5B,OAAA;AAEA,IAAA6B,kBAAA,GAAA7B,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_headless","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_markdown","_markdownStream","_MarkdownContext","_theme","_heading","_paragraph","_link","_blockquote","_horizontalRule","_code","_list","_table","_image","_math","_MarkdownSession","_useMarkdownStream"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,SAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAL,SAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAb,SAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AAEA,IAAAS,SAAA,GAAAb,OAAA;AACA,IAAAc,eAAA,GAAAd,OAAA;AAEA,IAAAe,gBAAA,GAAAf,OAAA;AAkBA,IAAAgB,MAAA,GAAAhB,OAAA;AAYA,IAAAiB,QAAA,GAAAjB,OAAA;AACA,IAAAkB,UAAA,GAAAlB,OAAA;AACA,IAAAmB,KAAA,GAAAnB,OAAA;AACA,IAAAoB,WAAA,GAAApB,OAAA;AACA,IAAAqB,eAAA,GAAArB,OAAA;AACA,IAAAsB,KAAA,GAAAtB,OAAA;AACA,IAAAuB,KAAA,GAAAvB,OAAA;AACA,IAAAwB,MAAA,GAAAxB,OAAA;AACA,IAAAyB,MAAA,GAAAzB,OAAA;AACA,IAAA0B,KAAA,GAAA1B,OAAA;AAEA,IAAA2B,gBAAA,GAAA3B,OAAA;AAEA,IAAA4B,kBAAA,GAAA5B,OAAA","ignoreList":[]}
@@ -13,16 +13,65 @@ var _jsxRuntime = require("react/jsx-runtime");
13
13
  */
14
14
  const MarkdownStream = ({
15
15
  session,
16
+ updateIntervalMs = 50,
17
+ updateStrategy = "interval",
18
+ useTransitionUpdates = false,
16
19
  ...props
17
20
  }) => {
18
21
  const [text, setText] = (0, _react.useState)(() => session.getAllText());
22
+ const pendingUpdateRef = (0, _react.useRef)(false);
23
+ const updateTimerRef = (0, _react.useRef)(null);
24
+ const rafRef = (0, _react.useRef)(null);
25
+ const lastEmittedRef = (0, _react.useRef)(text);
19
26
  (0, _react.useEffect)(() => {
20
27
  // Ensure initial state is synced
21
- setText(session.getAllText());
22
- return session.addListener(() => {
23
- setText(session.getAllText());
28
+ const initialText = session.getAllText();
29
+ setText(initialText);
30
+ lastEmittedRef.current = initialText;
31
+ const flushUpdate = () => {
32
+ updateTimerRef.current = null;
33
+ if (rafRef.current !== null) {
34
+ cancelAnimationFrame(rafRef.current);
35
+ rafRef.current = null;
36
+ }
37
+ if (!pendingUpdateRef.current) return;
38
+ pendingUpdateRef.current = false;
39
+ const latest = session.getAllText();
40
+ if (latest === lastEmittedRef.current) return;
41
+ lastEmittedRef.current = latest;
42
+ if (useTransitionUpdates) {
43
+ (0, _react.startTransition)(() => setText(latest));
44
+ } else {
45
+ setText(latest);
46
+ }
47
+ };
48
+ const scheduleFlush = () => {
49
+ if (updateStrategy === "raf") {
50
+ if (rafRef.current === null) {
51
+ rafRef.current = requestAnimationFrame(flushUpdate);
52
+ }
53
+ return;
54
+ }
55
+ if (!updateTimerRef.current) {
56
+ updateTimerRef.current = setTimeout(flushUpdate, updateIntervalMs);
57
+ }
58
+ };
59
+ const unsubscribe = session.addListener(() => {
60
+ pendingUpdateRef.current = true;
61
+ scheduleFlush();
24
62
  });
25
- }, [session]);
63
+ return () => {
64
+ unsubscribe();
65
+ if (updateTimerRef.current) {
66
+ clearTimeout(updateTimerRef.current);
67
+ updateTimerRef.current = null;
68
+ }
69
+ if (rafRef.current !== null) {
70
+ cancelAnimationFrame(rafRef.current);
71
+ rafRef.current = null;
72
+ }
73
+ };
74
+ }, [session, updateIntervalMs, updateStrategy, useTransitionUpdates]);
26
75
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_markdown.Markdown, {
27
76
  ...props,
28
77
  children: text
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_markdown","_jsxRuntime","MarkdownStream","session","props","text","setText","useState","getAllText","useEffect","addListener","jsx","Markdown","children","exports"],"sourceRoot":"../../src","sources":["markdown-stream.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,SAAA,GAAAD,OAAA;AAA0D,IAAAE,WAAA,GAAAF,OAAA;AAU1D;AACA;AACA;AACA;AACO,MAAMG,cAAuC,GAAGA,CAAC;EACtDC,OAAO;EACP,GAAGC;AACL,CAAC,KAAK;EACJ,MAAM,CAACC,IAAI,EAAEC,OAAO,CAAC,GAAG,IAAAC,eAAQ,EAAC,MAAMJ,OAAO,CAACK,UAAU,CAAC,CAAC,CAAC;EAE5D,IAAAC,gBAAS,EAAC,MAAM;IACd;IACAH,OAAO,CAACH,OAAO,CAACK,UAAU,CAAC,CAAC,CAAC;IAE7B,OAAOL,OAAO,CAACO,WAAW,CAAC,MAAM;MAC/BJ,OAAO,CAACH,OAAO,CAACK,UAAU,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC;EACJ,CAAC,EAAE,CAACL,OAAO,CAAC,CAAC;EAEb,oBAAO,IAAAF,WAAA,CAAAU,GAAA,EAACX,SAAA,CAAAY,QAAQ;IAAA,GAAKR,KAAK;IAAAS,QAAA,EAAGR;EAAI,CAAW,CAAC;AAC/C,CAAC;AAACS,OAAA,CAAAZ,cAAA,GAAAA,cAAA","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_markdown","_jsxRuntime","MarkdownStream","session","updateIntervalMs","updateStrategy","useTransitionUpdates","props","text","setText","useState","getAllText","pendingUpdateRef","useRef","updateTimerRef","rafRef","lastEmittedRef","useEffect","initialText","current","flushUpdate","cancelAnimationFrame","latest","startTransition","scheduleFlush","requestAnimationFrame","setTimeout","unsubscribe","addListener","clearTimeout","jsx","Markdown","children","exports"],"sourceRoot":"../../src","sources":["markdown-stream.tsx"],"mappings":"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","ignoreList":[]}
@@ -364,22 +364,37 @@ const createBaseStyles = theme => _reactNative.StyleSheet.create({
364
364
  errorText: {
365
365
  color: "#f87171",
366
366
  fontSize: 14,
367
- fontFamily: theme.fontFamilies.mono ?? "monospace"
367
+ fontFamily: theme.fontFamilies.mono ?? "monospace",
368
+ ...(_reactNative.Platform.OS === "android" && {
369
+ includeFontPadding: false
370
+ })
368
371
  },
369
372
  text: {
370
373
  color: theme.colors.text,
371
374
  fontSize: theme.fontSizes.m,
372
375
  lineHeight: theme.fontSizes.m * 1.6,
373
- fontFamily: theme.fontFamilies.regular
376
+ fontFamily: theme.fontFamilies.regular,
377
+ ...(_reactNative.Platform.OS === "android" && {
378
+ includeFontPadding: false
379
+ })
374
380
  },
375
381
  bold: {
376
- fontWeight: "700"
382
+ fontWeight: "700",
383
+ ...(_reactNative.Platform.OS === "android" && {
384
+ includeFontPadding: false
385
+ })
377
386
  },
378
387
  italic: {
379
- fontStyle: "italic"
388
+ fontStyle: "italic",
389
+ ...(_reactNative.Platform.OS === "android" && {
390
+ includeFontPadding: false
391
+ })
380
392
  },
381
393
  strikethrough: {
382
- textDecorationLine: "line-through"
394
+ textDecorationLine: "line-through",
395
+ ...(_reactNative.Platform.OS === "android" && {
396
+ includeFontPadding: false
397
+ })
383
398
  }
384
399
  });
385
400
  //# sourceMappingURL=markdown.js.map