react-native-nitro-markdown 0.1.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 (55) hide show
  1. package/README.md +276 -0
  2. package/android/CMakeLists.txt +40 -0
  3. package/android/build.gradle +92 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/cpp/cpp-adapter.cpp +7 -0
  7. package/android/src/main/java/com/nitromarkdown/NitroMarkdownPackage.kt +23 -0
  8. package/cpp/CMakeLists.txt +46 -0
  9. package/cpp/bindings/HybridMarkdownParser.cpp +112 -0
  10. package/cpp/bindings/HybridMarkdownParser.hpp +28 -0
  11. package/cpp/core/MD4CParser.cpp +442 -0
  12. package/cpp/core/MD4CParser.hpp +21 -0
  13. package/cpp/core/MarkdownTypes.hpp +119 -0
  14. package/cpp/md4c/md4c.c +6492 -0
  15. package/cpp/md4c/md4c.h +407 -0
  16. package/ios/NitroMarkdown-Bridging-Header.h +14 -0
  17. package/lib/commonjs/Markdown.nitro.js +6 -0
  18. package/lib/commonjs/Markdown.nitro.js.map +1 -0
  19. package/lib/commonjs/MarkdownJS.nitro.js +114 -0
  20. package/lib/commonjs/MarkdownJS.nitro.js.map +1 -0
  21. package/lib/commonjs/index.js +19 -0
  22. package/lib/commonjs/index.js.map +1 -0
  23. package/lib/module/Markdown.nitro.js +4 -0
  24. package/lib/module/Markdown.nitro.js.map +1 -0
  25. package/lib/module/MarkdownJS.nitro.js +107 -0
  26. package/lib/module/MarkdownJS.nitro.js.map +1 -0
  27. package/lib/module/index.js +13 -0
  28. package/lib/module/index.js.map +1 -0
  29. package/lib/typescript/Markdown.nitro.d.ts +13 -0
  30. package/lib/typescript/Markdown.nitro.d.ts.map +1 -0
  31. package/lib/typescript/MarkdownJS.nitro.d.ts +33 -0
  32. package/lib/typescript/MarkdownJS.nitro.d.ts.map +1 -0
  33. package/lib/typescript/index.d.ts +22 -0
  34. package/lib/typescript/index.d.ts.map +1 -0
  35. package/nitro.json +16 -0
  36. package/nitrogen/generated/.gitattributes +1 -0
  37. package/nitrogen/generated/android/NitroMarkdown+autolinking.cmake +81 -0
  38. package/nitrogen/generated/android/NitroMarkdown+autolinking.gradle +27 -0
  39. package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +44 -0
  40. package/nitrogen/generated/android/NitroMarkdownOnLoad.hpp +25 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitromarkdown/NitroMarkdownOnLoad.kt +35 -0
  42. package/nitrogen/generated/ios/NitroMarkdown+autolinking.rb +60 -0
  43. package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.cpp +17 -0
  44. package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Bridge.hpp +27 -0
  45. package/nitrogen/generated/ios/NitroMarkdown-Swift-Cxx-Umbrella.hpp +38 -0
  46. package/nitrogen/generated/ios/NitroMarkdownAutolinking.mm +35 -0
  47. package/nitrogen/generated/ios/NitroMarkdownAutolinking.swift +12 -0
  48. package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.cpp +22 -0
  49. package/nitrogen/generated/shared/c++/HybridMarkdownParserSpec.hpp +65 -0
  50. package/nitrogen/generated/shared/c++/ParserOptions.hpp +79 -0
  51. package/package.json +101 -0
  52. package/react-native-nitro-markdown.podspec +42 -0
  53. package/src/Markdown.nitro.ts +12 -0
  54. package/src/MarkdownJS.nitro.ts +113 -0
  55. package/src/index.ts +65 -0
