@lexical/markdown 0.53.0 → 0.53.1-nightly.20261030.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.
@@ -6,7 +6,7 @@
6
6
  *
7
7
  */
8
8
 
9
- import { $isParagraphNode, $isTextNode, $setState, $createTextNode, $getState, $findMatchingParent, $createLineBreakNode, createState, $isLineBreakNode, $getRoot, $isElementNode, $isDecoratorNode, $isTabNode, $createTabNode, $createParagraphNode, mergeRegister, COLLABORATION_TAG, HISTORIC_TAG, COMPOSITION_END_TAG, $getSelection, $isRangeSelection, $addUpdateTag, HISTORY_PUSH_TAG, KEY_ENTER_COMMAND, COMMAND_PRIORITY_LOW, $isRootOrShadowRoot, $createRangeSelection, $setSelection, TEXT_TYPE_TO_FORMAT, ArtificialNode__DO_NOT_USE } from 'lexical';
9
+ import { $isParagraphNode, $isTextNode, $setState, $createTextNode, $getState, $findMatchingParent, $createLineBreakNode, createState, $isLineBreakNode, $isRangeSelection, $getRoot, $isElementNode, $isDecoratorNode, $isTabNode, $createTabNode, $createParagraphNode, mergeRegister, COLLABORATION_TAG, HISTORIC_TAG, COMPOSITION_END_TAG, $getSelection, $addUpdateTag, HISTORY_PUSH_TAG, KEY_ENTER_COMMAND, COMMAND_PRIORITY_LOW, $isRootOrShadowRoot, $createRangeSelection, $setSelection, TEXT_TYPE_TO_FORMAT, ArtificialNode__DO_NOT_USE } from 'lexical';
10
10
  import { $sliceSelectedTextNodeContent } from '@lexical/selection';
11
11
  import { CodeNode, $createCodeNode, $isCodeNode } from '@lexical/code-core';
12
12
  import { LinkNode, $isLinkNode, $createLinkNode, $isAutoLinkNode } from '@lexical/link';
