tiptap-markdown-react 0.19.1 → 1.0.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 (59) hide show
  1. package/README.md +23 -37
  2. package/THIRD_PARTY_LICENSES.md +155 -1
  3. package/dist/{ReportContent-B8jIzhy3.js → ReportContent-w08GFm8v.js} +16 -8
  4. package/dist/{ReportContent-B8jIzhy3.js.map → ReportContent-w08GFm8v.js.map} +1 -1
  5. package/dist/blockDrag/actions.d.ts +30 -0
  6. package/dist/blockDrag/actions.test.d.ts +1 -0
  7. package/dist/blockDrag/listDrop.d.ts +21 -0
  8. package/dist/blockDrag/listDrop.test.d.ts +1 -0
  9. package/dist/blurSelection.dom.test.d.ts +1 -0
  10. package/dist/characterCount.d.ts +1 -0
  11. package/dist/characterCount.dom.test.d.ts +1 -0
  12. package/dist/characterCount.test.d.ts +1 -0
  13. package/dist/chart/createChart.d.ts +0 -3
  14. package/dist/components/BlockDragHandle.d.ts +14 -0
  15. package/dist/components/BlockDragHandle.dom.test.d.ts +1 -0
  16. package/dist/components/CodeBlockView.d.ts +2 -2
  17. package/dist/components/ColorPalette.d.ts +4 -3
  18. package/dist/components/EditorToolbar.d.ts +4 -3
  19. package/dist/components/FindReplaceBar.d.ts +4 -3
  20. package/dist/components/LinkPopover.d.ts +4 -3
  21. package/dist/components/MarkdownPreview.d.ts +4 -1
  22. package/dist/components/MarkdownWysiwygEditor.d.ts +30 -9
  23. package/dist/components/TocPanel.d.ts +4 -3
  24. package/dist/detailsMarkdown.d.ts +4 -0
  25. package/dist/detailsMarkdown.test.d.ts +1 -0
  26. package/dist/emoji/EmojiSuggestion.d.ts +23 -0
  27. package/dist/emoji/emojiCategories.d.ts +52 -0
  28. package/dist/emoji/emojiMarkdown.test.d.ts +1 -0
  29. package/dist/emoji/emojiSuggestion.dom.test.d.ts +1 -0
  30. package/dist/emoji/filterEmoji.d.ts +7 -0
  31. package/dist/emoji/markdownEmoji.d.ts +8 -0
  32. package/dist/{extractToc-CGrBMGvM.js → extractToc-Cr470fBy.js} +659 -5
  33. package/dist/extractToc-Cr470fBy.js.map +1 -0
  34. package/dist/fonts/OFL.txt +95 -0
  35. package/dist/i18n/index.d.ts +4 -0
  36. package/dist/i18n/store.d.ts +16 -0
  37. package/dist/i18n/types.d.ts +19 -0
  38. package/dist/i18n/zh.d.ts +3 -0
  39. package/dist/icons/index.d.ts +5 -0
  40. package/dist/imageUpload.d.ts +0 -3
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.js +1983 -407
  43. package/dist/index.js.map +1 -1
  44. package/dist/labels.d.ts +56 -2
  45. package/dist/numberingFormat.d.ts +67 -0
  46. package/dist/numberingFormat.test.d.ts +1 -0
  47. package/dist/reader.js +2 -2
  48. package/dist/{scrollToTocHeading-B2QmL4wi.js → scrollToTocHeading-kPgsn3cu.js} +2 -2
  49. package/dist/{scrollToTocHeading-B2QmL4wi.js.map → scrollToTocHeading-kPgsn3cu.js.map} +1 -1
  50. package/dist/server.js +4 -4
  51. package/dist/shortcuts/ShortcutPanel.d.ts +4 -4
  52. package/dist/shortcuts/types.d.ts +2 -2
  53. package/dist/slashMenu/SlashMenuExtension.d.ts +0 -3
  54. package/dist/slashMenu/items.d.ts +2 -2
  55. package/dist/style-inline.css +2110 -294
  56. package/dist/style.css +2110 -294
  57. package/dist/typography.test.d.ts +1 -0
  58. package/package.json +19 -3
  59. package/dist/extractToc-CGrBMGvM.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import { Node, mergeAttributes, Mark, Extension } from "@tiptap/core";
2
2
  import Code from "@tiptap/extension-code";
3
3
  import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
4
+ import { DetailsSummary, DetailsContent, Details } from "@tiptap/extension-details";
4
5
  import Heading from "@tiptap/extension-heading";
5
6
  import Highlight from "@tiptap/extension-highlight";
6
7
  import Image from "@tiptap/extension-image";
@@ -15,9 +16,13 @@ import { Color, TextStyle, FontSize } from "@tiptap/extension-text-style";
15
16
  import StarterKit from "@tiptap/starter-kit";
16
17
  import { createLowlight, common } from "lowlight";
