@tiptap/extension-list 3.26.0 → 3.27.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.cjs CHANGED
@@ -24,13 +24,23 @@ __export(index_exports, {
24
24
  ListItem: () => ListItem,
25
25
  ListKeymap: () => ListKeymap,
26
26
  ListKit: () => ListKit,
27
+ ORDERED_LIST_MARKER_PATTERN: () => ORDERED_LIST_MARKER_PATTERN,
27
28
  OrderedList: () => OrderedList,
28
29
  TaskItem: () => TaskItem,
29
30
  TaskList: () => TaskList,
31
+ areOrderedListMarkersSequential: () => areOrderedListMarkersSequential,
32
+ buildOrderedListAttrsFromMarker: () => buildOrderedListAttrsFromMarker,
30
33
  bulletListInputRegex: () => bulletListInputRegex,
34
+ detectMarkerType: () => detectMarkerType,
35
+ getListMarker: () => getListMarker,
31
36
  inputRegex: () => inputRegex,
32
37
  listHelpers: () => listHelpers_exports,
33
- orderedListInputRegex: () => orderedListInputRegex
38
+ markerToStart: () => markerToStart,
39
+ orderedListInputRegex: () => orderedListInputRegex,
40
+ parseListMarker: () => parseListMarker,
41
+ parsePlainTextOrderedListPaste: () => parsePlainTextOrderedListPaste,
42
+ toRoman: () => toRoman,
43
+ toRomanUpper: () => toRomanUpper
34
44
  });
35
45
  module.exports = __toCommonJS(index_exports);
36
46
 
@@ -224,6 +234,194 @@ var createBranchingListDeleteKeymap = (itemName, wrapperNames) => {
224
234
  });
225
235
  };
226
236
 
