react-native-jet-markdown 0.2.0 → 0.2.2

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 (51) hide show
  1. package/README.md +4 -56
  2. package/android/src/main/cpp/JniBindings.cpp +0 -106
  3. package/android/src/main/java/com/jetmarkdown/JetMarkdownNative.kt +0 -79
  4. package/android/src/main/java/com/jetmarkdown/JetMarkdownPackage.kt +1 -2
  5. package/android/src/main/java/com/jetmarkdown/render/spans/MarkdownLineHeightSpan.kt +2 -13
  6. package/cpp/react/override/react/renderer/components/JetMarkdownViewSpec/ComponentDescriptors.h +0 -4
  7. package/cpp/tests/parser_tests.cpp +0 -402
  8. package/cpp/tests/run_tests.sh +0 -3
  9. package/ios/JetMarkdownView.mm +33 -2
  10. package/lib/module/index.js +0 -2
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/typescript/src/index.d.ts +1 -3
  13. package/lib/typescript/src/index.d.ts.map +1 -1
  14. package/lib/typescript/src/types.d.ts +0 -127
  15. package/lib/typescript/src/types.d.ts.map +1 -1
  16. package/package.json +2 -5
  17. package/src/index.tsx +0 -9
  18. package/src/types.ts +0 -134
  19. package/android/src/main/java/com/jetmarkdown/editor/EditorSpans.kt +0 -217
  20. package/android/src/main/java/com/jetmarkdown/editor/JetMarkdownEditorManager.kt +0 -224
  21. package/android/src/main/java/com/jetmarkdown/editor/JetMarkdownEditorView.kt +0 -1736
  22. package/cpp/core/AstToMarkdown.cpp +0 -549
  23. package/cpp/core/AstToMarkdown.h +0 -16
  24. package/cpp/core/EditorRuns.cpp +0 -640
  25. package/cpp/core/EditorRuns.h +0 -94
  26. package/cpp/core/EditorText.cpp +0 -15
  27. package/cpp/core/EditorText.h +0 -18
  28. package/cpp/react/JetMarkdownEditorShadowNode.cpp +0 -54
  29. package/cpp/react/JetMarkdownEditorShadowNode.h +0 -55
  30. package/cpp/react/JetMarkdownEditorState.h +0 -35
  31. package/ios/editor/JetMarkdownEditor.h +0 -12
  32. package/ios/editor/JetMarkdownEditor.mm +0 -2002
  33. package/lib/module/JetMarkdownEditor.js +0 -85
  34. package/lib/module/JetMarkdownEditor.js.map +0 -1
  35. package/lib/module/JetMarkdownEditor.native.js +0 -244
  36. package/lib/module/JetMarkdownEditor.native.js.map +0 -1
  37. package/lib/module/JetMarkdownEditorNativeComponent.ts +0 -173
  38. package/lib/module/useJetMarkdownEditor.js +0 -41
  39. package/lib/module/useJetMarkdownEditor.js.map +0 -1
  40. package/lib/typescript/src/JetMarkdownEditor.d.ts +0 -6
  41. package/lib/typescript/src/JetMarkdownEditor.d.ts.map +0 -1
  42. package/lib/typescript/src/JetMarkdownEditor.native.d.ts +0 -6
  43. package/lib/typescript/src/JetMarkdownEditor.native.d.ts.map +0 -1
  44. package/lib/typescript/src/JetMarkdownEditorNativeComponent.d.ts +0 -105
  45. package/lib/typescript/src/JetMarkdownEditorNativeComponent.d.ts.map +0 -1
  46. package/lib/typescript/src/useJetMarkdownEditor.d.ts +0 -36
  47. package/lib/typescript/src/useJetMarkdownEditor.d.ts.map +0 -1
  48. package/src/JetMarkdownEditor.native.tsx +0 -301
  49. package/src/JetMarkdownEditor.tsx +0 -90
  50. package/src/JetMarkdownEditorNativeComponent.ts +0 -173
  51. package/src/useJetMarkdownEditor.ts +0 -50
@@ -8,9 +8,6 @@
8
8
  #include <vector>
9
9
 
10
10
  #include "../core/AstJson.h"
11
- #include "../core/AstToMarkdown.h"
12
- #include "../core/EditorRuns.h"
13
- #include "../core/EditorText.h"
14
11
  #include "../core/Parser.h"
15
12
 
16
13
  namespace {
@@ -51,113 +48,6 @@ void expectNotContains(const char* name, const std::string& markdown, const std:
51
48
  }
52
49
  }
53
50
 