17
18
  import { Plugin, PluginKey } from "@tiptap/pm/state";
18
- import { p as parseChartFenceBody, s as serializeChartPayload, n as normalizeChartMarkdown } from "./ReportContent-B8jIzhy3.js";
19
+ import { p as parseChartFenceBody, s as serializeChartPayload, n as normalizeChartMarkdown } from "./ReportContent-w08GFm8v.js";
19
20
  import { InlineMath, BlockMath } from "@tiptap/extension-mathematics";
20
21
  import katex from "katex";
22
+ import { Emoji, shortcodeToEmoji, emojis, emojiToShortcode } from "@tiptap/extension-emoji";
23
+ import emojiRegex from "emoji-regex";
24
+ import { ReplaceStep } from "@tiptap/pm/transform";
25
+ import { OrderedList } from "@tiptap/extension-list";
21
26
  const CitationRef = Node.create({
22
27
  name: "citationRef",
23
28
  group: "inline",
@@ -961,6 +966,626 @@ function paragraphParseMarkdown(token, helpers) {
961
966
  }
962
967
  return helpers.createNode("paragraph", attrs, content);
963
968
  }
969
+ const OPEN_TAG = /^<details(\s[^>]*)?>/i;
970
+ const OPEN_ATTR = /(?:^|\s)open(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?(?=\s|$)/i;
971
+ function tagIsOpen(attr) {
972
+ return attr != null && OPEN_ATTR.test(attr);
973
+ }
974
+ function findClosingDetails(src, from) {
975
+ const re = /<\/?details\b[^>]*>/gi;
976
+ re.lastIndex = from;
977
+ let depth = 1;
978
+ let match;
979
+ while (match = re.exec(src)) {
980
+ if (match[0].startsWith("</")) depth -= 1;
981
+ else depth += 1;
982
+ if (depth === 0) {
983
+ return { closeStart: match.index, rawEnd: re.lastIndex };
984
+ }
985
+ }
986
+ return null;
987
+ }
988
+ function isEmptyParagraph(node) {
989
+ return node.type === "paragraph" && (!node.content || node.content.length === 0);
990
+ }
991
+ function trimBlocks(nodes) {
992
+ const next = [...nodes];
993
+ while (next.length > 1 && isEmptyParagraph(next[next.length - 1])) {
994
+ next.pop();
995
+ }
996
+ if (next.length === 0) next.push({ type: "paragraph" });
997
+ return next;
998
+ }
999
+ const detailsMarkdownTokenizer = {
1000
+ name: "details",
1001
+ level: "block",
1002
+ start(src) {
1003
+ if (!OPEN_TAG.test(src)) return -1;
1004
+ return src.search(/<details\b/i);
1005
+ },
1006
+ tokenize(src, _tokens, lexer) {
1007
+ const opening = OPEN_TAG.exec(src);
1008
+ if (!opening) return void 0;
1009
+ const close = findClosingDetails(src, opening[0].length);
1010
+ if (!close) return void 0;
1011
+ const inner = src.slice(opening[0].length, close.closeStart);
1012
+ const summaryMatch = /^\s*<summary>([\s\S]*?)<\/summary>\s*/i.exec(inner);
1013
+ const summaryRaw = (summaryMatch == null ? void 0 : summaryMatch[1]) ?? "";
1014
+ const bodyRaw = summaryMatch ? inner.slice(summaryMatch[0].length) : inner;
1015
+ const body = bodyRaw.trim();
1016
+ const token = {
1017
+ type: "details",
1018
+ raw: src.slice(0, close.rawEnd),
1019
+ open: tagIsOpen(opening[1]),
1020
+ summaryTokens: lexer.inlineTokens(summaryRaw),
1021
+ tokens: body ? lexer.blockTokens(body) : []
1022
+ };
1023
+ return token;
1024
+ }
1025
+ };
1026
+ function detailsParseMarkdown(token, helpers) {
1027
+ const summary = helpers.parseInline(
1028
+ token.summaryTokens ?? []
1029
+ );
1030
+ const blocks = trimBlocks(helpers.parseChildren(token.tokens ?? []));
1031
+ return helpers.createNode(
1032
+ "details",
1033
+ { open: Boolean(token.open) },
1034
+ [
1035
+ helpers.createNode("detailsSummary", {}, summary),
1036
+ helpers.createNode("detailsContent", {}, blocks)
1037
+ ]
1038
+ );
1039
+ }
1040
+ function detailsRenderMarkdown(node, helpers) {
1041
+ var _a;
1042
+ const children = node.content ?? [];
1043
+ const summary = children.find((child) => child.type === "detailsSummary");
1044
+ const body = children.find((child) => child.type === "detailsContent");
1045
+ const summaryText = summary ? helpers.renderChildren(summary.content ?? []) : "";
1046
+ const bodyText = (body ? helpers.renderChildren(body.content ?? [], "\n\n") : "").replace(/\s+$/, "");
1047
+ const open = ((_a = node.attrs) == null ? void 0 : _a.open) ? " open" : "";
1048
+ return `<details${open}>
1049
+ <summary>${summaryText}</summary>
1050
+
1051
+ ${bodyText}
1052
+
1053
+ </details>`;
1054
+ }
1055
+ const shortcodeName = /* @__PURE__ */ new Map();
1056
+ for (const item of emojis) {
1057
+ if (!shortcodeName.has(item.name)) shortcodeName.set(item.name, item.name);
1058
+ for (const code of item.shortcodes) {
1059
+ if (!shortcodeName.has(code)) shortcodeName.set(code, item.name);
1060
+ }
1061
+ }
1062
+ const shortcodeAtStart = /^:([a-zA-Z0-9_+-]+):/;
1063
+ function findKnownShortcode(src) {
1064
+ const scan = /:([a-zA-Z0-9_+-]+):/g;
1065
+ let match;
1066
+ while (match = scan.exec(src)) {
1067
+ if (shortcodeName.has(match[1])) return match.index;
1068
+ }
1069
+ return -1;
1070
+ }
1071
+ function keepCodeMarkedEmoji(tr) {
1072
+ if (!tr.steps.length) return null;
1073
+ const reverts = [];
1074
+ tr.steps.forEach((step, index) => {
1075
+ if (!(step instanceof ReplaceStep)) return;
1076
+ const inserted = step.slice.content.firstChild;
1077
+ if (!inserted || inserted.type.name !== "emoji") return;
1078
+ const before = tr.docs[index];
1079
+ if (!before) return;
1080
+ const $from = before.resolve(step.from);
1081
+ const direct = before.nodeAt(step.from);
1082
+ const marks = (direct == null ? void 0 : direct.isText) ? direct.marks : $from.marks();
1083
+ if (!marks.some((mark) => mark.type.spec.code)) return;
1084
+ const text = before.textBetween(step.from, step.to);
1085
+ if (!text) return;
1086
+ let pos = step.from;
1087
+ for (let next = index + 1; next < tr.steps.length; next += 1) {
1088
+ pos = tr.steps[next].getMap().map(pos, -1);
1089
+ }
1090
+ reverts.push({ pos, text, marks });
1091
+ });
1092
+ if (reverts.length === 0) return tr;
1093
+ reverts.sort((a, b) => b.pos - a.pos);
1094
+ const schema = tr.doc.type.schema;
1095
+ for (const rev of reverts) {
1096
+ const node = tr.doc.nodeAt(rev.pos);
1097
+ if (!node || node.type.name !== "emoji") continue;
1098
+ tr.replaceWith(
1099
+ rev.pos,
1100
+ rev.pos + node.nodeSize,
1101
+ schema.text(rev.text, rev.marks)
1102
+ );
1103
+ }
1104
+ return tr;
1105
+ }
1106
+ function replaceLoadedUnicodeEmoji(editor) {
1107
+ var _a;
1108
+ const emojiType = editor.schema.nodes.emoji;
1109
+ if (!emojiType) return;
1110
+ const list = ((_a = editor.storage.emoji) == null ? void 0 : _a.emojis) ?? emojis;
1111
+ const { doc } = editor.state;
1112
+ const pattern = emojiRegex();
1113
+ const hits = [];
1114
+ doc.descendants((node, pos) => {
1115
+ if (!node.isText || !node.text) return;
1116
+ if (node.marks.some((mark) => mark.type.spec.code)) return;
1117
+ if (doc.resolve(pos).parent.type.spec.code) return;
1118
+ for (const match of node.text.matchAll(pattern)) {
1119
+ if (match.index === void 0) continue;
1120
+ const name = emojiToShortcode(match[0], list);
1121
+ if (!name) continue;
1122
+ hits.push({
1123
+ from: pos + match.index,
1124
+ to: pos + match.index + match[0].length,
1125
+ name
1126
+ });
1127
+ }
1128
+ });
1129
+ if (hits.length === 0) return;
1130
+ const tr = editor.state.tr;
1131
+ hits.sort((a, b) => b.from - a.from);
1132
+ for (const hit of hits) {
1133
+ tr.replaceWith(hit.from, hit.to, emojiType.create({ name: hit.name }));
1134
+ }
1135
+ tr.setMeta("addToHistory", false);
1136
+ editor.view.dispatch(tr);
1137
+ }
1138
+ const emojiMarkdownTokenizer = {
1139
+ name: "emoji",
1140
+ level: "inline",
1141
+ start(src) {
1142
+ return findKnownShortcode(src);
1143
+ },
1144
+ tokenize(src) {
1145
+ const match = shortcodeAtStart.exec(src);
1146
+ if (!match) return void 0;
1147
+ const name = shortcodeName.get(match[1]);
1148
+ if (!name || !shortcodeToEmoji(name, emojis)) return void 0;
1149
+ return {
1150
+ type: "emoji",
1151
+ raw: match[0],
1152
+ name
1153
+ };
1154
+ }
1155
+ };
1156
+ const MarkdownEmoji = Emoji.extend({
1157
+ markdownTokenizer: emojiMarkdownTokenizer,
1158
+ parseMarkdown(token, helpers) {
1159
+ const typed = typeof token.name === "string" ? token.name : "";
1160
+ const name = shortcodeName.get(typed);
1161
+ if (!name) return helpers.createTextNode(token.raw ?? "");
1162
+ return helpers.createNode("emoji", { name });
1163
+ },
1164
+ /**
1165
+ * 初始文档不经过 appendTransaction。mount 在视图建好后同步发出,
1166
+ * 这时把正文里的 Unicode 表情收成节点(代码块和行内 code 除外)。
1167
+ * 不进撤销历史。之后的键入 / 粘贴仍走官方插件。
1168
+ */
1169
+ onBeforeCreate() {
1170
+ this.editor.on("mount", () => {
1171
+ replaceLoadedUnicodeEmoji(this.editor);
1172
+ });
1173
+ },
1174
+ addProseMirrorPlugins() {
1175
+ var _a;
1176
+ const plugins = ((_a = this.parent) == null ? void 0 : _a.call(this)) ?? [];
1177
+ return plugins.map((plugin) => {
1178
+ const specKey = plugin.spec.key;
1179
+ const keyName = specKey && typeof specKey === "object" && "key" in specKey ? String(specKey.key) : "";
1180
+ const append = plugin.spec.appendTransaction;
1181
+ if (keyName !== "emoji$" || typeof append !== "function") return plugin;
1182
+ return new Plugin({
1183
+ key: new PluginKey("markdownEmoji"),
1184
+ props: plugin.props,
1185
+ appendTransaction(transactions, oldState, newState) {
1186
+ const produced = append.call(plugin, transactions, oldState, newState);
1187
+ if (!produced) return null;
1188
+ return keepCodeMarkedEmoji(produced);
1189
+ }
1190
+ });
1191
+ });
1192
+ }
1193
+ });
1194
+ const NUMBERING_FORMAT_IDS = [
1195
+ "decimal",
1196
+ "decimal-paren",
1197
+ "decimal-nested",
1198
+ "upper-alpha",
1199
+ "lower-alpha",
1200
+ "upper-roman",
1201
+ "lower-roman",
1202
+ "decimal-zero"
1203
+ ];
1204
+ const NUMBERING_FORMAT_LABELS = {
1205
+ decimal: "1. 2. 3.",
1206
+ "decimal-paren": "1) 2) 3)",
1207
+ "decimal-nested": "1. 1.1. 1.1.1.",
1208
+ "upper-alpha": "A. B. C.",
1209
+ "lower-alpha": "a. b. c.",
1210
+ "upper-roman": "I. II. III.",
1211
+ "lower-roman": "i. ii. iii.",
1212
+ "decimal-zero": "01. 02. 03."
1213
+ };
1214
+ const NUMBERING_FORMAT_PREVIEWS = {
1215
+ decimal: [
1216
+ { depth: 1, marker: "1." },
1217
+ { depth: 2, marker: "a." },
1218
+ { depth: 2, marker: "b." },
1219
+ { depth: 3, marker: "i." },
1220
+ { depth: 1, marker: "2." }
1221
+ ],
1222
+ "decimal-paren": [
1223
+ { depth: 1, marker: "1)" },
1224
+ { depth: 2, marker: "a)" },
1225
+ { depth: 2, marker: "b)" },
1226
+ { depth: 3, marker: "i)" },
1227
+ { depth: 1, marker: "2)" }
1228
+ ],
1229
+ "decimal-nested": [
1230
+ { depth: 1, marker: "1." },
1231
+ { depth: 2, marker: "1.1." },
1232
+ { depth: 2, marker: "1.2." },
1233
+ { depth: 3, marker: "1.2.1." },
1234
+ { depth: 1, marker: "2." }
1235
+ ],
1236
+ "upper-alpha": [
1237
+ { depth: 1, marker: "A." },
1238
+ { depth: 2, marker: "a." },
1239
+ { depth: 2, marker: "b." },
1240
+ { depth: 3, marker: "i." },
1241
+ { depth: 1, marker: "B." }
1242
+ ],
1243
+ "lower-alpha": [
1244
+ { depth: 1, marker: "a." },
1245
+ { depth: 2, marker: "1." },
1246
+ { depth: 2, marker: "2." },
1247
+ { depth: 3, marker: "i." },
1248
+ { depth: 1, marker: "b." }
1249
+ ],
1250
+ "upper-roman": [
1251
+ { depth: 1, marker: "I." },
1252
+ { depth: 2, marker: "A." },
1253
+ { depth: 2, marker: "B." },
1254
+ { depth: 3, marker: "1." },
1255
+ { depth: 1, marker: "II." }
1256
+ ],
1257
+ "lower-roman": [
1258
+ { depth: 1, marker: "i." },
1259
+ { depth: 2, marker: "1." },
1260
+ { depth: 2, marker: "2." },
1261
+ { depth: 3, marker: "a." },
1262
+ { depth: 1, marker: "ii." }
1263
+ ],
1264
+ "decimal-zero": [
1265
+ { depth: 1, marker: "01." },
1266
+ { depth: 2, marker: "a." },
1267
+ { depth: 2, marker: "b." },
1268
+ { depth: 3, marker: "i." },
1269
+ { depth: 1, marker: "02." }
1270
+ ]
1271
+ };
1272
+ const NUMBERING_LEVELS = {
1273
+ decimal: [
1274
+ { style: "decimal", template: "self", suffix: "." },
1275
+ { style: "lower-alpha", template: "self", suffix: "." },
1276
+ { style: "lower-roman", template: "self", suffix: "." }
1277
+ ],
1278
+ "decimal-paren": [
1279
+ { style: "decimal", template: "self", suffix: ")" },
1280
+ { style: "lower-alpha", template: "self", suffix: ")" },
1281
+ { style: "lower-roman", template: "self", suffix: ")" }
1282
+ ],
1283
+ "decimal-nested": [
1284
+ { style: "decimal", template: "chain", suffix: "." },
1285
+ { style: "decimal", template: "chain", suffix: "." },
1286
+ { style: "decimal", template: "chain", suffix: "." }
1287
+ ],
1288
+ "upper-alpha": [
1289
+ { style: "upper-alpha", template: "self", suffix: "." },
1290
+ { style: "lower-alpha", template: "self", suffix: "." },
1291
+ { style: "lower-roman", template: "self", suffix: "." }
1292
+ ],
1293
+ "lower-alpha": [
1294
+ { style: "lower-alpha", template: "self", suffix: "." },
1295
+ { style: "decimal", template: "self", suffix: "." },
1296
+ { style: "lower-roman", template: "self", suffix: "." }
1297
+ ],
1298
+ "upper-roman": [
1299
+ { style: "upper-roman", template: "self", suffix: "." },
1300
+ { style: "upper-alpha", template: "self", suffix: "." },
1301
+ { style: "decimal", template: "self", suffix: "." }
1302
+ ],
1303
+ "lower-roman": [
1304
+ { style: "lower-roman", template: "self", suffix: "." },
1305
+ { style: "decimal", template: "self", suffix: "." },
1306
+ { style: "lower-alpha", template: "self", suffix: "." }
1307
+ ],
1308
+ "decimal-zero": [
1309
+ { style: "decimal-leading-zero", template: "self", suffix: "." },
1310
+ { style: "lower-alpha", template: "self", suffix: "." },
1311
+ { style: "lower-roman", template: "self", suffix: "." }
1312
+ ]
1313
+ };
1314
+ const NUMBERING_DEPTHS = 6;
1315
+ const OTHER_LISTS = /* @__PURE__ */ new Set(["bulletList", "taskList"]);
1316
+ function isNumberingFormat(value) {
1317
+ return typeof value === "string" && NUMBERING_FORMAT_IDS.includes(value);
1318
+ }
1319
+ function counterName(format, depth) {
1320
+ return `tmr-${format}-${depth}`;
1321
+ }
1322
+ function counterFn(format, depth) {
1323
+ const level = NUMBERING_LEVELS[format][depth % NUMBERING_LEVELS[format].length];
1324
+ const name = counterName(format, depth);
1325
+ if (!level || level.style === "decimal") return `counter(${name})`;
1326
+ return `counter(${name}, ${level.style})`;
1327
+ }
1328
+ function numberingMarkerContent(format, depth) {
1329
+ const levels = NUMBERING_LEVELS[format];
1330
+ const level = levels[depth % levels.length];
1331
+ if (!level) return 'counter(tmr-decimal-0) "."';
1332
+ const suffix = level.suffix === ")" ? '")"' : '"."';
1333
+ if (level.template === "chain") {
1334
+ const parts = [];
1335
+ for (let i = 0; i <= depth; i += 1) {
1336
+ parts.push(counterFn(format, i), suffix);
1337
+ }
1338
+ return parts.join(" ");
1339
+ }
1340
+ return `${counterFn(format, depth)} ${suffix}`;
1341
+ }
1342
+ function olSelector(format, depth) {
1343
+ let selector = `ol[data-numbering-format='${format}']`;
1344
+ for (let i = 0; i < depth; i += 1) {
1345
+ selector += " > li > ol:not([data-numbering-format])";
1346
+ }
1347
+ return selector;
1348
+ }
1349
+ function buildNumberingFormatCss() {
1350
+ const rules = [
1351
+ `.editorContent :global(ol[data-numbering-format] > li > p),
1352
+ .editorContent :global(ol[data-numbering-format] ol > li > p) {
1353
+ margin-top: 0;
1354
+ }
1355
+ /* li 是 grid,嵌套 ol 的 0.7em 外边距不会和上一段塌缩,上下层会叠成约 1.4em。
1356
+ 清掉后只留段落自己的 0.7em,和普通列表一致。 */
1357
+ .editorContent :global(ol[data-numbering-format] ol) {
1358
+ margin-top: 0;
1359
+ margin-bottom: 0;
1360
+ }`
1361
+ ];
1362
+ for (const format of NUMBERING_FORMAT_IDS) {
1363
+ for (let depth = 0; depth < NUMBERING_DEPTHS; depth += 1) {
1364
+ const ol = olSelector(format, depth);
1365
+ const name = counterName(format, depth);
1366
+ rules.push(
1367
+ `.editorContent :global(${ol}) {
1368
+ list-style: none;
1369
+ padding-left: 0;
1370
+ counter-reset: ${name} 0;
1371
+ }
1372
+ .editorContent :global(${ol} > li) {
1373
+ display: grid;
1374
+ grid-template-columns: auto 1fr;
1375
+ column-gap: 0.45em;
1376
+ align-items: baseline;
1377
+ counter-increment: ${name};
1378
+ }
1379
+ .editorContent :global(${ol} > li::marker) {
1380
+ content: none;
1381
+ }
1382
+ .editorContent :global(${ol} > li::before) {
1383
+ content: ${numberingMarkerContent(format, depth)};
1384
+ grid-column: 1;
1385
+ grid-row: 1;
1386
+ white-space: nowrap;
1387
+ }
1388
+ .editorContent :global(${ol} > li > *) {
1389
+ grid-column: 2;
1390
+ min-width: 0;
1391
+ }`
1392
+ );
1393
+ }
1394
+ }
1395
+ return rules.join("\n\n");
1396
+ }
1397
+ buildNumberingFormatCss();
1398
+ const parentTokenizer = OrderedList.config.markdownTokenizer;
1399
+ const HTML_LIST_RE = /^<ol data-numbering-format="([a-z0-9-]+)">\n+([\s\S]*?)\n+<\/ol>(?:\n+|$)/;
1400
+ const numberingMarkdownTokenizer = {
1401
+ name: "orderedList",
1402
+ level: "block",
1403
+ start(src) {
1404
+ const match = /<ol data-numbering-format="[a-z0-9-]+">/.exec(src);
1405
+ return match ? match.index : -1;
1406
+ },
1407
+ tokenize(src, tokens, lexer) {
1408
+ const match = HTML_LIST_RE.exec(src);
1409
+ if (match && isNumberingFormat(match[1]) && parentTokenizer) {
1410
+ const inner = match[2] ?? "";
1411
+ const parsed = parentTokenizer.tokenize(
1412
+ inner.endsWith("\n") ? inner : `${inner}
1413
+ `,
1414
+ tokens,
1415
+ lexer
1416
+ );
1417
+ if (parsed == null ? void 0 : parsed.type) {
1418
+ return {
1419
+ ...parsed,
1420
+ numberingFormat: match[1],
1421
+ raw: match[0]
1422
+ };
1423
+ }
1424
+ }
1425
+ return parentTokenizer == null ? void 0 : parentTokenizer.tokenize(src, tokens, lexer);
1426
+ }
1427
+ };
1428
+ function numberingListDepth($pos) {
1429
+ let target = null;
1430
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
1431
+ const name = $pos.node(depth).type.name;
1432
+ if (OTHER_LISTS.has(name)) break;
1433
+ if (name === "orderedList") target = depth;
1434
+ }
1435
+ return target;
1436
+ }
1437
+ function hasOrderedListAncestor(doc, pos) {
1438
+ const $pos = doc.resolve(pos);
1439
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
1440
+ const name = $pos.node(depth).type.name;
1441
+ if (OTHER_LISTS.has(name)) return false;
1442
+ if (name === "orderedList") return true;
1443
+ }
1444
+ return false;
1445
+ }
1446
+ function callParent(self, ...args) {
1447
+ const parent = self.parent;
1448
+ return parent == null ? void 0 : parent(...args);
1449
+ }
1450
+ const OrderedListWithNumbering = OrderedList.extend({
1451
+ addAttributes() {
1452
+ var _a;
1453
+ return {
1454
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
1455
+ numberingFormat: {
1456
+ default: null,
1457
+ parseHTML: (element) => {
1458
+ const value = element.getAttribute("data-numbering-format");
1459
+ return isNumberingFormat(value) ? value : null;
1460
+ },
1461
+ renderHTML: (attributes) => {
1462
+ if (!isNumberingFormat(attributes.numberingFormat)) return {};
1463
+ return { "data-numbering-format": attributes.numberingFormat };
1464
+ }
1465
+ }
1466
+ };
1467
+ },
1468
+ renderHTML(props) {
1469
+ const rendered = callParent(
1470
+ this,
1471
+ props
1472
+ );
1473
+ if (!rendered) return ["ol", props.HTMLAttributes, 0];
1474
+ const format = props.node.attrs.numberingFormat;
1475
+ const start = Number(props.node.attrs.start);
1476
+ if (isNumberingFormat(format) && Number.isFinite(start) && start > 1) {
1477
+ const reset = `counter-reset: ${counterName(format, 0)} ${start - 1}`;
1478
+ const attrs = rendered[1];
1479
+ attrs.style = attrs.style ? `${attrs.style}; ${reset}` : reset;
1480
+ }
1481
+ return rendered;
1482
+ },
1483
+ renderMarkdown(node, helpers, ctx) {
1484
+ var _a;
1485
+ const body = callParent(this, node, helpers, ctx) ?? helpers.renderChildren(node.content ?? [], "\n");
1486
+ const format = (_a = node.attrs) == null ? void 0 : _a.numberingFormat;
1487
+ if (!isNumberingFormat(format)) return body;
1488
+ const inner = body.replace(/^\n+/, "").replace(/\s+$/, "");
1489
+ return `<ol data-numbering-format="${format}">
1490
+
1491
+ ${inner}
1492
+
1493
+ </ol>
1494
+ `;
1495
+ },
1496
+ parseMarkdown(token, helpers) {
1497
+ const parsed = callParent(
1498
+ this,
1499
+ token,
1500
+ helpers
1501
+ );
1502
+ if (!isNumberingFormat(token.numberingFormat) || !parsed || Array.isArray(parsed)) {
1503
+ return parsed ?? [];
1504
+ }
1505
+ if (parsed.type !== "orderedList") return parsed;
1506
+ return {
1507
+ ...parsed,
1508
+ attrs: { ...parsed.attrs, numberingFormat: token.numberingFormat }
1509
+ };
1510
+ },
1511
+ markdownTokenizer: numberingMarkdownTokenizer,
1512
+ addCommands() {
1513
+ var _a;
1514
+ return {
1515
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
1516
+ setOrderedListNumberingFormat: (format) => ({ state, tr, dispatch }) => {
1517
+ if (format != null && !isNumberingFormat(format)) return false;
1518
+ const depth = numberingListDepth(state.selection.$from);
1519
+ if (depth == null) return false;
1520
+ const node = state.selection.$from.node(depth);
1521
+ if (node.attrs.numberingFormat === format) return true;
1522
+ if (dispatch) {
1523
+ tr.setNodeMarkup(state.selection.$from.before(depth), void 0, {
1524
+ ...node.attrs,
1525
+ numberingFormat: format
1526
+ });
1527
+ dispatch(tr);
1528
+ }
1529
+ return true;
1530
+ },
1531
+ toggleOrderedListWithFormat: (format) => ({ state, chain, commands }) => {
1532
+ if (!isNumberingFormat(format)) return false;
1533
+ if (numberingListDepth(state.selection.$from) != null) {
1534
+ return commands.toggleOrderedList();
1535
+ }
1536
+ return chain().toggleOrderedList().command(({ tr, state: next }) => {
1537
+ const depth = numberingListDepth(next.selection.$from);
1538
+ if (depth == null) return false;
1539
+ const node = next.selection.$from.node(depth);
1540
+ tr.setNodeMarkup(next.selection.$from.before(depth), void 0, {
1541
+ ...node.attrs,
1542
+ numberingFormat: format
1543
+ });
1544
+ return true;
1545
+ }).run();
1546
+ }
1547
+ };
1548
+ },
1549
+ addProseMirrorPlugins() {
1550
+ var _a;
1551
+ const parent = ((_a = this.parent) == null ? void 0 : _a.call(this)) ?? [];
1552
+ return [
1553
+ ...parent,
1554
+ new Plugin({
1555
+ appendTransaction(transactions, _oldState, newState) {
1556
+ if (!transactions.some((tr2) => tr2.docChanged)) return null;
1557
+ const fixes = [];
1558
+ newState.doc.descendants((node, pos) => {
1559
+ if (node.type.name !== "orderedList") return;
1560
+ if (!isNumberingFormat(node.attrs.numberingFormat)) return;
1561
+ if (!hasOrderedListAncestor(newState.doc, pos)) return;
1562
+ fixes.push({
1563
+ pos,
1564
+ attrs: { ...node.attrs, numberingFormat: null }
1565
+ });
1566
+ });
1567
+ if (fixes.length === 0) return null;
1568
+ const tr = newState.tr;
1569
+ for (const fix of fixes) tr.setNodeMarkup(fix.pos, void 0, fix.attrs);
1570
+ return tr;
1571
+ }
1572
+ })
1573
+ ];
1574
+ }
1575
+ });
1576
+ function readCurrentNumberingFormat(editor) {
1577
+ const depth = numberingListDepth(editor.state.selection.$from);
1578
+ if (depth == null) return null;
1579
+ const value = editor.state.selection.$from.node(depth).attrs.numberingFormat;
1580
+ return isNumberingFormat(value) ? value : null;
1581
+ }
1582
+ function applyNumberingFormat(editor, format) {
1583
+ const chain = editor.chain().focus();
1584
+ if (editor.can().setOrderedListNumberingFormat(null)) {
1585
+ return chain.setOrderedListNumberingFormat(format).run();
1586
+ }
1587
+ return chain.toggleOrderedListWithFormat(format).run();
1588
+ }
964
1589
  function parseIndentValue(value, stepEm) {
965
1590
  if (!value) return null;
966
1591
  const match = /^([\d.]+)em$/.exec(value.trim());
@@ -1140,6 +1765,20 @@ const MarkdownParagraph = Paragraph.extend({
1140
1765
  const MarkdownHeading = Heading.extend({
1141
1766
  renderMarkdown: headingRenderMarkdown
1142
1767
  });
1768
+ const DetailsBlock = Details.extend({
1769
+ addAttributes() {
1770
+ var _a;
1771
+ const inherited = ((_a = this.parent) == null ? void 0 : _a.call(this)) ?? {};
1772
+ if (!("open" in inherited) || inherited.open == null) return inherited;
1773
+ return {
1774
+ ...inherited,
1775
+ open: { ...inherited.open, default: true }
1776
+ };
1777
+ },
1778
+ markdownTokenizer: detailsMarkdownTokenizer,
1779
+ parseMarkdown: detailsParseMarkdown,
1780
+ renderMarkdown: detailsRenderMarkdown
1781
+ }).configure({ persist: true });
1143
1782
  const baseExtensions = [
1144
1783
  // 禁用 StarterKit 内置 codeBlock / code(改用 lowlight 代码块 + 可共存的 InlineCode)
1145
1784
  // 与 paragraph / heading(改用带 markdown 样式往返的版本,见上)
@@ -1147,10 +1786,15 @@ const baseExtensions = [
1147
1786
  codeBlock: false,
1148
1787
  code: false,
1149
1788
  paragraph: false,
1150
- heading: false
1789
+ heading: false,
1790
+ // 有序列表改用带编号格式的版本。同名不能注册两份。
1791
+ orderedList: false,
1792
+ // 块拖拽的落点指示。颜色用主题变量(拖拽光标插在编辑器里,能读到 --tmr-accent)。
1793
+ dropcursor: { color: "var(--tmr-accent, #ff6719)", width: 2 }
1151
1794
  }),
1152
1795
  MarkdownParagraph,
1153
1796
  MarkdownHeading,
1797
+ OrderedListWithNumbering,
1154
1798
  // 段落级样式:文字对齐用官方扩展(免费);缩进 / 行高自研(官方无此扩展)。
1155
1799
  // 三者都往节点 style 上写片段,tiptap 的 mergeAttributes 对 style 做拼接合并。
1156
1800
  TextAlign.configure({ types: [MarkdownParagraph.name, MarkdownHeading.name] }),
@@ -1176,8 +1820,13 @@ const baseExtensions = [
1176
1820
  }),
1177
1821
  TaskList,
1178
1822
  TaskItem.configure({ nested: true }),
1823
+ DetailsBlock,
1824
+ DetailsSummary,
1825
+ DetailsContent,
1179
1826
  reportInlineMath,
1180
- reportBlockMath
1827
+ reportBlockMath,
1828
+ // 表情节点:`:shortcode:` 往返。建议列表是编辑器上的 configure,不进这份 schema。
1829
+ MarkdownEmoji
1181
1830
  ];
1182
1831
  function unwrapDataCardDivs(markdown) {
1183
1832
  return markdown.replace(
@@ -1254,8 +1903,13 @@ function extractToc(doc, lockedTitles = []) {
1254
1903
  return items;
1255
1904
  }
1256
1905
  export {
1257
- subscribeMathClick as A,
1906
+ readCurrentNumberingFormat as A,
1907
+ subscribeMathClick as B,
1258
1908
  CitationRef as C,
1909
+ applyNumberingFormat as D,
1910
+ NUMBERING_FORMAT_LABELS as E,
1911
+ NUMBERING_FORMAT_PREVIEWS as F,
1912
+ NUMBERING_FORMAT_IDS as N,
1259
1913
  ParagraphStyles as P,
1260
1914
  pureCodeBlock as a,
1261
1915
  baseExtensions as b,
@@ -1284,4 +1938,4 @@ export {
1284
1938
  normalizeImageCaption as y,
1285
1939
  renderMathInto as z
1286
1940
  };
1287
- //# sourceMappingURL=extractToc-CGrBMGvM.js.map
1941
+ //# sourceMappingURL=extractToc-Cr470fBy.js.map