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.
- package/README.md +23 -37
- package/THIRD_PARTY_LICENSES.md +155 -1
- package/dist/{ReportContent-B8jIzhy3.js → ReportContent-w08GFm8v.js} +16 -8
- package/dist/{ReportContent-B8jIzhy3.js.map → ReportContent-w08GFm8v.js.map} +1 -1
- package/dist/blockDrag/actions.d.ts +30 -0
- package/dist/blockDrag/actions.test.d.ts +1 -0
- package/dist/blockDrag/listDrop.d.ts +21 -0
- package/dist/blockDrag/listDrop.test.d.ts +1 -0
- package/dist/blurSelection.dom.test.d.ts +1 -0
- package/dist/characterCount.d.ts +1 -0
- package/dist/characterCount.dom.test.d.ts +1 -0
- package/dist/characterCount.test.d.ts +1 -0
- package/dist/chart/createChart.d.ts +0 -3
- package/dist/components/BlockDragHandle.d.ts +14 -0
- package/dist/components/BlockDragHandle.dom.test.d.ts +1 -0
- package/dist/components/CodeBlockView.d.ts +2 -2
- package/dist/components/ColorPalette.d.ts +4 -3
- package/dist/components/EditorToolbar.d.ts +4 -3
- package/dist/components/FindReplaceBar.d.ts +4 -3
- package/dist/components/LinkPopover.d.ts +4 -3
- package/dist/components/MarkdownPreview.d.ts +4 -1
- package/dist/components/MarkdownWysiwygEditor.d.ts +30 -9
- package/dist/components/TocPanel.d.ts +4 -3
- package/dist/detailsMarkdown.d.ts +4 -0
- package/dist/detailsMarkdown.test.d.ts +1 -0
- package/dist/emoji/EmojiSuggestion.d.ts +23 -0
- package/dist/emoji/emojiCategories.d.ts +52 -0
- package/dist/emoji/emojiMarkdown.test.d.ts +1 -0
- package/dist/emoji/emojiSuggestion.dom.test.d.ts +1 -0
- package/dist/emoji/filterEmoji.d.ts +7 -0
- package/dist/emoji/markdownEmoji.d.ts +8 -0
- package/dist/{extractToc-CGrBMGvM.js → extractToc-Cr470fBy.js} +659 -5
- package/dist/extractToc-Cr470fBy.js.map +1 -0
- package/dist/fonts/OFL.txt +95 -0
- package/dist/i18n/index.d.ts +4 -0
- package/dist/i18n/store.d.ts +16 -0
- package/dist/i18n/types.d.ts +19 -0
- package/dist/i18n/zh.d.ts +3 -0
- package/dist/icons/index.d.ts +5 -0
- package/dist/imageUpload.d.ts +0 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1983 -407
- package/dist/index.js.map +1 -1
- package/dist/labels.d.ts +56 -2
- package/dist/numberingFormat.d.ts +67 -0
- package/dist/numberingFormat.test.d.ts +1 -0
- package/dist/reader.js +2 -2
- package/dist/{scrollToTocHeading-B2QmL4wi.js → scrollToTocHeading-kPgsn3cu.js} +2 -2
- package/dist/{scrollToTocHeading-B2QmL4wi.js.map → scrollToTocHeading-kPgsn3cu.js.map} +1 -1
- package/dist/server.js +4 -4
- package/dist/shortcuts/ShortcutPanel.d.ts +4 -4
- package/dist/shortcuts/types.d.ts +2 -2
- package/dist/slashMenu/SlashMenuExtension.d.ts +0 -3
- package/dist/slashMenu/items.d.ts +2 -2
- package/dist/style-inline.css +2110 -294
- package/dist/style.css +2110 -294
- package/dist/typography.test.d.ts +1 -0
- package/package.json +19 -3
- 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-
|
|
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
|
-
|
|
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-
|
|
1941
|
+
//# sourceMappingURL=extractToc-Cr470fBy.js.map
|