237
+ // src/ordered-list/roman.ts
238
+ var ROMAN_NUMERALS = [
239
+ [1e3, "m"],
240
+ [900, "cm"],
241
+ [500, "d"],
242
+ [400, "cd"],
243
+ [100, "c"],
244
+ [90, "xc"],
245
+ [50, "l"],
246
+ [40, "xl"],
247
+ [10, "x"],
248
+ [9, "ix"],
249
+ [5, "v"],
250
+ [4, "iv"],
251
+ [1, "i"]
252
+ ];
253
+ var ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz";
254
+ var ORDERED_LIST_ALPHA_MARKER_PATTERN = "[a-zA-Z]{1,2}";
255
+ var ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${ORDERED_LIST_ALPHA_MARKER_PATTERN}`;
256
+ function toRoman(num) {
257
+ let remaining = num;
258
+ let result = "";
259
+ for (const [value, numeral] of ROMAN_NUMERALS) {
260
+ while (remaining >= value) {
261
+ result += numeral;
262
+ remaining -= value;
263
+ }
264
+ }
265
+ return result;
266
+ }
267
+ function toRomanUpper(num) {
268
+ return toRoman(num).toUpperCase();
269
+ }
270
+ function fromRoman(roman) {
271
+ const lower = roman.toLowerCase();
272
+ let index = 0;
273
+ let result = 0;
274
+ while (index < lower.length) {
275
+ let matched = false;
276
+ for (const [value, numeral] of ROMAN_NUMERALS) {
277
+ if (lower.startsWith(numeral, index)) {
278
+ result += value;
279
+ index += numeral.length;
280
+ matched = true;
281
+ break;
282
+ }
283
+ }
284
+ if (!matched) {
285
+ return 0;
286
+ }
287
+ }
288
+ return result;
289
+ }
290
+ function isValidRoman(marker) {
291
+ if (!/^[ivxlcdmIVXLCDM]+$/.test(marker)) {
292
+ return false;
293
+ }
294
+ const value = fromRoman(marker);
295
+ if (value <= 0) {
296
+ return false;
297
+ }
298
+ const expected = marker === marker.toLowerCase() ? toRoman(value) : toRomanUpper(value);
299
+ return expected === marker;
300
+ }
301
+ function fromAlpha(marker) {
302
+ const lower = marker.toLowerCase();
303
+ if (lower.length === 1) {
304
+ return lower.charCodeAt(0) - "a".charCodeAt(0) + 1;
305
+ }
306
+ if (lower.length === 2) {
307
+ const first = lower.charCodeAt(0) - "a".charCodeAt(0);
308
+ const second = lower.charCodeAt(1) - "a".charCodeAt(0);
309
+ return (first + 1) * 26 + second + 1;
310
+ }
311
+ return 0;
312
+ }
313
+ function toRomanAlpha(num) {
314
+ if (num <= 26) {
315
+ return ALPHA_NUMERALS[num - 1];
316
+ }
317
+ const first = Math.floor((num - 1) / 26) - 1;
318
+ const second = (num - 1) % 26;
319
+ if (first < 0) {
320
+ return ALPHA_NUMERALS[second];
321
+ }
322
+ return ALPHA_NUMERALS[first] + ALPHA_NUMERALS[second];
323
+ }
324
+ function detectMarkerType(marker) {
325
+ if (!marker || /^\d+$/.test(marker)) {
326
+ return void 0;
327
+ }
328
+ if (isValidRoman(marker)) {
329
+ return marker === marker.toLowerCase() ? "i" : "I";
330
+ }
331
+ if (/^[a-z]{1,2}$/.test(marker)) {
332
+ return "a";
333
+ }
334
+ if (/^[A-Z]{1,2}$/.test(marker)) {
335
+ return "A";
336
+ }
337
+ return void 0;
338
+ }
339
+ function markerToStart(marker) {
340
+ if (/^\d+$/.test(marker)) {
341
+ return parseInt(marker, 10);
342
+ }
343
+ const type = detectMarkerType(marker);
344
+ if (type === "i" || type === "I") {
345
+ return fromRoman(marker);
346
+ }
347
+ if (type === "a" || type === "A") {
348
+ const start = fromAlpha(marker);
349
+ return start > 0 ? start : 1;
350
+ }
351
+ const parsed = parseInt(marker, 10);
352
+ return Number.isNaN(parsed) ? 1 : parsed;
353
+ }
354
+ function startToMarker(type, start) {
355
+ if (type === "numeric") {
356
+ return String(start);
357
+ }
358
+ switch (type) {
359
+ case "a":
360
+ return toRomanAlpha(start);
361
+ case "A":
362
+ return toRomanAlpha(start).toUpperCase();
363
+ case "i":
364
+ return toRoman(start);
365
+ case "I":
366
+ return toRomanUpper(start);
367
+ default:
368
+ return String(start);
369
+ }
370
+ }
371
+ function areOrderedListMarkersSequential(markers) {
372
+ var _a;
373
+ if (markers.length === 0) {
374
+ return false;
375
+ }
376
+ const firstType = (_a = detectMarkerType(markers[0])) != null ? _a : "numeric";
377
+ const firstStart = markerToStart(markers[0]);
378
+ if (firstStart < 1) {
379
+ return false;
380
+ }
381
+ for (let i = 0; i < markers.length; i++) {
382
+ const expected = startToMarker(firstType, firstStart + i);
383
+ if (markers[i] !== expected) {
384
+ return false;
385
+ }
386
+ }
387
+ return true;
388
+ }
389
+ function parseListMarker(marker) {
390
+ return {
391
+ type: detectMarkerType(marker),
392
+ start: markerToStart(marker)
393
+ };
394
+ }
395
+ function buildOrderedListAttrsFromMarker(marker) {
396
+ const { type, start } = parseListMarker(marker);
397
+ const attrs = {};
398
+ if (type) {
399
+ attrs.type = type;
400
+ }
401
+ if (start !== 1) {
402
+ attrs.start = start;
403
+ }
404
+ return attrs;
405
+ }
406
+ function getListMarker(type, index, separator = ". ") {
407
+ const position = index + 1;
408
+ if (!type || type === "1") {
409
+ return `${position}${separator}`;
410
+ }
411
+ switch (type) {
412
+ case "a":
413
+ return `${toRomanAlpha(position)}${separator}`;
414
+ case "A":
415
+ return `${toRomanAlpha(position).toUpperCase()}${separator}`;
416
+ case "i":
417
+ return `${toRoman(position)}${separator}`;
418
+ case "I":
419
+ return `${toRomanUpper(position)}${separator}`;
420
+ default:
421
+ return `${position}${separator}`;
422
+ }
423
+ }
424
+
227
425
  // src/item/list-item.ts
228
426
  function isSameLineOrderedListToken(token) {
229
427
  var _a, _b;
@@ -326,13 +524,15 @@ var ListItem = import_core3.Node.create({
326
524
  node,
327
525
  h,
328
526
  (context) => {
329
- var _a, _b;
527
+ var _a, _b, _c, _d;
330
528
  if (context.parentType === "bulletList") {
331
529
  return "- ";
332
530
  }
333
531
  if (context.parentType === "orderedList") {
334
532
  const start = ((_b = (_a = context.meta) == null ? void 0 : _a.parentAttrs) == null ? void 0 : _b.start) || 1;
335
- return `${start + context.index}. `;
533
+ const type = (_d = (_c = context.meta) == null ? void 0 : _c.parentAttrs) == null ? void 0 : _d.type;
534
+ const index = start - 1 + (context.index || 0);
535
+ return getListMarker(type, index, ". ");
336
536
  }
337
537
  return "- ";
338
538
  },
@@ -626,17 +826,25 @@ var ListKeymap = import_core9.Extension.create({
626
826
  var import_core13 = require("@tiptap/core");
627
827
 
628
828
  // src/ordered-list/ordered-list.ts
829
+ var import_state = require("@tiptap/pm/state");
629
830
  var import_core10 = require("@tiptap/core");
630
831
 
631
832
  // src/ordered-list/utils.ts
632
- var ORDERED_LIST_ITEM_REGEX = /^(\s*)(\d+)\.\s+(.*)$/;
833
+ var ORDERED_LIST_ITEM_REGEX = new RegExp(
834
+ `^(\\s*)(${ORDERED_LIST_MARKER_PATTERN})([.)])\\s+(.*)$`
835
+ );
836
+ var ORDERED_LIST_LINE_START_REGEX = new RegExp(
837
+ `^(\\s*)(${ORDERED_LIST_MARKER_PATTERN})([.)])\\s+`
838
+ );
633
839
  var INDENTED_LINE_REGEX = /^\s/;
840
+ function isOrderedListMarkerLine(line) {
841
+ return ORDERED_LIST_ITEM_REGEX.test(line.trimStart());
842
+ }
634
843
  function isBlockContentLine(line) {
635
844
  const trimmedLine = line.trimStart();
636
845
  return (
637
846
  // oxlint-disable-next-line prefer-string-starts-ends-with
638
- /^[-+*]\s+/.test(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with
639
- /^\d+\.\s+/.test(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with
847
+ /^[-+*]\s+/.test(trimmedLine) || isOrderedListMarkerLine(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with
640
848
  /^>\s?/.test(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with
641
849
  /^```/.test(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with
642
850
  /^~~~/.test(trimmedLine)
@@ -678,8 +886,11 @@ function collectOrderedListItems(lines) {
678
886
  if (!match) {
679
887
  break;
680
888
  }
681
- const [, indent, number, content] = match;
889
+ const [, indent, marker, _separator, content] = match;
682
890
  const indentLevel = indent.length;
891
+ const number = parseInt(marker, 10);
892
+ const markerType = isNaN(number) ? detectMarkerType(marker) : void 0;
893
+ const itemNumber = isNaN(number) ? markerToStart(marker) : number;
683
894
  const itemContentLines = [content];
684
895
  let nextLineIndex = currentLineIndex + 1;
685
896
  const itemLines = [line];
@@ -710,7 +921,8 @@ function collectOrderedListItems(lines) {
710
921
  }
711
922
  listItems.push({
712
923
  indent: indentLevel,
713
- number: parseInt(number, 10),
924
+ number: itemNumber,
925
+ type: markerType,
714
926
  content: itemContentLines.join("\n").trim(),
715
927
  contentLines: itemContentLines,
716
928
  raw: itemLines.join("\n")
@@ -720,6 +932,44 @@ function collectOrderedListItems(lines) {
720
932
  }
721
933
  return [listItems, consumed];
722
934
  }
935
+ var PLAIN_TEXT_ORDERED_LIST_LINE_REGEX = new RegExp(
936
+ `^(${ORDERED_LIST_MARKER_PATTERN})([.)])\\s+(.+)$`
937
+ );
938
+ function parsePlainTextOrderedListPaste(text) {
939
+ const lines = text.split("\n").filter((l) => l.trim().length > 0);
940
+ if (lines.length === 0) {
941
+ return null;
942
+ }
943
+ const parsedItems = [];
944
+ for (const line of lines) {
945
+ const match = line.trim().match(PLAIN_TEXT_ORDERED_LIST_LINE_REGEX);
946
+ if (!match) {
947
+ return null;
948
+ }
949
+ parsedItems.push({
950
+ marker: match[1],
951
+ content: match[3]
952
+ });
953
+ }
954
+ const markers = parsedItems.map((item) => item.marker);
955
+ if (!areOrderedListMarkersSequential(markers)) {
956
+ return null;
957
+ }
958
+ const attrs = buildOrderedListAttrsFromMarker(parsedItems[0].marker);
959
+ return {
960
+ type: "orderedList",
961
+ attrs,
962
+ content: parsedItems.map((item) => ({
963
+ type: "listItem",
964
+ content: [
965
+ {
966
+ type: "paragraph",
967
+ content: [{ type: "text", text: item.content }]
968
+ }
969
+ ]
970
+ }))
971
+ };
972
+ }
723
973
  function buildNestedStructure(items, baseIndent, lexer) {
724
974
  const result = [];
725
975
  let currentIndex = 0;
@@ -754,6 +1004,7 @@ function buildNestedStructure(items, baseIndent, lexer) {
754
1004
  type: "list",
755
1005
  ordered: true,
756
1006
  start: nestedItems[0].number,
1007
+ typeMarker: nestedItems[0].type,
757
1008
  items: nestedListItems,
758
1009
  raw: nestedItems.map((nestedItem) => nestedItem.raw).join("\n")
759
1010
  });
@@ -805,6 +1056,27 @@ function parseListItems(items, helpers) {
805
1056
  var ListItemName2 = "listItem";
806
1057
  var TextStyleName2 = "textStyle";
807
1058
  var orderedListInputRegex = /^(\d+)\.\s$/;
1059
+ function cssListStyleTypeToHtmlType(style) {
1060
+ const match = style.match(/list-style-type\s*:\s*([^;]+)/i);
1061
+ if (!match) {
1062
+ return null;
1063
+ }
1064
+ const cssValue = match[1].trim().toLowerCase();
1065
+ switch (cssValue) {
1066
+ case "upper-roman":
1067
+ return "I";
1068
+ case "lower-roman":
1069
+ return "i";
1070
+ case "upper-alpha":
1071
+ case "upper-latin":
1072
+ return "A";
1073
+ case "lower-alpha":
1074
+ case "lower-latin":
1075
+ return "a";
1076
+ default:
1077
+ return null;
1078
+ }
1079
+ }
808
1080
  var OrderedList = import_core10.Node.create({
809
1081
  name: "orderedList",
810
1082
  addOptions() {
@@ -829,7 +1101,30 @@ var OrderedList = import_core10.Node.create({
829
1101
  },
830
1102
  type: {
831
1103
  default: null,
832
- parseHTML: (element) => element.getAttribute("type")
1104
+ parseHTML: (element) => {
1105
+ const htmlType = element.getAttribute("type");
1106
+ if (htmlType) {
1107
+ return htmlType;
1108
+ }
1109
+ const style = element.getAttribute("style");
1110
+ if (style) {
1111
+ const mappedFromOl = cssListStyleTypeToHtmlType(style);
1112
+ if (mappedFromOl) {
1113
+ return mappedFromOl;
1114
+ }
1115
+ }
1116
+ const firstLi = element.querySelector("li");
1117
+ if (firstLi) {
1118
+ const liStyle = firstLi.getAttribute("style");
1119
+ if (liStyle) {
1120
+ const mappedFromLi = cssListStyleTypeToHtmlType(liStyle);
1121
+ if (mappedFromLi) {
1122
+ return mappedFromLi;
1123
+ }
1124
+ }
1125
+ }
1126
+ return null;
1127
+ }
833
1128
  }
834
1129
  };
835
1130
  },
@@ -841,8 +1136,15 @@ var OrderedList = import_core10.Node.create({
841
1136
  ];
842
1137
  },
843
1138
  renderHTML({ HTMLAttributes }) {
844
- const { start, ...attributesWithoutStart } = HTMLAttributes;
845
- return start === 1 ? ["ol", (0, import_core10.mergeAttributes)(this.options.HTMLAttributes, attributesWithoutStart), 0] : ["ol", (0, import_core10.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes), 0];
1139
+ const { start, type, ...attributesWithoutType } = HTMLAttributes;
1140
+ const attrs = (0, import_core10.mergeAttributes)(this.options.HTMLAttributes, attributesWithoutType);
1141
+ if (start !== 1) {
1142
+ attrs.start = start;
1143
+ }
1144
+ if (type && type !== "1") {
1145
+ attrs.type = type;
1146
+ }
1147
+ return ["ol", attrs, 0];
846
1148
  },
847
1149
  markdownTokenName: "list",
848
1150
  parseMarkdown: (token, helpers) => {
@@ -850,11 +1152,19 @@ var OrderedList = import_core10.Node.create({
850
1152
  return [];
851
1153
  }
852
1154
  const startValue = token.start || 1;
1155
+ const typeValue = token.typeMarker;
853
1156
  const content = token.items ? parseListItems(token.items, helpers) : [];
1157
+ const attrs = {};
854
1158
  if (startValue !== 1) {
1159
+ attrs.start = startValue;
1160
+ }
1161
+ if (typeValue) {
1162
+ attrs.type = typeValue;
1163
+ }
1164
+ if (Object.keys(attrs).length > 0) {
855
1165
  return {
856
1166
  type: "orderedList",
857
- attrs: { start: startValue },
1167
+ attrs,
858
1168
  content
859
1169
  };
860
1170
  }
@@ -873,12 +1183,12 @@ var OrderedList = import_core10.Node.create({
873
1183
  name: "orderedList",
874
1184
  level: "block",
875
1185
  start: (src) => {
876
- const match = src.match(/^(\s*)(\d+)\.\s+/);
1186
+ const match = src.match(ORDERED_LIST_LINE_START_REGEX);
877
1187
  const index = match == null ? void 0 : match.index;
878
1188
  return index !== void 0 ? index : -1;
879
1189
  },
880
1190
  tokenize: (src, _tokens, lexer) => {
881
- var _a;
1191
+ var _a, _b;
882
1192
  const lines = src.split("\n");
883
1193
  const [listItems, consumed] = collectOrderedListItems(lines);
884
1194
  if (listItems.length === 0) {
@@ -889,10 +1199,12 @@ var OrderedList = import_core10.Node.create({
889
1199
  return void 0;
890
1200
  }
891
1201
  const startValue = ((_a = listItems[0]) == null ? void 0 : _a.number) || 1;
1202
+ const typeMarker = (_b = listItems[0]) == null ? void 0 : _b.type;
892
1203
  return {
893
1204
  type: "list",
894
1205
  ordered: true,
895
1206
  start: startValue,
1207
+ typeMarker,
896
1208
  items,
897
1209
  raw: lines.slice(0, consumed).join("\n")
898
1210
  };
@@ -916,12 +1228,47 @@ var OrderedList = import_core10.Node.create({
916
1228
  "Mod-Shift-7": () => this.editor.commands.toggleOrderedList()
917
1229
  };
918
1230
  },
1231
+ addProseMirrorPlugins() {
1232
+ return [
1233
+ new import_state.Plugin({
1234
+ props: {
1235
+ handlePaste: (view, event) => {
1236
+ var _a, _b;
1237
+ const html = (_a = event.clipboardData) == null ? void 0 : _a.getData("text/html");
1238
+ if (html == null ? void 0 : html.trim()) {
1239
+ return false;
1240
+ }
1241
+ const text = (_b = event.clipboardData) == null ? void 0 : _b.getData("text/plain");
1242
+ if (!text) {
1243
+ return false;
1244
+ }
1245
+ const orderedListContent = parsePlainTextOrderedListPaste(text);
1246
+ if (!orderedListContent) {
1247
+ return false;
1248
+ }
1249
+ try {
1250
+ const orderedListNode = view.state.schema.nodeFromJSON(orderedListContent);
1251
+ const tr = view.state.tr.replaceSelectionWith(orderedListNode);
1252
+ view.dispatch(tr);
1253
+ return true;
1254
+ } catch {
1255
+ return false;
1256
+ }
1257
+ }
1258
+ }
1259
+ })
1260
+ ];
1261
+ },
919
1262
  addInputRules() {
1263
+ const joinPredicate = (match, node) => {
1264
+ const hasDefaultType = !node.attrs.type || node.attrs.type === "1";
1265
+ return hasDefaultType && node.childCount + node.attrs.start === +match[1];
1266
+ };
920
1267
  let inputRule = (0, import_core10.wrappingInputRule)({
921
1268
  find: orderedListInputRegex,
922
1269
  type: this.type,
923
1270
  getAttributes: (match) => ({ start: +match[1] }),
924
- joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1]
1271
+ joinPredicate
925
1272
  });
926
1273
  if (this.options.keepMarks || this.options.keepAttributes) {
927
1274
  inputRule = (0, import_core10.wrappingInputRule)({
@@ -930,7 +1277,7 @@ var OrderedList = import_core10.Node.create({
930
1277
  keepMarks: this.options.keepMarks,
931
1278
  keepAttributes: this.options.keepAttributes,
932
1279
  getAttributes: (match) => ({ start: +match[1], ...this.editor.getAttributes(TextStyleName2) }),
933
- joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1],
1280
+ joinPredicate,
934
1281
  editor: this.editor
935
1282
  });
936
1283
  }
@@ -1309,12 +1656,22 @@ var ListKit = import_core13.Extension.create({
1309
1656
  ListItem,
1310
1657
  ListKeymap,
1311
1658
  ListKit,
1659
+ ORDERED_LIST_MARKER_PATTERN,
1312
1660
  OrderedList,
1313
1661
  TaskItem,
1314
1662
  TaskList,
1663
+ areOrderedListMarkersSequential,
1664
+ buildOrderedListAttrsFromMarker,
1315
1665
  bulletListInputRegex,
1666
+ detectMarkerType,
1667
+ getListMarker,
1316
1668
  inputRegex,
1317
1669
  listHelpers,
1318
- orderedListInputRegex
1670
+ markerToStart,
1671
+ orderedListInputRegex,
1672
+ parseListMarker,
1673
+ parsePlainTextOrderedListPaste,
1674
+ toRoman,
1675
+ toRomanUpper
1319
1676
  });
1320
1677
  //# sourceMappingURL=index.cjs.map