@thomasjahoda-forks/tiptap-extension-list 3.0.8 → 3.18.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/bullet-list/index.cjs +19 -0
  2. package/dist/bullet-list/index.cjs.map +1 -1
  3. package/dist/bullet-list/index.js +19 -0
  4. package/dist/bullet-list/index.js.map +1 -1
  5. package/dist/index.cjs +412 -4
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +2 -2
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.js +421 -7
  10. package/dist/index.js.map +1 -1
  11. package/dist/item/index.cjs +61 -0
  12. package/dist/item/index.cjs.map +1 -1
  13. package/dist/item/index.js +62 -1
  14. package/dist/item/index.js.map +1 -1
  15. package/dist/keymap/index.d.cts +2 -2
  16. package/dist/keymap/index.d.ts +2 -2
  17. package/dist/kit/index.cjs +412 -4
  18. package/dist/kit/index.cjs.map +1 -1
  19. package/dist/kit/index.js +421 -7
  20. package/dist/kit/index.js.map +1 -1
  21. package/dist/ordered-list/index.cjs +189 -0
  22. package/dist/ordered-list/index.cjs.map +1 -1
  23. package/dist/ordered-list/index.js +189 -0
  24. package/dist/ordered-list/index.js.map +1 -1
  25. package/dist/task-item/index.cjs +52 -4
  26. package/dist/task-item/index.cjs.map +1 -1
  27. package/dist/task-item/index.js +59 -5
  28. package/dist/task-item/index.js.map +1 -1
  29. package/dist/task-list/index.cjs +91 -0
  30. package/dist/task-list/index.cjs.map +1 -1
  31. package/dist/task-list/index.js +92 -1
  32. package/dist/task-list/index.js.map +1 -1
  33. package/package.json +5 -5
  34. package/src/bullet-list/bullet-list.ts +25 -0
  35. package/src/item/list-item.ts +82 -1
  36. package/src/ordered-list/ordered-list.ts +72 -0
  37. package/src/ordered-list/utils.ts +234 -0
  38. package/src/task-item/task-item.ts +85 -6
  39. package/src/task-list/task-list.ts +105 -1
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,67 @@ 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
+ var _a, _b;
159
+ if (context.parentType === "bulletList") {
160
+ return "- ";
161
+ }
162
+ if (context.parentType === "orderedList") {
163
+ const start = ((_b = (_a = context.meta) == null ? void 0 : _a.parentAttrs) == null ? void 0 : _b.start) || 1;
164
+ return `${start + context.index}. `;
165
+ }
166
+ return "- ";
167
+ },
168
+ ctx
169
+ );
170
+ },
91
171
  addKeyboardShortcuts() {
92
172
  return {
93
173
  Enter: () => this.editor.commands.splitListItem(this.name),
@@ -378,6 +458,138 @@ import { Extension as Extension2 } from "@tiptap/core";
378
458
 
379
459
  // src/ordered-list/ordered-list.ts
380
460
  import { mergeAttributes as mergeAttributes3, Node as Node3, wrappingInputRule as wrappingInputRule2 } from "@tiptap/core";
461
+
462
+ // src/ordered-list/utils.ts
463
+ var ORDERED_LIST_ITEM_REGEX = /^(\s*)(\d+)\.\s+(.*)$/;
464
+ var INDENTED_LINE_REGEX = /^\s/;
465
+ function collectOrderedListItems(lines) {
466
+ const listItems = [];
467
+ let currentLineIndex = 0;
468
+ let consumed = 0;
469
+ while (currentLineIndex < lines.length) {
470
+ const line = lines[currentLineIndex];
471
+ const match = line.match(ORDERED_LIST_ITEM_REGEX);
472
+ if (!match) {
473
+ break;
474
+ }
475
+ const [, indent, number, content] = match;
476
+ const indentLevel = indent.length;
477
+ let itemContent = content;
478
+ let nextLineIndex = currentLineIndex + 1;
479
+ const itemLines = [line];
480
+ while (nextLineIndex < lines.length) {
481
+ const nextLine = lines[nextLineIndex];
482
+ const nextMatch = nextLine.match(ORDERED_LIST_ITEM_REGEX);
483
+ if (nextMatch) {
484
+ break;
485
+ }
486
+ if (nextLine.trim() === "") {
487
+ itemLines.push(nextLine);
488
+ itemContent += "\n";
489
+ nextLineIndex += 1;
490
+ } else if (nextLine.match(INDENTED_LINE_REGEX)) {
491
+ itemLines.push(nextLine);
492
+ itemContent += `
493
+ ${nextLine.slice(indentLevel + 2)}`;
494
+ nextLineIndex += 1;
495
+ } else {
496
+ break;
497
+ }
498
+ }
499
+ listItems.push({
500
+ indent: indentLevel,
501
+ number: parseInt(number, 10),
502
+ content: itemContent.trim(),
503
+ raw: itemLines.join("\n")
504
+ });
505
+ consumed = nextLineIndex;
506
+ currentLineIndex = nextLineIndex;
507
+ }
508
+ return [listItems, consumed];
509
+ }
510
+ function buildNestedStructure(items, baseIndent, lexer) {
511
+ var _a;
512
+ const result = [];
513
+ let currentIndex = 0;
514
+ while (currentIndex < items.length) {
515
+ const item = items[currentIndex];
516
+ if (item.indent === baseIndent) {
517
+ const contentLines = item.content.split("\n");
518
+ const mainText = ((_a = contentLines[0]) == null ? void 0 : _a.trim()) || "";
519
+ const tokens = [];
520
+ if (mainText) {
521
+ tokens.push({
522
+ type: "paragraph",
523
+ raw: mainText,
524
+ tokens: lexer.inlineTokens(mainText)
525
+ });
526
+ }
527
+ const additionalContent = contentLines.slice(1).join("\n").trim();
528
+ if (additionalContent) {
529
+ const blockTokens = lexer.blockTokens(additionalContent);
530
+ tokens.push(...blockTokens);
531
+ }
532
+ let lookAheadIndex = currentIndex + 1;
533
+ const nestedItems = [];
534
+ while (lookAheadIndex < items.length && items[lookAheadIndex].indent > baseIndent) {
535
+ nestedItems.push(items[lookAheadIndex]);
536
+ lookAheadIndex += 1;
537
+ }
538
+ if (nestedItems.length > 0) {
539
+ const nextIndent = Math.min(...nestedItems.map((nestedItem) => nestedItem.indent));
540
+ const nestedListItems = buildNestedStructure(nestedItems, nextIndent, lexer);
541
+ tokens.push({
542
+ type: "list",
543
+ ordered: true,
544
+ start: nestedItems[0].number,
545
+ items: nestedListItems,
546
+ raw: nestedItems.map((nestedItem) => nestedItem.raw).join("\n")
547
+ });
548
+ }
549
+ result.push({
550
+ type: "list_item",
551
+ raw: item.raw,
552
+ tokens
553
+ });
554
+ currentIndex = lookAheadIndex;
555
+ } else {
556
+ currentIndex += 1;
557
+ }
558
+ }
559
+ return result;
560
+ }
561
+ function parseListItems(items, helpers) {
562
+ return items.map((item) => {
563
+ if (item.type !== "list_item") {
564
+ return helpers.parseChildren([item])[0];
565
+ }
566
+ const content = [];
567
+ if (item.tokens && item.tokens.length > 0) {
568
+ item.tokens.forEach((itemToken) => {
569
+ if (itemToken.type === "paragraph" || itemToken.type === "list" || itemToken.type === "blockquote" || itemToken.type === "code") {
570
+ content.push(...helpers.parseChildren([itemToken]));
571
+ } else if (itemToken.type === "text" && itemToken.tokens) {
572
+ const inlineContent = helpers.parseChildren([itemToken]);
573
+ content.push({
574
+ type: "paragraph",
575
+ content: inlineContent
576
+ });
577
+ } else {
578
+ const parsed = helpers.parseChildren([itemToken]);
579
+ if (parsed.length > 0) {
580
+ content.push(...parsed);
581
+ }
582
+ }
583
+ });
584
+ }
585
+ return {
586
+ type: "listItem",
587
+ content
588
+ };
589
+ });
590
+ }
591
+
592
+ // src/ordered-list/ordered-list.ts
381
593
  var ListItemName2 = "listItem";
382
594
  var TextStyleName2 = "textStyle";
383
595
  var orderedListInputRegex = /^(\d+)\.\s$/;
@@ -420,6 +632,63 @@ var OrderedList = Node3.create({
420
632
  const { start, ...attributesWithoutStart } = HTMLAttributes;
421
633
  return start === 1 ? ["ol", mergeAttributes3(this.options.HTMLAttributes, attributesWithoutStart), 0] : ["ol", mergeAttributes3(this.options.HTMLAttributes, HTMLAttributes), 0];
422
634
  },
635
+ markdownTokenName: "list",
636
+ parseMarkdown: (token, helpers) => {
637
+ if (token.type !== "list" || !token.ordered) {
638
+ return [];
639
+ }
640
+ const startValue = token.start || 1;
641
+ const content = token.items ? parseListItems(token.items, helpers) : [];
642
+ if (startValue !== 1) {
643
+ return {
644
+ type: "orderedList",
645
+ attrs: { start: startValue },
646
+ content
647
+ };
648
+ }
649
+ return {
650
+ type: "orderedList",
651
+ content
652
+ };
653
+ },
654
+ renderMarkdown: (node, h) => {
655
+ if (!node.content) {
656
+ return "";
657
+ }
658
+ return h.renderChildren(node.content, "\n");
659
+ },
660
+ markdownTokenizer: {
661
+ name: "orderedList",
662
+ level: "block",
663
+ start: (src) => {
664
+ const match = src.match(/^(\s*)(\d+)\.\s+/);
665
+ const index = match == null ? void 0 : match.index;
666
+ return index !== void 0 ? index : -1;
667
+ },
668
+ tokenize: (src, _tokens, lexer) => {
669
+ var _a;
670
+ const lines = src.split("\n");
671
+ const [listItems, consumed] = collectOrderedListItems(lines);
672
+ if (listItems.length === 0) {
673
+ return void 0;
674
+ }
675
+ const items = buildNestedStructure(listItems, 0, lexer);
676
+ if (items.length === 0) {
677
+ return void 0;
678
+ }
679
+ const startValue = ((_a = listItems[0]) == null ? void 0 : _a.number) || 1;
680
+ return {
681
+ type: "list",
682
+ ordered: true,
683
+ start: startValue,
684
+ items,
685
+ raw: lines.slice(0, consumed).join("\n")
686
+ };
687
+ }
688
+ },
689
+ markdownOptions: {
690
+ indentsContent: true
691
+ },
423
692
  addCommands() {
424
693
  return {
425
694
  toggleOrderedList: () => ({ commands, chain }) => {
@@ -458,7 +727,13 @@ var OrderedList = Node3.create({
458
727
  });
459
728
 
460
729
  // src/task-item/task-item.ts
461
- import { mergeAttributes as mergeAttributes4, Node as Node4, wrappingInputRule as wrappingInputRule3 } from "@tiptap/core";
730
+ import {
731
+ getRenderedAttributes,
732
+ mergeAttributes as mergeAttributes4,
733
+ Node as Node4,
734
+ renderNestedMarkdownContent as renderNestedMarkdownContent2,
735
+ wrappingInputRule as wrappingInputRule3
736
+ } from "@tiptap/core";
462
737
  var inputRegex = /^\s*((\[([( |x])?\])|\.)\s$/;
463
738
  var TaskItem = Node4.create({
464
739
  name: "taskItem",
@@ -517,6 +792,27 @@ var TaskItem = Node4.create({
517
792
  ["div", 0]
518
793
  ];
519
794
  },
795
+ parseMarkdown: (token, h) => {
796
+ const content = [];
797
+ if (token.tokens && token.tokens.length > 0) {
798
+ content.push(h.createNode("paragraph", {}, h.parseInline(token.tokens)));
799
+ } else if (token.text) {
800
+ content.push(h.createNode("paragraph", {}, [h.createNode("text", { text: token.text })]));
801
+ } else {
802
+ content.push(h.createNode("paragraph", {}, []));
803
+ }
804
+ if (token.nestedTokens && token.nestedTokens.length > 0) {
805
+ const nestedContent = h.parseChildren(token.nestedTokens);
806
+ content.push(...nestedContent);
807
+ }
808
+ return h.createNode("taskItem", { checked: token.checked || false }, content);
809
+ },
810
+ renderMarkdown: (node, h) => {
811
+ var _a;
812
+ const checkedChar = ((_a = node.attrs) == null ? void 0 : _a.checked) ? "x" : " ";
813
+ const prefix = `- [${checkedChar}] `;
814
+ return renderNestedMarkdownContent2(node, h, prefix);
815
+ },
520
816
  addKeyboardShortcuts() {
521
817
  const shortcuts = {
522
818
  Enter: () => this.editor.commands.splitListItem(this.name),
@@ -537,13 +833,14 @@ var TaskItem = Node4.create({
537
833
  const checkboxStyler = document.createElement("span");
538
834
  const checkbox = document.createElement("input");
539
835
  const content = document.createElement("div");
540
- const updateA11Y = () => {
836
+ const updateA11Y = (currentNode) => {
541
837
  var _a, _b;
542
- checkbox.ariaLabel = ((_b = (_a = this.options.a11y) == null ? void 0 : _a.checkboxLabel) == null ? void 0 : _b.call(_a, node, checkbox.checked)) || `Task item checkbox for ${node.textContent || "empty task item"}`;
838
+ checkbox.ariaLabel = ((_b = (_a = this.options.a11y) == null ? void 0 : _a.checkboxLabel) == null ? void 0 : _b.call(_a, currentNode, checkbox.checked)) || `Task item checkbox for ${currentNode.textContent || "empty task item"}`;
543
839
  };
544
- updateA11Y();
840
+ updateA11Y(node);
545
841
  checkboxWrapper.contentEditable = "false";
546
842
  checkbox.type = "checkbox";
843
+ checkbox.contentEditable = "false";
547
844
  checkbox.addEventListener("mousedown", (event) => event.preventDefault());
548
845
  checkbox.addEventListener("change", (event) => {
549
846
  if (!editor.isEditable && !this.options.onReadOnlyChecked) {
@@ -596,6 +893,7 @@ var TaskItem = Node4.create({
596
893
  Object.entries(HTMLAttributes).forEach(([key, value]) => {
597
894
  listItem.setAttribute(key, value);
598
895
  });
896
+ let prevRenderedAttributeKeys = new Set(Object.keys(HTMLAttributes));
599
897
  return {
600
898
  dom: listItem,
601
899
  contentDOM: content,
@@ -605,7 +903,32 @@ var TaskItem = Node4.create({
605
903
  }
606
904
  listItem.dataset.checked = updatedNode.attrs.checked;
607
905
  checkbox.checked = updatedNode.attrs.checked;
608
- updateA11Y();
906
+ updateA11Y(updatedNode);
907
+ const extensionAttributes = editor.extensionManager.attributes;
908
+ const newHTMLAttributes = getRenderedAttributes(updatedNode, extensionAttributes);
909
+ const newKeys = new Set(Object.keys(newHTMLAttributes));
910
+ const staticAttrs = this.options.HTMLAttributes;
911
+ prevRenderedAttributeKeys.forEach((key) => {
912
+ if (!newKeys.has(key)) {
913
+ if (key in staticAttrs) {
914
+ listItem.setAttribute(key, staticAttrs[key]);
915
+ } else {
916
+ listItem.removeAttribute(key);
917
+ }
918
+ }
919
+ });
920
+ Object.entries(newHTMLAttributes).forEach(([key, value]) => {
921
+ if (value === null || value === void 0) {
922
+ if (key in staticAttrs) {
923
+ listItem.setAttribute(key, staticAttrs[key]);
924
+ } else {
925
+ listItem.removeAttribute(key);
926
+ }
927
+ } else {
928
+ listItem.setAttribute(key, value);
929
+ }
930
+ });
931
+ prevRenderedAttributeKeys = newKeys;
609
932
  return true;
610
933
  }
611
934
  };
@@ -625,7 +948,7 @@ var TaskItem = Node4.create({
625
948
  });
626
949
 
627
950
  // src/task-list/task-list.ts
628
- import { mergeAttributes as mergeAttributes5, Node as Node5 } from "@tiptap/core";
951
+ import { mergeAttributes as mergeAttributes5, Node as Node5, parseIndentedBlocks } from "@tiptap/core";
629
952
  var TaskList = Node5.create({
630
953
  name: "taskList",
631
954
  addOptions() {
@@ -649,6 +972,97 @@ var TaskList = Node5.create({
649
972
  renderHTML({ HTMLAttributes }) {
650
973
  return ["ul", mergeAttributes5(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name }), 0];
651
974
  },
975
+ parseMarkdown: (token, h) => {
976
+ return h.createNode("taskList", {}, h.parseChildren(token.items || []));
977
+ },
978
+ renderMarkdown: (node, h) => {
979
+ if (!node.content) {
980
+ return "";
981
+ }
982
+ return h.renderChildren(node.content, "\n");
983
+ },
984
+ markdownTokenizer: {
985
+ name: "taskList",
986
+ level: "block",
987
+ start(src) {
988
+ var _a;
989
+ const index = (_a = src.match(/^\s*[-+*]\s+\[([ xX])\]\s+/)) == null ? void 0 : _a.index;
990
+ return index !== void 0 ? index : -1;
991
+ },
992
+ tokenize(src, tokens, lexer) {
993
+ const parseTaskListContent = (content) => {
994
+ const nestedResult = parseIndentedBlocks(
995
+ content,
996
+ {
997
+ itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
998
+ extractItemData: (match) => ({
999
+ indentLevel: match[1].length,
1000
+ mainContent: match[4],
1001
+ checked: match[3].toLowerCase() === "x"
1002
+ }),
1003
+ createToken: (data, nestedTokens) => ({
1004
+ type: "taskItem",
1005
+ raw: "",
1006
+ mainContent: data.mainContent,
1007
+ indentLevel: data.indentLevel,
1008
+ checked: data.checked,
1009
+ text: data.mainContent,
1010
+ tokens: lexer.inlineTokens(data.mainContent),
1011
+ nestedTokens
1012
+ }),
1013
+ // Allow recursive nesting
1014
+ customNestedParser: parseTaskListContent
1015
+ },
1016
+ lexer
1017
+ );
1018
+ if (nestedResult) {
1019
+ return [
1020
+ {
1021
+ type: "taskList",
1022
+ raw: nestedResult.raw,
1023
+ items: nestedResult.items
1024
+ }
1025
+ ];
1026
+ }
1027
+ return lexer.blockTokens(content);
1028
+ };
1029
+ const result = parseIndentedBlocks(
1030
+ src,
1031
+ {
1032
+ itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
1033
+ extractItemData: (match) => ({
1034
+ indentLevel: match[1].length,
1035
+ mainContent: match[4],
1036
+ checked: match[3].toLowerCase() === "x"
1037
+ }),
1038
+ createToken: (data, nestedTokens) => ({
1039
+ type: "taskItem",
1040
+ raw: "",
1041
+ mainContent: data.mainContent,
1042
+ indentLevel: data.indentLevel,
1043
+ checked: data.checked,
1044
+ text: data.mainContent,
1045
+ tokens: lexer.inlineTokens(data.mainContent),
1046
+ nestedTokens
1047
+ }),
1048
+ // Use the recursive parser for nested content
1049
+ customNestedParser: parseTaskListContent
1050
+ },
1051
+ lexer
1052
+ );
1053
+ if (!result) {
1054
+ return void 0;
1055
+ }
1056
+ return {
1057
+ type: "taskList",
1058
+ raw: result.raw,
1059
+ items: result.items
1060
+ };
1061
+ }
1062
+ },
1063
+ markdownOptions: {
1064
+ indentsContent: true
1065
+ },
652
1066
  addCommands() {
653
1067
  return {
654
1068
  toggleTaskList: () => ({ commands }) => {