54
- // The round-trip law: parsing the serialized output must reproduce the
55
- // original AST exactly, even when the spelling normalizes.
56
- void expectRoundTrip(const char* name, const std::string& markdown) {
57
- g_total++;
58
- auto doc = jetmarkdown::parseMarkdown(markdown);
59
- const std::string original = jetmarkdown::astToJson(doc->root);
60
- const std::string serialized = jetmarkdown::astToMarkdown(doc->root);
61
- auto reparsed = jetmarkdown::parseMarkdown(serialized);
62
- const std::string actual = jetmarkdown::astToJson(reparsed->root);
63
- if (actual != original) {
64
- g_failures++;
65
- std::printf(
66
- "FAIL roundtrip %s\n input: %s\n serialized: %s\n expected: %s\n actual: %s\n",
67
- name, markdown.c_str(), serialized.c_str(), original.c_str(), actual.c_str());
68
- }
69
- }
70
-
71
- void expectString(
72
- const char* name, const std::string& actual, const std::string& expected) {
73
- g_total++;
74
- if (actual != expected) {
75
- g_failures++;
76
- std::printf("FAIL %s\n expected: %s\n actual: %s\n",
77
- name, expected.c_str(), actual.c_str());
78
- }
79
- }
80
-
81
- // Canonical form: one maximal run per mark bit, so equivalent mark coverage
82
- // compares equal regardless of how runs were sliced or combined.
83
- std::string dumpStyled(const jetmarkdown::StyledText& styled) {
84
- std::string out = "\"" + styled.text + "\"";
85
- for (uint32_t bit = 1; bit <= jetmarkdown::MarkSubscript; bit <<= 1) {
86
- std::vector<std::pair<uint32_t, uint32_t>> intervals;
87
- for (const auto& run : styled.runs) {
88
- if ((run.flags & bit) == 0 || run.start >= run.end) {
89
- continue;
90
- }
91
- intervals.push_back({run.start, run.end});
92
- }
93
- std::sort(intervals.begin(), intervals.end());
94
- for (size_t i = 0; i < intervals.size(); i++) {
95
- auto [start, end] = intervals[i];
96
- while (i + 1 < intervals.size() && intervals[i + 1].first <= end) {
97
- end = std::max(end, intervals[++i].second);
98
- }
99
- out += " " + std::to_string(start) + ":" + std::to_string(end) + ":" +
100
- std::to_string(bit);
101
- }
102
- }
103
- return out;
104
- }
105
-
106
- std::string dumpEditor(const jetmarkdown::EditorDocument& document) {
107
- std::string out = dumpStyled({document.text, document.runs});
108
- out += " |";
109
- for (const auto& line : document.lines) {
110
- out += " " + std::to_string(static_cast<int>(line.type));
111
- if (line.level != 0) {
112
- out += "." + std::to_string(line.level);
113
- }
114
- }
115
- for (const auto& link : document.links) {
116
- out += " [" + std::to_string(link.start) + ":" + std::to_string(link.end) +
117
- " " + link.url + "]";
118
- }
119
- return out;
120
- }
121
-
122
- // The editor round-trip law with blocks: text + runs + line blocks must
123
- // survive serialization to markdown and re-extraction unchanged.
124
- void expectEditorRoundTrip(
125
- const char* name,
126
- const std::string& text,
127
- const std::vector<jetmarkdown::StyledRun>& runs,
128
- const std::vector<jetmarkdown::EditorLine>& lines) {
129
- g_total++;
130
- const std::string markdown = jetmarkdown::markdownFromEditor(text, runs, lines);
131
- const auto extracted = jetmarkdown::editorFromMarkdown(markdown);
132
- const std::string expected = dumpEditor({text, runs, lines, {}});
133
- const std::string actual = dumpEditor(extracted);
134
- if (actual != expected) {
135
- g_failures++;
136
- std::printf(
137
- "FAIL editor %s\n serialized: %s\n expected: %s\n actual: %s\n",
138
- name, markdown.c_str(), expected.c_str(), actual.c_str());
139
- }
140
- }
141
-
142
- // The editor round-trip law: text + mark runs must survive serialization to
143
- // markdown and re-extraction unchanged.
144
- void expectStyledRoundTrip(
145
- const char* name,
146
- const std::string& text,
147
- const std::vector<jetmarkdown::StyledRun>& runs) {
148
- g_total++;
149
- const std::string markdown = jetmarkdown::markdownFromStyledText(text, runs);
150
- const auto extracted = jetmarkdown::styledTextFromMarkdown(markdown);
151
- const std::string expected = dumpStyled({text, runs});
152
- const std::string actual = dumpStyled(extracted);
153
- if (actual != expected) {
154
- g_failures++;
155
- std::printf(
156
- "FAIL styled %s\n serialized: %s\n expected: %s\n actual: %s\n",
157
- name, markdown.c_str(), expected.c_str(), actual.c_str());
158
- }
159
- }
160
-
161
51
  } // namespace
162
52
 