@@ -1099,7 +1099,9 @@ function getCodeSpanDelimiter(content) {
1099
1099
  const backtickRuns = content.match(/`+/g);
1100
1100
  const longestRun = backtickRuns ? Math.max(...backtickRuns.map(run => run.length)) : 0;
1101
1101
  const fence = '`'.repeat(longestRun + 1);
1102
- const needsPadding = content.length === 0 || content.includes('`') || /^\s/.test(content) && /\s$/.test(content);
1102
+ // A reader strips one space from each end only when the content is not all
1103
+ // spaces, so content made only of spaces goes out as it is.
1104
+ const needsPadding = content.length === 0 || content.includes('`') || /^\s/.test(content) && /\s$/.test(content) && /[^ ]/.test(content);
1103
1105
  const padded = needsPadding ? ` ${content} ` : content;
1104
1106
  return {
1105
1107
  fence,
@@ -1150,14 +1152,36 @@ const ITALIC_UNDERSCORE = {
1150
1152
  type: 'text-format'
1151
1153
  };
1152
1154
 
1153
- // `unescapeText` decodes a numeric character reference and a CommonMark reader
1154
- // decodes the named ones too, so an `&` that begins one cannot be written raw
1155
- // in a link destination or the URL comes back as the character it names. It
1156
- // goes out as `&` instead, which both of them read back as a single `&`.
1157
- // An `&` that begins nothing is an ordinary character and stays as it is, so a
1158
- // query string keeps the separators it was written with.
1159
- function escapeCharacterReferences(value) {
1160
- return value.replace(/&(?=#\d+;|#[Xx][\dA-Fa-f]+;|[A-Za-z][\dA-Za-z]*;)/g, '&');
1155
+ // A raw link destination cannot hold an ASCII control character or a space in
1156
+ // CommonMark, nor any Unicode whitespace in the raw branch of `LINK`'s import
1157
+ // patterns, so export writes a URL with any of them in the pointy form. Import
1158
+ // still reads control characters in a raw destination, as older exports wrote
1159
+ // them there. A NUL reads back as U+FFFD in a CommonMark reader whichever form
1160
+ // it is in, since no spelling of it avoids that.
1161
+ // eslint-disable-next-line no-control-regex
1162
+ const NOT_RAW_DESTINATION = /[\s\0-\x1f\x7f]/;
1163
+
1164
+ // A link destination or title is read back through `unescapeText`, which
1165
+ // decodes a numeric character reference, and a CommonMark reader decodes the
1166
+ // named ones too. So an `&` that begins one goes out as `&`, or the part
1167
+ // would come back as the character the reference names. An `&` that begins
1168
+ // nothing is an ordinary character and stays as it is, so a query string keeps
1169
+ // the separators it was written with.
1170
+ //
1171
+ // Then every match of `syntax`, a global regular expression for the characters
1172
+ // the part's shape treats as syntax, gets a backslash. So does a backtick: this
1173
+ // package's importer reads code spans before links, so one anywhere in the
1174
+ // link could pair with one in the text after it and swallow the link, although
1175
+ // a CommonMark reader would not let it.
1176
+ //
1177
+ // Neither part may hold a line ending either. CommonMark allows none in a
1178
+ // destination and no blank line in a title, where two CRs make one as surely
1179
+ // as two LFs, and the importer reads one LF-separated line at a time, so even
1180
+ // a single LF would split the link. Each goes out as the character reference
1181
+ // that `unescapeText` and a CommonMark reader both turn back into the line
1182
+ // ending, written after the `&` escape so that one leaves it alone.
1183
+ function escapeLinkPart(value, syntax) {
1184
+ return value.replace(/&(?=#\d+;|#[Xx][\dA-Fa-f]+;|[A-Za-z][\dA-Za-z]*;)/g, '&').replace(syntax, '\\$&').replace(/`/g, '\\`').replace(/[\r\n]/g, lineEnding => `&#${lineEnding.charCodeAt(0)};`);
1161
1185
  }
1162
1186
 
1163
1187
  // Mask escaped/code brackets without changing source offsets, then find an inner
@@ -1207,35 +1231,20 @@ const LINK = {
1207
1231
  return null;
1208
1232
  }
1209
1233
  const textContent = exportChildren(node);
1210
- let title = node.getTitle();
1211
-
1212
- // A title is read back through `unescapeText` as well, so a character
1213
- // reference in it needs the same treatment the destination gets below.
1214
- if (title != null) {
1215
- title = escapeCharacterReferences(title).replace(/([\\"])/g, '\\$1');
1216
- }
1217
-
1218
- // A raw destination cannot hold whitespace, so a URL that has any is
1219
- // written in the pointy form, where only `<`, `>` and a backslash need an
1220
- // escape. An empty URL goes there too, since the raw form has nothing left
1221
- // to match. Everywhere else the destination is written raw, where a
1222
- // parenthesis would close it early, a backslash would start an escape, and
1223
- // only a `<` in first place turns it into the pointy form. An angle
1224
- // bracket anywhere else is an ordinary character and goes out as it is.
1225
- //
1226
- // Neither shape may hold a line ending, so a literal one would leave a
1227
- // destination that no reader can close and would split the paragraph in
1228
- // two. It goes out as the character reference that `unescapeText` and a
1229
- // CommonMark reader both turn back into the line ending.
1230
- //
1231
- // That spelling only survives because a reader decodes it, so an `&` that
1232
- // already begins a character reference has to go out as one itself, or the
1233
- // URL comes back as whatever the reference names. Escaping it first keeps
1234
- // the references written for the line endings below out of its way.
1234
+ const title = node.getTitle();
1235
+
1236
+ // A URL that `NOT_RAW_DESTINATION` matches goes in the pointy form, where
1237
+ // `<`, `>` and a backslash are syntax. An empty URL goes there too, since
1238
+ // the raw form has nothing left to match. Everywhere else the destination
1239
+ // is written raw, where a parenthesis would close it early, a backslash
1240
+ // would start an escape, and only a `<` in first place turns it into the
1241
+ // pointy form. An angle bracket anywhere else is an ordinary character and
1242
+ // goes out as it is.
1235
1243
  const rawUrl = node.getURL();
1236
- const escapedUrl = escapeCharacterReferences(rawUrl);
1237
- const url = rawUrl === '' || /\s/.test(rawUrl) ? `<${escapedUrl.replace(/([\\<>])/g, '\\$1').replace(/\r/g, '&#13;').replace(/\n/g, '&#10;')}>` : escapedUrl.replace(/([\\()])/g, '\\$1').replace(/^</, '\\<');
1238
- const linkContent = title ? `[${textContent}](${url} "${title}")` : `[${textContent}](${url})`;
1244
+ const url = rawUrl === '' || NOT_RAW_DESTINATION.test(rawUrl) ? `<${escapeLinkPart(rawUrl, /[\\<>]/g)}>` : escapeLinkPart(rawUrl, /[\\()]|^</g);
1245
+
1246
+ // In the double-quoted title only `"` and a backslash are syntax.
1247
+ const linkContent = title ? `[${textContent}](${url} "${escapeLinkPart(title, /[\\"]/g)}")` : `[${textContent}](${url})`;
1239
1248
  return linkContent;
1240
1249
  },
1241
1250
  // A link destination comes in two shapes, is itself optional, and may have
@@ -1265,8 +1274,8 @@ const LINK = {
1265
1274
  // whitespace side by side can be split between them in as many ways as there
1266
1275
  // are characters, which costs a quadratic walk of every run that never
1267
1276
  // reaches the closing parenthesis.
1268
- importRegExp: /(?:\[(.+?)\])(?:\(\s*(?:(?:<((?:\\.|[^<>\n\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:[^"]*\\")*[^"]*)"|'((?:[^']*\\')*[^']*)'|\(((?:\\.|[^()\\])*)\)))?\s*)?\))/,
1269
- regExp: /(?:\[([^[\]]*(?:\[[^[\]]*\][^[\]]*)*)\])(?:\(\s*(?:(?:<((?:\\.|[^<>\n\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:[^"]*\\")*[^"]*)"|'((?:[^']*\\')*[^']*)'|\(((?:\\.|[^()\\])*)\)))?\s*)?\))$/,
1277
+ importRegExp: /(?:\[(.+?)\])(?:\(\s*(?:(?:<((?:\\[^\n\r]|[^<>\n\r\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:\\[\s\S]|[^"\\])*)"|'((?:\\[\s\S]|[^'\\])*)'|\(((?:\\[\s\S]|[^()\\])*)\)))?\s*)?\))/,
1278
+ regExp: /(?:\[([^[\]]*(?:\[[^[\]]*\][^[\]]*)*)\])(?:\(\s*(?:(?:<((?:\\[^\n\r]|[^<>\n\r\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:\\[\s\S]|[^"\\])*)"|'((?:\\[\s\S]|[^'\\])*)'|\(((?:\\[\s\S]|[^()\\])*)\)))?\s*)?\))$/,
1270
1279
  replace: (textNode, match) => {
1271
1280
  // https://spec.commonmark.org/0.31.2/#inline-link
1272
1281
  const source = match.input || '';
@@ -1442,7 +1451,17 @@ function createSelectionMarkdownExport(transformers, shouldPreserveNewLines = fa
1442
1451
  const textFormatTransformers = byType.textFormat.filter(transformer => transformer.format.length === 1).sort((a, b) => {
1443
1452
  return Number(a.format.includes('code')) - Number(b.format.includes('code'));
1444
1453
  });
1445
- return selection => {
1454
+ return exportedSelection => {
1455
+ // Every node asks whether it is selected, which reads the selection's
1456
+ // nodes. A read-only selection doesn't cache them, so this works on a
1457
+ // copy that does, instead of recomputing them for every node.
1458
+ // Its direction is cached too, since slicing a node asks for it.
1459
+ const nodes = exportedSelection.getNodes();
1460
+ const selection = exportedSelection.clone();
1461
+ selection.setCachedNodes(nodes);
1462
+ if ($isRangeSelection(selection)) {
1463
+ selection._cachedIsBackward = exportedSelection.isBackward();
1464
+ }
1446
1465
  const output = [];
1447
1466
  // The separator depends on the previously *emitted* block, not on the
1448
1467
  // previous root child: unselected children produce no output, so keying
@@ -1523,10 +1542,7 @@ function $exportChildrenForSelection(node, selection, textFormatTransformers, te
1523
1542
  if (!transformer.export) {
1524
1543
  continue;
1525
1544
  }
1526
- const result = transformer.export(child, parentNode => $exportChildrenForSelection(parentNode, selection, textFormatTransformers, textMatchTransformers, shouldPreserveNewLines, unclosedTags, [...unclosableTags, ...unclosedTags]).markdown, (textNode, textContent) => {
1527
- const slicedNode = $sliceSelectedTextNodeContent(selection, textNode, 'clone');
1528
- return exportTextFormat(textNode, slicedNode.getTextContent(), textFormatTransformers, unclosedTags, unclosableTags, shouldPreserveNewLines);
1529
- });
1545
+ const result = transformer.export(child, parentNode => $exportChildrenForSelection(parentNode, selection, textFormatTransformers, textMatchTransformers, shouldPreserveNewLines, unclosedTags, [...unclosableTags, ...unclosedTags]).markdown, textNode => $exportTextFormat(textNode, $exportedText(textNode, selection), textFormatTransformers, unclosedTags, unclosableTags, shouldPreserveNewLines, selection));
1530
1546
  if (result != null) {
1531
1547
  // Check extractWithChild if this node wasn't directly selected
1532
1548
  if (!childIncluded && $isElementNode(child) && child.getChildren().some(c => c.isSelected(selection)) && child.extractWithChild(child, selection, 'html')) {
@@ -1546,8 +1562,7 @@ function $exportChildrenForSelection(node, selection, textFormatTransformers, te
1546
1562
  }
1547
1563
  } else if ($isTextNode(child)) {
1548
1564
  if (childIncluded) {
1549
- const target = $sliceSelectedTextNodeContent(selection, child, 'clone');
1550
- output.push(exportTextFormat(child, target.getTextContent(), textFormatTransformers, unclosedTags, unclosableTags, shouldPreserveNewLines));
1565
+ output.push($exportTextFormat(child, $exportedText(child, selection), textFormatTransformers, unclosedTags, unclosableTags, shouldPreserveNewLines, selection));
1551
1566
  anyChildIncluded = true;
1552
1567
  }
1553
1568
  } else if ($isElementNode(child)) {
@@ -1612,7 +1627,7 @@ function $exportChildren(node, textTransformersIndex, textMatchTransformers, unc
1612
1627
  // E.g. **text [text**](https://lexical.io)
1613
1628
  // is invalid markdown, as the closing ** is inside the link.
1614
1629
  //
1615
- [...unclosableTags, ...unclosedTags], shouldPreserveNewLines), (textNode, textContent) => exportTextFormat(textNode, textContent, textTransformersIndex, unclosedTags, unclosableTags, shouldPreserveNewLines));
1630
+ [...unclosableTags, ...unclosedTags], shouldPreserveNewLines), (textNode, textContent) => $exportTextFormat(textNode, textContent, textTransformersIndex, unclosedTags, unclosableTags, shouldPreserveNewLines, null));
1616
1631
  if (result != null) {
1617
1632
  output.push(result);
1618
1633
  continue mainLoop;
@@ -1621,7 +1636,7 @@ function $exportChildren(node, textTransformersIndex, textMatchTransformers, unc
1621
1636
  if ($isLineBreakNode(child)) {
1622
1637
  output.push($exportLineBreak(child));
1623
1638
  } else if ($isTextNode(child)) {
1624
- output.push(exportTextFormat(child, child.getTextContent(), textTransformersIndex, unclosedTags, unclosableTags, shouldPreserveNewLines));
1639
+ output.push($exportTextFormat(child, child.getTextContent(), textTransformersIndex, unclosedTags, unclosableTags, shouldPreserveNewLines, null));
1625
1640
  } else if ($isElementNode(child)) {
1626
1641
  // empty paragraph returns ""
1627
1642
  output.push($exportChildren(child, textTransformersIndex, textMatchTransformers, unclosedTags, unclosableTags, shouldPreserveNewLines));
@@ -1634,14 +1649,14 @@ function $exportChildren(node, textTransformersIndex, textMatchTransformers, unc
1634
1649
  function $exportLineBreak(node) {
1635
1650
  return $getState(node, hardLineBreakState) + '\n';
1636
1651
  }
1637
- function exportTextFormat(node, textContent, textTransformers,
1652
+ function $exportTextFormat(node, textContent, textTransformers,
1638
1653
  // unclosed tags include the markdown tags that haven't been closed yet, and their associated formats
1639
- unclosedTags, unclosableTags, shouldPreserveNewLines = false) {
1654
+ unclosedTags, unclosableTags, shouldPreserveNewLines,
1655
+ // the selection being exported, which ends the text a tag can extend over
1656
+ selection) {
1640
1657
  // This function handles the case of a string looking like this: " foo "
1641
1658
  // Where it would be invalid markdown to generate: "** foo **"
1642
- // If the node has no format, we use the original text.
1643
- // Otherwise, we escape leading and trailing whitespaces to their corresponding code points,
1644
- // ensuring the returned string maintains its original formatting, e.g., "**&#32;&#32;&#32;foo&#32;&#32;&#32;**".
1659
+ // so the tags go inside the whitespace instead: " **foo** ".
1645
1660
 
1646
1661
  const isCode = node.hasFormat('code');
1647
1662
  let output = textContent;
@@ -1652,30 +1667,7 @@ unclosedTags, unclosableTags, shouldPreserveNewLines = false) {
1652
1667
  // Guard ambiguous '<' and '&' at node ends for constructs split across nodes.
1653
1668
  output = shouldPreserveNewLines ? output.replace(/([*_`~])/g, '\\$1') : output.replace(/([[\]*_`~\\]|<(?=\S|$)|&(?=#|$))/g, character => /[*_`~\\]/.test(character) ? `\\${character}` : `&#${character.charCodeAt(0)};`);
1654
1669
  }
1655
- let leadingSpace;
1656
- let trimmedOutput;
1657
- let trailingSpace;
1658
- let isWhitespaceOnly;
1659
- if (isCode) {
1660
- // Inline code is an atomic literal span with a content-derived fence, so
1661
- // its whitespace stays inside the fence and other formats wrap around it.
1662
- const {
1663
- fence,
1664
- padded
1665
- } = getCodeSpanDelimiter(textContent);
1666
- leadingSpace = '';
1667
- trailingSpace = '';
1668
- trimmedOutput = fence + padded + fence;
1669
- isWhitespaceOnly = false;
1670
- } else {
1671
- // Extract leading and trailing whitespaces.
1672
- // CommonMark flanking rules require formatting tags to be adjacent to non-whitespace characters.
1673
- const match = output.match(/^(\s*)(.*?)(\s*)$/s) || ['', '', output, ''];
1674
- leadingSpace = match[1];
1675
- trimmedOutput = match[2];
1676
- trailingSpace = match[3];
1677
- isWhitespaceOnly = trimmedOutput === '';
1678
- }
1670
+ const holdsTag = canHoldTag(textContent);
1679
1671
 
1680
1672
  // the opening tags to be added to the result
1681
1673
  let openingTags = '';
@@ -1683,31 +1675,27 @@ unclosedTags, unclosableTags, shouldPreserveNewLines = false) {
1683
1675
  let closingTagsBefore = '';
1684
1676
  let closingTagsAfter = '';
1685
1677
  const prevNode = getTextSibling(node, true);
1686
- const nextNode = getTextSibling(node, false);
1687
1678
  const applied = new Set();
1688
1679
  for (const transformer of textTransformers) {
1689
1680
  const format = transformer.format[0];
1690
1681
  const tag = transformer.tag;
1691
1682
 
1692
- // Inline code uses a content-derived fence handled above, not a static tag.
1683
+ // Inline code uses a content-derived fence handled below, not a static tag.
1693
1684
  if (format === 'code') {
1694
1685
  continue;
1695
1686
  }
1696
1687
 
1697
1688
  // dedup applied formats
1698
- // `checkHasFormat` reads the node's whole text, but the text being
1699
- // exported may be a selection slice of it. When the slice is entirely
1700
- // whitespace the tags are dropped by the early return below, so
1701
- // registering them here would leave an unclosed tag for a later node to
1702
- // close. In the full-document path `checkHasFormat` already rejects
1703
- // whitespace-only nodes, so this guard only affects sliced content.
1704
- if (!isWhitespaceOnly && checkHasFormat(node, format) && !applied.has(format)) {
1689
+ if (holdsTag && node.hasFormat(format) && !applied.has(format)) {
1705
1690
  applied.add(format);
1706
1691
 
1707
1692
  // append the tag to openingTags, if it's not applied to the previous nodes,
1708
- // or the nodes before that (which would result in an unclosed tag)
1709
- if (!checkHasFormat(prevNode, format) || !unclosedTags.find(element => element.tag === tag)) {
1693
+ // or the nodes before that (which would result in an unclosed tag).
1694
+ // A whitespace-only previous node with the format keeps the tag open
1695
+ // without emitting it, so it counts as applied here.
1696
+ if (!hasFormat(prevNode, format) || !unclosedTags.find(element => element.tag === tag)) {
1710
1697
  unclosedTags.push({
1698
+ answers: new Map(),
1711
1699
  format,
1712
1700
  tag
1713
1701
  });
@@ -1719,10 +1707,8 @@ unclosedTags, unclosableTags, shouldPreserveNewLines = false) {
1719
1707
  // close any tags in the same order they were applied, if necessary
1720
1708
  for (let i = 0; i < unclosedTags.length; i++) {
1721
1709
  const nodeHasFormat = hasFormat(node, unclosedTags[i].format);
1722
- const nextNodeHasFormat = hasFormat(nextNode, unclosedTags[i].format);
1723
-
1724
- // prevent adding closing tag if next sibling will do it
1725
- if (nodeHasFormat && nextNodeHasFormat) {
1710
+ // prevent adding closing tag if later text with the format will do it
1711
+ if (nodeHasFormat && $continuesTag(node, unclosedTags[i], selection)) {
1726
1712
  continue;
1727
1713
  }
1728
1714
  const unhandledUnclosedTags = [...unclosedTags]; // Shallow copy to avoid modifying the original array
@@ -1732,7 +1718,7 @@ unclosedTags, unclosableTags, shouldPreserveNewLines = false) {
1732
1718
 
1733
1719
  // If tag is unclosable, don't close it and leave it in the original array,
1734
1720
  // So that it can be closed when it's no longer unclosable
1735
- if (unclosableTags && unclosedTag && unclosableTags.find(element => element.tag === unclosedTag.tag)) {
1721
+ if (unclosedTag && unclosableTags.find(element => element.tag === unclosedTag.tag)) {
1736
1722
  continue;
1737
1723
  }
1738
1724
  if (unclosedTag && typeof unclosedTag.tag === 'string') {
@@ -1740,7 +1726,7 @@ unclosedTags, unclosableTags, shouldPreserveNewLines = false) {
1740
1726
  // Handles cases where the tag has not been closed before, e.g. if the previous node
1741
1727
  // was a text match transformer that did not account for closing tags of the next node (e.g. a link)
1742
1728
  closingTagsBefore += unclosedTag.tag;
1743
- } else if (!nextNodeHasFormat) {
1729
+ } else {
1744
1730
  closingTagsAfter += unclosedTag.tag;
1745
1731
  }
1746
1732
  }
@@ -1749,13 +1735,28 @@ unclosedTags, unclosableTags, shouldPreserveNewLines = false) {
1749
1735
  }
1750
1736
  break;
1751
1737
  }
1752
- // If the node is entirely whitespace, we don't apply opening/closing tags around it.
1753
- // However, it must still output closing tags from previous nodes.
1754
- if (isWhitespaceOnly && !node.hasFormat('code')) {
1755
- return closingTagsBefore + output;
1756
- }
1757
-
1758
- // Flanking Compliance: Notice how openingTags and closingTagsAfter are placed INSIDE the whitespace boundaries!
1738
+ if (isCode && textContent !== '') {
1739
+ // Inline code is an atomic literal span with a content-derived fence, so
1740
+ // its whitespace stays inside the fence and other formats wrap around it.
1741
+ const {
1742
+ fence,
1743
+ padded
1744
+ } = getCodeSpanDelimiter(textContent);
1745
+ output = fence + padded + fence;
1746
+ } else if (!holdsTag) {
1747
+ // If the node is entirely whitespace, we don't apply opening/closing tags
1748
+ // around it, but it must still output any closing tags.
1749
+ return closingTagsBefore + closingTagsAfter + output;
1750
+ }
1751
+
1752
+ // Flanking Compliance: CommonMark flanking rules require formatting tags to be
1753
+ // adjacent to non-whitespace characters, so openingTags and closingTagsAfter
1754
+ // are placed INSIDE the whitespace boundaries!
1755
+ // (Trimming rather than a regex keeps this linear in long whitespace runs.)
1756
+ const trimmedStart = output.trimStart();
1757
+ const trimmedOutput = trimmedStart.trimEnd();
1758
+ const leadingSpace = output.slice(0, output.length - trimmedStart.length);
1759
+ const trailingSpace = trimmedStart.slice(trimmedOutput.length);
1759
1760
  return closingTagsBefore + leadingSpace + openingTags + trimmedOutput + closingTagsAfter + trailingSpace;
1760
1761
  }
1761
1762
  function getTextSibling(node, backward) {
@@ -1765,20 +1766,66 @@ function getTextSibling(node, backward) {
1765
1766
  }
1766
1767
  return null;
1767
1768
  }
1769
+
1770
+ /**
1771
+ * Whether `tag` carries on past `node`, which has its format, to exported
1772
+ * text that can hold it. Whitespace-only siblings cannot (the tag must close
1773
+ * before the space), so they are looked past while they keep the format.
1774
+ * Text outside the exported selection does not count. Every whitespace node
1775
+ * the walk passes over shares its answer, so that is remembered on the tag
1776
+ * and each run is walked once, whatever order its nodes are asked in.
1777
+ */
1778
+ function $continuesTag(node, tag, selection) {
1779
+ const {
1780
+ answers
1781
+ } = tag;
1782
+ const known = answers.get(node.getKey());
1783
+ if (known !== undefined) {
1784
+ return known;
1785
+ }
1786
+ const passed = [];
1787
+ let next = getTextSibling(node, false);
1788
+ let result = false;
1789
+ while (next && next.hasFormat(tag.format) && (!selection || next.isSelected(selection))) {
1790
+ const text = $exportedText(next, selection);
1791
+ if (canHoldTag(text)) {
1792
+ result = true;
1793
+ break;
1794
+ }
1795
+ // Markdown import matches a tag within one line.
1796
+ if (text.includes('\n')) {
1797
+ break;
1798
+ }
1799
+ // A whitespace node already answered for shares the answer.
1800
+ const passedAnswer = answers.get(next.getKey());
1801
+ if (passedAnswer !== undefined) {
1802
+ result = passedAnswer;
1803
+ break;
1804
+ }
1805
+ passed.push(next);
1806
+ next = getTextSibling(next, false);
1807
+ }
1808
+ answers.set(node.getKey(), result);
1809
+ for (const passedNode of passed) {
1810
+ answers.set(passedNode.getKey(), result);
1811
+ }
1812
+ return result;
1813
+ }
1814
+
1815
+ /** The text of `node` that the export emits: its selected slice, if any. */
1816
+ function $exportedText(node, selection) {
1817
+ return selection ? $sliceSelectedTextNodeContent(selection, node, 'clone').getTextContent() : node.getTextContent();
1818
+ }
1768
1819
  function hasFormat(node, format) {
1769
1820
  return $isTextNode(node) && node.hasFormat(format);
1770
1821
  }
1771
- function checkHasFormat(n, f) {
1772
- if (!hasFormat(n, f)) {
1773
- return false;
1774
- }
1775
- if (f === 'code') {
1776
- return true;
1777
- }
1778
- if (n && /^\s*$/.test(n.getTextContent())) {
1779
- return false;
1780
- }
1781
- return true;
1822
+
1823
+ /**
1824
+ * Whether exported text can carry a format's tag: CommonMark flanking rules
1825
+ * keep tags off whitespace.
1826
+ */
1827
+ function canHoldTag(text) {
1828
+ return /\S/.test(text);
1782
1829
  }
1783
1830
 
1784
1831
  /**
@@ -1891,7 +1938,7 @@ function importTextTransformers(textNode, textFormatTransformersIndex, textMatch
1891
1938
  // or links). A code span must never be partially consumed by a text
1892
1939
  // match, so prefer the code format unless the text match fully wraps it
1893
1940
  // (in which case the code span is part of the match's raw content).
1894
- // https://github.com/facebook/lexical/issues/8687
1941
+ // https://github.com/meta/lexical/issues/8687
1895
1942
  if (foundTextMatch.startIndex <= foundTextFormat.startIndex && foundTextMatch.endIndex >= foundTextFormat.endIndex) {
1896
1943
  // foundTextMatch wraps the code span - apply foundTextMatch
1897
1944
  foundTextFormat = null;
@@ -6,4 +6,4 @@
6
6
  *
7
7
  */
8
8
 
9
- import{$isParagraphNode as t,$isTextNode as e,$setState as n,$createTextNode as o,$getState as r,$findMatchingParent as s,$createLineBreakNode as l,createState as i,$isLineBreakNode as c,$getRoot as f,$isElementNode as u,$isDecoratorNode as a,$isTabNode as d,$createTabNode as g,$createParagraphNode as h,mergeRegister as p,COLLABORATION_TAG as x,HISTORIC_TAG as m,COMPOSITION_END_TAG as C,$getSelection as I,$isRangeSelection as T,$addUpdateTag as v,HISTORY_PUSH_TAG as $,KEY_ENTER_COMMAND as y,COMMAND_PRIORITY_LOW as E,$isRootOrShadowRoot as S,$createRangeSelection as b,$setSelection as w,TEXT_TYPE_TO_FORMAT as N,ArtificialNode__DO_NOT_USE as k}from"lexical";import{$sliceSelectedTextNodeContent as F}from"@lexical/selection";import{CodeNode as O,$createCodeNode as P,$isCodeNode as M}from"@lexical/code-core";import{LinkNode as R,$isLinkNode as _,$createLinkNode as B,$isAutoLinkNode as L}from"@lexical/link";import{ListNode as A,ListItemNode as j,$isListNode as z,$createListItemNode as W,$createListNode as U,$isListItemNode as D}from"@lexical/list";import{HeadingNode as X,QuoteNode as Z,$isHeadingNode as H,$isQuoteNode as K,$createQuoteNode as q,$createHeadingNode as G}from"@lexical/rich-text";function J(t,e){const n={};for(const o of t){const t=e(o);t&&(n[t]?n[t].push(o):n[t]=[o])}return n}function Q(t){const e=J(t,t=>t.type);return{element:e.element||[],multilineElement:e["multiline-element"]||[],textFormat:e["text-format"]||[],textMatch:e["text-match"]||[]}}const V=/[!-/:-@[-`{-~\s]/,Y=/\s/,tt=/[!"#$%&'()*+,\-./:;<=>?@[\]^_`{|}~]/,et=/^\s{0,3}$/;function nt(n){if(!t(n))return!1;const o=n.getFirstChild();return null==o||1===n.getChildrenSize()&&e(o)&&et.test(o.getTextContent())}function ot(t){return t.replace(/\\([!-/:-@[-`{-~])|&#(?:(\d{1,7})|[xX]([\da-fA-F]{1,6}));/g,(t,e,n,o)=>e??function(t,e){const n=void 0!==t?Number(t):parseInt(e,16);return 0===n||n>=55296&&n<=57343||n>1114111?"\ufffd":String.fromCodePoint(n)}(n,o))}function rt(t,e){let n=0;for(let o=e-1;o>=0&&"\\"===t[o];o--)n++;return n%2==1}function st(t){const e=e=>{let n=0;for(let o=e-1;o>=0&&"\\"===t[o];o--)n++;return n%2==1},n=[];let o=0;for(;o<t.length;)if("`"===t[o]){let e=1;for(;o+e<t.length&&"`"===t[o+e];)e++;n.push({index:o,length:e}),o+=e}else o++;const r=[];let s=0;for(;s<n.length;){const o=n[s];if(e(o.index)){s++;continue}let l=-1;for(let t=s+1;t<n.length;t++)if(n[t].length===o.length){l=t;break}if(-1===l){s++;continue}const i=n[l];let c=t.slice(o.index+o.length,i.index);c.length>=2&&c.startsWith(" ")&&c.endsWith(" ")&&/[^ ]/.test(c)&&(c=c.slice(1,-1)),r.push({content:c,endIndex:i.index+i.length,startIndex:o.index}),s=l+1}return r}function lt(t,e,n,o,r){if(!it(e,n,o,r))return!1;if("*"===t)return!0;if("_"===t){if(!it(e,n,o,!r))return!0;const t=r?e[n-1]:e[n+o];return void 0!==t&&tt.test(t)}return!0}function it(t,e,n,o){const r=t[e-1],s=t[e+n],[l,i]=o?[s,r]:[r,s];return void 0!==l&&!Y.test(l)&&(!tt.test(l)||void 0===i||Y.test(i)||tt.test(i))}const ct=/^(\s*)(\d{1,})\.\s/,ft=/^(\s*)[-*+]\s/,ut=/^(\s*)(?:[-*+]\s)?\s?(\[(\s|x)?\])\s/i,at=/^(#{1,6})\s/,dt=/^>\s/,gt=/^([ \t]*`{3,})([\w-]+)?[ \t]?/,ht=/^[ \t]*`{3,}$/,pt=/^[ \t]*```[^`]+(?:(?:`{1,2}|`{4,})[^`]+)*```(?:[^`]|$)/,xt=/^(?:\|)(.+)(?:\|)\s?$/;function mt(t){if("|"!==t[0])return!1;const{length:e}=t;let n=1,o=0;for(;n<e;){let e=n;for(" "===t[e]&&e++,":"===t[e]&&e++;"-"===t[e];)e++;if(":"===t[e]&&e++," "===t[e]&&e++,"|"!==t[e])break;o++,n=e+1}return o>0&&(n===e||n===e-1&&/\s/.test(t[n]))}const Ct=/^<[a-z_][\w-]*(?:\s[^<>]*)?\/?>/i,It=/^<\/[a-z_][\w-]*\s*>/i,Tt=t=>new RegExp(`(?:${t.source})$`,t.flags),vt=/* @__PURE__ */i("mdListMarker",{parse:t=>"string"==typeof t&&/^[-*+]$/.test(t)?t:"-",resetOnCopyNode:!0}),$t=/* @__PURE__ */i("mdCodeFence",{parse:t=>"string"==typeof t&&/^`{3,}$/.test(t)?t:"```",resetOnCopyNode:!0}),yt=/* @__PURE__ */i("mdCodeMeta",{parse:t=>"string"==typeof t?t:"",resetOnCopyNode:!0}),Et=/* @__PURE__ */i("mdHardLineBreak",{parse:t=>"string"==typeof t&&/^(\\| {2,})$/.test(t)?t:"",resetOnCopyNode:!0});function St(t){if(t.endsWith("\\")){let e=0;for(let n=t.length-1;n>=0&&"\\"===t[n];n--)e++;return e%2==1?[t.slice(0,-1),"\\"]:null}const e=t.match(/^(.*?\S)( {2,})$/);return e?[e[1],e[2]]:null}function bt(t){const o=l(),r=function(t){const n=t.getChildren(),o=n.length-1,r=n[o];if(!e(r))return null;const s=r.getTextContent(),l=St(s);if(null!==l){const[t,e]=l;return r.setTextContent(t),e}return/^ {2,}$/.test(s)&&function(t,e){for(let n=e-1;n>=0;n--){if(c(t[n]))return!1;if(/\S/.test(t[n].getTextContent()))return!0}return!1}(n,o)?(r.setTextContent(""),s):null}(t);return null!==r&&n(o,Et,r),o}function wt(t){return K(t)}function Nt(t,e){let n=0;for(;n<e&&(" "===t[n]||"\t"===t[n]);)n++;return t.slice(n)}function kt(t,e){const o=t.getLastChild();e&&M(o)&&n(o,yt,e)}const Ft=t=>(e,n,o,r)=>{const s=t(o);s.append(...n),e.replace(s),r||s.select(0,0)};function Ot(t){let e=0;for(const n of t)e+="\t"===n?4-e%4:1;return e}let Pt=null,Mt=!1;const Rt=t=>(e,o,r,s)=>{if(H(e)||!s&&wt(e))return!1;const l=e.getPreviousSibling(),i=e.getNextSibling(),c=W("check"===t?/^x$/i.test(r[3]||""):void 0),f=r[0].trim()[0],u="bullet"!==t&&"check"!==t||f!==vt.parse(f)?void 0:f,a=Pt;let d=l;if(null!==a){if(Mt)for(;null!==d&&nt(d);)d=d.getPreviousSibling();z(d)||(a.length=0)}const g=null===a||0===a.length?function(t){const e=t.match(/\t/g),n=t.match(/ /g);let o=0;return e&&(o+=e.length),n&&(o+=Math.floor(n.length/4)),o}(r[1]):function(t,e){const n=Ot(e);for(let e=t.length-1;e>=0;e--)if(n>=t[e])return e+1;return 0}(a,r[1]);if(z(i)&&i.getListType()===t){const n=i.getFirstChild();null!==n?n.insertBefore(c):i.append(c),"number"===t&&0===g&&i.setStart(Number(r[2])),e.remove()}else if(z(d)&&(d.getListType()===t||g>0))d.append(c),e.remove();else{const n=U(t,"number"===t?Number(r[2]):void 0);n.append(c),e.replace(n)}c.append(...o),s||c.select(0,0),g&&(c.setIndent(g),function(t,e,n){const o=t.getParent();if(!z(o)||o.getListType()===e)return;const r=o.getParent();if(!D(r))return;const s=U(e,"number"===e?Number(n[2]):void 0),l=null===t.getPreviousSibling();s.append(t);const i=W();i.append(s),l?r.insertBefore(i):r.insertAfter(i),0===o.getChildrenSize()&&r.remove()}(c,t,r));const h=c.getParent();u&&z(h)&&n(h,vt,u),null!==a&&function(t,e,n,o){for(let n=t.length;n<e;n++)t[n]=4*(n+1);t.length=e,t[e]=function(t,e){let n=t[0];if("check"===e){const e=n.slice(t[1].length).match(/^[-*+]\s/);e&&(n=t[1]+e[0])}return Math.min(Ot(n),Ot(t[1])+4)}(n,o)}(a,g,r,t)},_t=(t,e,n,o)=>{const s=[],l=t.getChildren();let i=0;for(const c of l)if(D(c)){if(1===c.getChildrenSize()){const t=c.getFirstChild();if(z(t)){const r=_t(t,e,n+1,o);r&&s.push(r);continue}}if(o&&!c.getChildren().some(t=>t.isSelected(o)))continue;const l=" ".repeat(4*n),f=t.getListType(),u=r(t,vt),a="number"===f?`${t.getStart()+i}. `:"check"===f?`${u} [${c.getChecked()?"x":" "}] `:u+" ";let d=e(c);"number"!==f&&(d=d.replace(/^(\s{0,3}\d+)(\.\s)/,"$1\\$2")),s.push(l+a+d),i++}return s.join("\n")},Bt=/* @__PURE__ */Ft(t=>{const e="h"+t[1].length;return G(e)}),Lt={dependencies:[X],export:(t,e)=>{if(!H(t))return null;const n=Number(t.getTag().slice(1));return"#".repeat(n)+" "+e(t)},regExp:at,replace:(t,e,n,o)=>!(!o&&wt(t))&&Bt(t,e,n,o),triggerOnEnter:!0,type:"element"},At={dependencies:[Z],export:(t,e)=>{if(!K(t))return null;const n=e(t).split("\n"),o=[];for(const t of n)o.push("> "+t);return o.join("\n")},regExp:dt,replace:(t,e,n,o)=>{if(o){const n=t.getPreviousSibling();if(K(n))return n.splice(n.getChildrenSize(),0,[bt(n),...e]),void t.remove()}const r=q();r.append(...e),t.replace(r),o||r.select(0,0)},triggerOnEnter:!0,type:"element"},jt={dependencies:[O],export:t=>{if(!M(t))return null;const e=t.getTextContent();let n=r(t,$t);if(e.indexOf(n)>-1){const t=e.match(/`{3,}/g);if(t){const e=Math.max(...t.map(t=>t.length));n="`".repeat(e+1)}}const o=t.getLanguage()||"",s=o?r(t,yt):"";return n+o+(s?" "+s:"")+(e?"\n"+e:"")+"\n"+n},handleImportAfterStartMatch:({lines:t,rootNode:e,startLineIndex:n,startMatch:o})=>{const r=o[1],s=r.trim().length,l=t[n],i=o.index+r.length,c=l.slice(i),f=new RegExp(`\`{${s},}$`);if(f.test(c)){const t=c.match(f),r=c.slice(0,c.lastIndexOf(t[0])),s=[...o];return s[2]="",jt.replace(e,null,s,t,[r],!0),[!0,n]}const u=new RegExp(`^[ \\t]*\`{${s},}$`);for(let r=n+1;r<t.length;r++){const s=t[r];if(u.test(s)){const i=s.match(u),c=t.slice(n+1,r),f=o[2]?l.slice(o[0].length).trim():"";return c.unshift(f?"":l.slice(o[0].length)),jt.replace(e,null,o,i,c,!0),kt(e,f),[!0,r]}}const a=t.slice(n+1);return a.unshift(l.slice(o[0].length)),jt.replace(e,null,o,null,a,!0),[!0,t.length-1]},regExpEnd:{optional:!0,regExp:ht},regExpStart:gt,replace:(t,e,r,s,l,i)=>{let c,f;const u=r[1]?r[1].trim():"```",a=r[2]||void 0,d=r[1]?r[1].length-r[1].trimStart().length:0;if(!e&&l){if(1===l.length)s?(c=P(a),f=Nt(l[0],d)):(c=P(a),f=l[0].startsWith(" ")?l[0].slice(1):l[0]);else{for(c=P(a),l.length>0&&(0===l[0].trim().length?l.shift():l[0].startsWith(" ")&&(l[0]=l[0].slice(1)));l.length>0&&!l[l.length-1].length;)l.pop();f=l.map(t=>Nt(t,d)).join("\n")}n(c,$t,u);const e=o(f);c.append(e),t.append(c)}else if(e){if(!i&&wt(t))return!1;Ft(t=>P(t?t[2]:void 0))(t,e,r,i)}},type:"multiline-element"},zt={dependencies:[A,j],export:(t,e,n)=>z(t)?_t(t,e,0,n):null,regExp:ft,replace:/* @__PURE__ */Rt("bullet"),triggerOnEnter:!0,type:"element"},Wt={dependencies:[A,j],export:(t,e,n)=>z(t)?_t(t,e,0,n):null,regExp:ut,replace:/* @__PURE__ */Rt("check"),triggerOnEnter:!0,type:"element"},Ut={dependencies:[A,j],export:(t,e,n)=>z(t)?_t(t,e,0,n):null,regExp:ct,replace:/* @__PURE__ */Rt("number"),triggerOnEnter:!0,type:"element"},Dt={format:["code"],tag:"`",type:"text-format"},Xt={format:["highlight"],tag:"==",type:"text-format"},Zt={format:["bold","italic"],tag:"***",type:"text-format"},Ht={format:["bold","italic"],intraword:!1,tag:"___",type:"text-format"},Kt={format:["bold"],tag:"**",type:"text-format"},qt={format:["bold"],intraword:!1,tag:"__",type:"text-format"},Gt={format:["strikethrough"],tag:"~~",type:"text-format"},Jt={format:["italic"],tag:"*",type:"text-format"},Qt={format:["italic"],intraword:!1,tag:"_",type:"text-format"};function Vt(t){return t.replace(/&(?=#\d+;|#[Xx][\dA-Fa-f]+;|[A-Za-z][\dA-Za-z]*;)/g,"&#38;")}const Yt={dependencies:[R],export:(t,e,n)=>{if(!_(t)||L(t))return null;const o=e(t);let r=t.getTitle();null!=r&&(r=Vt(r).replace(/([\\"])/g,"\\$1"));const s=t.getURL(),l=Vt(s),i=""===s||/\s/.test(s)?`<${l.replace(/([\\<>])/g,"\\$1").replace(/\r/g,"&#13;").replace(/\n/g,"&#10;")}>`:l.replace(/([\\()])/g,"\\$1").replace(/^</,"\\<");return r?`[${o}](${i} "${r}")`:`[${o}](${i})`},importRegExp:/(?:\[(.+?)\])(?:\(\s*(?:(?:<((?:\\.|[^<>\n\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:[^"]*\\")*[^"]*)"|'((?:[^']*\\')*[^']*)'|\(((?:\\.|[^()\\])*)\)))?\s*)?\))/,regExp:/(?:\[([^[\]]*(?:\[[^[\]]*\][^[\]]*)*)\])(?:\(\s*(?:(?:<((?:\\.|[^<>\n\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:[^"]*\\")*[^"]*)"|'((?:[^']*\\')*[^']*)'|\(((?:\\.|[^()\\])*)\)))?\s*)?\))$/,replace:(t,e)=>{const n=e.input||"",r=e.index||0;if(s(t,_)||rt(n,r)||rt(n,r+e[1].length+1))return;const[,l,i,c,f,u,a]=e,d=ot(i??c??""),g=f??u??a,h=null!=g?ot(g):void 0,p=B(d,{title:h}),x=function(t){const e=st(t);let n=0;const o=t.replace(/\\.|[[\]]/g,(t,o)=>{for(;n<e.length&&o>=e[n].endIndex;)n++;return n<e.length&&o>=e[n].startIndex||"\\"===t[0]?" ".repeat(t.length):t}),r=[];let s=!1;for(let t=0;t<o.length;t++){const e=o[t];"["===e?r.push(t):"]"===e&&void 0===r.pop()&&(s=!0)}return r.pop()??(s?null:-1)}(l);if(null===x)return;let m=l,C="";-1!==x&&(C="["+function(t){let e="",n=0;for(const{startIndex:o,endIndex:r}of st(t))e+=ot(t.slice(n,o))+t.slice(o,r),n=r;return e+ot(t.slice(n))}(l.slice(0,x)),m=l.slice(x+1));const I=t.getFormat(),T=o(m);return T.setFormat(I),p.append(T),t.replace(p),C&&p.insertBefore(o(C).setFormat(I)),T},trigger:")",type:"text-match"},te=[Lt,At,zt,Ut],ee=[jt],ne=[Dt,Zt,Ht,Kt,qt,Xt,Jt,Qt,Gt],oe=[Yt];function re(...t){return t.flat()}const se=/* @__PURE__ */re(te,ee,ne,oe);function le(t,e=!1){const n=t.split("\n");let o=0;const r=[];for(let t=0;t<n.length;t++){const s=n[t],l=s.trimEnd(),i=r[r.length-1],c=t<n.length-1?St(s):null,f=void 0!==i&&null!==St(i);if(pt.test(l))r.push(l);else if(0===o){{const t=l.match(gt);if(t){o=t[1].trim().length,r.push(l);continue}}""===l||""===i||!i||at.test(i)||at.test(l)||dt.test(l)||ct.test(l)||ft.test(l)||ut.test(l)||xt.test(l)||mt(l)||f||!e||Ct.test(l)||It.test(l)||Tt(It).test(i)||Tt(Ct).test(i)||ht.test(i)?r.push(!e&&""!==l||null!==c?s:l):r[r.length-1]=i+" "+(null===c?l:s).trimStart()}else{if(ht.test(l)&&l.trim().length>=o){o=0,r.push(l);continue}r.push(s)}}return r.join("\n")}function ie(t,e,n,o,r,s){let l=t.isSelected(e);for(const i of n){if(!i.export)continue;const n=i.export(t,t=>ce(t,e,o,r,s).markdown,e);if(null!=n)return!l&&u(t)&&ce(t,e,o,r,s).shouldInclude&&(l=!0),{markdown:n,shouldInclude:l}}if(u(t)){const n=ce(t,e,o,r,s);return{markdown:n.markdown,shouldInclude:l||n.shouldInclude}}return a(t)?{markdown:t.getTextContent(),shouldInclude:l}:{markdown:null,shouldInclude:l}}function ce(t,n,o,r,s,l,i){const f=[],d=t.getChildren();let g=!1;l||(l=[]),i||(i=[]);t:for(const t of d){let d=t.isSelected(n);for(const e of r){if(!e.export)continue;const c=e.export(t,t=>ce(t,n,o,r,s,l,[...i,...l]).markdown,(t,e)=>de(t,F(n,t,"clone").getTextContent(),o,l,i,s));if(null!=c){!d&&u(t)&&t.getChildren().some(t=>t.isSelected(n))&&t.extractWithChild(t,n,"html")&&(d=!0),d&&(f.push(c),g=!0);continue t}}if(c(t))d&&(f.push(ae(t)),g=!0);else if(e(t)){if(d){const e=F(n,t,"clone");f.push(de(t,e.getTextContent(),o,l,i,s)),g=!0}}else if(u(t)){const e=ce(t,n,o,r,s,l,i);!d&&e.shouldInclude&&t.extractWithChild(t,n,"html")&&(d=!0),(d||e.shouldInclude)&&(f.push(e.markdown),g=!0)}else a(t)&&d&&(f.push(t.getTextContent()),g=!0)}return{markdown:f.join(""),shouldInclude:g}}function fe(t,e,n,o,r){for(const s of e){if(!s.export)continue;const e=s.export(t,t=>ue(t,n,o,void 0,void 0,r));if(null!=e)return e}return u(t)?ue(t,n,o,void 0,void 0,r):a(t)?t.getTextContent():null}function ue(t,n,o,r,s,l=!1){const i=[],f=t.getChildren();r||(r=[]),s||(s=[]);t:for(const t of f){for(const e of o){if(!e.export)continue;const c=e.export(t,t=>ue(t,n,o,r,[...s,...r],l),(t,e)=>de(t,e,n,r,s,l));if(null!=c){i.push(c);continue t}}c(t)?i.push(ae(t)):e(t)?i.push(de(t,t.getTextContent(),n,r,s,l)):u(t)?i.push(ue(t,n,o,r,s,l)):a(t)&&i.push(t.getTextContent())}return i.join("")}function ae(t){return r(t,Et)+"\n"}function de(t,e,n,o,r,s=!1){const l=t.hasFormat("code");let i,c,f,u,a=e;if(l||(a=s?a.replace(/([*_`~])/g,"\\$1"):a.replace(/([[\]*_`~\\]|<(?=\S|$)|&(?=#|$))/g,t=>/[*_`~\\]/.test(t)?`\\${t}`:`&#${t.charCodeAt(0)};`)),l){const{fence:t,padded:n}=function(t){const e=t.match(/`+/g),n=e?Math.max(...e.map(t=>t.length)):0;return{fence:"`".repeat(n+1),padded:0===t.length||t.includes("`")||/^\s/.test(t)&&/\s$/.test(t)?` ${t} `:t}}(e);i="",f="",c=t+n+t,u=!1}else{const t=a.match(/^(\s*)(.*?)(\s*)$/s)||["","",a,""];i=t[1],c=t[2],f=t[3],u=""===c}let d="",g="",h="";const p=ge(t,!0),x=ge(t,!1),m=new Set;for(const e of n){const n=e.format[0],r=e.tag;"code"!==n&&(u||!pe(t,n)||m.has(n)||(m.add(n),pe(p,n)&&o.find(t=>t.tag===r)||(o.push({format:n,tag:r}),d+=r)))}for(let e=0;e<o.length;e++){const n=he(t,o[e].format),s=he(x,o[e].format);if(n&&s)continue;const l=[...o];for(;l.length>e;){const t=l.pop();r&&t&&r.find(e=>e.tag===t.tag)||(t&&"string"==typeof t.tag&&(n?s||(h+=t.tag):g+=t.tag),o.pop())}break}return u&&!t.hasFormat("code")?g+a:g+i+d+c+h+f}function ge(t,n){const o=n?t.getPreviousSibling():t.getNextSibling();return e(o)?o:null}function he(t,n){return e(t)&&t.hasFormat(n)}function pe(t,e){return!(!he(t,e)||"code"!==e&&t&&/^\s*$/.test(t.getTextContent()))}function xe(t,e){const n=new RegExp(e.source,e.flags.replace(/[gy]/g,"")+"g");let o;for(;null!==(o=n.exec(t));){if(!rt(t,o.index))return o;n.lastIndex=o.index+1}return null}function me(t){return e(t)&&!t.hasFormat("code")}function Ce(t,e,n){let o=function(t,e){const n=t.getTextContent(),o=e.transformersByTag["`"],r=[];let s=null;if(o){const t=st(n);for(const e of t)s||(s={content:e.content,endIndex:e.endIndex,startIndex:e.startIndex,tag:"`"}),r.push({end:e.endIndex,start:e.startIndex})}const l=function(t,e,n=[]){const o=[],r=new Set(Object.keys(e.transformersByTag).filter(t=>"`"!==t[0]).map(t=>t[0])),s=e=>{let n=0;for(let o=e-1;o>=0&&"\\"===t[o];o--)n++;return n%2==1},l=t=>n.some(e=>t>=e.start&&t<e.end);let i=0;for(;i<t.length;){const e=t[i];if(!r.has(e)||s(i)||l(i)){i++;continue}let n=1;for(;i+n<t.length&&t[i+n]===e;)n++;const c=lt(e,t,i,n,!0),f=lt(e,t,i,n,!1);(c||f)&&o.push({active:!0,canClose:f,canOpen:c,char:e,index:i,length:n}),i+=n}return o}(n,e,r),i=l.length>0?function(t,e,n){const o={};let r=0,s=null;for(;r<e.length;){const l=e[r];if(!l.active||!l.canClose||0===l.length){r++;continue}const i=`${l.char}${l.canOpen}${l.length%3}`,c=o[i]??-1;let f=!1;for(let o=r-1;o>c;o--){const i=e[o];if(!i.active||!i.canOpen||0===i.length||i.char!==l.char)continue;if((i.canClose||l.canOpen)&&(i.length+l.length)%3==0&&i.length%3!=0&&l.length%3!=0)continue;const c=Math.min(i.length,l.length),u=Object.keys(n.transformersByTag).filter(t=>t[0]===i.char&&t.length<=c).sort((t,e)=>e.length-t.length)[0];if(!u)continue;f=!0;const a=u.length,d={content:t.slice(i.index+i.length,l.index),endIndex:l.index+a,startIndex:i.index+(i.length-a),tag:u};(!s||d.startIndex<s.startIndex||d.startIndex===s.startIndex&&d.endIndex>s.endIndex)&&(s=d);for(let t=o+1;t<r;t++)e[t].active=!1;i.length-=a,l.length-=a,i.active=i.length>0,l.length>0?l.index+=a:(l.active=!1,r++);break}f||(o[i]=r-1,l.canOpen||(l.active=!1),r++)}return s}(n,l,e):null;let c=null,f=null;if(s&&i?i.startIndex<=s.startIndex&&i.endIndex>=s.endIndex?(c=i,f=e.transformersByTag[i.tag]):(c=s,f=o):s?(c=s,f=o):i&&(c=i,f=e.transformersByTag[i.tag]),!c||!f)return null;const u=[n.slice(c.startIndex,c.endIndex),c.tag,c.content];return u.index=c.startIndex,u.input=n,{endIndex:c.endIndex,isCodeSpan:f===o,match:u,startIndex:c.startIndex,transformer:f}}(t,e),r=function(t,e){const n=t;let o,r,s,l;for(const t of e){if(!t.replace||!t.importRegExp)continue;const e=n.getTextContent(),i=void 0!==Yt.importRegExp&&t.importRegExp.source===Yt.importRegExp.source?xe(e,t.importRegExp):e.match(t.importRegExp);if(!i)continue;const c=i.index||0,f=t.getEndIndex?t.getEndIndex(n,i):c+i[0].length;!1!==f&&(void 0===o||void 0===r||c<o&&(f>r||f<=o))&&(o=c,r=f,s=t,l=i)}return void 0===o||void 0===r||void 0===s||void 0===l?null:{endIndex:r,match:l,startIndex:o,transformer:s}}(t,n);if(o&&r&&(o.isCodeSpan?r.startIndex<=o.startIndex&&r.endIndex>=o.endIndex?o=null:r=null:o.startIndex<=r.startIndex&&o.endIndex>=r.endIndex||r.startIndex>o.endIndex?r=null:o=null),o){const r=function(t,e,n,o,r){const s=t.getTextContent();let l,i,c;if(r[0]===s?l=t:0===e?[l,i]=t.splitText(n):[c,l,i]=t.splitText(e,n),l.setTextContent(r[2]),o)for(const t of o.format)l.hasFormat(t)||l.toggleFormat(t);return{nodeAfter:i,nodeBefore:c,transformedNode:l}}(t,o.startIndex,o.endIndex,o.transformer,o.match);me(r.nodeAfter)&&Ce(r.nodeAfter,e,n),me(r.nodeBefore)&&Ce(r.nodeBefore,e,n),me(r.transformedNode)&&Ce(r.transformedNode,e,n)}else if(r){const o=function(t,e,n,o,r){let s,l,i;return 0===e?[s,l]=t.splitText(n):[i,s,l]=t.splitText(e,n),o.replace?{matchedNode:s,nodeAfter:l,nodeBefore:i,transformedNode:o.replace(s,r)||void 0}:null}(t,r.startIndex,r.endIndex,r.transformer,r.match);if(!o)return;me(o.nodeAfter)&&Ce(o.nodeAfter,e,n),me(o.nodeBefore)&&Ce(o.nodeBefore,e,n),me(o.transformedNode)?Ce(o.transformedNode,e,n):void 0===o.transformedNode&&me(o.matchedNode)&&null!==o.matchedNode.getParent()&&o.matchedNode.setTextContent(ot(o.matchedNode.getTextContent()))}if(!o&&!r){const e=ot(t.getTextContent());t.setTextContent(e)}}function Ie(t,e,n,o=!1){const r=Q(n),s=function(t){const e={},n={},o=[];for(const r of t){const{tag:t}=r;e[t]=r;const s=t.replace(/(\*|\^|\+)/g,"\\$1");o.push(s),1===t.length?n[t]="`"===t?new RegExp("(^|[^\\\\`])(`)((?:\\\\`|[^`])+?)(`)(?!`)"):new RegExp(`(^|[^\\\\${s}])(${s})((\\\\${s})?.*?[^${s}\\s](\\\\${s})?)(${s})(?![\\\\${s}])`):n[t]=new RegExp(`(^|[^\\\\])(${s})((\\\\${s})?.*?[^\\s](\\\\${s})?)(${s})(?!\\\\)`)}return{fullMatchRegExpByTag:n,openTagsRegExp:new RegExp(`(${o.join("|")})`,"g"),transformersByTag:e}}(r.textFormat),l=t.split("\n"),i=l.length;!function(t){const n=Pt,c=Mt;Pt=[],Mt=t;try{return(()=>{for(let t=0;t<i;t++){const n=l[t],[i,c]=Te(l,t,r.multilineElement,e);i?t=c:ve(n,e,r.element,s,r.textMatch,o)}})()}finally{Pt=n,Mt=c}}(!o);const c=e.getChildren();for(const t of c)if(!o&&nt(t)&&e.getChildrenSize()>1)t.remove();else if(u(t))for(const e of t.getAllTextNodes())$e(e)}function Te(t,e,n,o){for(const r of n){const{handleImportAfterStartMatch:n,regExpEnd:s,regExpStart:l,replace:i}=r,c=t[e].match(l);if(!c)continue;if(n){const s=n({lines:t,rootNode:o,startLineIndex:e,startMatch:c,transformer:r});if(null===s)continue;if(s)return s}const f="object"==typeof s&&"regExp"in s?s.regExp:s,u=s&&"object"==typeof s&&"optional"in s?s.optional:!s;let a=e;const d=t.length;for(;a<d;){const n=f?t[a].match(f):null;if(!n&&(!u||u&&a<d-1)){a++;continue}if(n&&e===a&&n.index===c.index){a++;continue}const r=[];if(n&&e===a)r.push(t[e].slice(c[0].length,-n[0].length));else for(let o=e;o<=a;o++)if(o===e){const e=t[o].slice(c[0].length);r.push(e)}else if(o===a&&n){const e=t[o].slice(0,-n[0].length);r.push(e)}else r.push(t[o]);if(!1!==i(o,null,c,n,r,!0))return[!0,a];break}}return[!1,e]}function ve(e,n,r,l,i,c){const f=o(e),u=h();u.append(f),n.append(u);for(const{regExp:t,replace:n}of r){const o=e.match(t);if(o&&(f.setTextContent(e.slice(o[0].length)),!1!==n(u,[f],o,!0)))break}if(Ce(f,l,i),null!==u.getParent()&&e.length>0){const e=u.getPreviousSibling();if(!c&&(t(e)||K(e)||z(e))){let t=e;if(z(e)){const n=e.getLastDescendant();t=null==n?null:s(n,D)}null!=t&&t.getTextContentSize()>0&&(t.splice(t.getChildrenSize(),0,[bt(t),...u.getChildren()]),u.remove())}}}function $e(t){if(d(t))return;const e=t.getTextContent();if(!e.includes("\t"))return;const n=t.getFormat(),r=t.getStyle(),s=[];let l=0;for(let t=e.indexOf("\t");-1!==t;t=e.indexOf("\t",t+1))t>l&&s.push(o(e.slice(l,t)).setFormat(n).setStyle(r)),s.push(g()),l=t+1;l<e.length&&s.push(o(e.slice(l)).setFormat(n).setStyle(r)),t.getParentOrThrow().splice(t.getIndexWithinParent(),1,s)}function ye(t,...e){throw function(t,...e){const n=new URL("https://lexical.dev/docs/error"),o=new URLSearchParams;o.append("code",t);for(const t of e)o.append("v",t);return n.search=o.toString(),new Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}(t,...e)}function Ee(t,e,n,o,r){const s=t.getParent();if(!S(s)||t.getFirstChild()!==e)return!1;const l=e.getTextContent();if(!r&&" "!==l[n-1])return!1;for(const{regExp:s,replace:i}of o){const o=l.match(s),c=r||o&&o[0].endsWith(" ")?n:n-1;if(o&&o[0].length===c){const r=e.getNextSiblings(),[s,l]=e.splitText(n);if(!1!==i(t,l?[l,...r]:r,o,!1))return s.remove(),!0}}return!1}function Se(t,e,n,o,r){const s=t.getParent();if(!S(s)||t.getFirstChild()!==e)return!1;const l=e.getTextContent();if(!r&&" "!==l[n-1])return!1;for(const{regExpStart:s,replace:i,regExpEnd:c}of o){if(c&&!("optional"in c)||c&&"optional"in c&&!c.optional)continue;const o=l.match(s);if(o){const s=r||o[0].endsWith(" ")?n:n-1;if(o[0].length!==s)continue;const l=e.getNextSiblings(),[c,f]=e.splitText(n);if(!1!==i(t,f?[f,...l]:l,o,null,null,!1))return c.remove(),!0}}return!1}function be(t,e){let n=0;const o=t.getTextContent();for(let t=0;t<e;t++)"`"===o[t]&&n++;return n%2!=0}function we(t,e,n){const o=n.length;for(let r=e;r>=o;r--){const e=r-o;if(ke(t,e,n,0,o)&&" "!==t[e+o])return e}return-1}function Ne(t){const n=t.getNode();let o=0;if(e(n))o=t.offset;else if(u(n))for(const e of n.getChildren().slice(0,t.offset))o+=e.getTextContentSize();for(let t=n.getPreviousSibling();null!==t;t=t.getPreviousSibling())o+=t.getTextContentSize();return o}function ke(t,e,n,o,r){for(let s=0;s<r;s++)if(t[e+s]!==n[o+s])return!1;return!0}function Fe(t,n=se){const o=Q(n),r=o.element.filter(t=>t.triggerOnEnter),s=J(o.textFormat,({tag:t})=>t[t.length-1]),l=J(o.textMatch,({trigger:t})=>t),i=new Set([" "]);for(const t of o.textFormat)i.add(t.tag.slice(-1));for(const t of o.textMatch)void 0!==t.trigger&&i.add(t.trigger);for(const e of n){const n=e.type;if("element"===n||"text-match"===n||"multiline-element"===n){const n=e.dependencies;for(const e of n)t.hasNode(e)||ye(173,e.getType())}}return p(t.registerUpdateListener(({tags:n,dirtyLeaves:r,editorState:f,prevEditorState:u})=>{if(n.has(x)||n.has(m))return;if(t.isComposing())return;const a=n.has(C),d=f.read(I),g=u.read(I);if(!T(g)||!T(d)||!d.isCollapsed()||d.is(g)&&!a)return;const h=d.anchor.key,p=d.anchor.offset,y=f._nodeMap.get(h);if(e(y)&&r.has(h)&&(a||1===p||!(f.read(()=>Ne(d.anchor))>u.read(()=>Ne(g.anchor))+1))){if(a){const t=f.read(()=>y.getTextContent())[p-1];if(!i.has(t))return}t.update(()=>{if(!me(y))return;const t=y.getParent();null===t||M(t)||((t,n,r)=>!!(Ee(t,n,r,o.element)||Se(t,n,r,o.multilineElement)||function(t,e,n){let o=t.getTextContent();const r=n[o[e-1]];if(null==r)return!1;e<o.length&&(o=o.slice(0,e));for(const e of r){if(!e.replace||!e.regExp)continue;const n=o.match(e.regExp);if(null===n)continue;const r=n.index||0,s=r+n[0].length;let l;return 0===r?[l]=t.splitText(s):[,l]=t.splitText(r,s),l.selectNext(0,0),e.replace(l,n),!0}return!1}(n,r,l)||function(t,n,o){const r=t.getTextContent(),s=n-1,l=r[s],i=o[l];if(!i)return!1;for(const n of i){const{tag:o}=n,i=o.length,f=s-i+1;if(i>1&&!ke(r,f,o,0,i))continue;if(" "===r[f-1])continue;const u=r[s+1];if(!1===n.intraword&&u&&!V.test(u))continue;const a=t;let d=a,g=we(r,f,o),h=d;for(;g<0&&(h=h.getPreviousSibling())&&!c(h);)if(e(h)){if(h.hasFormat("code"))continue;const t=h.getTextContent();d=h,g=we(t,t.length,o)}if(g<0)continue;if(d===a&&g+i===f)continue;const p=d.getTextContent();if(g>0&&p[g-1]===l)continue;const x=p[g-1];if(!1===n.intraword&&x&&!V.test(x))continue;if(!n.format.includes("code")&&be(d,g))continue;const m=a.getTextContent(),C=m.slice(0,f)+m.slice(s+1);a.setTextContent(C);const v=d===a?C:p;d.setTextContent(v.slice(0,g)+v.slice(g+i));const $=I(),y=b();w(y);const E=s-i*(d===a?2:1)+1;y.anchor.set(d.__key,g,"text"),y.focus.set(a.__key,E,"text");for(const t of n.format)y.formatText(t,N[t]);y.anchor.set(y.focus.key,y.focus.offset,y.focus.type);for(const t of n.format)y.hasFormat(t)&&y.toggleFormat(t);return T($)&&(y.format=$.format),!0}return!1}(n,r,s)))(t,y,d.anchor.offset)&&v($)})}}),t.registerCommand(y,t=>{if(null!==t&&t.shiftKey)return!1;const n=I();if(!T(n)||!n.isCollapsed())return!1;const s=n.anchor.offset,l=n.anchor.getNode();if(!e(l)||!me(l))return!1;const i=l.getParent();return null!==i&&!M(i)&&!(s!==l.getTextContent().length||!Se(i,l,s,o.multilineElement,!0)&&!Ee(i,l,s,r,!0)||(null!==t&&t.preventDefault(),0))},E))}function Oe(t,e=se,n,o=!1,r=!1){const s=o?t:le(t,r),l=n||f();l.clear(),Ie(s,l,e,o),null!==I()&&l.selectStart()}function Pe(t,e=se,n=!1,o=!1){const r=n?t:le(t,o),s=new k;return Ie(r,s,e,n),s.getChildren()}function Me(t=se,e,n=!1){const o=function(t,e=!1){const n=Q(t),o=[...n.multilineElement,...n.element],r=!e,s=n.textFormat.filter(t=>1===t.format.length).sort((t,e)=>Number(t.format.includes("code"))-Number(e.format.includes("code")));return t=>{const l=[],i=(t||f()).getChildren();for(let t=0;t<i.length;t++){const c=i[t],f=fe(c,o,s,n.textMatch,e);null!=f&&l.push(r&&t>0&&!nt(c)&&!nt(i[t-1])?"\n".concat(f):f)}return l.join("\n")}}(t,n);return o(e)}function Re(t=se,e,n=!1){if(!e||T(e)&&e.isCollapsed())return"";const o=function(t,e=!1){const n=Q(t),o=[...n.multilineElement,...n.element],r=!e,s=n.textFormat.filter(t=>1===t.format.length).sort((t,e)=>Number(t.format.includes("code"))-Number(e.format.includes("code")));return t=>{const l=[];let i=null;for(const c of f().getChildren()){const{shouldInclude:f,markdown:u}=ie(c,t,o,s,n.textMatch,e);f&&null!=u&&(l.push(!r||null===i||nt(c)||nt(i)?u:"\n".concat(u)),i=c)}return l.join("\n")}}(t,n);return o(e)}export{Oe as $convertFromMarkdownString,Re as $convertSelectionToMarkdownString,Me as $convertToMarkdownString,Pe as $generateNodesFromMarkdownString,Zt as BOLD_ITALIC_STAR,Ht as BOLD_ITALIC_UNDERSCORE,Kt as BOLD_STAR,qt as BOLD_UNDERSCORE,Wt as CHECK_LIST,jt as CODE,te as ELEMENT_TRANSFORMERS,Lt as HEADING,Xt as HIGHLIGHT,Dt as INLINE_CODE,Jt as ITALIC_STAR,Qt as ITALIC_UNDERSCORE,Yt as LINK,ee as MULTILINE_ELEMENT_TRANSFORMERS,Ut as ORDERED_LIST,At as QUOTE,Gt as STRIKETHROUGH,ne as TEXT_FORMAT_TRANSFORMERS,oe as TEXT_MATCH_TRANSFORMERS,se as TRANSFORMERS,zt as UNORDERED_LIST,mt as isTableRowDivider,Fe as registerMarkdownShortcuts};
9
+ import{$isParagraphNode as t,$isTextNode as e,$setState as n,$createTextNode as r,$getState as o,$findMatchingParent as s,$createLineBreakNode as i,createState as l,$isLineBreakNode as c,$isRangeSelection as f,$getRoot as u,$isElementNode as a,$isDecoratorNode as d,$isTabNode as g,$createTabNode as h,$createParagraphNode as p,mergeRegister as x,COLLABORATION_TAG as m,HISTORIC_TAG as C,COMPOSITION_END_TAG as I,$getSelection as T,$addUpdateTag as v,HISTORY_PUSH_TAG as S,KEY_ENTER_COMMAND as y,COMMAND_PRIORITY_LOW as E,$isRootOrShadowRoot as $,$createRangeSelection as b,$setSelection as w,TEXT_TYPE_TO_FORMAT as N,ArtificialNode__DO_NOT_USE as k}from"lexical";import{$sliceSelectedTextNodeContent as F}from"@lexical/selection";import{CodeNode as O,$createCodeNode as P,$isCodeNode as M}from"@lexical/code-core";import{LinkNode as R,$isLinkNode as B,$createLinkNode as _,$isAutoLinkNode as L}from"@lexical/link";import{ListNode as A,ListItemNode as j,$isListNode as z,$createListItemNode as W,$createListNode as K,$isListItemNode as U}from"@lexical/list";import{HeadingNode as D,QuoteNode as X,$isHeadingNode as Z,$isQuoteNode as H,$createQuoteNode as q,$createHeadingNode as G}from"@lexical/rich-text";function J(t,e){const n={};for(const r of t){const t=e(r);t&&(n[t]?n[t].push(r):n[t]=[r])}return n}function Q(t){const e=J(t,t=>t.type);return{element:e.element||[],multilineElement:e["multiline-element"]||[],textFormat:e["text-format"]||[],textMatch:e["text-match"]||[]}}const V=/[!-/:-@[-`{-~\s]/,Y=/\s/,tt=/[!"#$%&'()*+,\-./:;<=>?@[\]^_`{|}~]/,et=/^\s{0,3}$/;function nt(n){if(!t(n))return!1;const r=n.getFirstChild();return null==r||1===n.getChildrenSize()&&e(r)&&et.test(r.getTextContent())}function rt(t){return t.replace(/\\([!-/:-@[-`{-~])|&#(?:(\d{1,7})|[xX]([\da-fA-F]{1,6}));/g,(t,e,n,r)=>e??function(t,e){const n=void 0!==t?Number(t):parseInt(e,16);return 0===n||n>=55296&&n<=57343||n>1114111?"\ufffd":String.fromCodePoint(n)}(n,r))}function ot(t,e){let n=0;for(let r=e-1;r>=0&&"\\"===t[r];r--)n++;return n%2==1}function st(t){const e=e=>{let n=0;for(let r=e-1;r>=0&&"\\"===t[r];r--)n++;return n%2==1},n=[];let r=0;for(;r<t.length;)if("`"===t[r]){let e=1;for(;r+e<t.length&&"`"===t[r+e];)e++;n.push({index:r,length:e}),r+=e}else r++;const o=[];let s=0;for(;s<n.length;){const r=n[s];if(e(r.index)){s++;continue}let i=-1;for(let t=s+1;t<n.length;t++)if(n[t].length===r.length){i=t;break}if(-1===i){s++;continue}const l=n[i];let c=t.slice(r.index+r.length,l.index);c.length>=2&&c.startsWith(" ")&&c.endsWith(" ")&&/[^ ]/.test(c)&&(c=c.slice(1,-1)),o.push({content:c,endIndex:l.index+l.length,startIndex:r.index}),s=i+1}return o}function it(t,e,n,r,o){if(!lt(e,n,r,o))return!1;if("*"===t)return!0;if("_"===t){if(!lt(e,n,r,!o))return!0;const t=o?e[n-1]:e[n+r];return void 0!==t&&tt.test(t)}return!0}function lt(t,e,n,r){const o=t[e-1],s=t[e+n],[i,l]=r?[s,o]:[o,s];return void 0!==i&&!Y.test(i)&&(!tt.test(i)||void 0===l||Y.test(l)||tt.test(l))}const ct=/^(\s*)(\d{1,})\.\s/,ft=/^(\s*)[-*+]\s/,ut=/^(\s*)(?:[-*+]\s)?\s?(\[(\s|x)?\])\s/i,at=/^(#{1,6})\s/,dt=/^>\s/,gt=/^([ \t]*`{3,})([\w-]+)?[ \t]?/,ht=/^[ \t]*`{3,}$/,pt=/^[ \t]*```[^`]+(?:(?:`{1,2}|`{4,})[^`]+)*```(?:[^`]|$)/,xt=/^(?:\|)(.+)(?:\|)\s?$/;function mt(t){if("|"!==t[0])return!1;const{length:e}=t;let n=1,r=0;for(;n<e;){let e=n;for(" "===t[e]&&e++,":"===t[e]&&e++;"-"===t[e];)e++;if(":"===t[e]&&e++," "===t[e]&&e++,"|"!==t[e])break;r++,n=e+1}return r>0&&(n===e||n===e-1&&/\s/.test(t[n]))}const Ct=/^<[a-z_][\w-]*(?:\s[^<>]*)?\/?>/i,It=/^<\/[a-z_][\w-]*\s*>/i,Tt=t=>new RegExp(`(?:${t.source})$`,t.flags),vt=/* @__PURE__ */l("mdListMarker",{parse:t=>"string"==typeof t&&/^[-*+]$/.test(t)?t:"-",resetOnCopyNode:!0}),St=/* @__PURE__ */l("mdCodeFence",{parse:t=>"string"==typeof t&&/^`{3,}$/.test(t)?t:"```",resetOnCopyNode:!0}),yt=/* @__PURE__ */l("mdCodeMeta",{parse:t=>"string"==typeof t?t:"",resetOnCopyNode:!0}),Et=/* @__PURE__ */l("mdHardLineBreak",{parse:t=>"string"==typeof t&&/^(\\| {2,})$/.test(t)?t:"",resetOnCopyNode:!0});function $t(t){if(t.endsWith("\\")){let e=0;for(let n=t.length-1;n>=0&&"\\"===t[n];n--)e++;return e%2==1?[t.slice(0,-1),"\\"]:null}const e=t.match(/^(.*?\S)( {2,})$/);return e?[e[1],e[2]]:null}function bt(t){const r=i(),o=function(t){const n=t.getChildren(),r=n.length-1,o=n[r];if(!e(o))return null;const s=o.getTextContent(),i=$t(s);if(null!==i){const[t,e]=i;return o.setTextContent(t),e}return/^ {2,}$/.test(s)&&function(t,e){for(let n=e-1;n>=0;n--){if(c(t[n]))return!1;if(/\S/.test(t[n].getTextContent()))return!0}return!1}(n,r)?(o.setTextContent(""),s):null}(t);return null!==o&&n(r,Et,o),r}function wt(t){return H(t)}function Nt(t,e){let n=0;for(;n<e&&(" "===t[n]||"\t"===t[n]);)n++;return t.slice(n)}function kt(t,e){const r=t.getLastChild();e&&M(r)&&n(r,yt,e)}const Ft=t=>(e,n,r,o)=>{const s=t(r);s.append(...n),e.replace(s),o||s.select(0,0)};function Ot(t){let e=0;for(const n of t)e+="\t"===n?4-e%4:1;return e}let Pt=null,Mt=!1;const Rt=t=>(e,r,o,s)=>{if(Z(e)||!s&&wt(e))return!1;const i=e.getPreviousSibling(),l=e.getNextSibling(),c=W("check"===t?/^x$/i.test(o[3]||""):void 0),f=o[0].trim()[0],u="bullet"!==t&&"check"!==t||f!==vt.parse(f)?void 0:f,a=Pt;let d=i;if(null!==a){if(Mt)for(;null!==d&&nt(d);)d=d.getPreviousSibling();z(d)||(a.length=0)}const g=null===a||0===a.length?function(t){const e=t.match(/\t/g),n=t.match(/ /g);let r=0;return e&&(r+=e.length),n&&(r+=Math.floor(n.length/4)),r}(o[1]):function(t,e){const n=Ot(e);for(let e=t.length-1;e>=0;e--)if(n>=t[e])return e+1;return 0}(a,o[1]);if(z(l)&&l.getListType()===t){const n=l.getFirstChild();null!==n?n.insertBefore(c):l.append(c),"number"===t&&0===g&&l.setStart(Number(o[2])),e.remove()}else if(z(d)&&(d.getListType()===t||g>0))d.append(c),e.remove();else{const n=K(t,"number"===t?Number(o[2]):void 0);n.append(c),e.replace(n)}c.append(...r),s||c.select(0,0),g&&(c.setIndent(g),function(t,e,n){const r=t.getParent();if(!z(r)||r.getListType()===e)return;const o=r.getParent();if(!U(o))return;const s=K(e,"number"===e?Number(n[2]):void 0),i=null===t.getPreviousSibling();s.append(t);const l=W();l.append(s),i?o.insertBefore(l):o.insertAfter(l),0===r.getChildrenSize()&&o.remove()}(c,t,o));const h=c.getParent();u&&z(h)&&n(h,vt,u),null!==a&&function(t,e,n,r){for(let n=t.length;n<e;n++)t[n]=4*(n+1);t.length=e,t[e]=function(t,e){let n=t[0];if("check"===e){const e=n.slice(t[1].length).match(/^[-*+]\s/);e&&(n=t[1]+e[0])}return Math.min(Ot(n),Ot(t[1])+4)}(n,r)}(a,g,o,t)},Bt=(t,e,n,r)=>{const s=[],i=t.getChildren();let l=0;for(const c of i)if(U(c)){if(1===c.getChildrenSize()){const t=c.getFirstChild();if(z(t)){const o=Bt(t,e,n+1,r);o&&s.push(o);continue}}if(r&&!c.getChildren().some(t=>t.isSelected(r)))continue;const i=" ".repeat(4*n),f=t.getListType(),u=o(t,vt),a="number"===f?`${t.getStart()+l}. `:"check"===f?`${u} [${c.getChecked()?"x":" "}] `:u+" ";let d=e(c);"number"!==f&&(d=d.replace(/^(\s{0,3}\d+)(\.\s)/,"$1\\$2")),s.push(i+a+d),l++}return s.join("\n")},_t=/* @__PURE__ */Ft(t=>{const e="h"+t[1].length;return G(e)}),Lt={dependencies:[D],export:(t,e)=>{if(!Z(t))return null;const n=Number(t.getTag().slice(1));return"#".repeat(n)+" "+e(t)},regExp:at,replace:(t,e,n,r)=>!(!r&&wt(t))&&_t(t,e,n,r),triggerOnEnter:!0,type:"element"},At={dependencies:[X],export:(t,e)=>{if(!H(t))return null;const n=e(t).split("\n"),r=[];for(const t of n)r.push("> "+t);return r.join("\n")},regExp:dt,replace:(t,e,n,r)=>{if(r){const n=t.getPreviousSibling();if(H(n))return n.splice(n.getChildrenSize(),0,[bt(n),...e]),void t.remove()}const o=q();o.append(...e),t.replace(o),r||o.select(0,0)},triggerOnEnter:!0,type:"element"},jt={dependencies:[O],export:t=>{if(!M(t))return null;const e=t.getTextContent();let n=o(t,St);if(e.indexOf(n)>-1){const t=e.match(/`{3,}/g);if(t){const e=Math.max(...t.map(t=>t.length));n="`".repeat(e+1)}}const r=t.getLanguage()||"",s=r?o(t,yt):"";return n+r+(s?" "+s:"")+(e?"\n"+e:"")+"\n"+n},handleImportAfterStartMatch:({lines:t,rootNode:e,startLineIndex:n,startMatch:r})=>{const o=r[1],s=o.trim().length,i=t[n],l=r.index+o.length,c=i.slice(l),f=new RegExp(`\`{${s},}$`);if(f.test(c)){const t=c.match(f),o=c.slice(0,c.lastIndexOf(t[0])),s=[...r];return s[2]="",jt.replace(e,null,s,t,[o],!0),[!0,n]}const u=new RegExp(`^[ \\t]*\`{${s},}$`);for(let o=n+1;o<t.length;o++){const s=t[o];if(u.test(s)){const l=s.match(u),c=t.slice(n+1,o),f=r[2]?i.slice(r[0].length).trim():"";return c.unshift(f?"":i.slice(r[0].length)),jt.replace(e,null,r,l,c,!0),kt(e,f),[!0,o]}}const a=t.slice(n+1);return a.unshift(i.slice(r[0].length)),jt.replace(e,null,r,null,a,!0),[!0,t.length-1]},regExpEnd:{optional:!0,regExp:ht},regExpStart:gt,replace:(t,e,o,s,i,l)=>{let c,f;const u=o[1]?o[1].trim():"```",a=o[2]||void 0,d=o[1]?o[1].length-o[1].trimStart().length:0;if(!e&&i){if(1===i.length)s?(c=P(a),f=Nt(i[0],d)):(c=P(a),f=i[0].startsWith(" ")?i[0].slice(1):i[0]);else{for(c=P(a),i.length>0&&(0===i[0].trim().length?i.shift():i[0].startsWith(" ")&&(i[0]=i[0].slice(1)));i.length>0&&!i[i.length-1].length;)i.pop();f=i.map(t=>Nt(t,d)).join("\n")}n(c,St,u);const e=r(f);c.append(e),t.append(c)}else if(e){if(!l&&wt(t))return!1;Ft(t=>P(t?t[2]:void 0))(t,e,o,l)}},type:"multiline-element"},zt={dependencies:[A,j],export:(t,e,n)=>z(t)?Bt(t,e,0,n):null,regExp:ft,replace:/* @__PURE__ */Rt("bullet"),triggerOnEnter:!0,type:"element"},Wt={dependencies:[A,j],export:(t,e,n)=>z(t)?Bt(t,e,0,n):null,regExp:ut,replace:/* @__PURE__ */Rt("check"),triggerOnEnter:!0,type:"element"},Kt={dependencies:[A,j],export:(t,e,n)=>z(t)?Bt(t,e,0,n):null,regExp:ct,replace:/* @__PURE__ */Rt("number"),triggerOnEnter:!0,type:"element"},Ut={format:["code"],tag:"`",type:"text-format"},Dt={format:["highlight"],tag:"==",type:"text-format"},Xt={format:["bold","italic"],tag:"***",type:"text-format"},Zt={format:["bold","italic"],intraword:!1,tag:"___",type:"text-format"},Ht={format:["bold"],tag:"**",type:"text-format"},qt={format:["bold"],intraword:!1,tag:"__",type:"text-format"},Gt={format:["strikethrough"],tag:"~~",type:"text-format"},Jt={format:["italic"],tag:"*",type:"text-format"},Qt={format:["italic"],intraword:!1,tag:"_",type:"text-format"},Vt=/[\s\0-\x1f\x7f]/;function Yt(t,e){return t.replace(/&(?=#\d+;|#[Xx][\dA-Fa-f]+;|[A-Za-z][\dA-Za-z]*;)/g,"&#38;").replace(e,"\\$&").replace(/`/g,"\\`").replace(/[\r\n]/g,t=>`&#${t.charCodeAt(0)};`)}const te={dependencies:[R],export:(t,e,n)=>{if(!B(t)||L(t))return null;const r=e(t),o=t.getTitle(),s=t.getURL(),i=""===s||Vt.test(s)?`<${Yt(s,/[\\<>]/g)}>`:Yt(s,/[\\()]|^</g);return o?`[${r}](${i} "${Yt(o,/[\\"]/g)}")`:`[${r}](${i})`},importRegExp:/(?:\[(.+?)\])(?:\(\s*(?:(?:<((?:\\[^\n\r]|[^<>\n\r\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:\\[\s\S]|[^"\\])*)"|'((?:\\[\s\S]|[^'\\])*)'|\(((?:\\[\s\S]|[^()\\])*)\)))?\s*)?\))/,regExp:/(?:\[([^[\]]*(?:\[[^[\]]*\][^[\]]*)*)\])(?:\(\s*(?:(?:<((?:\\[^\n\r]|[^<>\n\r\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:\\[\s\S]|[^"\\])*)"|'((?:\\[\s\S]|[^'\\])*)'|\(((?:\\[\s\S]|[^()\\])*)\)))?\s*)?\))$/,replace:(t,e)=>{const n=e.input||"",o=e.index||0;if(s(t,B)||ot(n,o)||ot(n,o+e[1].length+1))return;const[,i,l,c,f,u,a]=e,d=rt(l??c??""),g=f??u??a,h=null!=g?rt(g):void 0,p=_(d,{title:h}),x=function(t){const e=st(t);let n=0;const r=t.replace(/\\.|[[\]]/g,(t,r)=>{for(;n<e.length&&r>=e[n].endIndex;)n++;return n<e.length&&r>=e[n].startIndex||"\\"===t[0]?" ".repeat(t.length):t}),o=[];let s=!1;for(let t=0;t<r.length;t++){const e=r[t];"["===e?o.push(t):"]"===e&&void 0===o.pop()&&(s=!0)}return o.pop()??(s?null:-1)}(i);if(null===x)return;let m=i,C="";-1!==x&&(C="["+function(t){let e="",n=0;for(const{startIndex:r,endIndex:o}of st(t))e+=rt(t.slice(n,r))+t.slice(r,o),n=o;return e+rt(t.slice(n))}(i.slice(0,x)),m=i.slice(x+1));const I=t.getFormat(),T=r(m);return T.setFormat(I),p.append(T),t.replace(p),C&&p.insertBefore(r(C).setFormat(I)),T},trigger:")",type:"text-match"},ee=[Lt,At,zt,Kt],ne=[jt],re=[Ut,Xt,Zt,Ht,qt,Dt,Jt,Qt,Gt],oe=[te];function se(...t){return t.flat()}const ie=/* @__PURE__ */se(ee,ne,re,oe);function le(t,e=!1){const n=t.split("\n");let r=0;const o=[];for(let t=0;t<n.length;t++){const s=n[t],i=s.trimEnd(),l=o[o.length-1],c=t<n.length-1?$t(s):null,f=void 0!==l&&null!==$t(l);if(pt.test(i))o.push(i);else if(0===r){{const t=i.match(gt);if(t){r=t[1].trim().length,o.push(i);continue}}""===i||""===l||!l||at.test(l)||at.test(i)||dt.test(i)||ct.test(i)||ft.test(i)||ut.test(i)||xt.test(i)||mt(i)||f||!e||Ct.test(i)||It.test(i)||Tt(It).test(l)||Tt(Ct).test(l)||ht.test(l)?o.push(!e&&""!==i||null!==c?s:i):o[o.length-1]=l+" "+(null===c?i:s).trimStart()}else{if(ht.test(i)&&i.trim().length>=r){r=0,o.push(i);continue}o.push(s)}}return o.join("\n")}function ce(t,e,n,r,o,s){let i=t.isSelected(e);for(const l of n){if(!l.export)continue;const n=l.export(t,t=>fe(t,e,r,o,s).markdown,e);if(null!=n)return!i&&a(t)&&fe(t,e,r,o,s).shouldInclude&&(i=!0),{markdown:n,shouldInclude:i}}if(a(t)){const n=fe(t,e,r,o,s);return{markdown:n.markdown,shouldInclude:i||n.shouldInclude}}return d(t)?{markdown:t.getTextContent(),shouldInclude:i}:{markdown:null,shouldInclude:i}}function fe(t,n,r,o,s,i,l){const f=[],u=t.getChildren();let g=!1;i||(i=[]),l||(l=[]);t:for(const t of u){let u=t.isSelected(n);for(const e of o){if(!e.export)continue;const c=e.export(t,t=>fe(t,n,r,o,s,i,[...l,...i]).markdown,t=>ge(t,xe(t,n),r,i,l,s,n));if(null!=c){!u&&a(t)&&t.getChildren().some(t=>t.isSelected(n))&&t.extractWithChild(t,n,"html")&&(u=!0),u&&(f.push(c),g=!0);continue t}}if(c(t))u&&(f.push(de(t)),g=!0);else if(e(t))u&&(f.push(ge(t,xe(t,n),r,i,l,s,n)),g=!0);else if(a(t)){const e=fe(t,n,r,o,s,i,l);!u&&e.shouldInclude&&t.extractWithChild(t,n,"html")&&(u=!0),(u||e.shouldInclude)&&(f.push(e.markdown),g=!0)}else d(t)&&u&&(f.push(t.getTextContent()),g=!0)}return{markdown:f.join(""),shouldInclude:g}}function ue(t,e,n,r,o){for(const s of e){if(!s.export)continue;const e=s.export(t,t=>ae(t,n,r,void 0,void 0,o));if(null!=e)return e}return a(t)?ae(t,n,r,void 0,void 0,o):d(t)?t.getTextContent():null}function ae(t,n,r,o,s,i=!1){const l=[],f=t.getChildren();o||(o=[]),s||(s=[]);t:for(const t of f){for(const e of r){if(!e.export)continue;const c=e.export(t,t=>ae(t,n,r,o,[...s,...o],i),(t,e)=>ge(t,e,n,o,s,i,null));if(null!=c){l.push(c);continue t}}c(t)?l.push(de(t)):e(t)?l.push(ge(t,t.getTextContent(),n,o,s,i,null)):a(t)?l.push(ae(t,n,r,o,s,i)):d(t)&&l.push(t.getTextContent())}return l.join("")}function de(t){return o(t,Et)+"\n"}function ge(t,e,n,r,o,s,i){const l=t.hasFormat("code");let c=e;l||(c=s?c.replace(/([*_`~])/g,"\\$1"):c.replace(/([[\]*_`~\\]|<(?=\S|$)|&(?=#|$))/g,t=>/[*_`~\\]/.test(t)?`\\${t}`:`&#${t.charCodeAt(0)};`));const f=Ce(e);let u="",a="",d="";const g=he(t,!0),h=new Set;for(const e of n){const n=e.format[0],o=e.tag;"code"!==n&&f&&t.hasFormat(n)&&!h.has(n)&&(h.add(n),me(g,n)&&r.find(t=>t.tag===o)||(r.push({answers:new Map,format:n,tag:o}),u+=o))}for(let e=0;e<r.length;e++){const n=me(t,r[e].format);if(n&&pe(t,r[e],i))continue;const s=[...r];for(;s.length>e;){const t=s.pop();t&&o.find(e=>e.tag===t.tag)||(t&&"string"==typeof t.tag&&(n?d+=t.tag:a+=t.tag),r.pop())}break}if(l&&""!==e){const{fence:t,padded:n}=function(t){const e=t.match(/`+/g),n=e?Math.max(...e.map(t=>t.length)):0;return{fence:"`".repeat(n+1),padded:0===t.length||t.includes("`")||/^\s/.test(t)&&/\s$/.test(t)&&/[^ ]/.test(t)?` ${t} `:t}}(e);c=t+n+t}else if(!f)return a+d+c;const p=c.trimStart(),x=p.trimEnd();return a+c.slice(0,c.length-p.length)+u+x+d+p.slice(x.length)}function he(t,n){const r=n?t.getPreviousSibling():t.getNextSibling();return e(r)?r:null}function pe(t,e,n){const{answers:r}=e,o=r.get(t.getKey());if(void 0!==o)return o;const s=[];let i=he(t,!1),l=!1;for(;i&&i.hasFormat(e.format)&&(!n||i.isSelected(n));){const t=xe(i,n);if(Ce(t)){l=!0;break}if(t.includes("\n"))break;const e=r.get(i.getKey());if(void 0!==e){l=e;break}s.push(i),i=he(i,!1)}r.set(t.getKey(),l);for(const t of s)r.set(t.getKey(),l);return l}function xe(t,e){return e?F(e,t,"clone").getTextContent():t.getTextContent()}function me(t,n){return e(t)&&t.hasFormat(n)}function Ce(t){return/\S/.test(t)}function Ie(t,e){const n=new RegExp(e.source,e.flags.replace(/[gy]/g,"")+"g");let r;for(;null!==(r=n.exec(t));){if(!ot(t,r.index))return r;n.lastIndex=r.index+1}return null}function Te(t){return e(t)&&!t.hasFormat("code")}function ve(t,e,n){let r=function(t,e){const n=t.getTextContent(),r=e.transformersByTag["`"],o=[];let s=null;if(r){const t=st(n);for(const e of t)s||(s={content:e.content,endIndex:e.endIndex,startIndex:e.startIndex,tag:"`"}),o.push({end:e.endIndex,start:e.startIndex})}const i=function(t,e,n=[]){const r=[],o=new Set(Object.keys(e.transformersByTag).filter(t=>"`"!==t[0]).map(t=>t[0])),s=e=>{let n=0;for(let r=e-1;r>=0&&"\\"===t[r];r--)n++;return n%2==1},i=t=>n.some(e=>t>=e.start&&t<e.end);let l=0;for(;l<t.length;){const e=t[l];if(!o.has(e)||s(l)||i(l)){l++;continue}let n=1;for(;l+n<t.length&&t[l+n]===e;)n++;const c=it(e,t,l,n,!0),f=it(e,t,l,n,!1);(c||f)&&r.push({active:!0,canClose:f,canOpen:c,char:e,index:l,length:n}),l+=n}return r}(n,e,o),l=i.length>0?function(t,e,n){const r={};let o=0,s=null;for(;o<e.length;){const i=e[o];if(!i.active||!i.canClose||0===i.length){o++;continue}const l=`${i.char}${i.canOpen}${i.length%3}`,c=r[l]??-1;let f=!1;for(let r=o-1;r>c;r--){const l=e[r];if(!l.active||!l.canOpen||0===l.length||l.char!==i.char)continue;if((l.canClose||i.canOpen)&&(l.length+i.length)%3==0&&l.length%3!=0&&i.length%3!=0)continue;const c=Math.min(l.length,i.length),u=Object.keys(n.transformersByTag).filter(t=>t[0]===l.char&&t.length<=c).sort((t,e)=>e.length-t.length)[0];if(!u)continue;f=!0;const a=u.length,d={content:t.slice(l.index+l.length,i.index),endIndex:i.index+a,startIndex:l.index+(l.length-a),tag:u};(!s||d.startIndex<s.startIndex||d.startIndex===s.startIndex&&d.endIndex>s.endIndex)&&(s=d);for(let t=r+1;t<o;t++)e[t].active=!1;l.length-=a,i.length-=a,l.active=l.length>0,i.length>0?i.index+=a:(i.active=!1,o++);break}f||(r[l]=o-1,i.canOpen||(i.active=!1),o++)}return s}(n,i,e):null;let c=null,f=null;if(s&&l?l.startIndex<=s.startIndex&&l.endIndex>=s.endIndex?(c=l,f=e.transformersByTag[l.tag]):(c=s,f=r):s?(c=s,f=r):l&&(c=l,f=e.transformersByTag[l.tag]),!c||!f)return null;const u=[n.slice(c.startIndex,c.endIndex),c.tag,c.content];return u.index=c.startIndex,u.input=n,{endIndex:c.endIndex,isCodeSpan:f===r,match:u,startIndex:c.startIndex,transformer:f}}(t,e),o=function(t,e){const n=t;let r,o,s,i;for(const t of e){if(!t.replace||!t.importRegExp)continue;const e=n.getTextContent(),l=void 0!==te.importRegExp&&t.importRegExp.source===te.importRegExp.source?Ie(e,t.importRegExp):e.match(t.importRegExp);if(!l)continue;const c=l.index||0,f=t.getEndIndex?t.getEndIndex(n,l):c+l[0].length;!1!==f&&(void 0===r||void 0===o||c<r&&(f>o||f<=r))&&(r=c,o=f,s=t,i=l)}return void 0===r||void 0===o||void 0===s||void 0===i?null:{endIndex:o,match:i,startIndex:r,transformer:s}}(t,n);if(r&&o&&(r.isCodeSpan?o.startIndex<=r.startIndex&&o.endIndex>=r.endIndex?r=null:o=null:r.startIndex<=o.startIndex&&r.endIndex>=o.endIndex||o.startIndex>r.endIndex?o=null:r=null),r){const o=function(t,e,n,r,o){const s=t.getTextContent();let i,l,c;if(o[0]===s?i=t:0===e?[i,l]=t.splitText(n):[c,i,l]=t.splitText(e,n),i.setTextContent(o[2]),r)for(const t of r.format)i.hasFormat(t)||i.toggleFormat(t);return{nodeAfter:l,nodeBefore:c,transformedNode:i}}(t,r.startIndex,r.endIndex,r.transformer,r.match);Te(o.nodeAfter)&&ve(o.nodeAfter,e,n),Te(o.nodeBefore)&&ve(o.nodeBefore,e,n),Te(o.transformedNode)&&ve(o.transformedNode,e,n)}else if(o){const r=function(t,e,n,r,o){let s,i,l;return 0===e?[s,i]=t.splitText(n):[l,s,i]=t.splitText(e,n),r.replace?{matchedNode:s,nodeAfter:i,nodeBefore:l,transformedNode:r.replace(s,o)||void 0}:null}(t,o.startIndex,o.endIndex,o.transformer,o.match);if(!r)return;Te(r.nodeAfter)&&ve(r.nodeAfter,e,n),Te(r.nodeBefore)&&ve(r.nodeBefore,e,n),Te(r.transformedNode)?ve(r.transformedNode,e,n):void 0===r.transformedNode&&Te(r.matchedNode)&&null!==r.matchedNode.getParent()&&r.matchedNode.setTextContent(rt(r.matchedNode.getTextContent()))}if(!r&&!o){const e=rt(t.getTextContent());t.setTextContent(e)}}function Se(t,e,n,r=!1){const o=Q(n),s=function(t){const e={},n={},r=[];for(const o of t){const{tag:t}=o;e[t]=o;const s=t.replace(/(\*|\^|\+)/g,"\\$1");r.push(s),1===t.length?n[t]="`"===t?new RegExp("(^|[^\\\\`])(`)((?:\\\\`|[^`])+?)(`)(?!`)"):new RegExp(`(^|[^\\\\${s}])(${s})((\\\\${s})?.*?[^${s}\\s](\\\\${s})?)(${s})(?![\\\\${s}])`):n[t]=new RegExp(`(^|[^\\\\])(${s})((\\\\${s})?.*?[^\\s](\\\\${s})?)(${s})(?!\\\\)`)}return{fullMatchRegExpByTag:n,openTagsRegExp:new RegExp(`(${r.join("|")})`,"g"),transformersByTag:e}}(o.textFormat),i=t.split("\n"),l=i.length;!function(t){const n=Pt,c=Mt;Pt=[],Mt=t;try{return(()=>{for(let t=0;t<l;t++){const n=i[t],[l,c]=ye(i,t,o.multilineElement,e);l?t=c:Ee(n,e,o.element,s,o.textMatch,r)}})()}finally{Pt=n,Mt=c}}(!r);const c=e.getChildren();for(const t of c)if(!r&&nt(t)&&e.getChildrenSize()>1)t.remove();else if(a(t))for(const e of t.getAllTextNodes())$e(e)}function ye(t,e,n,r){for(const o of n){const{handleImportAfterStartMatch:n,regExpEnd:s,regExpStart:i,replace:l}=o,c=t[e].match(i);if(!c)continue;if(n){const s=n({lines:t,rootNode:r,startLineIndex:e,startMatch:c,transformer:o});if(null===s)continue;if(s)return s}const f="object"==typeof s&&"regExp"in s?s.regExp:s,u=s&&"object"==typeof s&&"optional"in s?s.optional:!s;let a=e;const d=t.length;for(;a<d;){const n=f?t[a].match(f):null;if(!n&&(!u||u&&a<d-1)){a++;continue}if(n&&e===a&&n.index===c.index){a++;continue}const o=[];if(n&&e===a)o.push(t[e].slice(c[0].length,-n[0].length));else for(let r=e;r<=a;r++)if(r===e){const e=t[r].slice(c[0].length);o.push(e)}else if(r===a&&n){const e=t[r].slice(0,-n[0].length);o.push(e)}else o.push(t[r]);if(!1!==l(r,null,c,n,o,!0))return[!0,a];break}}return[!1,e]}function Ee(e,n,o,i,l,c){const f=r(e),u=p();u.append(f),n.append(u);for(const{regExp:t,replace:n}of o){const r=e.match(t);if(r&&(f.setTextContent(e.slice(r[0].length)),!1!==n(u,[f],r,!0)))break}if(ve(f,i,l),null!==u.getParent()&&e.length>0){const e=u.getPreviousSibling();if(!c&&(t(e)||H(e)||z(e))){let t=e;if(z(e)){const n=e.getLastDescendant();t=null==n?null:s(n,U)}null!=t&&t.getTextContentSize()>0&&(t.splice(t.getChildrenSize(),0,[bt(t),...u.getChildren()]),u.remove())}}}function $e(t){if(g(t))return;const e=t.getTextContent();if(!e.includes("\t"))return;const n=t.getFormat(),o=t.getStyle(),s=[];let i=0;for(let t=e.indexOf("\t");-1!==t;t=e.indexOf("\t",t+1))t>i&&s.push(r(e.slice(i,t)).setFormat(n).setStyle(o)),s.push(h()),i=t+1;i<e.length&&s.push(r(e.slice(i)).setFormat(n).setStyle(o)),t.getParentOrThrow().splice(t.getIndexWithinParent(),1,s)}function be(t,...e){throw function(t,...e){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",t);for(const t of e)r.append("v",t);return n.search=r.toString(),new Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}(t,...e)}function we(t,e,n,r,o){const s=t.getParent();if(!$(s)||t.getFirstChild()!==e)return!1;const i=e.getTextContent();if(!o&&" "!==i[n-1])return!1;for(const{regExp:s,replace:l}of r){const r=i.match(s),c=o||r&&r[0].endsWith(" ")?n:n-1;if(r&&r[0].length===c){const o=e.getNextSiblings(),[s,i]=e.splitText(n);if(!1!==l(t,i?[i,...o]:o,r,!1))return s.remove(),!0}}return!1}function Ne(t,e,n,r,o){const s=t.getParent();if(!$(s)||t.getFirstChild()!==e)return!1;const i=e.getTextContent();if(!o&&" "!==i[n-1])return!1;for(const{regExpStart:s,replace:l,regExpEnd:c}of r){if(c&&!("optional"in c)||c&&"optional"in c&&!c.optional)continue;const r=i.match(s);if(r){const s=o||r[0].endsWith(" ")?n:n-1;if(r[0].length!==s)continue;const i=e.getNextSiblings(),[c,f]=e.splitText(n);if(!1!==l(t,f?[f,...i]:i,r,null,null,!1))return c.remove(),!0}}return!1}function ke(t,e){let n=0;const r=t.getTextContent();for(let t=0;t<e;t++)"`"===r[t]&&n++;return n%2!=0}function Fe(t,e,n){const r=n.length;for(let o=e;o>=r;o--){const e=o-r;if(Pe(t,e,n,0,r)&&" "!==t[e+r])return e}return-1}function Oe(t){const n=t.getNode();let r=0;if(e(n))r=t.offset;else if(a(n))for(const e of n.getChildren().slice(0,t.offset))r+=e.getTextContentSize();for(let t=n.getPreviousSibling();null!==t;t=t.getPreviousSibling())r+=t.getTextContentSize();return r}function Pe(t,e,n,r,o){for(let s=0;s<o;s++)if(t[e+s]!==n[r+s])return!1;return!0}function Me(t,n=ie){const r=Q(n),o=r.element.filter(t=>t.triggerOnEnter),s=J(r.textFormat,({tag:t})=>t[t.length-1]),i=J(r.textMatch,({trigger:t})=>t),l=new Set([" "]);for(const t of r.textFormat)l.add(t.tag.slice(-1));for(const t of r.textMatch)void 0!==t.trigger&&l.add(t.trigger);for(const e of n){const n=e.type;if("element"===n||"text-match"===n||"multiline-element"===n){const n=e.dependencies;for(const e of n)t.hasNode(e)||be(173,e.getType())}}return x(t.registerUpdateListener(({tags:n,dirtyLeaves:o,editorState:u,prevEditorState:a})=>{if(n.has(m)||n.has(C))return;if(t.isComposing())return;const d=n.has(I),g=u.read(T),h=a.read(T);if(!f(h)||!f(g)||!g.isCollapsed()||g.is(h)&&!d)return;const p=g.anchor.key,x=g.anchor.offset,y=u._nodeMap.get(p);if(e(y)&&o.has(p)&&(d||1===x||!(u.read(()=>Oe(g.anchor))>a.read(()=>Oe(h.anchor))+1))){if(d){const t=u.read(()=>y.getTextContent())[x-1];if(!l.has(t))return}t.update(()=>{if(!Te(y))return;const t=y.getParent();null===t||M(t)||((t,n,o)=>!!(we(t,n,o,r.element)||Ne(t,n,o,r.multilineElement)||function(t,e,n){let r=t.getTextContent();const o=n[r[e-1]];if(null==o)return!1;e<r.length&&(r=r.slice(0,e));for(const e of o){if(!e.replace||!e.regExp)continue;const n=r.match(e.regExp);if(null===n)continue;const o=n.index||0,s=o+n[0].length;let i;return 0===o?[i]=t.splitText(s):[,i]=t.splitText(o,s),i.selectNext(0,0),e.replace(i,n),!0}return!1}(n,o,i)||function(t,n,r){const o=t.getTextContent(),s=n-1,i=o[s],l=r[i];if(!l)return!1;for(const n of l){const{tag:r}=n,l=r.length,u=s-l+1;if(l>1&&!Pe(o,u,r,0,l))continue;if(" "===o[u-1])continue;const a=o[s+1];if(!1===n.intraword&&a&&!V.test(a))continue;const d=t;let g=d,h=Fe(o,u,r),p=g;for(;h<0&&(p=p.getPreviousSibling())&&!c(p);)if(e(p)){if(p.hasFormat("code"))continue;const t=p.getTextContent();g=p,h=Fe(t,t.length,r)}if(h<0)continue;if(g===d&&h+l===u)continue;const x=g.getTextContent();if(h>0&&x[h-1]===i)continue;const m=x[h-1];if(!1===n.intraword&&m&&!V.test(m))continue;if(!n.format.includes("code")&&ke(g,h))continue;const C=d.getTextContent(),I=C.slice(0,u)+C.slice(s+1);d.setTextContent(I);const v=g===d?I:x;g.setTextContent(v.slice(0,h)+v.slice(h+l));const S=T(),y=b();w(y);const E=s-l*(g===d?2:1)+1;y.anchor.set(g.__key,h,"text"),y.focus.set(d.__key,E,"text");for(const t of n.format)y.formatText(t,N[t]);y.anchor.set(y.focus.key,y.focus.offset,y.focus.type);for(const t of n.format)y.hasFormat(t)&&y.toggleFormat(t);return f(S)&&(y.format=S.format),!0}return!1}(n,o,s)))(t,y,g.anchor.offset)&&v(S)})}}),t.registerCommand(y,t=>{if(null!==t&&t.shiftKey)return!1;const n=T();if(!f(n)||!n.isCollapsed())return!1;const s=n.anchor.offset,i=n.anchor.getNode();if(!e(i)||!Te(i))return!1;const l=i.getParent();return null!==l&&!M(l)&&!(s!==i.getTextContent().length||!Ne(l,i,s,r.multilineElement,!0)&&!we(l,i,s,o,!0)||(null!==t&&t.preventDefault(),0))},E))}function Re(t,e=ie,n,r=!1,o=!1){const s=r?t:le(t,o),i=n||u();i.clear(),Se(s,i,e,r),null!==T()&&i.selectStart()}function Be(t,e=ie,n=!1,r=!1){const o=n?t:le(t,r),s=new k;return Se(o,s,e,n),s.getChildren()}function _e(t=ie,e,n=!1){const r=function(t,e=!1){const n=Q(t),r=[...n.multilineElement,...n.element],o=!e,s=n.textFormat.filter(t=>1===t.format.length).sort((t,e)=>Number(t.format.includes("code"))-Number(e.format.includes("code")));return t=>{const i=[],l=(t||u()).getChildren();for(let t=0;t<l.length;t++){const c=l[t],f=ue(c,r,s,n.textMatch,e);null!=f&&i.push(o&&t>0&&!nt(c)&&!nt(l[t-1])?"\n".concat(f):f)}return i.join("\n")}}(t,n);return r(e)}function Le(t=ie,e,n=!1){if(!e||f(e)&&e.isCollapsed())return"";const r=function(t,e=!1){const n=Q(t),r=[...n.multilineElement,...n.element],o=!e,s=n.textFormat.filter(t=>1===t.format.length).sort((t,e)=>Number(t.format.includes("code"))-Number(e.format.includes("code")));return t=>{const i=t.getNodes(),l=t.clone();l.setCachedNodes(i),f(l)&&(l._cachedIsBackward=t.isBackward());const c=[];let a=null;for(const t of u().getChildren()){const{shouldInclude:i,markdown:f}=ce(t,l,r,s,n.textMatch,e);i&&null!=f&&(c.push(!o||null===a||nt(t)||nt(a)?f:"\n".concat(f)),a=t)}return c.join("\n")}}(t,n);return r(e)}export{Re as $convertFromMarkdownString,Le as $convertSelectionToMarkdownString,_e as $convertToMarkdownString,Be as $generateNodesFromMarkdownString,Xt as BOLD_ITALIC_STAR,Zt as BOLD_ITALIC_UNDERSCORE,Ht as BOLD_STAR,qt as BOLD_UNDERSCORE,Wt as CHECK_LIST,jt as CODE,ee as ELEMENT_TRANSFORMERS,Lt as HEADING,Dt as HIGHLIGHT,Ut as INLINE_CODE,Jt as ITALIC_STAR,Qt as ITALIC_UNDERSCORE,te as LINK,ne as MULTILINE_ELEMENT_TRANSFORMERS,Kt as ORDERED_LIST,At as QUOTE,Gt as STRIKETHROUGH,re as TEXT_FORMAT_TRANSFORMERS,oe as TEXT_MATCH_TRANSFORMERS,ie as TRANSFORMERS,zt as UNORDERED_LIST,mt as isTableRowDivider,Me as registerMarkdownShortcuts};
package/package.json CHANGED
@@ -8,23 +8,23 @@
8
8
  "markdown"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.53.0",
11
+ "version": "0.53.1-nightly.20261030.0",
12
12
  "main": "./dist/LexicalMarkdown.js",
13
13
  "types": "./dist/typescript-too-old.d.ts",
14
14
  "dependencies": {
15
- "@lexical/code-core": "0.53.0",
16
- "@lexical/internal": "0.53.0",
17
- "@lexical/link": "0.53.0",
18
- "@lexical/list": "0.53.0",
19
- "@lexical/rich-text": "0.53.0",
20
- "@lexical/selection": "0.53.0",
21
- "@lexical/text": "0.53.0",
22
- "@lexical/utils": "0.53.0",
23
- "lexical": "0.53.0"
15
+ "@lexical/code-core": "0.53.1-nightly.20261030.0",
16
+ "@lexical/internal": "0.53.1-nightly.20261030.0",
17
+ "@lexical/link": "0.53.1-nightly.20261030.0",
18
+ "@lexical/list": "0.53.1-nightly.20261030.0",
19
+ "@lexical/rich-text": "0.53.1-nightly.20261030.0",
20
+ "@lexical/selection": "0.53.1-nightly.20261030.0",
21
+ "@lexical/text": "0.53.1-nightly.20261030.0",
22
+ "@lexical/utils": "0.53.1-nightly.20261030.0",
23
+ "lexical": "0.53.1-nightly.20261030.0"
24
24
  },
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "git+https://github.com/facebook/lexical.git",
27
+ "url": "git+https://github.com/meta/lexical.git",
28
28
  "directory": "packages/lexical-markdown"
29
29
  },
30
30
  "module": "./dist/LexicalMarkdown.js",
@@ -13,11 +13,13 @@ import {
13
13
  $isDecoratorNode,
14
14
  $isElementNode,
15
15
  $isLineBreakNode,
16
+ $isRangeSelection,
16
17
  $isTextNode,
17
18
  type BaseSelection,
18
19
  type ElementNode,
19
20
  type LexicalNode,
20
21
  type LineBreakNode,
22
+ type NodeKey,
21
23
  type TextFormatType,
22
24
  type TextNode,
23
25
  } from 'lexical';
@@ -111,7 +113,17 @@ export function createSelectionMarkdownExport(
111
113
  );
112
114
  });
113
115
 
114
- return selection => {
116
+ return exportedSelection => {
117
+ // Every node asks whether it is selected, which reads the selection's
118
+ // nodes. A read-only selection doesn't cache them, so this works on a
119
+ // copy that does, instead of recomputing them for every node.
120
+ // Its direction is cached too, since slicing a node asks for it.
121
+ const nodes = exportedSelection.getNodes();
122
+ const selection = exportedSelection.clone();
123
+ selection.setCachedNodes(nodes);
124
+ if ($isRangeSelection(selection)) {
125
+ selection._cachedIsBackward = exportedSelection.isBackward();
126
+ }
115
127
  const output = [];
116
128
  // The separator depends on the previously *emitted* block, not on the
117
129
  // previous root child: unselected children produce no output, so keying
@@ -212,14 +224,21 @@ function $processNodeForSelection(
212
224
  }
213
225
  }
214
226
 
227
+ interface OpenTag {
228
+ format: TextFormatType;
229
+ tag: string;
230
+ /** `$continuesTag`'s answers so far, by node key. */
231
+ answers: Map<NodeKey, boolean>;
232
+ }
233
+
215
234
  function $exportChildrenForSelection(
216
235
  node: ElementNode,
217
236
  selection: BaseSelection,
218
237
  textFormatTransformers: TextFormatTransformer[],
219
238
  textMatchTransformers: TextMatchTransformer[],
220
239
  shouldPreserveNewLines: boolean,
221
- unclosedTags?: {format: TextFormatType; tag: string}[],
222
- unclosableTags?: {format: TextFormatType; tag: string}[],
240
+ unclosedTags?: OpenTag[],
241
+ unclosableTags?: OpenTag[],
223
242
  ): {shouldInclude: boolean; markdown: string} {
224
243
  const output = [];
225
244
  const children = node.getChildren();
@@ -253,21 +272,16 @@ function $exportChildrenForSelection(
253
272
  unclosedTags,
254
273
  [...unclosableTags, ...unclosedTags],
255
274
  ).markdown,
256
- (textNode, textContent) => {
257
- const slicedNode = $sliceSelectedTextNodeContent(
258
- selection,
259
- textNode,
260
- 'clone',
261
- );
262
- return exportTextFormat(
275
+ textNode =>
276
+ $exportTextFormat(
263
277
  textNode,
264
- slicedNode.getTextContent(),
278
+ $exportedText(textNode, selection),
265
279
  textFormatTransformers,
266
280
  unclosedTags,
267
281
  unclosableTags,
268
282
  shouldPreserveNewLines,
269
- );
270
- },
283
+ selection,
284
+ ),
271
285
  );
272
286
 
273
287
  if (result != null) {
@@ -295,15 +309,15 @@ function $exportChildrenForSelection(
295
309
  }
296
310
  } else if ($isTextNode(child)) {
297
311
  if (childIncluded) {
298
- const target = $sliceSelectedTextNodeContent(selection, child, 'clone');
299
312
  output.push(
300
- exportTextFormat(
313
+ $exportTextFormat(
301
314
  child,
302
- target.getTextContent(),
315
+ $exportedText(child, selection),
303
316
  textFormatTransformers,
304
317
  unclosedTags,
305
318
  unclosableTags,
306
319
  shouldPreserveNewLines,
320
+ selection,
307
321
  ),
308
322
  );
309
323
  anyChildIncluded = true;
@@ -390,8 +404,8 @@ function $exportChildren(
390
404
  node: ElementNode,
391
405
  textTransformersIndex: TextFormatTransformer[],
392
406
  textMatchTransformers: TextMatchTransformer[],
393
- unclosedTags?: {format: TextFormatType; tag: string}[],
394
- unclosableTags?: {format: TextFormatType; tag: string}[],
407
+ unclosedTags?: OpenTag[],
408
+ unclosableTags?: OpenTag[],
395
409
  shouldPreserveNewLines: boolean = false,
396
410
  ): string {
397
411
  const output = [];
@@ -427,13 +441,14 @@ function $exportChildren(
427
441
  shouldPreserveNewLines,
428
442
  ),
429
443
  (textNode, textContent) =>
430
- exportTextFormat(
444
+ $exportTextFormat(
431
445
  textNode,
432
446
  textContent,
433
447
  textTransformersIndex,
434
448
  unclosedTags,
435
449
  unclosableTags,
436
450
  shouldPreserveNewLines,
451
+ null,
437
452
  ),
438
453
  );
439
454
 
@@ -447,13 +462,14 @@ function $exportChildren(
447
462
  output.push($exportLineBreak(child));
448
463
  } else if ($isTextNode(child)) {
449
464
  output.push(
450
- exportTextFormat(
465
+ $exportTextFormat(
451
466
  child,
452
467
  child.getTextContent(),
453
468
  textTransformersIndex,
454
469
  unclosedTags,
455
470
  unclosableTags,
456
471
  shouldPreserveNewLines,
472
+ null,
457
473
  ),
458
474
  );
459
475
  } else if ($isElementNode(child)) {
@@ -480,20 +496,20 @@ function $exportLineBreak(node: LineBreakNode): string {
480
496
  return $getState(node, hardLineBreakState) + '\n';
481
497
  }
482
498
 
483
- function exportTextFormat(
499
+ function $exportTextFormat(
484
500
  node: TextNode,
485
501
  textContent: string,
486
502
  textTransformers: TextFormatTransformer[],
487
503
  // unclosed tags include the markdown tags that haven't been closed yet, and their associated formats
488
- unclosedTags: {format: TextFormatType; tag: string}[],
489
- unclosableTags?: {format: TextFormatType; tag: string}[],
490
- shouldPreserveNewLines: boolean = false,
504
+ unclosedTags: OpenTag[],
505
+ unclosableTags: OpenTag[],
506
+ shouldPreserveNewLines: boolean,
507
+ // the selection being exported, which ends the text a tag can extend over
508
+ selection: BaseSelection | null,
491
509
  ): string {
492
510
  // This function handles the case of a string looking like this: " foo "
493
511
  // Where it would be invalid markdown to generate: "** foo **"
494
- // If the node has no format, we use the original text.
495
- // Otherwise, we escape leading and trailing whitespaces to their corresponding code points,
496
- // ensuring the returned string maintains its original formatting, e.g., "**&#32;&#32;&#32;foo&#32;&#32;&#32;**".
512
+ // so the tags go inside the whitespace instead: " **foo** ".
497
513
 
498
514
  const isCode = node.hasFormat('code');
499
515
 
@@ -512,28 +528,7 @@ function exportTextFormat(
512
528
  );
513
529
  }
514
530
 
515
- let leadingSpace: string;
516
- let trimmedOutput: string;
517
- let trailingSpace: string;
518
- let isWhitespaceOnly: boolean;
519
-
520
- if (isCode) {
521
- // Inline code is an atomic literal span with a content-derived fence, so
522
- // its whitespace stays inside the fence and other formats wrap around it.
523
- const {fence, padded} = getCodeSpanDelimiter(textContent);
524
- leadingSpace = '';
525
- trailingSpace = '';
526
- trimmedOutput = fence + padded + fence;
527
- isWhitespaceOnly = false;
528
- } else {
529
- // Extract leading and trailing whitespaces.
530
- // CommonMark flanking rules require formatting tags to be adjacent to non-whitespace characters.
531
- const match = output.match(/^(\s*)(.*?)(\s*)$/s) || ['', '', output, ''];
532
- leadingSpace = match[1];
533
- trimmedOutput = match[2];
534
- trailingSpace = match[3];
535
- isWhitespaceOnly = trimmedOutput === '';
536
- }
531
+ const holdsTag = canHoldTag(textContent);
537
532
 
538
533
  // the opening tags to be added to the result
539
534
  let openingTags = '';
@@ -542,7 +537,6 @@ function exportTextFormat(
542
537
  let closingTagsAfter = '';
543
538
 
544
539
  const prevNode = getTextSibling(node, true);
545
- const nextNode = getTextSibling(node, false);
546
540
 
547
541
  const applied = new Set();
548
542
 
@@ -550,32 +544,24 @@ function exportTextFormat(
550
544
  const format = transformer.format[0];
551
545
  const tag = transformer.tag;
552
546
 
553
- // Inline code uses a content-derived fence handled above, not a static tag.
547
+ // Inline code uses a content-derived fence handled below, not a static tag.
554
548
  if (format === 'code') {
555
549
  continue;
556
550
  }
557
551
 
558
552
  // dedup applied formats
559
- // `checkHasFormat` reads the node's whole text, but the text being
560
- // exported may be a selection slice of it. When the slice is entirely
561
- // whitespace the tags are dropped by the early return below, so
562
- // registering them here would leave an unclosed tag for a later node to
563
- // close. In the full-document path `checkHasFormat` already rejects
564
- // whitespace-only nodes, so this guard only affects sliced content.
565
- if (
566
- !isWhitespaceOnly &&
567
- checkHasFormat(node, format) &&
568
- !applied.has(format)
569
- ) {
553
+ if (holdsTag && node.hasFormat(format) && !applied.has(format)) {
570
554
  applied.add(format);
571
555
 
572
556
  // append the tag to openingTags, if it's not applied to the previous nodes,
573
- // or the nodes before that (which would result in an unclosed tag)
557
+ // or the nodes before that (which would result in an unclosed tag).
558
+ // A whitespace-only previous node with the format keeps the tag open
559
+ // without emitting it, so it counts as applied here.
574
560
  if (
575
- !checkHasFormat(prevNode, format) ||
561
+ !hasFormat(prevNode, format) ||
576
562
  !unclosedTags.find(element => element.tag === tag)
577
563
  ) {
578
- unclosedTags.push({format, tag});
564
+ unclosedTags.push({answers: new Map(), format, tag});
579
565
  openingTags += tag;
580
566
  }
581
567
  }
@@ -584,10 +570,8 @@ function exportTextFormat(
584
570
  // close any tags in the same order they were applied, if necessary
585
571
  for (let i = 0; i < unclosedTags.length; i++) {
586
572
  const nodeHasFormat = hasFormat(node, unclosedTags[i].format);
587
- const nextNodeHasFormat = hasFormat(nextNode, unclosedTags[i].format);
588
-
589
- // prevent adding closing tag if next sibling will do it
590
- if (nodeHasFormat && nextNodeHasFormat) {
573
+ // prevent adding closing tag if later text with the format will do it
574
+ if (nodeHasFormat && $continuesTag(node, unclosedTags[i], selection)) {
591
575
  continue;
592
576
  }
593
577
 
@@ -599,7 +583,6 @@ function exportTextFormat(
599
583
  // If tag is unclosable, don't close it and leave it in the original array,
600
584
  // So that it can be closed when it's no longer unclosable
601
585
  if (
602
- unclosableTags &&
603
586
  unclosedTag &&
604
587
  unclosableTags.find(element => element.tag === unclosedTag.tag)
605
588
  ) {
@@ -611,7 +594,7 @@ function exportTextFormat(
611
594
  // Handles cases where the tag has not been closed before, e.g. if the previous node
612
595
  // was a text match transformer that did not account for closing tags of the next node (e.g. a link)
613
596
  closingTagsBefore += unclosedTag.tag;
614
- } else if (!nextNodeHasFormat) {
597
+ } else {
615
598
  closingTagsAfter += unclosedTag.tag;
616
599
  }
617
600
  }
@@ -620,13 +603,25 @@ function exportTextFormat(
620
603
  }
621
604
  break;
622
605
  }
623
- // If the node is entirely whitespace, we don't apply opening/closing tags around it.
624
- // However, it must still output closing tags from previous nodes.
625
- if (isWhitespaceOnly && !node.hasFormat('code')) {
626
- return closingTagsBefore + output;
606
+ if (isCode && textContent !== '') {
607
+ // Inline code is an atomic literal span with a content-derived fence, so
608
+ // its whitespace stays inside the fence and other formats wrap around it.
609
+ const {fence, padded} = getCodeSpanDelimiter(textContent);
610
+ output = fence + padded + fence;
611
+ } else if (!holdsTag) {
612
+ // If the node is entirely whitespace, we don't apply opening/closing tags
613
+ // around it, but it must still output any closing tags.
614
+ return closingTagsBefore + closingTagsAfter + output;
627
615
  }
628
616
 
629
- // Flanking Compliance: Notice how openingTags and closingTagsAfter are placed INSIDE the whitespace boundaries!
617
+ // Flanking Compliance: CommonMark flanking rules require formatting tags to be
618
+ // adjacent to non-whitespace characters, so openingTags and closingTagsAfter
619
+ // are placed INSIDE the whitespace boundaries!
620
+ // (Trimming rather than a regex keeps this linear in long whitespace runs.)
621
+ const trimmedStart = output.trimStart();
622
+ const trimmedOutput = trimmedStart.trimEnd();
623
+ const leadingSpace = output.slice(0, output.length - trimmedStart.length);
624
+ const trailingSpace = trimmedStart.slice(trimmedOutput.length);
630
625
  return (
631
626
  closingTagsBefore +
632
627
  leadingSpace +
@@ -647,6 +642,64 @@ function getTextSibling(node: TextNode, backward: boolean): TextNode | null {
647
642
  return null;
648
643
  }
649
644
 
645
+ /**
646
+ * Whether `tag` carries on past `node`, which has its format, to exported
647
+ * text that can hold it. Whitespace-only siblings cannot (the tag must close
648
+ * before the space), so they are looked past while they keep the format.
649
+ * Text outside the exported selection does not count. Every whitespace node
650
+ * the walk passes over shares its answer, so that is remembered on the tag
651
+ * and each run is walked once, whatever order its nodes are asked in.
652
+ */
653
+ function $continuesTag(
654
+ node: TextNode,
655
+ tag: OpenTag,
656
+ selection: BaseSelection | null,
657
+ ): boolean {
658
+ const {answers} = tag;
659
+ const known = answers.get(node.getKey());
660
+ if (known !== undefined) {
661
+ return known;
662
+ }
663
+ const passed: TextNode[] = [];
664
+ let next = getTextSibling(node, false);
665
+ let result = false;
666
+ while (
667
+ next &&
668
+ next.hasFormat(tag.format) &&
669
+ (!selection || next.isSelected(selection))
670
+ ) {
671
+ const text = $exportedText(next, selection);
672
+ if (canHoldTag(text)) {
673
+ result = true;
674
+ break;
675
+ }
676
+ // Markdown import matches a tag within one line.
677
+ if (text.includes('\n')) {
678
+ break;
679
+ }
680
+ // A whitespace node already answered for shares the answer.
681
+ const passedAnswer = answers.get(next.getKey());
682
+ if (passedAnswer !== undefined) {
683
+ result = passedAnswer;
684
+ break;
685
+ }
686
+ passed.push(next);
687
+ next = getTextSibling(next, false);
688
+ }
689
+ answers.set(node.getKey(), result);
690
+ for (const passedNode of passed) {
691
+ answers.set(passedNode.getKey(), result);
692
+ }
693
+ return result;
694
+ }
695
+
696
+ /** The text of `node` that the export emits: its selected slice, if any. */
697
+ function $exportedText(node: TextNode, selection: BaseSelection | null) {
698
+ return selection
699
+ ? $sliceSelectedTextNodeContent(selection, node, 'clone').getTextContent()
700
+ : node.getTextContent();
701
+ }
702
+
650
703
  function hasFormat(
651
704
  node: LexicalNode | null | undefined,
652
705
  format: TextFormatType,
@@ -654,15 +707,10 @@ function hasFormat(
654
707
  return $isTextNode(node) && node.hasFormat(format);
655
708
  }
656
709
 
657
- function checkHasFormat(n: TextNode | null, f: TextFormatType): boolean {
658
- if (!hasFormat(n, f)) {
659
- return false;
660
- }
661
- if (f === 'code') {
662
- return true;
663
- }
664
- if (n && /^\s*$/.test(n.getTextContent())) {
665
- return false;
666
- }
667
- return true;
710
+ /**
711
+ * Whether exported text can carry a format's tag: CommonMark flanking rules
712
+ * keep tags off whitespace.
713
+ */
714
+ function canHoldTag(text: string): boolean {
715
+ return /\S/.test(text);
668
716
  }
@@ -1096,10 +1096,12 @@ export function getCodeSpanDelimiter(content: string): {
1096
1096
  ? Math.max(...backtickRuns.map(run => run.length))
1097
1097
  : 0;
1098
1098
  const fence = '`'.repeat(longestRun + 1);
1099
+ // A reader strips one space from each end only when the content is not all
1100
+ // spaces, so content made only of spaces goes out as it is.
1099
1101
  const needsPadding =
1100
1102
  content.length === 0 ||
1101
1103
  content.includes('`') ||
1102
- (/^\s/.test(content) && /\s$/.test(content));
1104
+ (/^\s/.test(content) && /\s$/.test(content) && /[^ ]/.test(content));
1103
1105
  const padded = needsPadding ? ` ${content} ` : content;
1104
1106
  return {fence, padded};
1105
1107
  }
@@ -1155,17 +1157,40 @@ export const ITALIC_UNDERSCORE: TextFormatTransformer = {
1155
1157
  type: 'text-format',
1156
1158
  };
1157
1159
 
1158
- // `unescapeText` decodes a numeric character reference and a CommonMark reader
1159
- // decodes the named ones too, so an `&` that begins one cannot be written raw
1160
- // in a link destination or the URL comes back as the character it names. It
1161
- // goes out as `&#38;` instead, which both of them read back as a single `&`.
1162
- // An `&` that begins nothing is an ordinary character and stays as it is, so a
1163
- // query string keeps the separators it was written with.
1164
- function escapeCharacterReferences(value: string): string {
1165
- return value.replace(
1166
- /&(?=#\d+;|#[Xx][\dA-Fa-f]+;|[A-Za-z][\dA-Za-z]*;)/g,
1167
- '&#38;',
1168
- );
1160
+ // A raw link destination cannot hold an ASCII control character or a space in
1161
+ // CommonMark, nor any Unicode whitespace in the raw branch of `LINK`'s import
1162
+ // patterns, so export writes a URL with any of them in the pointy form. Import
1163
+ // still reads control characters in a raw destination, as older exports wrote
1164
+ // them there. A NUL reads back as U+FFFD in a CommonMark reader whichever form
1165
+ // it is in, since no spelling of it avoids that.
1166
+ // eslint-disable-next-line no-control-regex
1167
+ const NOT_RAW_DESTINATION = /[\s\0-\x1f\x7f]/;
1168
+
1169
+ // A link destination or title is read back through `unescapeText`, which
1170
+ // decodes a numeric character reference, and a CommonMark reader decodes the
1171
+ // named ones too. So an `&` that begins one goes out as `&#38;`, or the part
1172
+ // would come back as the character the reference names. An `&` that begins
1173
+ // nothing is an ordinary character and stays as it is, so a query string keeps
1174
+ // the separators it was written with.
1175
+ //
1176
+ // Then every match of `syntax`, a global regular expression for the characters
1177
+ // the part's shape treats as syntax, gets a backslash. So does a backtick: this
1178
+ // package's importer reads code spans before links, so one anywhere in the
1179
+ // link could pair with one in the text after it and swallow the link, although
1180
+ // a CommonMark reader would not let it.
1181
+ //
1182
+ // Neither part may hold a line ending either. CommonMark allows none in a
1183
+ // destination and no blank line in a title, where two CRs make one as surely
1184
+ // as two LFs, and the importer reads one LF-separated line at a time, so even
1185
+ // a single LF would split the link. Each goes out as the character reference
1186
+ // that `unescapeText` and a CommonMark reader both turn back into the line
1187
+ // ending, written after the `&` escape so that one leaves it alone.
1188
+ function escapeLinkPart(value: string, syntax: RegExp): string {
1189
+ return value
1190
+ .replace(/&(?=#\d+;|#[Xx][\dA-Fa-f]+;|[A-Za-z][\dA-Za-z]*;)/g, '&#38;')
1191
+ .replace(syntax, '\\$&')
1192
+ .replace(/`/g, '\\`')
1193
+ .replace(/[\r\n]/g, lineEnding => `&#${lineEnding.charCodeAt(0)};`);
1169
1194
  }
1170
1195
 
1171
1196
  // Mask escaped/code brackets without changing source offsets, then find an inner
@@ -1223,43 +1248,24 @@ export const LINK: TextMatchTransformer = {
1223
1248
  return null;
1224
1249
  }
1225
1250
  const textContent = exportChildren(node);
1226
- let title = node.getTitle();
1227
-
1228
- // A title is read back through `unescapeText` as well, so a character
1229
- // reference in it needs the same treatment the destination gets below.
1230
- if (title != null) {
1231
- title = escapeCharacterReferences(title).replace(/([\\"])/g, '\\$1');
1232
- }
1233
-
1234
- // A raw destination cannot hold whitespace, so a URL that has any is
1235
- // written in the pointy form, where only `<`, `>` and a backslash need an
1236
- // escape. An empty URL goes there too, since the raw form has nothing left
1237
- // to match. Everywhere else the destination is written raw, where a
1238
- // parenthesis would close it early, a backslash would start an escape, and
1239
- // only a `<` in first place turns it into the pointy form. An angle
1240
- // bracket anywhere else is an ordinary character and goes out as it is.
1241
- //
1242
- // Neither shape may hold a line ending, so a literal one would leave a
1243
- // destination that no reader can close and would split the paragraph in
1244
- // two. It goes out as the character reference that `unescapeText` and a
1245
- // CommonMark reader both turn back into the line ending.
1246
- //
1247
- // That spelling only survives because a reader decodes it, so an `&` that
1248
- // already begins a character reference has to go out as one itself, or the
1249
- // URL comes back as whatever the reference names. Escaping it first keeps
1250
- // the references written for the line endings below out of its way.
1251
+ const title = node.getTitle();
1252
+
1253
+ // A URL that `NOT_RAW_DESTINATION` matches goes in the pointy form, where
1254
+ // `<`, `>` and a backslash are syntax. An empty URL goes there too, since
1255
+ // the raw form has nothing left to match. Everywhere else the destination
1256
+ // is written raw, where a parenthesis would close it early, a backslash
1257
+ // would start an escape, and only a `<` in first place turns it into the
1258
+ // pointy form. An angle bracket anywhere else is an ordinary character and
1259
+ // goes out as it is.
1251
1260
  const rawUrl = node.getURL();
1252
- const escapedUrl = escapeCharacterReferences(rawUrl);
1253
1261
  const url =
1254
- rawUrl === '' || /\s/.test(rawUrl)
1255
- ? `<${escapedUrl
1256
- .replace(/([\\<>])/g, '\\$1')
1257
- .replace(/\r/g, '&#13;')
1258
- .replace(/\n/g, '&#10;')}>`
1259
- : escapedUrl.replace(/([\\()])/g, '\\$1').replace(/^</, '\\<');
1262
+ rawUrl === '' || NOT_RAW_DESTINATION.test(rawUrl)
1263
+ ? `<${escapeLinkPart(rawUrl, /[\\<>]/g)}>`
1264
+ : escapeLinkPart(rawUrl, /[\\()]|^</g);
1260
1265
 
1266
+ // In the double-quoted title only `"` and a backslash are syntax.
1261
1267
  const linkContent = title
1262
- ? `[${textContent}](${url} "${title}")`
1268
+ ? `[${textContent}](${url} "${escapeLinkPart(title, /[\\"]/g)}")`
1263
1269
  : `[${textContent}](${url})`;
1264
1270
 
1265
1271
  return linkContent;
@@ -1292,9 +1298,9 @@ export const LINK: TextMatchTransformer = {
1292
1298
  // are characters, which costs a quadratic walk of every run that never
1293
1299
  // reaches the closing parenthesis.
1294
1300
  importRegExp:
1295
- /(?:\[(.+?)\])(?:\(\s*(?:(?:<((?:\\.|[^<>\n\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:[^"]*\\")*[^"]*)"|'((?:[^']*\\')*[^']*)'|\(((?:\\.|[^()\\])*)\)))?\s*)?\))/,
1301
+ /(?:\[(.+?)\])(?:\(\s*(?:(?:<((?:\\[^\n\r]|[^<>\n\r\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:\\[\s\S]|[^"\\])*)"|'((?:\\[\s\S]|[^'\\])*)'|\(((?:\\[\s\S]|[^()\\])*)\)))?\s*)?\))/,
1296
1302
  regExp:
1297
- /(?:\[([^[\]]*(?:\[[^[\]]*\][^[\]]*)*)\])(?:\(\s*(?:(?:<((?:\\.|[^<>\n\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:[^"]*\\")*[^"]*)"|'((?:[^']*\\')*[^']*)'|\(((?:\\.|[^()\\])*)\)))?\s*)?\))$/,
1303
+ /(?:\[([^[\]]*(?:\[[^[\]]*\][^[\]]*)*)\])(?:\(\s*(?:(?:<((?:\\[^\n\r]|[^<>\n\r\\])*)>|((?!<)(?:\\[^\s]|\\(?=\s)|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\]|\((?:\\[^\s]|[^\s()\\])*\))*\))*\)|[^\s()\\])+))(?:\s+(?:"((?:\\[\s\S]|[^"\\])*)"|'((?:\\[\s\S]|[^'\\])*)'|\(((?:\\[\s\S]|[^()\\])*)\)))?\s*)?\))$/,
1298
1304
  replace: (textNode, match) => {
1299
1305
  // https://spec.commonmark.org/0.31.2/#inline-link
1300
1306
  const source = match.input || '';
@@ -59,7 +59,7 @@ export function importTextTransformers(
59
59
  // or links). A code span must never be partially consumed by a text
60
60
  // match, so prefer the code format unless the text match fully wraps it
61
61
  // (in which case the code span is part of the match's raw content).
62
- // https://github.com/facebook/lexical/issues/8687
62
+ // https://github.com/meta/lexical/issues/8687
63
63
  if (
64
64
  foundTextMatch.startIndex <= foundTextFormat.startIndex &&
65
65
  foundTextMatch.endIndex >= foundTextFormat.endIndex