@tiptap/extension-list 3.6.7 → 3.7.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.
package/dist/index.js CHANGED
@@ -29,6 +29,25 @@ var BulletList = Node.create({
29
29
  renderHTML({ HTMLAttributes }) {
30
30
  return ["ul", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
31
31
  },
32
+ markdownTokenName: "list",
33
+ parseMarkdown: (token, helpers) => {
34
+ if (token.type !== "list" || token.ordered) {
35
+ return [];
36
+ }
37
+ return {
38
+ type: "bulletList",
39
+ content: token.items ? helpers.parseChildren(token.items) : []
40
+ };
41
+ },
42
+ renderMarkdown: (node, h) => {
43
+ if (!node.content) {
44
+ return "";
45
+ }
46
+ return h.renderChildren(node.content, "\n");
47
+ },
48
+ markdownOptions: {
49
+ indentsContent: true
50
+ },
32
51
  addCommands() {
33
52
  return {
34
53
  toggleBulletList: () => ({ commands, chain }) => {
@@ -66,7 +85,7 @@ var BulletList = Node.create({
66
85
  });
67
86
 
68
87
  // src/item/list-item.ts
69
- import { mergeAttributes as mergeAttributes2, Node as Node2 } from "@tiptap/core";
88
+ import { mergeAttributes as mergeAttributes2, Node as Node2, renderNestedMarkdownContent } from "@tiptap/core";
70
89
  var ListItem = Node2.create({
71
90
  name: "listItem",
72
91
  addOptions() {
@@ -88,6 +107,65 @@ var ListItem = Node2.create({
88
107
  renderHTML({ HTMLAttributes }) {
89
108
  return ["li", mergeAttributes2(this.options.HTMLAttributes, HTMLAttributes), 0];
90
109
  },
110
+ markdownTokenName: "list_item",
111
+ parseMarkdown: (token, helpers) => {
112
+ if (token.type !== "list_item") {
113
+ return [];
114
+ }
115
+ let content = [];
116
+ if (token.tokens && token.tokens.length > 0) {
117
+ const hasParagraphTokens = token.tokens.some((t) => t.type === "paragraph");
118
+ if (hasParagraphTokens) {
119
+ content = helpers.parseChildren(token.tokens);
120
+ } else {
121
+ const firstToken = token.tokens[0];
122
+ if (firstToken && firstToken.type === "text" && firstToken.tokens && firstToken.tokens.length > 0) {
123
+ const inlineContent = helpers.parseInline(firstToken.tokens);
124
+ content = [
125
+ {
126
+ type: "paragraph",
127
+ content: inlineContent
128
+ }
129
+ ];
130
+ if (token.tokens.length > 1) {
131
+ const remainingTokens = token.tokens.slice(1);
132
+ const additionalContent = helpers.parseChildren(remainingTokens);
133
+ content.push(...additionalContent);
134
+ }
135
+ } else {
136
+ content = helpers.parseChildren(token.tokens);
137
+ }
138
+ }
139
+ }
140
+ if (content.length === 0) {
141
+ content = [
142
+ {
143
+ type: "paragraph",
144
+ content: []
145
+ }
146
+ ];
147
+ }
148
+ return {
149
+ type: "listItem",
150
+ content
151
+ };
152
+ },
153
+ renderMarkdown: (node, h, ctx) => {
154
+ return renderNestedMarkdownContent(
155
+ node,
156
+ h,
157
+ (context) => {
158
+ if (context.parentType === "bulletList") {
159
+ return "- ";
160
+ }
161
+ if (context.parentType === "orderedList") {
162
+ return `${context.index + 1}. `;
163
+ }
164
+ return "- ";
165
+ },
166
+ ctx
167
+ );
168
+ },
91
169
  addKeyboardShortcuts() {
92
170
  return {
93
171
  Enter: () => this.editor.commands.splitListItem(this.name),
@@ -378,6 +456,138 @@ import { Extension as Extension2 } from "@tiptap/core";
378
456
 
379
457
  // src/ordered-list/ordered-list.ts
380
458
  import { mergeAttributes as mergeAttributes3, Node as Node3, wrappingInputRule as wrappingInputRule2 } from "@tiptap/core";
459
+
460
+ // src/ordered-list/utils.ts
461
+ var ORDERED_LIST_ITEM_REGEX = /^(\s*)(\d+)\.\s+(.*)$/;
462
+ var INDENTED_LINE_REGEX = /^\s/;
463
+ function collectOrderedListItems(lines) {
464
+ const listItems = [];
465
+ let currentLineIndex = 0;
466
+ let consumed = 0;
467
+ while (currentLineIndex < lines.length) {
468
+ const line = lines[currentLineIndex];
469
+ const match = line.match(ORDERED_LIST_ITEM_REGEX);
470
+ if (!match) {
471
+ break;
472
+ }
473
+ const [, indent, number, content] = match;
474
+ const indentLevel = indent.length;
475
+ let itemContent = content;
476
+ let nextLineIndex = currentLineIndex + 1;
477
+ const itemLines = [line];
478
+ while (nextLineIndex < lines.length) {
479
+ const nextLine = lines[nextLineIndex];
480
+ const nextMatch = nextLine.match(ORDERED_LIST_ITEM_REGEX);
481
+ if (nextMatch) {
482
+ break;
483
+ }
484
+ if (nextLine.trim() === "") {
485
+ itemLines.push(nextLine);
486
+ itemContent += "\n";
487
+ nextLineIndex += 1;
488
+ } else if (nextLine.match(INDENTED_LINE_REGEX)) {
489
+ itemLines.push(nextLine);
490
+ itemContent += `
491
+ ${nextLine.slice(indentLevel + 2)}`;
492
+ nextLineIndex += 1;
493
+ } else {
494
+ break;
495
+ }
496
+ }
497
+ listItems.push({
498
+ indent: indentLevel,
499
+ number: parseInt(number, 10),
500
+ content: itemContent.trim(),
501
+ raw: itemLines.join("\n")
502
+ });
503
+ consumed = nextLineIndex;
504
+ currentLineIndex = nextLineIndex;
505
+ }
506
+ return [listItems, consumed];
507
+ }
508
+ function buildNestedStructure(items, baseIndent, lexer) {
509
+ var _a;
510
+ const result = [];
511
+ let currentIndex = 0;
512
+ while (currentIndex < items.length) {
513
+ const item = items[currentIndex];
514
+ if (item.indent === baseIndent) {
515
+ const contentLines = item.content.split("\n");
516
+ const mainText = ((_a = contentLines[0]) == null ? void 0 : _a.trim()) || "";
517
+ const tokens = [];
518
+ if (mainText) {
519
+ tokens.push({
520
+ type: "paragraph",
521
+ raw: mainText,
522
+ tokens: lexer.inlineTokens(mainText)
523
+ });
524
+ }
525
+ const additionalContent = contentLines.slice(1).join("\n").trim();
526
+ if (additionalContent) {
527
+ const blockTokens = lexer.blockTokens(additionalContent);
528
+ tokens.push(...blockTokens);
529
+ }
530
+ let lookAheadIndex = currentIndex + 1;
531
+ const nestedItems = [];
532
+ while (lookAheadIndex < items.length && items[lookAheadIndex].indent > baseIndent) {
533
+ nestedItems.push(items[lookAheadIndex]);
534
+ lookAheadIndex += 1;
535
+ }
536
+ if (nestedItems.length > 0) {
537
+ const nextIndent = Math.min(...nestedItems.map((nestedItem) => nestedItem.indent));
538
+ const nestedListItems = buildNestedStructure(nestedItems, nextIndent, lexer);
539
+ tokens.push({
540
+ type: "list",
541
+ ordered: true,
542
+ start: nestedItems[0].number,
543
+ items: nestedListItems,
544
+ raw: nestedItems.map((nestedItem) => nestedItem.raw).join("\n")
545
+ });
546
+ }
547
+ result.push({
548
+ type: "list_item",
549
+ raw: item.raw,
550
+ tokens
551
+ });
552
+ currentIndex = lookAheadIndex;
553
+ } else {
554
+ currentIndex += 1;
555
+ }
556
+ }
557
+ return result;
558
+ }
559
+ function parseListItems(items, helpers) {
560
+ return items.map((item) => {
561
+ if (item.type !== "list_item") {
562
+ return helpers.parseChildren([item])[0];
563
+ }
564
+ const content = [];
565
+ if (item.tokens && item.tokens.length > 0) {
566
+ item.tokens.forEach((itemToken) => {
567
+ if (itemToken.type === "paragraph" || itemToken.type === "list" || itemToken.type === "blockquote" || itemToken.type === "code") {
568
+ content.push(...helpers.parseChildren([itemToken]));
569
+ } else if (itemToken.type === "text" && itemToken.tokens) {
570
+ const inlineContent = helpers.parseChildren([itemToken]);
571
+ content.push({
572
+ type: "paragraph",
573
+ content: inlineContent
574
+ });
575
+ } else {
576
+ const parsed = helpers.parseChildren([itemToken]);
577
+ if (parsed.length > 0) {
578
+ content.push(...parsed);
579
+ }
580
+ }
581
+ });
582
+ }
583
+ return {
584
+ type: "listItem",
585
+ content
586
+ };
587
+ });
588
+ }
589
+
590
+ // src/ordered-list/ordered-list.ts
381
591
  var ListItemName2 = "listItem";
382
592
  var TextStyleName2 = "textStyle";
383
593
  var orderedListInputRegex = /^(\d+)\.\s$/;
@@ -420,6 +630,63 @@ var OrderedList = Node3.create({
420
630
  const { start, ...attributesWithoutStart } = HTMLAttributes;
421
631
  return start === 1 ? ["ol", mergeAttributes3(this.options.HTMLAttributes, attributesWithoutStart), 0] : ["ol", mergeAttributes3(this.options.HTMLAttributes, HTMLAttributes), 0];
422
632
  },
633
+ markdownTokenName: "list",
634
+ parseMarkdown: (token, helpers) => {
635
+ if (token.type !== "list" || !token.ordered) {
636
+ return [];
637
+ }
638
+ const startValue = token.start || 1;
639
+ const content = token.items ? parseListItems(token.items, helpers) : [];
640
+ if (startValue !== 1) {
641
+ return {
642
+ type: "orderedList",
643
+ attrs: { start: startValue },
644
+ content
645
+ };
646
+ }
647
+ return {
648
+ type: "orderedList",
649
+ content
650
+ };
651
+ },
652
+ renderMarkdown: (node, h) => {
653
+ if (!node.content) {
654
+ return "";
655
+ }
656
+ return h.renderChildren(node.content, "\n");
657
+ },
658
+ markdownTokenizer: {
659
+ name: "orderedList",
660
+ level: "block",
661
+ start: (src) => {
662
+ const match = src.match(/^(\s*)(\d+)\.\s+/);
663
+ const index = match == null ? void 0 : match.index;
664
+ return index !== void 0 ? index : -1;
665
+ },
666
+ tokenize: (src, _tokens, lexer) => {
667
+ var _a;
668
+ const lines = src.split("\n");
669
+ const [listItems, consumed] = collectOrderedListItems(lines);
670
+ if (listItems.length === 0) {
671
+ return void 0;
672
+ }
673
+ const items = buildNestedStructure(listItems, 0, lexer);
674
+ if (items.length === 0) {
675
+ return void 0;
676
+ }
677
+ const startValue = ((_a = listItems[0]) == null ? void 0 : _a.number) || 1;
678
+ return {
679
+ type: "list",
680
+ ordered: true,
681
+ start: startValue,
682
+ items,
683
+ raw: lines.slice(0, consumed).join("\n")
684
+ };
685
+ }
686
+ },
687
+ markdownOptions: {
688
+ indentsContent: true
689
+ },
423
690
  addCommands() {
424
691
  return {
425
692
  toggleOrderedList: () => ({ commands, chain }) => {
@@ -458,7 +725,7 @@ var OrderedList = Node3.create({
458
725
  });
459
726
 
460
727
  // src/task-item/task-item.ts
461
- import { mergeAttributes as mergeAttributes4, Node as Node4, wrappingInputRule as wrappingInputRule3 } from "@tiptap/core";
728
+ import { mergeAttributes as mergeAttributes4, Node as Node4, renderNestedMarkdownContent as renderNestedMarkdownContent2, wrappingInputRule as wrappingInputRule3 } from "@tiptap/core";
462
729
  var inputRegex = /^\s*(\[([( |x])?\])\s$/;
463
730
  var TaskItem = Node4.create({
464
731
  name: "taskItem",
@@ -517,6 +784,27 @@ var TaskItem = Node4.create({
517
784
  ["div", 0]
518
785
  ];
519
786
  },
787
+ parseMarkdown: (token, h) => {
788
+ const content = [];
789
+ if (token.tokens && token.tokens.length > 0) {
790
+ content.push(h.createNode("paragraph", {}, h.parseInline(token.tokens)));
791
+ } else if (token.text) {
792
+ content.push(h.createNode("paragraph", {}, [h.createNode("text", { text: token.text })]));
793
+ } else {
794
+ content.push(h.createNode("paragraph", {}, []));
795
+ }
796
+ if (token.nestedTokens && token.nestedTokens.length > 0) {
797
+ const nestedContent = h.parseChildren(token.nestedTokens);
798
+ content.push(...nestedContent);
799
+ }
800
+ return h.createNode("taskItem", { checked: token.checked || false }, content);
801
+ },
802
+ renderMarkdown: (node, h) => {
803
+ var _a;
804
+ const checkedChar = ((_a = node.attrs) == null ? void 0 : _a.checked) ? "x" : " ";
805
+ const prefix = `- [${checkedChar}] `;
806
+ return renderNestedMarkdownContent2(node, h, prefix);
807
+ },
520
808
  addKeyboardShortcuts() {
521
809
  const shortcuts = {
522
810
  Enter: () => this.editor.commands.splitListItem(this.name),
@@ -610,7 +898,7 @@ var TaskItem = Node4.create({
610
898
  });
611
899
 
612
900
  // src/task-list/task-list.ts
613
- import { mergeAttributes as mergeAttributes5, Node as Node5 } from "@tiptap/core";
901
+ import { mergeAttributes as mergeAttributes5, Node as Node5, parseIndentedBlocks } from "@tiptap/core";
614
902
  var TaskList = Node5.create({
615
903
  name: "taskList",
616
904
  addOptions() {
@@ -634,6 +922,97 @@ var TaskList = Node5.create({
634
922
  renderHTML({ HTMLAttributes }) {
635
923
  return ["ul", mergeAttributes5(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name }), 0];
636
924
  },
925
+ parseMarkdown: (token, h) => {
926
+ return h.createNode("taskList", {}, h.parseChildren(token.items || []));
927
+ },
928
+ renderMarkdown: (node, h) => {
929
+ if (!node.content) {
930
+ return "";
931
+ }
932
+ return h.renderChildren(node.content, "\n");
933
+ },
934
+ markdownTokenizer: {
935
+ name: "taskList",
936
+ level: "block",
937
+ start(src) {
938
+ var _a;
939
+ const index = (_a = src.match(/^\s*[-+*]\s+\[([ xX])\]\s+/)) == null ? void 0 : _a.index;
940
+ return index !== void 0 ? index : -1;
941
+ },
942
+ tokenize(src, tokens, lexer) {
943
+ const parseTaskListContent = (content) => {
944
+ const nestedResult = parseIndentedBlocks(
945
+ content,
946
+ {
947
+ itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
948
+ extractItemData: (match) => ({
949
+ indentLevel: match[1].length,
950
+ mainContent: match[4],
951
+ checked: match[3].toLowerCase() === "x"
952
+ }),
953
+ createToken: (data, nestedTokens) => ({
954
+ type: "taskItem",
955
+ raw: "",
956
+ mainContent: data.mainContent,
957
+ indentLevel: data.indentLevel,
958
+ checked: data.checked,
959
+ text: data.mainContent,
960
+ tokens: lexer.inlineTokens(data.mainContent),
961
+ nestedTokens
962
+ }),
963
+ // Allow recursive nesting
964
+ customNestedParser: parseTaskListContent
965
+ },
966
+ lexer
967
+ );
968
+ if (nestedResult) {
969
+ return [
970
+ {
971
+ type: "taskList",
972
+ raw: nestedResult.raw,
973
+ items: nestedResult.items
974
+ }
975
+ ];
976
+ }
977
+ return lexer.blockTokens(content);
978
+ };
979
+ const result = parseIndentedBlocks(
980
+ src,
981
+ {
982
+ itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
983
+ extractItemData: (match) => ({
984
+ indentLevel: match[1].length,
985
+ mainContent: match[4],
986
+ checked: match[3].toLowerCase() === "x"
987
+ }),
988
+ createToken: (data, nestedTokens) => ({
989
+ type: "taskItem",
990
+ raw: "",
991
+ mainContent: data.mainContent,
992
+ indentLevel: data.indentLevel,
993
+ checked: data.checked,
994
+ text: data.mainContent,
995
+ tokens: lexer.inlineTokens(data.mainContent),
996
+ nestedTokens
997
+ }),
998
+ // Use the recursive parser for nested content
999
+ customNestedParser: parseTaskListContent
1000
+ },
1001
+ lexer
1002
+ );
1003
+ if (!result) {
1004
+ return void 0;
1005
+ }
1006
+ return {
1007
+ type: "taskList",
1008
+ raw: result.raw,
1009
+ items: result.items
1010
+ };
1011
+ }
1012
+ },
1013
+ markdownOptions: {
1014
+ indentsContent: true
1015
+ },
637
1016
  addCommands() {
638
1017
  return {
639
1018
  toggleTaskList: () => ({ commands }) => {