163
53
  int main() {
@@ -337,82 +227,6 @@ int main() {
337
227
  "- a\n - b",
338
228
  R"("type":"list")");
339
229
 
340
- // --- Serializer round trips (AstToMarkdown) ---
341
- expectRoundTrip("rt paragraph", "hello world");
342
- expectRoundTrip("rt two paragraphs", "one\n\ntwo");
343
- expectRoundTrip("rt soft break", "line one\nline two");
344
- expectRoundTrip("rt hard break", "line one\\\nline two");
345
- expectRoundTrip("rt headings", "# H1\n\n## H2\n\n###### H6");
346
- expectRoundTrip("rt bold italic", "**bold** and _italic_ and **bold _nested_**");
347
- expectRoundTrip("rt strikethrough", "~~gone~~ stays");
348
- expectRoundTrip("rt spoiler", "both ||secret|| kinds >!hidden!< here");
349
- expectRoundTrip("rt sup sub", "x^2^ and H~2~O and reddit ^word too");
350
- expectRoundTrip("rt sup multiword", "reddit ^(multi word) form");
351
- expectRoundTrip("rt inline code", "run `npm install` now");
352
- expectRoundTrip("rt inline code with backticks", "a ``code `with` ticks`` b");
353
- expectRoundTrip("rt link", "see [the docs](https://example.com) now");
354
- expectRoundTrip("rt link with parens", "see [x](https://en.wikipedia.org/wiki/Bracket_(disambiguation))");
355
- expectRoundTrip("rt mention", "ping [@ali](users://ali) in [#general](channels://general)");
356
- expectRoundTrip("rt autolink", "visit https://example.com today");
357
- {
358
- // A link whose label equals its URL serializes as the bare URL (the
359
- // permissive-autolink form), not the noisy [url](url) form.
360
- auto doc = jetmarkdown::parseMarkdown("visit https://example.com today");
361
- expectString("autolink serializes bare",
362
- jetmarkdown::astToMarkdown(doc->root),
363
- "visit https://example.com today\n");
364
- auto labeled = jetmarkdown::parseMarkdown("[docs](https://example.com)");
365
- expectString("labeled link keeps brackets",
366
- jetmarkdown::astToMarkdown(labeled->root),
367
- "[docs](https://example.com)\n");
368
- }
369
- expectRoundTrip("rt image", "![alt text](https://example.com/img.png)");
370
- expectRoundTrip("rt quote", "> quoted **bold**\n>\n> second paragraph");
371
- expectRoundTrip("rt nested quote content", "> outer\n>\n> - a\n> - b");
372
- expectRoundTrip("rt code block", "```ts\nconst x = 1;\nconst y = \"two\";\n```");
373
- expectRoundTrip("rt code block with backticks", "````\na ``` fence inside\n````");
374
- expectRoundTrip("rt unordered list", "- one\n- two\n- three");
375
- expectRoundTrip("rt ordered list", "1. first\n2. second");
376
- expectRoundTrip("rt ordered list start", "5. five\n6. six");
377
- expectRoundTrip("rt nested list", "- a\n - a1\n - a2\n- b");
378
- expectRoundTrip("rt list with paragraphs", "- first para\n\n second para\n- next item");
379
- expectRoundTrip("rt table", "| A | B |\n|---|---|\n| 1 | 2 |");
380
- expectRoundTrip("rt table alignment", "| L | C | R |\n|:--|:-:|--:|\n| a | b | c |");
381
- expectRoundTrip("rt thematic break", "one\n\n---\n\ntwo");
382
- expectRoundTrip("rt literal specials", "not *bold* stays\\* literal \\_x\\_ and \\`tick\\`");
383
- expectRoundTrip("rt literal hash", "\\# not a heading");
384
- expectRoundTrip("rt literal list", "1\\. not a list");
385
- expectRoundTrip("rt literal pipe", "a \\| b");
386
- expectRoundTrip("rt exclamation", "wow! and ![not image");
387
- expectRoundTrip("rt mixed document",
388
- "# Title\n\nIntro with **bold**, `code`, and [a link](https://x.dev).\n\n"
389
- "> A quote\n\n- item one\n- item two\n\n```js\nconsole.log(1);\n```\n\n"
390
- "| K | V |\n|---|---|\n| a | 1 |\n\n---\n\nThe ||end|| ^fin^");
391
-
392
- // --- Escaped extension delimiters must stay literal through round trips
393
- // (md4c strips backslashes before the inline-extension scanner runs; the
394
- // preprocess entity rewrite + verbatim text nodes keep them de-fanged).
395
- expectStyledRoundTrip("escape literal spoiler", "a ||spoiler|| b", {});
396
- expectStyledRoundTrip("escape literal caret", "a^b c", {});
397
- expectStyledRoundTrip("escape literal tilde pair", "a ~sub~ b", {});
398
- expectStyledRoundTrip("escape literal strikethrough", "x ~~strike~~ y", {});
399
- expectStyledRoundTrip("escape literal reddit spoiler", ">!hidden!< tail", {});
400
- expectStyledRoundTrip("escape literal entity text", "use &#124; here", {});
401
- expectStyledRoundTrip("escape literal angle autolink", "see <http://example.com> now", {});
402
- expectStyledRoundTrip("escape leading spaces", " indented text", {});
403
- expectRoundTrip("escaped pipe still literal in backticks", "keep `a \\| b` code");
404
-
405
- // --- Emphasis adjacency: stars must survive intraword adjacency and the
406
- // 4+ star-run fallback must use flanking-legal underscores.
407
- expectStyledRoundTrip("adjacent bold italic intraword", "XyZ",
408
- {{0, 1, jetmarkdown::MarkBold}, {1, 2, jetmarkdown::MarkItalic}});
409
- expectStyledRoundTrip("adjacent italic bold intraword", "XyZ",
410
- {{0, 1, jetmarkdown::MarkItalic}, {1, 2, jetmarkdown::MarkBold}});
411
-
412
- // --- Superscript content that fits neither caret form.
413
- expectStyledRoundTrip("sup paren without space", "ab",
414
- {{0, 2, jetmarkdown::MarkSuperscript}});
415
-
416
230
  // --- Hostile nesting must parse without blowing the stack (depth cap).
417
231
  {
418
232
  std::string hostile;
@@ -425,13 +239,6 @@ int main() {
425
239
  if (doc->root == nullptr) {
426
240
  g_failures++;
427
241
  std::printf("FAIL deep nesting parse\n");
428
- } else {
429
- // Serialization must also complete (bounded recursion).
430
- const std::string out = jetmarkdown::astToMarkdown(doc->root);
431
- if (out.empty()) {
432
- g_failures++;
433
- std::printf("FAIL deep nesting serialize\n");
434
- }
435
242
  }
436
243
  }
437
244
 
@@ -448,215 +255,6 @@ int main() {
448
255
  "bad &#xD800; ref",
449
256
  "\xEF\xBF\xBD");
450
257
 
451
- // --- Editor plain-text bridge (E1) ---
452
- expectString(
453
- "editor markdown from text",
454
- jetmarkdown::markdownFromPlainText("hello\nworld"),
455
- "hello\n\nworld\n");
456
- expectString(
457
- "editor text escapes literals",
458
- jetmarkdown::markdownFromPlainText("**not bold** #tag"),
459
- "\\*\\*not bold\\*\\* #tag\n");
460
- expectString(
461
- "editor text escapes line starts",
462
- jetmarkdown::markdownFromPlainText("# not a heading\n1. not a list"),
463
- "\\# not a heading\n\n1\\. not a list\n");
464
- expectString(
465
- "editor text stable round trip",
466
- jetmarkdown::plainTextFromMarkdown(
467
- jetmarkdown::markdownFromPlainText("line one\nline two")),
468
- "line one\nline two");
469
- expectString(
470
- "editor setValue flattens structure",
471
- jetmarkdown::plainTextFromMarkdown("# Title\n\nbody with **bold**\n\n- a\n- b"),
472
- "Title\nbody with bold\na\nb");
473
-
474
- // --- Editor styled runs (E2) ---
475
- using jetmarkdown::MarkBold;
476
- using jetmarkdown::MarkInlineCode;
477
- using jetmarkdown::MarkItalic;
478
- using jetmarkdown::MarkSpoiler;
479
- using jetmarkdown::MarkStrikethrough;
480
- using jetmarkdown::MarkSubscript;
481
- using jetmarkdown::MarkSuperscript;
482
-
483
- expectString(
484
- "styled bold run",
485
- jetmarkdown::markdownFromStyledText("hello world", {{0, 5, MarkBold}}),
486
- "**hello** world\n");
487
- expectString(
488
- "styled code ignores nested marks",
489
- jetmarkdown::markdownFromStyledText(
490
- "x code y", {{2, 6, MarkInlineCode | MarkBold}}),
491
- "x **`code`** y\n");
492
- expectString(
493
- "styled run clipped at newline",
494
- jetmarkdown::markdownFromStyledText("ab\ncd", {{0, 5, MarkBold}}),
495
- "**ab**\n\n**cd**\n");
496
- expectString(
497
- "styled marks escape content",
498
- jetmarkdown::markdownFromStyledText("a*b", {{0, 3, MarkBold}}),
499
- "**a\\*b**\n");
500
-
501
- expectStyledRoundTrip("styled rt plain", "just text", {});
502
- expectStyledRoundTrip("styled rt bold", "hello world", {{0, 5, MarkBold}});
503
- expectStyledRoundTrip(
504
- "styled rt every mark",
505
- "abcdefg",
506
- {{0, 1, MarkBold},
507
- {1, 2, MarkItalic},
508
- {2, 3, MarkStrikethrough},
509
- {3, 4, MarkInlineCode},
510
- {4, 5, MarkSpoiler},
511
- {5, 6, MarkSuperscript},
512
- {6, 7, MarkSubscript}});
513
- expectStyledRoundTrip(
514
- "styled rt overlap", "abc", {{0, 2, MarkBold}, {1, 3, MarkItalic}});
515
- expectStyledRoundTrip(
516
- "styled rt overlap glued tail",
517
- "bcd",
518
- {{0, 1, MarkBold}, {0, 2, MarkItalic}});
519
- expectStyledRoundTrip(
520
- "styled rt partial bold in code",
521
- "x bc y",
522
- {{2, 4, MarkInlineCode}, {3, 4, MarkBold}});
523
- expectStyledRoundTrip(
524
- "styled rt nested combo",
525
- "bold and italic",
526
- {{0, 15, MarkBold}, {9, 15, MarkItalic}});
527
- expectStyledRoundTrip(
528
- "styled rt multiline",
529
- "first line\nsecond line",
530
- {{0, 5, MarkBold}, {11, 17, MarkStrikethrough}});
531
- expectStyledRoundTrip(
532
- "styled rt emoji offsets", "\xF0\x9F\x98\x80 xy", {{3, 5, MarkBold}});
533
- expectStyledRoundTrip(
534
- "styled rt literal specials", "keep **this** literal", {{5, 13, MarkSpoiler}});
535
- expectStyledRoundTrip(
536
- "styled rt adjacent same mark merge", "abc", {{0, 2, MarkBold}, {2, 3, MarkBold}});
537
-
538
- expectString(
539
- "styled extraction",
540
- dumpStyled(jetmarkdown::styledTextFromMarkdown("a **b** `c`")),
541
- "\"a b c\" 2:3:1 4:5:8");
542
-
543
- expectString(
544
- "styled run trims edge spaces",
545
- jetmarkdown::markdownFromStyledText("hi bold", {{2, 7, MarkBold}}),
546
- "hi **bold**\n");
547
- expectString(
548
- "styled whitespace-only run drops",
549
- jetmarkdown::markdownFromStyledText("a b", {{1, 2, MarkBold}}),
550
- "a b\n");
551
- // The pad space survives the code-span round trip (CommonMark strips one
552
- // leading/trailing space pair), so the content keeps its edge spaces.
553
- expectStyledRoundTrip(
554
- "styled code run keeps spaces", "x y z", {{1, 4, MarkInlineCode}});
555
-
556
- // --- Editor line blocks (E3) ---
557
- using jetmarkdown::EditorBlockType;
558
- using jetmarkdown::EditorLine;
559
- const EditorLine P = {EditorBlockType::Paragraph, 0};
560
- const EditorLine Q = {EditorBlockType::Quote, 0};
561
- const EditorLine C = {EditorBlockType::Code, 0};
562
- const EditorLine UL = {EditorBlockType::Bullet, 0};
563
- const EditorLine OL = {EditorBlockType::Ordered, 0};
564
-
565
- expectString(
566
- "editor heading line",
567
- jetmarkdown::markdownFromEditor(
568
- "Title\nbody", {}, {{EditorBlockType::Heading, 2}, P}),
569
- "## Title\n\nbody\n");
570
- expectString(
571
- "editor quote lines merge",
572
- jetmarkdown::markdownFromEditor("a\nb", {}, {Q, Q}),
573
- "> a\n>\n> b\n");
574
- expectString(
575
- "editor code lines merge raw",
576
- jetmarkdown::markdownFromEditor(
577
- "x = 1\ny *= 2", {{0, 5, MarkBold}}, {C, C}),
578
- "```\nx = 1\ny *= 2\n```\n");
579
- expectString(
580
- "editor bullet list",
581
- jetmarkdown::markdownFromEditor("a\nb", {}, {UL, UL}),
582
- "- a\n- b\n");
583
- expectString(
584
- "editor ordered list",
585
- jetmarkdown::markdownFromEditor("a\nb", {}, {OL, OL}),
586
- "1. a\n2. b\n");
587
- expectString(
588
- "editor mixed blocks",
589
- jetmarkdown::markdownFromEditor(
590
- "Title\nintro\nitem", {{0, 5, MarkBold}},
591
- {{EditorBlockType::Heading, 1}, P, UL}),
592
- "# **Title**\n\nintro\n\n- item\n");
593
-
594
- expectEditorRoundTrip("editor rt paragraphs", "one\ntwo", {}, {P, P});
595
- expectEditorRoundTrip(
596
- "editor rt heading levels",
597
- "h1\nh6",
598
- {},
599
- {{EditorBlockType::Heading, 1}, {EditorBlockType::Heading, 6}});
600
- expectEditorRoundTrip("editor rt quote", "quoted\nlines", {}, {Q, Q});
601
- expectEditorRoundTrip(
602
- "editor rt code block", "const x = 1;\nreturn x;", {}, {C, C});
603
- expectEditorRoundTrip("editor rt bullets", "a\nb\nc", {}, {UL, UL, UL});
604
- expectEditorRoundTrip("editor rt ordered", "a\nb", {}, {OL, OL});
605
- expectEditorRoundTrip(
606
- "editor rt marked list items",
607
- "bold item\nplain",
608
- {{0, 4, MarkBold}},
609
- {UL, UL});
610
- expectEditorRoundTrip(
611
- "editor rt block transitions",
612
- "head\npara\nquote\ncode\nitem one\nitem two\ntail",
613
- {},
614
- {{EditorBlockType::Heading, 3}, P, Q, C, UL, UL, P});
615
-
616
- expectString(
617
- "editor extraction with blocks",
618
- dumpEditor(jetmarkdown::editorFromMarkdown(
619
- "# Title\n\n- a\n- **b**\n\n> q\n\n```\nx\n```")),
620
- "\"Title\na\nb\nq\nx\" 8:9:1 | 1.1 4 4 2 3");
621
-
622
- // --- Editor links + mentions (E4) ---
623
- expectString(
624
- "editor link run serializes",
625
- jetmarkdown::markdownFromEditor(
626
- "visit the docs now", {}, {}, {{6, 14, "https://x.dev"}}),
627
- "visit [the docs](https://x.dev) now\n");
628
- expectString(
629
- "editor mention link",
630
- jetmarkdown::markdownFromEditor(
631
- "ping @ali ok", {}, {}, {{5, 9, "users://ali"}}),
632
- "ping [@ali](users://ali) ok\n");
633
- expectString(
634
- "editor marked link label",
635
- jetmarkdown::markdownFromEditor(
636
- "see docs here",
637
- {{4, 8, jetmarkdown::MarkBold}},
638
- {},
639
- {{4, 8, "https://x.dev"}}),
640
- "see [**docs**](https://x.dev) here\n");
641
- expectString(
642
- "editor link extraction",
643
- dumpEditor(jetmarkdown::editorFromMarkdown(
644
- "visit [the docs](https://x.dev) and [@ali](users://ali)")),
645
- "\"visit the docs and @ali\" | 0 [6:14 https://x.dev] [19:23 users://ali]");
646
- expectString(
647
- "editor autolink extraction",
648
- dumpEditor(jetmarkdown::editorFromMarkdown("see https://x.dev now")),
649
- "\"see https://x.dev now\" | 0 [4:17 https://x.dev]");
650
- expectString(
651
- "editor link in list item",
652
- jetmarkdown::markdownFromEditor(
653
- "docs\nother",
654
- {},
655
- {{jetmarkdown::EditorBlockType::Bullet, 0},
656
- {jetmarkdown::EditorBlockType::Bullet, 0}},
657
- {{0, 4, "https://x.dev"}}),
658
- "- [docs](https://x.dev)\n- other\n");
659
-
660
258
  std::printf("%d/%d passed\n", g_total - g_failures, g_total);
661
259
  return g_failures == 0 ? 0 : 1;
662
260
  }
@@ -17,9 +17,6 @@ clang++ -std=c++17 -O1 -Wall -Wextra \
17
17
  ../core/InlineExtensions.cpp \
18
18
  ../core/AstJson.cpp \
19
19
  ../core/AstSerializer.cpp \
20
- ../core/AstToMarkdown.cpp \
21
- ../core/EditorText.cpp \
22
- ../core/EditorRuns.cpp \
23
20
  "$BUILD_DIR/md4c.o"
24
21
 
25
22
  "$BUILD_DIR/parser_tests"
@@ -34,6 +34,7 @@ using namespace facebook::react;
34
34
 
35
35
  static void JMDSetNeedsDisplayDeep(UIView *view);
36
36
  static CGRect JMDScreenFrameOfView(UIView *view);
37
+ static BOOL JMDIsScrollInProgress(UIView *view);
37
38
 
38
39
  @implementation JetMarkdownView {
39
40
  NSString *_markdown;
@@ -166,10 +167,17 @@ static CGRect JMDScreenFrameOfView(UIView *view);
166
167
  }
167
168
 
168
169
  // Touches that do not start on an interactive range are never tracked, so
169
- // they cannot interfere with an ancestor scroll view's pan.
170
+ // they cannot interfere with an ancestor scroll view's pan. A touch that
171
+ // lands mid-scroll is a scroll-stopper, not a press (Pressable semantics) —
172
+ // checked from the deepest content view so nested code/table scrollers and
173
+ // the outer list both count.
170
174
  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)recognizer