@@ -0,0 +1,442 @@
1
+ #include "MD4CParser.hpp"
2
+ #include "../md4c/md4c.h"
3
+
4
+ #include <stack>
5
+ #include <cstring>
6
+
7
+ namespace NitroMarkdown {
8
+
9
+ class MD4CParser::Impl {
10
+ public:
11
+ std::shared_ptr<MarkdownNode> root;
12
+ std::stack<std::shared_ptr<MarkdownNode>> nodeStack;
13
+ std::string currentText;
14
+ const char* inputText = nullptr;
15
+
16
+ void reset() {
17
+ root = std::make_shared<MarkdownNode>(NodeType::Document);
18
+ while (!nodeStack.empty()) nodeStack.pop();
19
+ nodeStack.push(root);
20
+ currentText.clear();
21
+ }
22
+
23
+ void flushText() {
24
+ if (!currentText.empty() && !nodeStack.empty()) {
25
+ auto textNode = std::make_shared<MarkdownNode>(NodeType::Text);
26
+ textNode->content = currentText;
27
+ nodeStack.top()->addChild(textNode);
28
+ currentText.clear();
29
+ }
30
+ }
31
+
32
+ void pushNode(std::shared_ptr<MarkdownNode> node) {
33
+ flushText();
34
+ if (node && !nodeStack.empty()) {
35
+ nodeStack.top()->addChild(node);
36
+ }
37
+ if (node) {
38
+ nodeStack.push(node);
39
+ }
40
+ }
41
+
42
+ void popNode() {
43
+ flushText();
44
+ if (nodeStack.size() > 1) {
45
+ nodeStack.pop();
46
+ }
47
+ }
48
+
49
+ std::string getAttributeText(const MD_ATTRIBUTE* attr) {
50
+ if (!attr || attr->size == 0) return "";
51
+
52
+ // Safety check for attribute size
53
+ MD_SIZE safeSize = attr->size;
54
+ if (safeSize > 100000) { // 100KB limit for attributes
55
+ safeSize = 100000;
56
+ }
57
+
58
+ std::string result;
59
+ for (unsigned i = 0; i < safeSize; i++) {
60
+ if (attr->substr_types[i] == MD_TEXT_NORMAL ||
61
+ attr->substr_types[i] == MD_TEXT_ENTITY ||
62
+ attr->substr_types[i] == MD_TEXT_NULLCHAR) {
63
+ result.append(attr->substr_offsets[i],
64
+ i + 1 < attr->size ? attr->substr_offsets[i + 1] - attr->substr_offsets[i]
65
+ : attr->size - attr->substr_offsets[i]);
66
+ }
67
+ }
68
+
69
+ if (result.empty() && attr->text && attr->size > 0) {
70
+ result = std::string(attr->text, attr->size);
71
+ }
72
+
73
+ return result;
74
+ }
75
+
76
+ static int enterBlock(MD_BLOCKTYPE type, void* detail, void* userdata) {
77
+ auto* impl = static_cast<Impl*>(userdata);
78
+
79
+ switch (type) {
80
+ case MD_BLOCK_DOC:
81
+ break;
82
+
83
+ case MD_BLOCK_QUOTE: {
84
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Blockquote));
85
+ break;
86
+ }
87
+
88
+ case MD_BLOCK_UL: {
89
+ auto node = std::make_shared<MarkdownNode>(NodeType::List);
90
+ node->ordered = false;
91
+ impl->pushNode(node);
92
+ break;
93
+ }
94
+
95
+ case MD_BLOCK_OL: {
96
+ auto* d = static_cast<MD_BLOCK_OL_DETAIL*>(detail);
97
+ auto node = std::make_shared<MarkdownNode>(NodeType::List);
98
+ node->ordered = true;
99
+ node->start = d->start;
100
+ impl->pushNode(node);
101
+ break;
102
+ }
103
+
104
+ case MD_BLOCK_LI: {
105
+ auto* d = static_cast<MD_BLOCK_LI_DETAIL*>(detail);
106
+ if (d->is_task) {
107
+ auto node = std::make_shared<MarkdownNode>(NodeType::TaskListItem);
108
+ node->checked = (d->task_mark == 'x' || d->task_mark == 'X');
109
+ impl->pushNode(node);
110
+ } else {
111
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::ListItem));
112
+ }
113
+ break;
114
+ }
115
+
116
+ case MD_BLOCK_HR: {
117
+ impl->flushText();
118
+ impl->nodeStack.top()->addChild(
119
+ std::make_shared<MarkdownNode>(NodeType::HorizontalRule));
120
+ break;
121
+ }
122
+
123
+ case MD_BLOCK_H: {
124
+ auto* d = static_cast<MD_BLOCK_H_DETAIL*>(detail);
125
+ auto node = std::make_shared<MarkdownNode>(NodeType::Heading);
126
+ node->level = d->level;
127
+ impl->pushNode(node);
128
+ break;
129
+ }
130
+
131
+ case MD_BLOCK_CODE: {
132
+ auto* d = static_cast<MD_BLOCK_CODE_DETAIL*>(detail);
133
+ auto node = std::make_shared<MarkdownNode>(NodeType::CodeBlock);
134
+ if (d->lang.text && d->lang.size > 0) {
135
+ node->language = std::string(d->lang.text, d->lang.size);
136
+ }
137
+ impl->pushNode(node);
138
+ break;
139
+ }
140
+
141
+ case MD_BLOCK_HTML: {
142
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::HtmlBlock));
143
+ break;
144
+ }
145
+
146
+ case MD_BLOCK_P: {
147
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Paragraph));
148
+ break;
149
+ }
150
+
151
+ case MD_BLOCK_TABLE: {
152
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Table));
153
+ break;
154
+ }
155
+
156
+ case MD_BLOCK_THEAD: {
157
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::TableHead));
158
+ break;
159
+ }
160
+
161
+ case MD_BLOCK_TBODY: {
162
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::TableBody));
163
+ break;
164
+ }
165
+
166
+ case MD_BLOCK_TR: {
167
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::TableRow));
168
+ break;
169
+ }
170
+
171
+ case MD_BLOCK_TH: {
172
+ auto* d = static_cast<MD_BLOCK_TD_DETAIL*>(detail);
173
+ auto node = std::make_shared<MarkdownNode>(NodeType::TableCell);
174
+ node->isHeader = true;
175
+ switch (d->align) {
176
+ case MD_ALIGN_LEFT: node->align = TextAlign::Left; break;
177
+ case MD_ALIGN_CENTER: node->align = TextAlign::Center; break;
178
+ case MD_ALIGN_RIGHT: node->align = TextAlign::Right; break;
179
+ default: node->align = TextAlign::Default; break;
180
+ }
181
+ impl->pushNode(node);
182
+ break;
183
+ }
184
+
185
+ case MD_BLOCK_TD: {
186
+ auto* d = static_cast<MD_BLOCK_TD_DETAIL*>(detail);
187
+ auto node = std::make_shared<MarkdownNode>(NodeType::TableCell);
188
+ node->isHeader = false;
189
+ switch (d->align) {
190
+ case MD_ALIGN_LEFT: node->align = TextAlign::Left; break;
191
+ case MD_ALIGN_CENTER: node->align = TextAlign::Center; break;
192
+ case MD_ALIGN_RIGHT: node->align = TextAlign::Right; break;
193
+ default: node->align = TextAlign::Default; break;
194
+ }
195
+ impl->pushNode(node);
196
+ break;
197
+ }
198
+ }
199
+
200
+ return 0;
201
+ }
202
+
203
+ static int leaveBlock(MD_BLOCKTYPE type, void* detail, void* userdata) {
204
+ (void)detail;
205
+ auto* impl = static_cast<Impl*>(userdata);
206
+
207
+ switch (type) {
208
+ case MD_BLOCK_DOC:
209
+ case MD_BLOCK_HR:
210
+ break;
211
+ default:
212
+ impl->popNode();
213
+ break;
214
+ }
215
+
216
+ return 0;
217
+ }
218
+
219
+ static int enterSpan(MD_SPANTYPE type, void* detail, void* userdata) {
220
+ auto* impl = static_cast<Impl*>(userdata);
221
+
222
+ switch (type) {
223
+ case MD_SPAN_EM: {
224
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Italic));
225
+ break;
226
+ }
227
+
228
+ case MD_SPAN_STRONG: {
229
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Bold));
230
+ break;
231
+ }
232
+
233
+ case MD_SPAN_DEL: {
234
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Strikethrough));
235
+ break;
236
+ }
237
+
238
+ case MD_SPAN_A: {
239
+ auto* d = static_cast<MD_SPAN_A_DETAIL*>(detail);
240
+ auto node = std::make_shared<MarkdownNode>(NodeType::Link);
241
+ if (d->href.text && d->href.size > 0) {
242
+ node->href = std::string(d->href.text, d->href.size);
243
+ }
244
+ if (d->title.text && d->title.size > 0) {
245
+ node->title = std::string(d->title.text, d->title.size);
246
+ }
247
+ impl->pushNode(node);
248
+ break;
249
+ }
250
+
251
+ case MD_SPAN_IMG: {
252
+ auto* d = static_cast<MD_SPAN_IMG_DETAIL*>(detail);
253
+ auto node = std::make_shared<MarkdownNode>(NodeType::Image);
254
+ if (d->src.text && d->src.size > 0) {
255
+ node->href = std::string(d->src.text, d->src.size);
256
+ }
257
+ if (d->title.text && d->title.size > 0) {
258
+ node->title = std::string(d->title.text, d->title.size);
259
+ }
260
+ impl->pushNode(node);
261
+ break;
262
+ }
263
+
264
+ case MD_SPAN_CODE: {
265
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::CodeInline));
266
+ break;
267
+ }
268
+
269
+ case MD_SPAN_LATEXMATH: {
270
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::MathInline));
271
+ break;
272
+ }
273
+
274
+ case MD_SPAN_LATEXMATH_DISPLAY: {
275
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::MathBlock));
276
+ break;
277
+ }
278
+
279
+ case MD_SPAN_U: {
280
+ impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Italic));
281
+ break;
282
+ }
283
+
284
+ case MD_SPAN_WIKILINK: {
285
+ auto node = std::make_shared<MarkdownNode>(NodeType::Link);
286
+ impl->pushNode(node);
287
+ break;
288
+ }
289
+ }
290
+
291
+ return 0;
292
+ }
293
+
294
+ static int leaveSpan(MD_SPANTYPE type, void* detail, void* userdata) {
295
+ (void)detail;
296
+ auto* impl = static_cast<Impl*>(userdata);
297
+
298
+ // For spans that capture content directly (not as child nodes)
299
+ if (!impl->nodeStack.empty()) {
300
+ auto currentNode = impl->nodeStack.top();
301
+
302
+ switch (type) {
303
+ case MD_SPAN_CODE:
304
+ // For inline code, capture the accumulated text as content
305
+ currentNode->content = impl->currentText;
306
+ impl->currentText.clear();
307
+ break;
308
+
309
+ case MD_SPAN_IMG:
310
+ // For images, capture the accumulated text as alt text
311
+ currentNode->alt = impl->currentText;
312
+ impl->currentText.clear();
313
+ break;
314
+
315
+ default:
316
+ // For other spans, use normal text flushing
317
+ break;
318
+ }
319
+ }
320
+
321
+ impl->popNode();
322
+ return 0;
323
+ }
324
+
325
+ static int text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) {
326
+ auto* impl = static_cast<Impl*>(userdata);
327
+
328
+ // Safety check for null or invalid text
329
+ if (!text || size == 0) {
330
+ return 0;
331
+ }
332
+
333
+ // Prevent excessive memory usage from very long text segments
334
+ if (size > 1000000) { // 1MB limit per text segment
335
+ size = 1000000;
336
+ }
337
+
338
+ switch (type) {
339
+ case MD_TEXT_NULLCHAR:
340
+ impl->currentText += '\0';
341
+ break;
342
+
343
+ case MD_TEXT_BR:
344
+ impl->flushText();
345
+ impl->nodeStack.top()->addChild(
346
+ std::make_shared<MarkdownNode>(NodeType::LineBreak));
347
+ break;
348
+
349
+ case MD_TEXT_SOFTBR:
350
+ impl->flushText();
351
+ impl->nodeStack.top()->addChild(
352
+ std::make_shared<MarkdownNode>(NodeType::SoftBreak));
353
+ break;
354
+
355
+ case MD_TEXT_HTML:
356
+ impl->flushText();
357
+ {
358
+ auto node = std::make_shared<MarkdownNode>(NodeType::HtmlInline);
359
+ node->content = std::string(text, size);
360
+ impl->nodeStack.top()->addChild(node);
361
+ }
362
+ break;
363
+
364
+ case MD_TEXT_ENTITY:
365
+ impl->currentText.append(text, size);
366
+ break;
367
+
368
+ case MD_TEXT_NORMAL:
369
+ case MD_TEXT_CODE:
370
+ case MD_TEXT_LATEXMATH:
371
+ default:
372
+ impl->currentText.append(text, size);
373
+ break;
374
+ }
375
+
376
+ return 0;
377
+ }
378
+ };
379
+
380
+ MD4CParser::MD4CParser() : impl_(std::make_unique<Impl>()) {}
381
+
382
+ MD4CParser::~MD4CParser() = default;
383
+
384
+ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, const ParserOptions& options) {
385
+ // Input validation and safety checks
386
+ if (markdown.size() > 10000000) { // 10MB limit to prevent excessive memory usage
387
+ // Return empty document for extremely large inputs
388
+ auto root = std::make_shared<MarkdownNode>(NodeType::Document);
389
+ return root;
390
+ }
391
+
392
+ impl_->reset();
393
+ impl_->inputText = markdown.c_str();
394
+
395
+ unsigned int flags = MD_FLAG_NOHTML;
396
+
397
+ if (options.gfm) {
398
+ flags |= MD_FLAG_TABLES;
399
+ flags |= MD_FLAG_STRIKETHROUGH;
400
+ flags |= MD_FLAG_TASKLISTS;
401
+ flags |= MD_FLAG_PERMISSIVEAUTOLINKS;
402
+ }
403
+
404
+ if (options.math) {
405
+ flags |= MD_FLAG_LATEXMATHSPANS;
406
+ }
407
+
408
+ MD_PARSER parser = {
409
+ 0, // abi_version
410
+ flags,
411
+ &Impl::enterBlock,
412
+ &Impl::leaveBlock,
413
+ &Impl::enterSpan,
414
+ &Impl::leaveSpan,
415
+ &Impl::text,
416
+ nullptr, // debug_log
417
+ nullptr
418
+ };
419
+
420
+ int result = md_parse(markdown.c_str(),
421
+ static_cast<MD_SIZE>(markdown.size()),
422
+ &parser,
423
+ impl_.get());
424
+
425
+ impl_->flushText();
426
+
427
+ if (result != 0) {
428
+ // Parse error - return empty document
429
+ auto errorDoc = std::make_shared<MarkdownNode>(NodeType::Document);
430
+ auto errorPara = std::make_shared<MarkdownNode>(NodeType::Paragraph);
431
+ auto errorText = std::make_shared<MarkdownNode>(NodeType::Text);
432
+ errorText->content = "[Parse Error]";
433
+ errorPara->addChild(errorText);
434
+ errorDoc->addChild(errorPara);
435
+ return errorDoc;
436
+ }
437
+
438
+ return impl_->root;
439
+ }
440
+
441
+ } // namespace NitroMarkdown
442
+
@@ -0,0 +1,21 @@
1
+ #pragma once
2
+
3
+ #include "MarkdownTypes.hpp"
4
+ #include <string>
5
+ #include <memory>
6
+
7
+ namespace NitroMarkdown {
8
+
9
+ class MD4CParser {
10
+ public:
11
+ MD4CParser();
12
+ ~MD4CParser();
13
+ std::shared_ptr<MarkdownNode> parse(const std::string& markdown, const ParserOptions& options);
14
+
15
+ private:
16
+ class Impl;
17
+ std::unique_ptr<Impl> impl_;
18
+ };
19
+
20
+ } // namespace NitroMarkdown
21
+
@@ -0,0 +1,119 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+ #include <vector>
5
+ #include <optional>
6
+ #include <memory>
7
+
8
+ namespace NitroMarkdown {
9
+
10
+ enum class NodeType {
11
+ Document,
12
+ Heading,
13
+ Paragraph,
14
+ Text,
15
+ Bold,
16
+ Italic,
17
+ Strikethrough,
18
+ Link,
19
+ Image,
20
+ CodeInline,
21
+ CodeBlock,
22
+ Blockquote,
23
+ HorizontalRule,
24
+ LineBreak,
25
+ SoftBreak,
26
+ Table,
27
+ TableHead,
28
+ TableBody,
29
+ TableRow,
30
+ TableCell,
31
+ List,
32
+ ListItem,
33
+ TaskListItem,
34
+ MathInline,
35
+ MathBlock,
36
+ HtmlBlock,
37
+ HtmlInline
38
+ };
39
+
40
+ inline std::string nodeTypeToString(NodeType type) {
41
+ switch (type) {
42
+ case NodeType::Document: return "document";
43
+ case NodeType::Heading: return "heading";
44
+ case NodeType::Paragraph: return "paragraph";
45
+ case NodeType::Text: return "text";
46
+ case NodeType::Bold: return "bold";
47
+ case NodeType::Italic: return "italic";
48
+ case NodeType::Strikethrough: return "strikethrough";
49
+ case NodeType::Link: return "link";
50
+ case NodeType::Image: return "image";
51
+ case NodeType::CodeInline: return "code_inline";
52
+ case NodeType::CodeBlock: return "code_block";
53
+ case NodeType::Blockquote: return "blockquote";
54
+ case NodeType::HorizontalRule: return "horizontal_rule";
55
+ case NodeType::LineBreak: return "line_break";
56
+ case NodeType::SoftBreak: return "soft_break";
57
+ case NodeType::Table: return "table";
58
+ case NodeType::TableHead: return "table_head";
59
+ case NodeType::TableBody: return "table_body";
60
+ case NodeType::TableRow: return "table_row";
61
+ case NodeType::TableCell: return "table_cell";
62
+ case NodeType::List: return "list";
63
+ case NodeType::ListItem: return "list_item";
64
+ case NodeType::TaskListItem: return "task_list_item";
65
+ case NodeType::MathInline: return "math_inline";
66
+ case NodeType::MathBlock: return "math_block";
67
+ case NodeType::HtmlBlock: return "html_block";
68
+ case NodeType::HtmlInline: return "html_inline";
69
+ }
70
+ return "unknown";
71
+ }
72
+
73
+ enum class TextAlign {
74
+ Default,
75
+ Left,
76
+ Center,
77
+ Right
78
+ };
79
+
80
+ inline std::string textAlignToString(TextAlign align) {
81
+ switch (align) {
82
+ case TextAlign::Left: return "left";
83
+ case TextAlign::Center: return "center";
84
+ case TextAlign::Right: return "right";
85
+ default: return "";
86
+ }
87
+ }
88
+
89
+ struct MarkdownNode {
90
+ NodeType type;
91
+ std::optional<std::string> content;
92
+ std::optional<int> level;
93
+ std::optional<std::string> href;
94
+ std::optional<std::string> title;
95
+ std::optional<std::string> alt;
96
+ std::optional<std::string> language;
97
+ std::optional<bool> ordered;
98
+ std::optional<int> start;
99
+ std::optional<bool> checked;
100
+ std::optional<bool> isHeader;
101
+ std::optional<TextAlign> align;
102
+ std::vector<std::shared_ptr<MarkdownNode>> children;
103
+
104
+ explicit MarkdownNode(NodeType t) : type(t) {}
105
+
106
+ void addChild(std::shared_ptr<MarkdownNode> child) {
107
+ if (child) {
108
+ children.push_back(std::move(child));
109
+ }
110
+ }
111
+ };
112
+
113
+ struct ParserOptions {
114
+ bool gfm = true;
115
+ bool math = true;
116
+ };
117
+
118
+ } // namespace NitroMarkdown
119
+