tiptap-markdown-react 1.3.3 → 1.4.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -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-Bc5q1bYp.js";
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-B0HdrWAi.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";
@@ -1505,6 +1505,66 @@ const InlineCode = Code.extend({
1505
1505
  excludes: ""
1506
1506
  });
1507
1507
  const lowlight = createLowlight(common);
1508
+ const FONT_SIZE_VALUE = /^\d+(?:\.\d+)?(?:px|em|rem|%)$/i;
1509
+ const HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;
1510
+ const RGB_COLOR = /^rgba?\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/;
1511
+ const HSL_COLOR = /^hsla?\(\s*[\d.]+\s*,\s*[\d.]+%\s*,\s*[\d.]+%(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/;
1512
+ const NAMED_COLOR = /^[a-zA-Z]{1,30}$/;
1513
+ function isSafeColor(value) {
1514
+ if (/[<>"'\\{};]/.test(value)) return false;
1515
+ return HEX_COLOR.test(value) || RGB_COLOR.test(value) || HSL_COLOR.test(value) || NAMED_COLOR.test(value);
1516
+ }
1517
+ function parseTextStyleAttrs(style) {
1518
+ const attrs = {};
1519
+ for (const entry of style.split(";")) {
1520
+ const index = entry.indexOf(":");
1521
+ if (index < 0) continue;
1522
+ const key = entry.slice(0, index).trim().toLowerCase();
1523
+ const value = entry.slice(index + 1).trim();
1524
+ if (key === "color" && isSafeColor(value)) attrs.color = value;
1525
+ else if (key === "font-size" && FONT_SIZE_VALUE.test(value)) {
1526
+ attrs.fontSize = value;
1527
+ }
1528
+ }
1529
+ if (!attrs.color && !attrs.fontSize) return null;
1530
+ return attrs;
1531
+ }
1532
+ function readStyleAttribute(attrs) {
1533
+ const match = /\bstyle\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/i.exec(
1534
+ attrs
1535
+ );
1536
+ if (!match) return void 0;
1537
+ return (match[1] ?? match[2] ?? match[3] ?? "").trim();
1538
+ }
1539
+ function matchTextStyleSpan(src) {
1540
+ const open = /^<span\b([^>]*)>/i.exec(src);
1541
+ if (!open || /\/\s*$/.test(open[1])) return void 0;
1542
+ const style = readStyleAttribute(open[1]);
1543
+ if (style == null || !parseTextStyleAttrs(style)) return void 0;
1544
+ const tagRe = /<\/?span\b[^>]*>/i;
1545
+ let depth = 1;
1546
+ let cursor = open[0].length;
1547
+ while (cursor < src.length) {
1548
+ const found = tagRe.exec(src.slice(cursor));
1549
+ if (!found) return void 0;
1550
+ const tag = found[0];
1551
+ const at = cursor + found.index;
1552
+ if (tag.startsWith("</")) {
1553
+ depth -= 1;
1554
+ if (depth === 0) {
1555
+ return {
1556
+ raw: src.slice(0, at + tag.length),
1557
+ style,
1558
+ inner: src.slice(open[0].length, at)
1559
+ };
1560
+ }
1561
+ } else if (!/\/\s*>$/.test(tag)) {
1562
+ depth += 1;
1563
+ }
1564
+ cursor = at + tag.length;
1565
+ }
1566
+ return void 0;
1567
+ }
1508
1568
  const MarkdownTextStyle = TextStyle.extend({
1509
1569
  renderMarkdown(node, helpers) {
1510
1570
  const { color, fontSize } = node.attrs ?? {};
@@ -1514,6 +1574,127 @@ const MarkdownTextStyle = TextStyle.extend({
1514
1574
  if (styles.length === 0) return "";
1515
1575
  const children = helpers.renderChildren();
1516
1576
  return `<span style="${styles.join(";")}">${children}</span>`;
1577
+ },
1578
+ markdownTokenizer: {
1579
+ name: "textStyle",
1580
+ level: "inline",
1581
+ start(src) {
1582
+ const match = /<span\b[^>]*\bstyle\s*=/i.exec(src);
1583
+ return match ? match.index : -1;
1584
+ },
1585
+ tokenize(src, _tokens, lexer) {
1586
+ const matched = matchTextStyleSpan(src);
1587
+ if (!matched) return void 0;
1588
+ return {
1589
+ type: "textStyle",
1590
+ raw: matched.raw,
1591
+ style: matched.style,
1592
+ tokens: lexer.inlineTokens(matched.inner)
1593
+ };
1594
+ }
1595
+ },
1596
+ parseMarkdown: (token, helpers) => {
1597
+ const attrs = parseTextStyleAttrs(String(token.style ?? ""));
1598
+ const content = helpers.parseInline(token.tokens ?? []);
1599
+ if (!attrs) return content;
1600
+ return helpers.applyMark("textStyle", content, attrs);
1601
+ }
1602
+ });
1603
+ function readHighlightColor(attrs) {
1604
+ const data = /\bdata-color\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(attrs);
1605
+ const fromData = ((data == null ? void 0 : data[1]) ?? (data == null ? void 0 : data[2]) ?? "").trim();
1606
+ if (fromData && isSafeColor(fromData)) return fromData;
1607
+ const style = /\bstyle\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(attrs);
1608
+ const styleText = (style == null ? void 0 : style[1]) ?? (style == null ? void 0 : style[2]) ?? "";
1609
+ for (const entry of styleText.split(";")) {
1610
+ const index = entry.indexOf(":");
1611
+ if (index < 0) continue;
1612
+ const key = entry.slice(0, index).trim().toLowerCase();
1613
+ const value = entry.slice(index + 1).trim();
1614
+ if (key === "background-color" && isSafeColor(value)) return value;
1615
+ }
1616
+ return null;
1617
+ }
1618
+ function matchHighlightMark(src) {
1619
+ const open = /^<mark\b([^>]*)>/i.exec(src);
1620
+ if (!open || /\/\s*$/.test(open[1])) return void 0;
1621
+ if (/data-comment-ids/i.test(open[1]) || /\btmr-comment\b/.test(open[1])) {
1622
+ return void 0;
1623
+ }
1624
+ const color = readHighlightColor(open[1]);
1625
+ const tagRe = /<\/?mark\b[^>]*>/i;
1626
+ let depth = 1;
1627
+ let cursor = open[0].length;
1628
+ while (cursor < src.length) {
1629
+ const found = tagRe.exec(src.slice(cursor));
1630
+ if (!found) return void 0;
1631
+ const tag = found[0];
1632
+ const at = cursor + found.index;
1633
+ if (tag.startsWith("</")) {
1634
+ depth -= 1;
1635
+ if (depth === 0) {
1636
+ return {
1637
+ raw: src.slice(0, at + tag.length),
1638
+ inner: src.slice(open[0].length, at),
1639
+ color
1640
+ };
1641
+ }
1642
+ } else if (!/\/\s*>$/.test(tag)) {
1643
+ depth += 1;
1644
+ }
1645
+ cursor = at + tag.length;
1646
+ }
1647
+ return void 0;
1648
+ }
1649
+ const MarkdownHighlight = Highlight.extend({
1650
+ renderMarkdown(node, helpers) {
1651
+ var _a;
1652
+ const children = helpers.renderChildren(node);
1653
+ const color = typeof ((_a = node.attrs) == null ? void 0 : _a.color) === "string" ? node.attrs.color : "";
1654
+ if (!color || !isSafeColor(color)) return `==${children}==`;
1655
+ const encoded = color.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
1656
+ return `<mark data-color="${encoded}" style="background-color:${encoded}">${children}</mark>`;
1657
+ },
1658
+ markdownTokenizer: {
1659
+ name: "highlight",
1660
+ level: "inline",
1661
+ start(src) {
1662
+ const mark = /<mark\b/i.exec(src);
1663
+ const eq = src.indexOf("==");
1664
+ const indexes = [mark == null ? void 0 : mark.index, eq >= 0 ? eq : void 0].filter(
1665
+ (index) => index != null
1666
+ );
1667
+ return indexes.length ? Math.min(...indexes) : -1;
1668
+ },
1669
+ tokenize(src, _tokens, lexer) {
1670
+ if (src.startsWith("==")) {
1671
+ const match = /^(==)([^=]+)(==)/.exec(src);
1672
+ if (!match) return void 0;
1673
+ const inner = match[2].trim();
1674
+ return {
1675
+ type: "highlight",
1676
+ raw: match[0],
1677
+ text: inner,
1678
+ tokens: lexer.inlineTokens(inner)
1679
+ };
1680
+ }
1681
+ const matched = matchHighlightMark(src);
1682
+ if (!matched) return void 0;
1683
+ return {
1684
+ type: "highlight",
1685
+ raw: matched.raw,
1686
+ color: matched.color,
1687
+ tokens: lexer.inlineTokens(matched.inner)
1688
+ };
1689
+ }
1690
+ },
1691
+ parseMarkdown: (token, helpers) => {
1692
+ const content = helpers.parseInline(token.tokens ?? []);
1693
+ const color = typeof token.color === "string" ? token.color : "";
1694
+ if (color && isSafeColor(color)) {
1695
+ return helpers.applyMark("highlight", content, { color });
1696
+ }
1697
+ return helpers.applyMark("highlight", content);
1517
1698
  }
1518
1699
  });
1519
1700
  function htmlTagTokenizer(tag, tokenType) {
@@ -1593,8 +1774,62 @@ const MarkdownParagraph = Paragraph.extend({
1593
1774
  markdownTokenizer: paragraphMarkdownTokenizer,
1594
1775
  parseMarkdown: paragraphParseMarkdown
1595
1776
  });
1777
+ function clearFontSizeInTextblocks({ state, tr }) {
1778
+ const markType = state.schema.marks.textStyle;
1779
+ const boldType = state.schema.marks.bold;
1780
+ const ranges = [];
1781
+ const { from, to } = state.selection;
1782
+ state.doc.nodesBetween(from, to, (node, pos) => {
1783
+ if (!node.isTextblock) return;
1784
+ const start = pos + 1;
1785
+ const end = pos + node.nodeSize - 1;
1786
+ if (boldType) tr.removeMark(start, end, boldType);
1787
+ if (!markType) return false;
1788
+ node.descendants((child, childPos) => {
1789
+ if (!child.isText) return;
1790
+ const mark = child.marks.find((item) => item.type === markType);
1791
+ if (!(mark == null ? void 0 : mark.attrs.fontSize)) return;
1792
+ const childStart = pos + 1 + childPos;
1793
+ ranges.push({
1794
+ from: childStart,
1795
+ to: childStart + child.nodeSize,
1796
+ attrs: { ...mark.attrs, fontSize: null }
1797
+ });
1798
+ });
1799
+ return false;
1800
+ });
1801
+ for (const range of ranges) {
1802
+ if (!markType) continue;
1803
+ tr.removeMark(range.from, range.to, markType);
1804
+ const keep = Object.values(range.attrs).some(
1805
+ (value) => value != null && value !== ""
1806
+ );
1807
+ if (keep) tr.addMark(range.from, range.to, markType.create(range.attrs));
1808
+ }
1809
+ }
1596
1810
  const MarkdownHeading = Heading.extend({
1597
- renderMarkdown: headingRenderMarkdown
1811
+ renderMarkdown: headingRenderMarkdown,
1812
+ addCommands() {
1813
+ var _a;
1814
+ const parent = (_a = this.parent) == null ? void 0 : _a.call(this);
1815
+ return {
1816
+ ...parent,
1817
+ setHeading: (attributes) => (props) => {
1818
+ var _a2, _b;
1819
+ if (!this.options.levels.includes(attributes.level)) return false;
1820
+ clearFontSizeInTextblocks(props);
1821
+ return ((_b = (_a2 = parent == null ? void 0 : parent.setHeading) == null ? void 0 : _a2.call(parent, attributes)) == null ? void 0 : _b(props)) ?? false;
1822
+ },
1823
+ toggleHeading: (attributes) => (props) => {
1824
+ var _a2, _b;
1825
+ if (!this.options.levels.includes(attributes.level)) return false;
1826
+ if (!props.editor.isActive(this.name, attributes)) {
1827
+ clearFontSizeInTextblocks(props);
1828
+ }
1829
+ return ((_b = (_a2 = parent == null ? void 0 : parent.toggleHeading) == null ? void 0 : _a2.call(parent, attributes)) == null ? void 0 : _b(props)) ?? false;
1830
+ }
1831
+ };
1832
+ }
1598
1833
  });
1599
1834
  const DetailsBlock = Details.extend({
1600
1835
  addAttributes() {
@@ -1638,7 +1873,7 @@ const baseExtensions = [
1638
1873
  // 正文字号:官方 FontSize(v3 起并入 text-style 包),以内联样式挂在 TextStyle
1639
1874
  // 的 <span> 上(如 style="font-size: 16px"),与 Color 同基座,三端共用。
1640
1875
  FontSize.configure({ types: [TextStyle.name] }),
1641
- Highlight.configure({ multicolor: true }),
1876
+ MarkdownHighlight.configure({ multicolor: true }),
1642
1877
  MarkdownSuperscript,
1643
1878
  MarkdownSubscript,
1644
1879
  TableKit.configure({
@@ -1760,4 +1995,4 @@ export {
1760
1995
  NUMBERING_FORMAT_LABELS as u,
1761
1996
  NUMBERING_FORMAT_PREVIEWS as v
1762
1997
  };
1763
- //# sourceMappingURL=extractToc-DiI2437D.js.map
1998
+ //# sourceMappingURL=extractToc-Bz924Nh4.js.map