171
175
  shouldReceiveTouch:(UITouch *)touch {
172
- return [self jmdIsInteractiveAtPoint:[touch locationInView:self]];
176
+ const CGPoint point = [touch locationInView:self];
177
+ if (![self jmdIsInteractiveAtPoint:point]) {
178
+ return NO;
179
+ }
180
+ return !JMDIsScrollInProgress([self jmdContentViewAtPoint:point inView:self]);
173
181
  }
174
182
 
175
183
  // Non-interactive points fall through to ancestors so a wrapping pressable
@@ -182,9 +190,32 @@ static CGRect JMDScreenFrameOfView(UIView *view);
182
190
  if (result == self && ![self jmdIsInteractiveAtPoint:point]) {
183
191
  return nil;
184
192
  }
193
+ // Mid-scroll, an interactive point is not claimed either: hit-testing runs
194
+ // before the touch is delivered, so isDecelerating is still reliable here
195
+ // (by shouldReceiveTouch: time the same touch may have already stopped the
196
+ // scroll). Falling through lets the ancestor scroll view take the touch
197
+ // and stop, exactly like tapping a Pressable during momentum.
198
+ if (result == self && JMDIsScrollInProgress(self)) {
199
+ return nil;
200
+ }
185
201
  return result;
186
202
  }
