react-native-nitro-markdown 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +78 -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 +11 -4
  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 +44 -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 +12 -5
  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 +43 -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 +1 -3
  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 +1 -3
  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 +6 -1
  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 +43 -86
  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
174
-
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
121
+ ### Option 3: Custom Renderers
186
122
 
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,50 @@ 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
+ - `borderRadius` - Border radius tokens (s, m, l)
193
+ - `showCodeLanguage` - Show/hide code block language labels
194
+
195
+ ### Option 5: Minimal Styling Strategy
196
+
197
+ Start with a clean slate using the `stylingStrategy` prop:
198
+
199
+ ```tsx
200
+ <Markdown stylingStrategy="minimal" theme={myLightTheme}>
201
+ {content}
202
+ </Markdown>
203
+ ```
204
+
205
+ This zeros out all spacing and removes opinionated colors, letting you build up from scratch.
206
+
207
+ ### Option 6: Style Props on Individual Renderers
229
208
 
230
209
  All built-in renderers accept a `style` prop for fine-grained overrides:
231
210
 
@@ -238,7 +217,7 @@ import { Heading, CodeBlock, InlineCode } from "react-native-nitro-markdown";
238
217
  <InlineCode style={{ backgroundColor: "#ff0" }}>code</InlineCode>
239
218
  ```
240
219
 
241
- ### Option 8: Auto Content Extraction for Code
220
+ ### Option 7: Auto Content Extraction for Code
242
221
 
243
222
  The `CodeBlock` and `InlineCode` components now accept a `node` prop for automatic content extraction:
244
223
 
@@ -257,7 +236,7 @@ code_block: ({ content, language }) => (
257
236
  );
258
237
  ```
259
238
 
260
- ### Option 9: Headless (Minimal Bundle)
239
+ ### Option 8: Headless (Minimal Bundle)
261
240
 
262
241
  For maximum control, data processing, or minimal JS overhead:
263
242
 
@@ -273,9 +252,9 @@ const text = getTextContent(ast); // "Hello World"
273
252
  const fullText = getFlattenedText(ast); // "Hello World\n\n" (Normalized with line breaks)
274
253
  ```
275
254
 
276
- ### Option 10: High-Performance Streaming (LLMs)
255
+ ### Option 9: High-Performance Streaming (LLMs)
277
256
 
278
- When streaming text token-by-token (e.g., from ChatGPT or Gemini):
257
+ 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
258
 
280
259
  ```tsx
281
260
  import {
@@ -292,12 +271,31 @@ export function AIResponseStream() {
292
271
  }, [session]);
293
272
 
294
273
  return (
295
- <MarkdownStream session={session.getSession()} options={{ gfm: true }} />
274
+ <MarkdownStream
275
+ session={session.getSession()}
276
+ options={{ gfm: true }}
277
+ updateIntervalMs={60}
278
+ updateStrategy="raf"
279
+ useTransitionUpdates
280
+ />
296
281
  );
297
282
  }
298
283
  ```
299
284
 
300
- ### Option 11: Extracting Plain Text
285
+ **Recommended defaults for token streaming:**
286
+ - `updateStrategy="raf"` for smooth, frame-aligned updates
287
+ - `updateIntervalMs={50–100}` when using `updateStrategy="interval"`
288
+ - Avoid per-token UI updates by batching token appends
289
+
290
+ **Streaming props:**
291
+
292
+ | Prop | Type | Default | Description |
293
+ | :-- | :-- | :-- | :-- |
294
+ | `updateIntervalMs` | `number` | `50` | Throttle UI updates when using interval strategy |
295
+ | `updateStrategy` | `"interval" \| "raf"` | `"interval"` | Interval batching or frame-aligned updates |
296
+ | `useTransitionUpdates` | `boolean` | `false` | Use React transitions to keep input responsive |
297
+
298
+ ### Option 10: Extracting Plain Text
301
299
 
302
300
  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
301
 
@@ -344,11 +342,9 @@ export {
344
342
  getFlattenedText,
345
343
  } from "./headless";
346
344
 
347
- // Theme presets
345
+ // Theme tokens
348
346
  export {
349
347
  defaultMarkdownTheme,
350
- lightMarkdownTheme,
351
- darkMarkdownTheme,
352
348
  minimalMarkdownTheme,
353
349
  mergeThemes,
354
350
  };
@@ -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