tiptap-markdown-react 1.3.3 → 1.5.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/{ReportContent-Bc5q1bYp.js → ReportContent-DUvhic0o.js} +13 -13
- package/dist/{ReportContent-Bc5q1bYp.js.map → ReportContent-DUvhic0o.js.map} +1 -1
- package/dist/{ReportContentWithCharts-BOskBjBI.js → ReportContentWithCharts-CMMUE51M.js} +36 -26
- package/dist/ReportContentWithCharts-CMMUE51M.js.map +1 -0
- package/dist/demoContent.test.d.ts +1 -0
- package/dist/{extractToc-DiI2437D.js → extractToc-B-hg0XuH.js} +241 -4
- package/dist/extractToc-B-hg0XuH.js.map +1 -0
- package/dist/fontSizeHeading.test.d.ts +1 -0
- package/dist/formatBrush.d.ts +34 -0
- package/dist/formatBrush.test.d.ts +1 -0
- package/dist/highlightMarkdown.test.d.ts +1 -0
- package/dist/icons/index.d.ts +3 -1
- package/dist/index.js +425 -72
- package/dist/index.js.map +1 -1
- package/dist/labels.d.ts +4 -0
- package/dist/markOrder.test.d.ts +1 -0
- package/dist/reader.js +2 -2
- package/dist/server.js +33 -7
- package/dist/server.js.map +1 -1
- package/dist/style-inline.css +484 -445
- package/dist/style.css +484 -445
- package/dist/textStyleServer.test.d.ts +1 -0
- package/package.json +1 -1
- package/dist/ReportContentWithCharts-BOskBjBI.js.map +0 -1
- package/dist/extractToc-DiI2437D.js.map +0 -1
|
@@ -16,7 +16,7 @@ import { Color, TextStyle, FontSize } from "@tiptap/extension-text-style";
|
|
|
16
16
|
import StarterKit from "@tiptap/starter-kit";
|
|
17
17
|
import { createLowlight, common } from "lowlight";
|
|
18
18
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
19
|
-
import { p as parseChartFenceBody, s as serializeChartPayload, i as imageMarkdownTokenizer, c as imageParseMarkdown, d as imageRenderMarkdown, f as imageNodeHtml, g as imageFigureParseRule, h as imageCropAttribute, j as imageCaptionAttribute, k as imageAlignAttribute, n as normalizeChartMarkdown } from "./ReportContent-
|
|
19
|
+
import { p as parseChartFenceBody, s as serializeChartPayload, i as imageMarkdownTokenizer, c as imageParseMarkdown, d as imageRenderMarkdown, f as imageNodeHtml, g as imageFigureParseRule, h as imageCropAttribute, j as imageCaptionAttribute, k as imageAlignAttribute, n as normalizeChartMarkdown } from "./ReportContent-DUvhic0o.js";
|
|
20
20
|
import { InlineMath, BlockMath } from "@tiptap/extension-mathematics";
|
|
21
21
|
import katex from "katex";
|
|
22
22
|
import { Emoji, shortcodeToEmoji, emojis, emojiToShortcode } from "@tiptap/extension-emoji";
|
|
@@ -135,6 +135,8 @@ const CommentMark = Mark.create({
|
|
|
135
135
|
name: "commentAnchor",
|
|
136
136
|
// 评论是「引用快照」:光标在区间边界输入不应自动扩展高亮。
|
|
137
137
|
inclusive: false,
|
|
138
|
+
// 官方 unsetAllMarks 只卸 clearable 的 mark。评论不是排版,清除格式必须留下。
|
|
139
|
+
clearable: false,
|
|
138
140
|
// 与加粗/斜体等格式可共存。
|
|
139
141
|
excludes: "",
|
|
140
142
|
addOptions() {
|
|
@@ -1505,6 +1507,66 @@ const InlineCode = Code.extend({
|
|
|
1505
1507
|
excludes: ""
|
|
1506
1508
|
});
|
|
1507
1509
|
const lowlight = createLowlight(common);
|
|
1510
|
+
const FONT_SIZE_VALUE = /^\d+(?:\.\d+)?(?:px|em|rem|%)$/i;
|
|
1511
|
+
const HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;
|
|
1512
|
+
const RGB_COLOR = /^rgba?\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/;
|
|
1513
|
+
const HSL_COLOR = /^hsla?\(\s*[\d.]+\s*,\s*[\d.]+%\s*,\s*[\d.]+%(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/;
|
|
1514
|
+
const NAMED_COLOR = /^[a-zA-Z]{1,30}$/;
|
|
1515
|
+
function isSafeColor(value) {
|
|
1516
|
+
if (/[<>"'\\{};]/.test(value)) return false;
|
|
1517
|
+
return HEX_COLOR.test(value) || RGB_COLOR.test(value) || HSL_COLOR.test(value) || NAMED_COLOR.test(value);
|
|
1518
|
+
}
|
|
1519
|
+
function parseTextStyleAttrs(style) {
|
|
1520
|
+
const attrs = {};
|
|
1521
|
+
for (const entry of style.split(";")) {
|
|
1522
|
+
const index = entry.indexOf(":");
|
|
1523
|
+
if (index < 0) continue;
|
|
1524
|
+
const key = entry.slice(0, index).trim().toLowerCase();
|
|
1525
|
+
const value = entry.slice(index + 1).trim();
|
|
1526
|
+
if (key === "color" && isSafeColor(value)) attrs.color = value;
|
|
1527
|
+
else if (key === "font-size" && FONT_SIZE_VALUE.test(value)) {
|
|
1528
|
+
attrs.fontSize = value;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
if (!attrs.color && !attrs.fontSize) return null;
|
|
1532
|
+
return attrs;
|
|
1533
|
+
}
|
|
1534
|
+
function readStyleAttribute(attrs) {
|
|
1535
|
+
const match = /\bstyle\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/i.exec(
|
|
1536
|
+
attrs
|
|
1537
|
+
);
|
|
1538
|
+
if (!match) return void 0;
|
|
1539
|
+
return (match[1] ?? match[2] ?? match[3] ?? "").trim();
|
|
1540
|
+
}
|
|
1541
|
+
function matchTextStyleSpan(src) {
|
|
1542
|
+
const open = /^<span\b([^>]*)>/i.exec(src);
|
|
1543
|
+
if (!open || /\/\s*$/.test(open[1])) return void 0;
|
|
1544
|
+
const style = readStyleAttribute(open[1]);
|
|
1545
|
+
if (style == null || !parseTextStyleAttrs(style)) return void 0;
|
|
1546
|
+
const tagRe = /<\/?span\b[^>]*>/i;
|
|
1547
|
+
let depth = 1;
|
|
1548
|
+
let cursor = open[0].length;
|
|
1549
|
+
while (cursor < src.length) {
|
|
1550
|
+
const found = tagRe.exec(src.slice(cursor));
|
|
1551
|
+
if (!found) return void 0;
|
|
1552
|
+
const tag = found[0];
|
|
1553
|
+
const at = cursor + found.index;
|
|
1554
|
+
if (tag.startsWith("</")) {
|
|
1555
|
+
depth -= 1;
|
|
1556
|
+
if (depth === 0) {
|
|
1557
|
+
return {
|
|
1558
|
+
raw: src.slice(0, at + tag.length),
|
|
1559
|
+
style,
|
|
1560
|
+
inner: src.slice(open[0].length, at)
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
} else if (!/\/\s*>$/.test(tag)) {
|
|
1564
|
+
depth += 1;
|
|
1565
|
+
}
|
|
1566
|
+
cursor = at + tag.length;
|
|
1567
|
+
}
|
|
1568
|
+
return void 0;
|
|
1569
|
+
}
|
|
1508
1570
|
const MarkdownTextStyle = TextStyle.extend({
|
|
1509
1571
|
renderMarkdown(node, helpers) {
|
|
1510
1572
|
const { color, fontSize } = node.attrs ?? {};
|
|
@@ -1514,6 +1576,127 @@ const MarkdownTextStyle = TextStyle.extend({
|
|
|
1514
1576
|
if (styles.length === 0) return "";
|
|
1515
1577
|
const children = helpers.renderChildren();
|
|
1516
1578
|
return `<span style="${styles.join(";")}">${children}</span>`;
|
|
1579
|
+
},
|
|
1580
|
+
markdownTokenizer: {
|
|
1581
|
+
name: "textStyle",
|
|
1582
|
+
level: "inline",
|
|
1583
|
+
start(src) {
|
|
1584
|
+
const match = /<span\b[^>]*\bstyle\s*=/i.exec(src);
|
|
1585
|
+
return match ? match.index : -1;
|
|
1586
|
+
},
|
|
1587
|
+
tokenize(src, _tokens, lexer) {
|
|
1588
|
+
const matched = matchTextStyleSpan(src);
|
|
1589
|
+
if (!matched) return void 0;
|
|
1590
|
+
return {
|
|
1591
|
+
type: "textStyle",
|
|
1592
|
+
raw: matched.raw,
|
|
1593
|
+
style: matched.style,
|
|
1594
|
+
tokens: lexer.inlineTokens(matched.inner)
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
},
|
|
1598
|
+
parseMarkdown: (token, helpers) => {
|
|
1599
|
+
const attrs = parseTextStyleAttrs(String(token.style ?? ""));
|
|
1600
|
+
const content = helpers.parseInline(token.tokens ?? []);
|
|
1601
|
+
if (!attrs) return content;
|
|
1602
|
+
return helpers.applyMark("textStyle", content, attrs);
|
|
1603
|
+
}
|
|
1604
|
+
});
|
|
1605
|
+
function readHighlightColor(attrs) {
|
|
1606
|
+
const data = /\bdata-color\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(attrs);
|
|
1607
|
+
const fromData = ((data == null ? void 0 : data[1]) ?? (data == null ? void 0 : data[2]) ?? "").trim();
|
|
1608
|
+
if (fromData && isSafeColor(fromData)) return fromData;
|
|
1609
|
+
const style = /\bstyle\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(attrs);
|
|
1610
|
+
const styleText = (style == null ? void 0 : style[1]) ?? (style == null ? void 0 : style[2]) ?? "";
|
|
1611
|
+
for (const entry of styleText.split(";")) {
|
|
1612
|
+
const index = entry.indexOf(":");
|
|
1613
|
+
if (index < 0) continue;
|
|
1614
|
+
const key = entry.slice(0, index).trim().toLowerCase();
|
|
1615
|
+
const value = entry.slice(index + 1).trim();
|
|
1616
|
+
if (key === "background-color" && isSafeColor(value)) return value;
|
|
1617
|
+
}
|
|
1618
|
+
return null;
|
|
1619
|
+
}
|
|
1620
|
+
function matchHighlightMark(src) {
|
|
1621
|
+
const open = /^<mark\b([^>]*)>/i.exec(src);
|
|
1622
|
+
if (!open || /\/\s*$/.test(open[1])) return void 0;
|
|
1623
|
+
if (/data-comment-ids/i.test(open[1]) || /\btmr-comment\b/.test(open[1])) {
|
|
1624
|
+
return void 0;
|
|
1625
|
+
}
|
|
1626
|
+
const color = readHighlightColor(open[1]);
|
|
1627
|
+
const tagRe = /<\/?mark\b[^>]*>/i;
|
|
1628
|
+
let depth = 1;
|
|
1629
|
+
let cursor = open[0].length;
|
|
1630
|
+
while (cursor < src.length) {
|
|
1631
|
+
const found = tagRe.exec(src.slice(cursor));
|
|
1632
|
+
if (!found) return void 0;
|
|
1633
|
+
const tag = found[0];
|
|
1634
|
+
const at = cursor + found.index;
|
|
1635
|
+
if (tag.startsWith("</")) {
|
|
1636
|
+
depth -= 1;
|
|
1637
|
+
if (depth === 0) {
|
|
1638
|
+
return {
|
|
1639
|
+
raw: src.slice(0, at + tag.length),
|
|
1640
|
+
inner: src.slice(open[0].length, at),
|
|
1641
|
+
color
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
} else if (!/\/\s*>$/.test(tag)) {
|
|
1645
|
+
depth += 1;
|
|
1646
|
+
}
|
|
1647
|
+
cursor = at + tag.length;
|
|
1648
|
+
}
|
|
1649
|
+
return void 0;
|
|
1650
|
+
}
|
|
1651
|
+
const MarkdownHighlight = Highlight.extend({
|
|
1652
|
+
renderMarkdown(node, helpers) {
|
|
1653
|
+
var _a;
|
|
1654
|
+
const children = helpers.renderChildren(node);
|
|
1655
|
+
const color = typeof ((_a = node.attrs) == null ? void 0 : _a.color) === "string" ? node.attrs.color : "";
|
|
1656
|
+
if (!color || !isSafeColor(color)) return `==${children}==`;
|
|
1657
|
+
const encoded = color.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
1658
|
+
return `<mark data-color="${encoded}" style="background-color:${encoded}">${children}</mark>`;
|
|
1659
|
+
},
|
|
1660
|
+
markdownTokenizer: {
|
|
1661
|
+
name: "highlight",
|
|
1662
|
+
level: "inline",
|
|
1663
|
+
start(src) {
|
|
1664
|
+
const mark = /<mark\b/i.exec(src);
|
|
1665
|
+
const eq = src.indexOf("==");
|
|
1666
|
+
const indexes = [mark == null ? void 0 : mark.index, eq >= 0 ? eq : void 0].filter(
|
|
1667
|
+
(index) => index != null
|
|
1668
|
+
);
|
|
1669
|
+
return indexes.length ? Math.min(...indexes) : -1;
|
|
1670
|
+
},
|
|
1671
|
+
tokenize(src, _tokens, lexer) {
|
|
1672
|
+
if (src.startsWith("==")) {
|
|
1673
|
+
const match = /^(==)([^=]+)(==)/.exec(src);
|
|
1674
|
+
if (!match) return void 0;
|
|
1675
|
+
const inner = match[2].trim();
|
|
1676
|
+
return {
|
|
1677
|
+
type: "highlight",
|
|
1678
|
+
raw: match[0],
|
|
1679
|
+
text: inner,
|
|
1680
|
+
tokens: lexer.inlineTokens(inner)
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
const matched = matchHighlightMark(src);
|
|
1684
|
+
if (!matched) return void 0;
|
|
1685
|
+
return {
|
|
1686
|
+
type: "highlight",
|
|
1687
|
+
raw: matched.raw,
|
|
1688
|
+
color: matched.color,
|
|
1689
|
+
tokens: lexer.inlineTokens(matched.inner)
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
},
|
|
1693
|
+
parseMarkdown: (token, helpers) => {
|
|
1694
|
+
const content = helpers.parseInline(token.tokens ?? []);
|
|
1695
|
+
const color = typeof token.color === "string" ? token.color : "";
|
|
1696
|
+
if (color && isSafeColor(color)) {
|
|
1697
|
+
return helpers.applyMark("highlight", content, { color });
|
|
1698
|
+
}
|
|
1699
|
+
return helpers.applyMark("highlight", content);
|
|
1517
1700
|
}
|
|
1518
1701
|
});
|
|
1519
1702
|
function htmlTagTokenizer(tag, tokenType) {
|
|
@@ -1593,8 +1776,62 @@ const MarkdownParagraph = Paragraph.extend({
|
|
|
1593
1776
|
markdownTokenizer: paragraphMarkdownTokenizer,
|
|
1594
1777
|
parseMarkdown: paragraphParseMarkdown
|
|
1595
1778
|
});
|
|
1779
|
+
function clearFontSizeInTextblocks({ state, tr }) {
|
|
1780
|
+
const markType = state.schema.marks.textStyle;
|
|
1781
|
+
const boldType = state.schema.marks.bold;
|
|
1782
|
+
const ranges = [];
|
|
1783
|
+
const { from, to } = state.selection;
|
|
1784
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1785
|
+
if (!node.isTextblock) return;
|
|
1786
|
+
const start = pos + 1;
|
|
1787
|
+
const end = pos + node.nodeSize - 1;
|
|
1788
|
+
if (boldType) tr.removeMark(start, end, boldType);
|
|
1789
|
+
if (!markType) return false;
|
|
1790
|
+
node.descendants((child, childPos) => {
|
|
1791
|
+
if (!child.isText) return;
|
|
1792
|
+
const mark = child.marks.find((item) => item.type === markType);
|
|
1793
|
+
if (!(mark == null ? void 0 : mark.attrs.fontSize)) return;
|
|
1794
|
+
const childStart = pos + 1 + childPos;
|
|
1795
|
+
ranges.push({
|
|
1796
|
+
from: childStart,
|
|
1797
|
+
to: childStart + child.nodeSize,
|
|
1798
|
+
attrs: { ...mark.attrs, fontSize: null }
|
|
1799
|
+
});
|
|
1800
|
+
});
|
|
1801
|
+
return false;
|
|
1802
|
+
});
|
|
1803
|
+
for (const range of ranges) {
|
|
1804
|
+
if (!markType) continue;
|
|
1805
|
+
tr.removeMark(range.from, range.to, markType);
|
|
1806
|
+
const keep = Object.values(range.attrs).some(
|
|
1807
|
+
(value) => value != null && value !== ""
|
|
1808
|
+
);
|
|
1809
|
+
if (keep) tr.addMark(range.from, range.to, markType.create(range.attrs));
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1596
1812
|
const MarkdownHeading = Heading.extend({
|
|
1597
|
-
renderMarkdown: headingRenderMarkdown
|
|
1813
|
+
renderMarkdown: headingRenderMarkdown,
|
|
1814
|
+
addCommands() {
|
|
1815
|
+
var _a;
|
|
1816
|
+
const parent = (_a = this.parent) == null ? void 0 : _a.call(this);
|
|
1817
|
+
return {
|
|
1818
|
+
...parent,
|
|
1819
|
+
setHeading: (attributes) => (props) => {
|
|
1820
|
+
var _a2, _b;
|
|
1821
|
+
if (!this.options.levels.includes(attributes.level)) return false;
|
|
1822
|
+
clearFontSizeInTextblocks(props);
|
|
1823
|
+
return ((_b = (_a2 = parent == null ? void 0 : parent.setHeading) == null ? void 0 : _a2.call(parent, attributes)) == null ? void 0 : _b(props)) ?? false;
|
|
1824
|
+
},
|
|
1825
|
+
toggleHeading: (attributes) => (props) => {
|
|
1826
|
+
var _a2, _b;
|
|
1827
|
+
if (!this.options.levels.includes(attributes.level)) return false;
|
|
1828
|
+
if (!props.editor.isActive(this.name, attributes)) {
|
|
1829
|
+
clearFontSizeInTextblocks(props);
|
|
1830
|
+
}
|
|
1831
|
+
return ((_b = (_a2 = parent == null ? void 0 : parent.toggleHeading) == null ? void 0 : _a2.call(parent, attributes)) == null ? void 0 : _b(props)) ?? false;
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1598
1835
|
});
|
|
1599
1836
|
const DetailsBlock = Details.extend({
|
|
1600
1837
|
addAttributes() {
|
|
@@ -1638,7 +1875,7 @@ const baseExtensions = [
|
|
|
1638
1875
|
// 正文字号:官方 FontSize(v3 起并入 text-style 包),以内联样式挂在 TextStyle
|
|
1639
1876
|
// 的 <span> 上(如 style="font-size: 16px"),与 Color 同基座,三端共用。
|
|
1640
1877
|
FontSize.configure({ types: [TextStyle.name] }),
|
|
1641
|
-
|
|
1878
|
+
MarkdownHighlight.configure({ multicolor: true }),
|
|
1642
1879
|
MarkdownSuperscript,
|
|
1643
1880
|
MarkdownSubscript,
|
|
1644
1881
|
TableKit.configure({
|
|
@@ -1760,4 +1997,4 @@ export {
|
|
|
1760
1997
|
NUMBERING_FORMAT_LABELS as u,
|
|
1761
1998
|
NUMBERING_FORMAT_PREVIEWS as v
|
|
1762
1999
|
};
|
|
1763
|
-
//# sourceMappingURL=extractToc-
|
|
2000
|
+
//# sourceMappingURL=extractToc-B-hg0XuH.js.map
|