187
203
 
204
+ // YES when any scroll view on the path from `view` up through the window is
205
+ // being dragged or is decelerating — a press landing then must only stop
206
+ // the scroll, never fire.
207
+ static BOOL JMDIsScrollInProgress(UIView *view) {
208
+ for (UIView *ancestor = view; ancestor != nil; ancestor = ancestor.superview) {
209
+ if ([ancestor isKindOfClass:UIScrollView.class]) {
210
+ UIScrollView *scroll = (UIScrollView *)ancestor;
211
+ if (scroll.isDecelerating || scroll.isDragging) {
212
+ return YES;
213
+ }
214
+ }
215
+ }
216
+ return NO;
217
+ }
218
+
188
219
  // Scrolling always wins: a drag that begins on a link still pans the list
189
220
  // (the tap/long-press simply fail on movement).
190
221
  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)recognizer
@@ -3,7 +3,5 @@
3
3
  /** biome-ignore-all lint/performance/noBarrelFile: go away */
4
4
 
5
5
  export { defaultStyles, mergeStyles } from "./defaultStyles.js";
6
- export { JetMarkdownEditor } from "./JetMarkdownEditor";
7
6
  export { JetMarkdownView } from "./JetMarkdownView";
8
- export { useJetMarkdownEditor } from "./useJetMarkdownEditor.js";
9
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["defaultStyles","mergeStyles","JetMarkdownEditor","JetMarkdownView","useJetMarkdownEditor"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA;;AAEA,SAASA,aAAa,EAAEC,WAAW,QAAQ,oBAAiB;AAC5D,SAASC,iBAAiB,QAAQ,qBAAqB;AACvD,SAASC,eAAe,QAAQ,mBAAmB;AA6BnD,SAASC,oBAAoB,QAAQ,2BAAwB","ignoreList":[]}
1
+ {"version":3,"names":["defaultStyles","mergeStyles","JetMarkdownView"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA;;AAEA,SAASA,aAAa,EAAEC,WAAW,QAAQ,oBAAiB;AAC5D,SAASC,eAAe,QAAQ,mBAAmB","ignoreList":[]}
@@ -1,7 +1,5 @@
1
1
  /** biome-ignore-all lint/performance/noBarrelFile: go away */
2
2
  export { defaultStyles, mergeStyles } from "./defaultStyles.js";
3
- export { JetMarkdownEditor } from "./JetMarkdownEditor";
4
3
  export { JetMarkdownView } from "./JetMarkdownView";
5
- export type { FontVariant, FontWeight, JetMarkdownEditorProps, JetMarkdownEditorRef, JetMarkdownViewProps, MarkdownContainerStyle, MarkdownDividerStyle, MarkdownEditorState, MarkdownHeadingLevel, MarkdownImageData, MarkdownImageEvent, MarkdownImageStyle, MarkdownInlineCodeStyle, MarkdownLayoutStyle, MarkdownListMarkerStyle, MarkdownListStyle, MarkdownMentionEvent, MarkdownMentionQueryEvent, MarkdownMentionStyle, MarkdownPasteEvent, MarkdownSelection, MarkdownSpoilerStyle, MarkdownStyles, MarkdownTableStyle, MarkdownTextStyle, MarkdownUrlEvent, } from "./types.js";
6
- export { useJetMarkdownEditor } from "./useJetMarkdownEditor.js";
4
+ export type { FontVariant, FontWeight, JetMarkdownViewProps, MarkdownContainerStyle, MarkdownDividerStyle, MarkdownHeadingLevel, MarkdownImageData, MarkdownImageEvent, MarkdownImageStyle, MarkdownInlineCodeStyle, MarkdownLayoutStyle, MarkdownListMarkerStyle, MarkdownListStyle, MarkdownMentionStyle, MarkdownSpoilerStyle, MarkdownStyles, MarkdownTableStyle, MarkdownTextStyle, MarkdownUrlEvent, } from "./types.js";
7
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,8DAA8D;AAE9D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAiB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EACV,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAS,CAAC;AACjB,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,8DAA8D;AAE9D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAiB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EACV,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAS,CAAC"}
@@ -195,10 +195,6 @@ export interface MarkdownContainerStyle extends MarkdownTextStyle {
195
195
  backgroundColor?: ColorValue;
196
196
  /** Vertical spacing between blocks; overrides `styles.gap`. */
197
197
  gap?: number;
198
- /**
199
- * Editor: caps autogrow — past this height the editor scrolls
200
- * internally like a textarea. Viewer: plain layout clamp.
201
- */
202
198
  maxHeight?: number;
203
199
  padding?: number;
204
200
  paddingBottom?: number;
@@ -208,129 +204,6 @@ export interface MarkdownContainerStyle extends MarkdownTextStyle {
208
204
  paddingTop?: number;
209
205
  paddingVertical?: number;
210
206
  }
211
- /** Formatting active at the cursor / selection. */
212
- export interface MarkdownEditorState {
213
- /** 0 = no heading. */
214
- headingLevel: number;
215
- isBlockQuote: boolean;
216
- isBold: boolean;
217
- isCodeBlock: boolean;
218
- isInlineCode: boolean;
219
- isItalic: boolean;
220
- isOrderedList: boolean;
221
- isSpoiler: boolean;
222
- isStrikethrough: boolean;
223
- isSubscript: boolean;
224
- isSuperscript: boolean;
225
- isUnorderedList: boolean;
226
- }
227
- export interface MarkdownSelection {
228
- end: number;
229
- start: number;
230
- }
231
- export interface MarkdownMentionEvent {
232
- trigger: string;
233
- }
234
- export interface MarkdownMentionQueryEvent {
235
- query: string;
236
- trigger: string;
237
- }
238
- /**
239
- * Paste payload. `text` is present when the clipboard has text (parsed as
240
- * markdown and inserted by default); `images` when it has images (reported
241
- * only, never auto-inserted). `preventDefault()` suppresses the default
242
- * text insertion.
243
- */
244
- export interface MarkdownPasteEvent {
245
- images?: MarkdownImageData[];
246
- preventDefault(): void;
247
- text?: string;
248
- }
249
- export interface JetMarkdownEditorRef {
250
- blur(): void;
251
- focus(): void;
252
- /** Resolves the markdown as of the latest edit. */
253
- getMarkdown(): Promise<string>;
254
- /**
255
- * Links the selection to `url`; with a collapsed cursor, inserts `label`
256
- * (or the URL itself) as linked text.
257
- */
258
- insertLink(url: string, label?: string): void;
259
- /** Parses `markdown` and inserts it at the cursor (or over the selection). */
260
- insertMarkdown(markdown: string): void;
261
- /**
262
- * Inserts an atomic mention token (`trigger + label`, e.g. `@ali`)
263
- * linked to `url` (an app scheme like `users://ali`), replacing any
264
- * active mention query.
265
- */
266
- insertMention(trigger: string, label: string, url: string): void;
267
- /** Removes the link covering the selection or cursor. */
268
- removeLink(): void;
269
- setSelection(start: number, end: number): void;
270
- /** Replaces the content; the value is parsed as markdown. */
271
- setValue(markdown: string): void;
272
- /** Block toggles apply to every line the selection touches. */
273
- toggleBlockQuote(): void;
274
- /**
275
- * Each inline toggle applies to the selection, or arms/disarms the mark
276
- * for text typed at the collapsed cursor.
277
- */
278
- toggleBold(): void;
279
- toggleCode(): void;
280
- toggleCodeBlock(): void;
281
- /** Toggles the heading level (1-6) on the selected lines. */
282
- toggleHeading(level: number): void;
283
- toggleItalic(): void;
284
- toggleOrderedList(): void;
285
- toggleSpoiler(): void;
286
- toggleStrikethrough(): void;
287
- toggleSubscript(): void;
288
- toggleSuperscript(): void;
289
- toggleUnorderedList(): void;
290
- }
291
- export interface JetMarkdownEditorProps {
292
- /** Scale text with the system font size setting. Defaults to true. */
293
- allowFontScaling?: boolean;
294
- autoCapitalize?: "none" | "sentences" | "words" | "characters";
295
- autoCorrect?: boolean;
296
- autoFocus?: boolean;
297
- /**
298
- * Caret color. On iOS the caret and selection share one tint, so
299
- * `selectionColor` wins when both are set. Android 10+ only. Platform
300
- * colors (`PlatformColor`, `DynamicColorIOS`) are supported.
301
- */
302
- cursorColor?: ColorValue;
303
- /** Initial markdown content, applied once. */
304
- defaultValue?: string;
305
- editable?: boolean;
306
- /** Trigger characters (e.g. '@', '#') that begin a mention. */
307
- mentionTriggers?: string[];
308
- multiline?: boolean;
309
- onBlur?: () => void;
310
- onChangeMarkdown?: (markdown: string) => void;
311
- onChangeSelection?: (selection: MarkdownSelection) => void;
312
- onChangeState?: (state: MarkdownEditorState) => void;
313
- onChangeText?: (text: string) => void;
314
- onFocus?: () => void;
315
- onLinkDetected?: (event: MarkdownUrlEvent) => void;
316
- onMentionChange?: (event: MarkdownMentionQueryEvent) => void;
317
- onMentionEnd?: (event: MarkdownMentionEvent) => void;
318
- onMentionStart?: (event: MarkdownMentionEvent) => void;
319
- onPaste?: (event: MarkdownPasteEvent) => void;
320
- placeholder?: string;
321
- placeholderTextColor?: ColorValue;
322
- scrollEnabled?: boolean;
323
- /**
324
- * Selection highlight color. On iOS this also tints the caret (UIKit
325
- * shares one tint). Platform colors (`PlatformColor`, `DynamicColorIOS`)
326
- * are supported.
327
- */
328
- selectionColor?: ColorValue;
329
- /** Same container style contract as the viewer. */
330
- style?: StyleProp<MarkdownContainerStyle>;
331
- /** Per-element markdown styles; shared shape with the viewer. */
332
- styles?: MarkdownStyles;
333
- }
334
207
  export interface JetMarkdownViewProps {
335
208
  /** Scale text with the system font size setting. Defaults to true. */
336
209
  allowFontScaling?: boolean;