@meowdown/core 0.82.0 → 0.83.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ import { normalizeIdentifier } from "micromark-util-normalize-identifier";
49
49
  import { bulletListInputRule, createDedentListCommand, createIndentListCommand, createListInputRuleHandler, createListRenderingPlugin, createSafariInputMethodWorkaroundPlugin, createSplitListCommand, createToggleCollapsedCommand, defaultAttributesGetter, deleteCommand, enterCommand, findCheckboxInListItem, handleListMarkerMouseDown, joinCollapsedListBackward, joinListElements, joinListUp, listToDOM, orderedListInputRule, protectCollapsed, taskListInputRule, unwrapListSlice } from "prosemirror-flat-list";
50
50
  import { defineTableCellSpec, defineTableCommands, defineTableDropIndicator, defineTableEditingPlugin, defineTableHeaderCellSpec, defineTableRowSpec, defineTableSpec, deleteTable, isCellSelection } from "@prosekit/extensions/table";
51
51
 
52
- //#region src/utils/composition.ts
52
+ // src/utils/composition.ts
53
53
  const COMPOSITION_TAIL_MS = 50;
54
54
  let compositionEndedAt = -1;
55
55
  let isComposing = false;
@@ -72,8 +72,40 @@ function getIsComposing() {
72
72
  return isComposing || compositionEndedAt > 0 && Date.now() - compositionEndedAt <= COMPOSITION_TAIL_MS;
73
73
  }
74
74
 
75
- //#endregion
76
- //#region src/utils/input-modality.ts
75
+ // src/utils/guard-plugin.ts
76
+ /**
77
+ * Wrap an `appendTransaction` so that a throw is reported with `reportError`
78
+ * and appends nothing. Otherwise the whole dispatch fails, and text the
79
+ * browser already inserted never reaches the editor state.
80
+ */
81
+ function guardAppendTransaction(appendTransaction) {
82
+ return (transactions, oldState, newState) => {
83
+ try {
84
+ return appendTransaction(transactions, oldState, newState);
85
+ } catch (error) {
86
+ reportError(error);
87
+ return null;
88
+ }
89
+ };
90
+ }
91
+ /**
92
+ * Wrap a `decorations` prop so that a throw is reported with `reportError` and
93
+ * draws no decorations. ProseMirror reads decorations in the middle of a view
94
+ * update and in `EditorView.destroy`, where a throw leaves the view half
95
+ * updated.
96
+ */
97
+ function guardDecorations(decorations) {
98
+ return (state) => {
99
+ try {
100
+ return decorations(state);
101
+ } catch (error) {
102
+ reportError(error);
103
+ return null;
104
+ }
105
+ };
106
+ }
107
+
108
+ // src/utils/input-modality.ts
77
109
  const KEYBOARD_MODALITY_KEYS = /* @__PURE__ */ new Set([
78
110
  "ArrowLeft",
79
111
  "ArrowRight",
@@ -146,8 +178,7 @@ function onIsTouchInputChange(listener) {
146
178
  };
147
179
  }
148
180
 
149
- //#endregion
150
- //#region src/utils/transaction.ts
181
+ // src/utils/transaction.ts
151
182
  /**
152
183
  * Whether any of the given transactions are selection transactions directly caused by mouse or touch input.
153
184
  */
@@ -155,8 +186,7 @@ function hasPointerSelectionTransaction(transactions) {
155
186
  return getIsPointerSelection() && transactions.some((tr) => tr.selectionSet && !tr.docChanged);
156
187
  }
157
188
 
158
- //#endregion
159
- //#region src/extensions/editor-config-getter.ts
189
+ // src/extensions/editor-config-getter.ts
160
190
  const defaultConfig = Object.freeze({});
161
191
  const configKey = new PluginKey("meowdown-editor-config");
162
192
  /**
@@ -166,8 +196,7 @@ function getEditorConfig(state) {
166
196
  return configKey.getState(state)?.config ?? defaultConfig;
167
197
  }
168
198
 
169
- //#endregion
170
- //#region src/extensions/mark-mode-config.ts
199
+ // src/extensions/mark-mode-config.ts
171
200
  /**
172
201
  * The active mark mode, defaulting to focus when no configuration is provided.
173
202
  */
@@ -175,8 +204,7 @@ function getMarkMode(state) {
175
204
  return getEditorConfig(state).markMode ?? "focus";
176
205
  }
177
206
 
178
- //#endregion
179
- //#region src/extensions/mark-names.ts
207
+ // src/extensions/mark-names.ts
180
208
  function isMarkOfType(mark, name) {
181
209
  return mark.type.name === name;
182
210
  }
@@ -200,8 +228,7 @@ const ATOM_SOURCE_MARK_NAMES = [
200
228
  "mdFile"
201
229
  ];
202
230
 
203
- //#endregion
204
- //#region src/extensions/hidden-run.ts
231
+ // src/extensions/hidden-run.ts
205
232
  function getCharMarks(state, pos) {
206
233
  if (pos < 0 || pos + 1 > state.doc.content.size) return;
207
234
  const $pos = state.doc.resolve(pos);
@@ -341,8 +368,7 @@ function getUnitMarkerRuns(state, charPos) {
341
368
  return runs;
342
369
  }
343
370
 
344
- //#endregion
345
- //#region src/extensions/mark-range.ts
371
+ // src/extensions/mark-range.ts
346
372
  /**
347
373
  * Returns the resolved position, or `undefined` when it falls outside the
348
374
  * document or in a place that carries no inline marks: marks describe inline
@@ -425,8 +451,7 @@ function getMarkRangeStrictlyAround(state, pos, markNames) {
425
451
  }
426
452
  }
427
453
 
428
- //#endregion
429
- //#region src/extensions/atom-mark-navigation.ts
454
+ // src/extensions/atom-mark-navigation.ts
430
455
  function getActiveMarkNames(marks, state) {
431
456
  const mode = getMarkMode(state);
432
457
  const result = [];
@@ -563,7 +588,7 @@ function getUnitEdge(range, oldPos, newPos, isPointer) {
563
588
  function createCaretSnapPlugin(marks) {
564
589
  return new Plugin({
565
590
  key: new PluginKey("atom-mark-caret-snap"),
566
- appendTransaction: (transactions, oldState, newState) => {
591
+ appendTransaction: guardAppendTransaction((transactions, oldState, newState) => {
567
592
  if (getIsComposing()) return null;
568
593
  const markNames = getActiveMarkNames(marks, newState);
569
594
  if (markNames.length === 0) return null;
@@ -583,14 +608,14 @@ function createCaretSnapPlugin(marks) {
583
608
  const isPointer = hasPointerSelectionTransaction(transactions);
584
609
  const head = getUnitEdge(range, oldState.selection.head, selection.head, isPointer);
585
610
  return newState.tr.setSelection(TextSelection.create(newState.doc, head));
586
- }
611
+ })
587
612
  });
588
613
  }
589
614
  const SELECTED_CLASS = "md-atom-selected";
590
615
  function createSelectionPlugin(marks) {
591
616
  return new Plugin({
592
617
  key: new PluginKey("atom-mark-selection"),
593
- props: { decorations: (state) => {
618
+ props: { decorations: guardDecorations((state) => {
594
619
  const markNames = getActiveMarkNames(marks, state);
595
620
  if (markNames.length === 0) return;
596
621
  if (isNodeSelection(state.selection)) return;
@@ -603,7 +628,7 @@ function createSelectionPlugin(marks) {
603
628
  if (node.marks.some((mark) => markNames.includes(mark.type.name))) decorations.push(Decoration.inline(pos, pos + node.nodeSize, { class: SELECTED_CLASS }));
604
629
  });
605
630
  return DecorationSet.create(state.doc, decorations);
606
- } }
631
+ }) }
607
632
  });
608
633
  }
609
634
  /**
@@ -624,14 +649,12 @@ function defineAtomMarkNavigation({ marks }) {
624
649
  }), Priority$1.high), definePlugin(createCaretSnapPlugin(marks)), definePlugin(createSelectionPlugin(marks)));
625
650
  }
626
651
 
627
- //#endregion
628
- //#region src/extensions/node-names.ts
652
+ // src/extensions/node-names.ts
629
653
  function isNodeOfType(node, name) {
630
654
  return node.type.name === name;
631
655
  }
632
656
 
633
- //#endregion
634
- //#region src/extensions/backspace-empty-first-block.ts
657
+ // src/extensions/backspace-empty-first-block.ts
635
658
  /**
636
659
  * Claim Backspace only when the selection is an empty caret in an empty
637
660
  * paragraph that is the document's first block and has a block after it.
@@ -662,8 +685,7 @@ function defineBackspaceEmptyFirstBlock(enabled) {
662
685
  } }), Priority$1.lowest);
663
686
  }
664
687
 
665
- //#endregion
666
- //#region src/converters/paragraph.ts
688
+ // src/converters/paragraph.ts
667
689
  /**
668
690
  * Build one paragraph without interpreting block-opening Markdown.
669
691
  */
@@ -689,8 +711,7 @@ function docToParagraphMarkdown(doc) {
689
711
  return paragraphs.join("\n").replaceAll(/\n[ \t]*\n+/g, "\n");
690
712
  }
691
713
 
692
- //#endregion
693
- //#region src/extensions/single-paragraph.ts
714
+ // src/extensions/single-paragraph.ts
694
715
  const singleParagraphPluginKey = new PluginKey("single-paragraph");
695
716
  /**
696
717
  * Whether the editor holds one paragraph of inline Markdown (the
@@ -708,7 +729,7 @@ function isSingleParagraph(state) {
708
729
  function defineSingleParagraph() {
709
730
  return withPriority$1(definePlugin(new Plugin({
710
731
  key: singleParagraphPluginKey,
711
- appendTransaction(transactions, _oldState, state) {
732
+ appendTransaction: guardAppendTransaction((transactions, _oldState, state) => {
712
733
  if (!isSingleParagraph(state)) return;
713
734
  if (!transactions.some((transaction) => transaction.docChanged)) return;
714
735
  const markdown = docToParagraphMarkdown(state.doc);
@@ -717,12 +738,11 @@ function defineSingleParagraph() {
717
738
  const position = Math.min(state.selection.head, doc.content.size - 1);
718
739
  const transaction = state.tr.replaceWith(0, state.doc.content.size, doc.content);
719
740
  return transaction.setSelection(TextSelection.create(transaction.doc, Math.max(1, position)));
720
- }
741
+ })
721
742
  })), Priority$1.highest);
722
743
  }
723
744
 
724
- //#endregion
725
- //#region src/extensions/block-rule.ts
745
+ // src/extensions/block-rule.ts
726
746
  /**
727
747
  * An input rule whose match turns the textblock into, or wraps it in, a block
728
748
  * node. In a single paragraph the match is left as typed text: there is no
@@ -747,14 +767,12 @@ function defineBlockEnterRule(options) {
747
767
  });
748
768
  }
749
769
 
750
- //#endregion
751
- //#region src/extensions/blockquote.ts
770
+ // src/extensions/blockquote.ts
752
771
  function defineMeowdownBlockquote() {
753
772
  return union(defineBlockquoteSpec(), defineBlockquoteCommands(), defineBlockquoteKeymap(), defineBlockInputRule(blockquoteInputRule.regex, createWrappingInputRuleHandler(blockquoteInputRule)));
754
773
  }
755
774
 
756
- //#endregion
757
- //#region src/extensions/bullet-after-heading.ts
775
+ // src/extensions/bullet-after-heading.ts
758
776
  /**
759
777
  * Claim Enter only when the selection is an empty caret at the very end of the
760
778
  * document's first heading (the note's title line). Every other Enter (a later
@@ -788,8 +806,7 @@ function defineBulletAfterHeading(enabled) {
788
806
  } }), Priority$1.high);
789
807
  }
790
808
 
791
- //#endregion
792
- //#region src/extensions/click-below.ts
809
+ // src/extensions/click-below.ts
793
810
  const clickBelowKey = new PluginKey("meowdown-click-below");
794
811
  function acceptsCaretAtEnd(node) {
795
812
  return node.isTextblock && !node.type.spec.code;
@@ -827,8 +844,7 @@ function defineClickBelow() {
827
844
  }));
828
845
  }
829
846
 
830
- //#endregion
831
- //#region src/converters/html-to-md.ts
847
+ // src/converters/html-to-md.ts
832
848
  /**
833
849
  * Convert a `<mark>` element into a `highlight` node. There is no built-in
834
850
  * `hast-util-to-mdast` handler for `<mark>`, so without this the highlight is
@@ -974,8 +990,7 @@ function htmlToMarkdown(html, parser) {
974
990
  return processor.stringify(processor.runSync(hast));
975
991
  }
976
992
 
977
- //#endregion
978
- //#region src/utils/parse-integer.ts
993
+ // src/utils/parse-integer.ts
979
994
  function parseInteger(raw) {
980
995
  if (raw == null) return void 0;
981
996
  const value = Number.parseInt(raw, 10);
@@ -986,8 +1001,7 @@ function parsePositiveInteger(raw) {
986
1001
  return value != null && value > 0 ? value : void 0;
987
1002
  }
988
1003
 
989
- //#endregion
990
- //#region src/extensions/inline-runs.ts
1004
+ // src/extensions/inline-runs.ts
991
1005
  function findAtomMark(marks) {
992
1006
  return marks.find((mark) => isMarkOfTypes(mark, ATOM_MARK_NAMES));
993
1007
  }
@@ -1029,8 +1043,7 @@ function groupInlineRuns(textblock) {
1029
1043
  return runs;
1030
1044
  }
1031
1045
 
1032
- //#endregion
1033
- //#region src/extensions/clipboard/semantic-inline.ts
1046
+ // src/extensions/clipboard/semantic-inline.ts
1034
1047
  const SEMANTIC_TAGS = {
1035
1048
  mdStrong: "strong",
1036
1049
  mdEm: "em",
@@ -1134,8 +1147,7 @@ function atomUnitToDOM(atom, sourceText) {
1134
1147
  }
1135
1148
  }
1136
1149
 
1137
- //#endregion
1138
- //#region src/extensions/heading.ts
1150
+ // src/extensions/heading.ts
1139
1151
  /**
1140
1152
  * Merge `whitespace: 'pre'` onto the base heading spec. A multi-line setext
1141
1153
  * heading keeps a soft line break as a literal `\n`; without `whitespace: 'pre'`
@@ -1222,8 +1234,7 @@ function defineHeading() {
1222
1234
  return union(defineHeadingSpec(), defineHeadingWhitespace(), defineSetextUnderlineAttr(), defineHeadingClosingHashesAttr(), defineBlockInputRule(headingInputRule.regex, createTextBlockInputRuleHandler(headingInputRule)), defineHeadingCommands(), defineHeadingKeymap());
1223
1235
  }
1224
1236
 
1225
- //#endregion
1226
- //#region src/extensions/paragraph.ts
1237
+ // src/extensions/paragraph.ts
1227
1238
  function defineMeowdownParagraphSpec() {
1228
1239
  return defineNodeSpec({
1229
1240
  name: "paragraph",
@@ -1252,8 +1263,7 @@ function defineMeowdownParagraph() {
1252
1263
  return union(withPriority$1(defineMeowdownParagraphSpec(), Priority$1.highest), defineParagraphCommands(), defineParagraphKeymap());
1253
1264
  }
1254
1265
 
1255
- //#endregion
1256
- //#region src/extensions/clipboard/clipboard-serializer.ts
1266
+ // src/extensions/clipboard/clipboard-serializer.ts
1257
1267
  function withSemanticTextblocks(nodes) {
1258
1268
  return {
1259
1269
  ...nodes,
@@ -1297,8 +1307,7 @@ function getSemanticDOMSerializer(schema) {
1297
1307
  return serializer;
1298
1308
  }
1299
1309
 
1300
- //#endregion
1301
- //#region src/extensions/html-paste.ts
1310
+ // src/extensions/html-paste.ts
1302
1311
  const htmlPasteKey = new PluginKey("meowdown-html-paste");
1303
1312
  /**
1304
1313
  * meowdown's own clipboard HTML, which must skip the markdown conversion and
@@ -1422,8 +1431,7 @@ function defineHTMLPaste() {
1422
1431
  }));
1423
1432
  }
1424
1433
 
1425
- //#endregion
1426
- //#region src/extensions/clipboard/clipboard-parser.ts
1434
+ // src/extensions/clipboard/clipboard-parser.ts
1427
1435
  /**
1428
1436
  * The clipboard parser: schema rules plus the `data-md` rules that restore a
1429
1437
  * textblock's source text from meowdown's own clipboard HTML. Registered as
@@ -1446,8 +1454,7 @@ function defineClipboardParser() {
1446
1454
  });
1447
1455
  }
1448
1456
 
1449
- //#endregion
1450
- //#region src/extensions/clipboard/plain-paste.ts
1457
+ // src/extensions/clipboard/plain-paste.ts
1451
1458
  /**
1452
1459
  * Parse pasted plain text as markdown: `- [ ] task`, `# heading`, fenced code
1453
1460
  * and the other block constructs become real nodes, while inline syntax stays
@@ -1504,8 +1511,7 @@ function definePlainTextPaste() {
1504
1511
  }));
1505
1512
  }
1506
1513
 
1507
- //#endregion
1508
- //#region src/converters/pm-to-ast.ts
1514
+ // src/converters/pm-to-ast.ts
1509
1515
  /**
1510
1516
  * Read persisted block data. Editor marks do not add Markdown delimiters.
1511
1517
  */
@@ -1513,6 +1519,7 @@ function docToAst(node) {
1513
1519
  if (isNodeOfType(node, "doc")) return {
1514
1520
  type: "document",
1515
1521
  frontmatter: node.attrs.frontmatter ?? void 0,
1522
+ frontmatterTight: node.attrs.frontmatterTight ? true : void 0,
1516
1523
  children: readBlocks(node)
1517
1524
  };
1518
1525
  if (isNodeOfType(node, "tableRow")) return readTableRow(node);
@@ -1642,8 +1649,7 @@ function readTableCell(node) {
1642
1649
  };
1643
1650
  }
1644
1651
 
1645
- //#endregion
1646
- //#region src/converters/pm-to-md.ts
1652
+ // src/converters/pm-to-md.ts
1647
1653
  /**
1648
1654
  * Convert persisted editor content to Markdown through the shared block serializer.
1649
1655
  */
@@ -1651,8 +1657,7 @@ function docToMarkdown(node, options = {}) {
1651
1657
  return serializeMarkdownAst(docToAst(node), options);
1652
1658
  }
1653
1659
 
1654
- //#endregion
1655
- //#region src/utils/top-level-block-boundary.ts
1660
+ // src/utils/top-level-block-boundary.ts
1656
1661
  function isAtTopLevelBlockStart($pos) {
1657
1662
  if ($pos.depth === 0) return true;
1658
1663
  if ($pos.parentOffset !== 0) return false;
@@ -1666,8 +1671,7 @@ function isAtTopLevelBlockEnd($pos) {
1666
1671
  return true;
1667
1672
  }
1668
1673
 
1669
- //#endregion
1670
- //#region src/extensions/clipboard/plain-text.ts
1674
+ // src/extensions/clipboard/plain-text.ts
1671
1675
  /**
1672
1676
  * Serialize a slice to Markdown. A block whose content start is not selected is
1673
1677
  * flattened so its opening markers are not synthesized. Incomplete fenced code
@@ -1783,8 +1787,7 @@ function filterTextblock(textblock) {
1783
1787
  return textblock.copy(Fragment.from(parts));
1784
1788
  }
1785
1789
 
1786
- //#endregion
1787
- //#region src/extensions/clipboard/clipboard.ts
1790
+ // src/extensions/clipboard/clipboard.ts
1788
1791
  /**
1789
1792
  * The clipboard pipeline: semantic HTML with `data-md` round-trip attributes
1790
1793
  * plus a markdown-shaped `text/plain` on copy; the matching `data-md` parser,
@@ -1795,8 +1798,7 @@ function defineClipboard() {
1795
1798
  return union(defineSemanticClipboardSerializer(), definePlainTextSerializer(), defineClipboardParser(), defineHTMLPaste(), definePlainTextPaste());
1796
1799
  }
1797
1800
 
1798
- //#endregion
1799
- //#region src/extensions/code-block-highlight.ts
1801
+ // src/extensions/code-block-highlight.ts
1800
1802
  const supportCache = /* @__PURE__ */ new Map();
1801
1803
  const parserCache = /* @__PURE__ */ new Map();
1802
1804
  const LANGUAGE_ALIASES = { math: "latex" };
@@ -1876,8 +1878,7 @@ function getCodeTokens(code, language) {
1876
1878
  return loadLanguageSupport(trimmed).then((loaded) => loaded ? tokenize(code, loaded) : []);
1877
1879
  }
1878
1880
 
1879
- //#endregion
1880
- //#region src/extensions/code-block-exit.ts
1881
+ // src/extensions/code-block-exit.ts
1881
1882
  /**
1882
1883
  * With the caret at the end of a code block, move it into the block below when
1883
1884
  * that block is an empty paragraph, and into a new default block otherwise.
@@ -1908,8 +1909,7 @@ function defineCodeBlockExitKeymap() {
1908
1909
  return defineKeymap({ "Mod-Enter": exitCodeBlockAtEnd });
1909
1910
  }
1910
1911
 
1911
- //#endregion
1912
- //#region src/extensions/code-block.ts
1912
+ // src/extensions/code-block.ts
1913
1913
  function defineFenceStyleAttr() {
1914
1914
  return defineNodeAttr({
1915
1915
  type: "codeBlock",
@@ -1934,6 +1934,29 @@ function defineFenceLengthAttr() {
1934
1934
  }
1935
1935
  });
1936
1936
  }
1937
+ /**
1938
+ * Re-declare the code block `toDOM` with a `data-code-block` root attribute.
1939
+ */
1940
+ function defineCodeBlockDOM() {
1941
+ return defineNodeSpec({
1942
+ name: "codeBlock",
1943
+ toDOM: (node) => {
1944
+ const { language } = node.attrs;
1945
+ return [
1946
+ "pre",
1947
+ {
1948
+ "data-code-block": "",
1949
+ "data-language": language || void 0
1950
+ },
1951
+ [
1952
+ "code",
1953
+ { class: language ? `language-${language}` : void 0 },
1954
+ 0
1955
+ ]
1956
+ ];
1957
+ }
1958
+ });
1959
+ }
1937
1960
  function getTildeFenceAttrs(match) {
1938
1961
  return {
1939
1962
  language: match[1] || "",
@@ -1963,11 +1986,10 @@ function defineDollarFenceEnterRule() {
1963
1986
  });
1964
1987
  }
1965
1988
  function defineCodeBlock() {
1966
- return union(defineCodeBlockSpec(), defineCodeBlockKeymap(), defineCodeBlockCommands(), defineBlockInputRule(codeBlockInputRule.regex, createTextBlockInputRuleHandler(codeBlockInputRule)), defineBlockEnterRule(codeBlockEnterRule), defineFenceStyleAttr(), defineFenceLengthAttr(), defineBlockInputRule(tildeFenceInputRule.regex, createTextBlockInputRuleHandler(tildeFenceInputRule)), defineTildeFenceEnterRule(), defineDollarFenceEnterRule(), defineCodeBlockExitKeymap());
1989
+ return union(defineCodeBlockSpec(), defineCodeBlockDOM(), defineCodeBlockKeymap(), defineCodeBlockCommands(), defineBlockInputRule(codeBlockInputRule.regex, createTextBlockInputRuleHandler(codeBlockInputRule)), defineBlockEnterRule(codeBlockEnterRule), defineFenceStyleAttr(), defineFenceLengthAttr(), defineBlockInputRule(tildeFenceInputRule.regex, createTextBlockInputRuleHandler(tildeFenceInputRule)), defineTildeFenceEnterRule(), defineDollarFenceEnterRule(), defineCodeBlockExitKeymap());
1967
1990
  }
1968
1991
 
1969
- //#endregion
1970
- //#region src/extensions/commands.ts
1992
+ // src/extensions/commands.ts
1971
1993
  function selectText(anchor, head) {
1972
1994
  return (state, dispatch) => {
1973
1995
  if (dispatch) {
@@ -2052,8 +2074,7 @@ function defineEditorCommands() {
2052
2074
  });
2053
2075
  }
2054
2076
 
2055
- //#endregion
2056
- //#region src/extensions/cross-editor-drag.ts
2077
+ // src/extensions/cross-editor-drag.ts
2057
2078
  function deleteDraggedContent(view, dragging) {
2058
2079
  const tr = view.state.tr;
2059
2080
  if (dragging.node) {
@@ -2100,8 +2121,7 @@ function defineCrossEditorDrag() {
2100
2121
  return definePlugin(createCrossEditorDragPlugin());
2101
2122
  }
2102
2123
 
2103
- //#endregion
2104
- //#region src/extensions/editor-config.ts
2124
+ // src/extensions/editor-config.ts
2105
2125
  var ConfigController = class {
2106
2126
  constructor(config) {
2107
2127
  this.config = config;
@@ -2158,16 +2178,14 @@ function defineEditorConfig(initialConfig) {
2158
2178
  }));
2159
2179
  }
2160
2180
 
2161
- //#endregion
2162
- //#region src/extensions/paste.ts
2181
+ // src/extensions/paste.ts
2163
2182
  function getPastedText(event, slice) {
2164
2183
  const fromClipboard = event.clipboardData?.getData("text/plain");
2165
2184
  if (fromClipboard) return fromClipboard;
2166
2185
  return slice.content.textBetween(0, slice.content.size, "\n");
2167
2186
  }
2168
2187
 
2169
- //#endregion
2170
- //#region src/extensions/embed-paste.ts
2188
+ // src/extensions/embed-paste.ts
2171
2189
  const embedPasteKey = new PluginKey("meowdown-embed-paste");
2172
2190
  function detectEmbedUrl(text) {
2173
2191
  const trimmed = text.trim();
@@ -2203,8 +2221,7 @@ function defineEmbedPaste(enabled) {
2203
2221
  }));
2204
2222
  }
2205
2223
 
2206
- //#endregion
2207
- //#region src/extensions/escape-collapse.ts
2224
+ // src/extensions/escape-collapse.ts
2208
2225
  /**
2209
2226
  * Collapses a non-empty selection to a caret at its head. Leaves an empty
2210
2227
  * selection alone so Escape can serve whoever binds it next.
@@ -2224,8 +2241,7 @@ function defineEscapeCollapse() {
2224
2241
  return withPriority$1(defineKeymap({ Escape: collapseSelection }), Priority$1.low);
2225
2242
  }
2226
2243
 
2227
- //#endregion
2228
- //#region src/extensions/exit-boundary.ts
2244
+ // src/extensions/exit-boundary.ts
2229
2245
  const exitBoundaryKey = new PluginKey("meowdown-exit-boundary");
2230
2246
  function canMoveBlockwise(state, direction) {
2231
2247
  const { $anchor, $head } = state.selection;
@@ -2266,8 +2282,7 @@ function defineExitBoundaryHandler(getExitBoundaryHandler) {
2266
2282
  return withPriority$1(definePlugin(createExitBoundaryPlugin(getExitBoundaryHandler)), Priority$1.low);
2267
2283
  }
2268
2284
 
2269
- //#endregion
2270
- //#region src/utils/is-mod-event.ts
2285
+ // src/utils/is-mod-event.ts
2271
2286
  /**
2272
2287
  * Whether the platform's mod key is held on `event`: `Command` on Apple, `Ctrl` elsewhere.
2273
2288
  */
@@ -2275,8 +2290,7 @@ function isModEvent(event) {
2275
2290
  return isApple ? event.metaKey : event.ctrlKey;
2276
2291
  }
2277
2292
 
2278
- //#endregion
2279
- //#region src/extensions/file-click.ts
2293
+ // src/extensions/file-click.ts
2280
2294
  const fileClickKey = new PluginKey("meowdown-file-click");
2281
2295
  function findFileAt(state, pos) {
2282
2296
  const range = getMarkRangeAt(state, pos, "mdFile");
@@ -2317,8 +2331,7 @@ function defineFileClickHandler(getOnClick) {
2317
2331
  }));
2318
2332
  }
2319
2333
 
2320
- //#endregion
2321
- //#region src/extensions/file-paste.ts
2334
+ // src/extensions/file-paste.ts
2322
2335
  const filePasteKey = new PluginKey("file-paste");
2323
2336
  const IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
2324
2337
  "avif",
@@ -2421,8 +2434,7 @@ function defineFilePaste(getOptions) {
2421
2434
  return withPriority$1(definePlugin(createFilePastePlugin(getOptions)), Priority$1.high);
2422
2435
  }
2423
2436
 
2424
- //#endregion
2425
- //#region src/utils/format-file-size.ts
2437
+ // src/utils/format-file-size.ts
2426
2438
  const UNITS = [
2427
2439
  "KB",
2428
2440
  "MB",
@@ -2445,8 +2457,46 @@ function formatFileSize(bytes) {
2445
2457
  return `${value < 9.95 ? Math.round(value * 10) / 10 : Math.round(value)} ${unit}`;
2446
2458
  }
2447
2459
 
2448
- //#endregion
2449
- //#region src/extensions/file-view.ts
2460
+ // src/utils/guard-mark-view.ts
2461
+ /**
2462
+ * Wrap a mark view constructor so that a throw from it, or from the view's
2463
+ * `update` or `destroy`, is reported with `reportError` instead of leaving
2464
+ * ProseMirror's view tree half updated. A constructor that throws gets a bare
2465
+ * `<span>`, which keeps the mark's source text editable.
2466
+ */
2467
+ function guardMarkView(constructor) {
2468
+ return (mark, view, inline) => {
2469
+ let markView;
2470
+ try {
2471
+ markView = constructor(mark, view, inline);
2472
+ } catch (error) {
2473
+ reportError(error);
2474
+ return { dom: document.createElement("span") };
2475
+ }
2476
+ return {
2477
+ dom: markView.dom,
2478
+ contentDOM: markView.contentDOM,
2479
+ ignoreMutation: markView.ignoreMutation?.bind(markView),
2480
+ update: (next) => {
2481
+ try {
2482
+ return markView.update?.(next) ?? false;
2483
+ } catch (error) {
2484
+ reportError(error);
2485
+ return false;
2486
+ }
2487
+ },
2488
+ destroy: () => {
2489
+ try {
2490
+ markView.destroy?.();
2491
+ } catch (error) {
2492
+ reportError(error);
2493
+ }
2494
+ }
2495
+ };
2496
+ };
2497
+ }
2498
+
2499
+ // src/extensions/file-view.ts
2450
2500
  /**
2451
2501
  * `data-file-kind` values by file extension, for host CSS theming.
2452
2502
  */
@@ -2596,17 +2646,16 @@ var FileMarkView = class {
2596
2646
  function defineFileView(getOptions) {
2597
2647
  return defineMarkView({
2598
2648
  name: "mdFile",
2599
- constructor: (mark, view) => new FileMarkView(mark, getOptions?.(view.state))
2649
+ constructor: guardMarkView((mark, view) => new FileMarkView(mark, getOptions?.(view.state)))
2600
2650
  });
2601
2651
  }
2602
2652
 
2603
- //#endregion
2604
- //#region src/extensions/find.ts
2605
- const revealActiveMatch = new Plugin({ props: { decorations: (state) => {
2653
+ // src/extensions/find.ts
2654
+ const revealActiveMatch = new Plugin({ props: { decorations: guardDecorations((state) => {
2606
2655
  const { from, to, empty } = state.selection;
2607
2656
  if (empty || getSearchStatus$1(state).active === 0) return;
2608
2657
  return DecorationSet.create(state.doc, [Decoration.inline(from, to, { class: "show" })]);
2609
- } } });
2658
+ }) } });
2610
2659
  /**
2611
2660
  * Find over the editor's text. `setSearchQuery` highlights every match and
2612
2661
  * selects the first one at or after the caret; `findNext` and `findPrev` walk
@@ -2617,8 +2666,7 @@ function defineFind() {
2617
2666
  return union(defineSearchQuery(), defineSearchCommands(), definePlugin(revealActiveMatch));
2618
2667
  }
2619
2668
 
2620
- //#endregion
2621
- //#region src/extensions/get-link-unit-at.ts
2669
+ // src/extensions/get-link-unit-at.ts
2622
2670
  /**
2623
2671
  * The last text run carrying `markName` inside `range`. "Last" so a linked
2624
2672
  * image's inner url/title (which comes first) never shadows the link's own.
@@ -2712,8 +2760,7 @@ function getLinkUnitAt(state, pos) {
2712
2760
  }
2713
2761
  }
2714
2762
 
2715
- //#endregion
2716
- //#region src/extensions/image-click.ts
2763
+ // src/extensions/image-click.ts
2717
2764
  const imageClickKey = new PluginKey("meowdown-image-click");
2718
2765
  /**
2719
2766
  * The image preview wrapper around `target`, or nothing when the wrapper holds
@@ -2850,8 +2897,7 @@ function defineImageClickHandler(getOnClick) {
2850
2897
  }));
2851
2898
  }
2852
2899
 
2853
- //#endregion
2854
- //#region src/extensions/mark-click.ts
2900
+ // src/extensions/mark-click.ts
2855
2901
  /**
2856
2902
  * Shared click plumbing for text-backed marks (wikilinks, Markdown links, tags):
2857
2903
  * a click anywhere on the rendered mark fires `onClick`.
@@ -2872,8 +2918,7 @@ function defineMarkClickHandler(config) {
2872
2918
  }));
2873
2919
  }
2874
2920
 
2875
- //#endregion
2876
- //#region src/extensions/tag-click.ts
2921
+ // src/extensions/tag-click.ts
2877
2922
  const tagClickKey = new PluginKey("meowdown-tag-click");
2878
2923
  /**
2879
2924
  * The tag covering `pos`, found via the `mdTag` run. The tag name is read from
@@ -2914,8 +2959,7 @@ function defineTagClickHandler(getOnClick) {
2914
2959
  });
2915
2960
  }
2916
2961
 
2917
- //#endregion
2918
- //#region src/extensions/wikilink-click.ts
2962
+ // src/extensions/wikilink-click.ts
2919
2963
  const wikilinkClickKey = new PluginKey("meowdown-wikilink-click");
2920
2964
  /**
2921
2965
  * Exported for tests.
@@ -2966,8 +3010,7 @@ function defineWikilinkClickHandler(getOnClick) {
2966
3010
  });
2967
3011
  }
2968
3012
 
2969
- //#endregion
2970
- //#region src/extensions/follow-link.ts
3013
+ // src/extensions/follow-link.ts
2971
3014
  const followLinkKey = new PluginKey("meowdown-follow-link");
2972
3015
  function createFollowLinkPlugin(getHandlers) {
2973
3016
  return new Plugin({
@@ -3059,33 +3102,35 @@ function defineFollowLinkHandler(getHandlers) {
3059
3102
  return withPriority$1(definePlugin(createFollowLinkPlugin(getHandlers)), Priority$1.high);
3060
3103
  }
3061
3104
 
3062
- //#endregion
3063
- //#region src/extensions/frontmatter.ts
3105
+ // src/extensions/frontmatter.ts
3064
3106
  /**
3065
- * Stores YAML frontmatter as a non-rendered attribute on the root `doc` node.
3107
+ * Stores YAML frontmatter as a non-rendered attribute on the root `doc` node,
3108
+ * plus whether the first block starts right under its closing fence.
3066
3109
  */
3067
3110
  function defineDocFrontmatterAttr() {
3068
- return defineNodeAttr({
3111
+ return union(defineNodeAttr({
3069
3112
  type: "doc",
3070
3113
  attr: "frontmatter",
3071
3114
  default: null
3072
- });
3115
+ }), defineNodeAttr({
3116
+ type: "doc",
3117
+ attr: "frontmatterTight",
3118
+ default: false
3119
+ }));
3073
3120
  }
3074
3121
 
3075
- //#endregion
3076
- //#region src/utils/execute-command.ts
3122
+ // src/utils/execute-command.ts
3077
3123
  function executeCommand(view, command) {
3078
3124
  return command(view.state, view.dispatch, view);
3079
3125
  }
3080
3126
 
3081
- //#endregion
3082
- //#region src/extensions/hidden-run-caret.ts
3127
+ // src/extensions/hidden-run-caret.ts
3083
3128
  const snapKey = new PluginKey("meowdown-hidden-run-snap");
3084
3129
  const beforeInputKey = new PluginKey("meowdown-hidden-run-beforeinput");
3085
3130
  function createSnapPlugin() {
3086
3131
  return new Plugin({
3087
3132
  key: snapKey,
3088
- appendTransaction: (transactions, oldState, newState) => {
3133
+ appendTransaction: guardAppendTransaction((transactions, oldState, newState) => {
3089
3134
  if (getIsComposing()) return null;
3090
3135
  if (getMarkMode(newState) !== "hide") return null;
3091
3136
  const selection = newState.selection;
@@ -3102,7 +3147,7 @@ function createSnapPlugin() {
3102
3147
  const anchor = selection.anchor === selection.from ? from : to;
3103
3148
  const head = selection.head === selection.from ? from : to;
3104
3149
  return newState.tr.setSelection(TextSelection.create(newState.doc, anchor, head));
3105
- }
3150
+ })
3106
3151
  });
3107
3152
  }
3108
3153
  const relocateEnterSplit = (state, dispatch) => {
@@ -3155,8 +3200,7 @@ function defineHiddenRunCaret() {
3155
3200
  }), Priority$1.highest));
3156
3201
  }
3157
3202
 
3158
- //#endregion
3159
- //#region src/extensions/horizontal-rule.ts
3203
+ // src/extensions/horizontal-rule.ts
3160
3204
  function defineHorizontalRuleMarkerAttr() {
3161
3205
  return defineNodeAttr({
3162
3206
  type: "horizontalRule",
@@ -3170,8 +3214,7 @@ function defineMeowdownHorizontalRule() {
3170
3214
  return union(defineHorizontalRuleSpec(), defineHorizontalRuleCommands(), defineHorizontalRuleMarkerAttr(), defineBlockInputRule(horizontalRuleInputRule.regex, horizontalRuleInputRule.handler));
3171
3215
  }
3172
3216
 
3173
- //#endregion
3174
- //#region src/utils/non-prose-attrs.ts
3217
+ // src/utils/non-prose-attrs.ts
3175
3218
  const NON_PROSE_ATTRS = {
3176
3219
  spellcheck: "false",
3177
3220
  autocorrect: "off",
@@ -3179,8 +3222,7 @@ const NON_PROSE_ATTRS = {
3179
3222
  writingsuggestions: "false"
3180
3223
  };
3181
3224
 
3182
- //#endregion
3183
- //#region src/extensions/html-comment.ts
3225
+ // src/extensions/html-comment.ts
3184
3226
  /**
3185
3227
  * A block-level HTML comment (`<!-- ... -->`) as an invisible, atomic node.
3186
3228
  *
@@ -3219,8 +3261,7 @@ function defineHTMLComment() {
3219
3261
  });
3220
3262
  }
3221
3263
 
3222
- //#endregion
3223
- //#region src/utils/to-positive-number.ts
3264
+ // src/utils/to-positive-number.ts
3224
3265
  /**
3225
3266
  * The value rounded to an integer, or nothing unless it is a finite number
3226
3267
  * above zero.
@@ -3229,8 +3270,7 @@ function toPositiveNumber(value) {
3229
3270
  if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.round(value);
3230
3271
  }
3231
3272
 
3232
- //#endregion
3233
- //#region src/extensions/magic-comment.ts
3273
+ // src/extensions/magic-comment.ts
3234
3274
  const MAGIC_COMMENT_RE = /^<!--\s*(\{[\s\S]*?\})\s*-->/;
3235
3275
  const TRAILING_MAGIC_COMMENT_RE = /(?:<!--\s*\{[\s\S]*?\}\s*-->)+$/;
3236
3276
  /**
@@ -3277,8 +3317,7 @@ function stripMagicComment(source) {
3277
3317
  return source.replace(TRAILING_MAGIC_COMMENT_RE, "");
3278
3318
  }
3279
3319
 
3280
- //#endregion
3281
- //#region src/extensions/post-embed.ts
3320
+ // src/extensions/post-embed.ts
3282
3321
  /**
3283
3322
  * Wrap a loader in a 64-entry LRU keyed by URL. A settled URL answers
3284
3323
  * synchronously, so a revisited document renders its cards in the first
@@ -3339,8 +3378,7 @@ function parsePostEmbedSnapshot(value) {
3339
3378
  return result.success ? result.output : void 0;
3340
3379
  }
3341
3380
 
3342
- //#endregion
3343
- //#region src/extensions/wiki-embed.ts
3381
+ // src/extensions/wiki-embed.ts
3344
3382
  const WIKI_EMBED_SIZE = /^(\d+)(?:x(\d+))?$/i;
3345
3383
  function positiveInteger(value) {
3346
3384
  if (!value) return null;
@@ -3402,8 +3440,7 @@ function wikiEmbedBasename(target) {
3402
3440
  }
3403
3441
  }
3404
3442
 
3405
- //#endregion
3406
- //#region src/extensions/image.ts
3443
+ // src/extensions/image.ts
3407
3444
  /**
3408
3445
  * Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it.
3409
3446
  */
@@ -3762,12 +3799,11 @@ var ImageMarkView = class {
3762
3799
  function defineImage(getOptions) {
3763
3800
  return defineMarkView({
3764
3801
  name: "mdImage",
3765
- constructor: (mark, view) => new ImageMarkView(mark, view, getOptions?.(view.state) ?? {})
3802
+ constructor: guardMarkView((mark, view) => new ImageMarkView(mark, view, getOptions?.(view.state) ?? {}))
3766
3803
  });
3767
3804
  }
3768
3805
 
3769
- //#endregion
3770
- //#region src/extensions/mark-chunk.ts
3806
+ // src/extensions/mark-chunk.ts
3771
3807
  function markChunkToJSON(chunk) {
3772
3808
  const [from, to, marks] = chunk;
3773
3809
  return [
@@ -3785,8 +3821,7 @@ function markChunkFromJSON(schema, json) {
3785
3821
  ];
3786
3822
  }
3787
3823
 
3788
- //#endregion
3789
- //#region src/extensions/marks-equal.ts
3824
+ // src/extensions/marks-equal.ts
3790
3825
  /**
3791
3826
  * Position-wise equality on two mark arrays. Returns `true` only when
3792
3827
  * the arrays have the same length and `a[i].eq(b[i])` for every index.
@@ -3798,8 +3833,7 @@ function marksEqual(a, b) {
3798
3833
  return true;
3799
3834
  }
3800
3835
 
3801
- //#endregion
3802
- //#region src/extensions/batch-set-mark-step.ts
3836
+ // src/extensions/batch-set-mark-step.ts
3803
3837
  const SPARSE_CHUNK_LIMIT = 32;
3804
3838
  /**
3805
3839
  * Apply a small batch by visiting each chunk's narrow range independently.
@@ -3968,8 +4002,7 @@ var BatchSetMarkStep = class BatchSetMarkStep extends Step {
3968
4002
  Step.jsonID("batchSetMark", BatchSetMarkStep);
3969
4003
  const emptyBatchSetMarkStep = new BatchSetMarkStep([]);
3970
4004
 
3971
- //#endregion
3972
- //#region src/extensions/reference-links.ts
4005
+ // src/extensions/reference-links.ts
3973
4006
  const MAX_DEFINITION_LENGTH = 1024;
3974
4007
  function normalizeReferenceLabel(label) {
3975
4008
  return normalizeIdentifier(label);
@@ -4076,8 +4109,7 @@ function updateReferenceDefinitions(previous, transaction, doc) {
4076
4109
  return collectReferenceDefinitions(doc);
4077
4110
  }
4078
4111
 
4079
- //#endregion
4080
- //#region src/extensions/inline-text-to-mark-chunks.ts
4112
+ // src/extensions/inline-text-to-mark-chunks.ts
4081
4113
  /**
4082
4114
  * Lookup from Lezer node type id to the ProseMirror mark.
4083
4115
  *
@@ -4650,8 +4682,7 @@ function emit(out, from, to, marks) {
4650
4682
  ]);
4651
4683
  }
4652
4684
 
4653
- //#endregion
4654
- //#region src/extensions/inline-mark-plugin.ts
4685
+ // src/extensions/inline-mark-plugin.ts
4655
4686
  const META_KEY = "inline-marks-applied";
4656
4687
  const TRIGGER_KEY = "inline-marks-trigger";
4657
4688
  const RESTYLE_KEY = "inline-marks-restyle";
@@ -4810,7 +4841,7 @@ function createInlineMarkPlugin(getOptions) {
4810
4841
  };
4811
4842
  }
4812
4843
  },
4813
- appendTransaction(transactions, oldState, newState) {
4844
+ appendTransaction: guardAppendTransaction((transactions, oldState, newState) => {
4814
4845
  for (const tr of transactions) if (tr.getMeta(META_KEY)) return null;
4815
4846
  const restyle = transactions.some((transaction) => transaction.getMeta(RESTYLE_KEY));
4816
4847
  if (!(restyle || transactions.some((transaction) => {
@@ -4831,9 +4862,13 @@ function createInlineMarkPlugin(getOptions) {
4831
4862
  tr.setMeta(META_KEY, true);
4832
4863
  tr.setMeta("addToHistory", false);
4833
4864
  return tr;
4834
- },
4865
+ }),
4835
4866
  view(view) {
4836
- view.dispatch(triggerInlineMarks(view.state));
4867
+ try {
4868
+ view.dispatch(triggerInlineMarks(view.state));
4869
+ } catch (error) {
4870
+ reportError(error);
4871
+ }
4837
4872
  const controller = new AbortController();
4838
4873
  const flush = (currentView) => {
4839
4874
  if (currentView.isDestroyed) return;
@@ -4877,8 +4912,7 @@ function defineInlineMarkPlugin(getOptions) {
4877
4912
  return definePlugin(createInlineMarkPlugin(getOptions));
4878
4913
  }
4879
4914
 
4880
- //#endregion
4881
- //#region src/extensions/inline-marks.ts
4915
+ // src/extensions/inline-marks.ts
4882
4916
  function defineMdImage() {
4883
4917
  return defineMarkSpec({
4884
4918
  name: "mdImage",
@@ -5107,8 +5141,7 @@ function defineInlineMarks() {
5107
5141
  return union(defineMdMark(), defineMdEm(), defineMdStrong(), defineMdCode(), defineMdLinkText(), defineMdLinkUri(), defineMdLinkTitle(), defineMdDel(), defineMdHighlight(), defineMdTag(), defineMdWikilink(), defineMdImage(), defineMdFile(), defineMdMath(), defineMdPack());
5108
5142
  }
5109
5143
 
5110
- //#endregion
5111
- //#region src/utils/backticks.ts
5144
+ // src/utils/backticks.ts
5112
5145
  /**
5113
5146
  * Length of the longest run of `charCode` in `text`, at least `min`.
5114
5147
  */
@@ -5128,8 +5161,7 @@ function longestBacktickRun(text, min = 0) {
5128
5161
  return longestCharRun(text, 96, min);
5129
5162
  }
5130
5163
 
5131
- //#endregion
5132
- //#region src/extensions/inline-toggle.ts
5164
+ // src/extensions/inline-toggle.ts
5133
5165
  const TOGGLE_SPECS = {
5134
5166
  em: {
5135
5167
  node: LEZER_NODE_IDS.Emphasis,
@@ -5383,8 +5415,7 @@ function insideAtom(nodes, pos) {
5383
5415
  return false;
5384
5416
  }
5385
5417
 
5386
- //#endregion
5387
- //#region src/extensions/inline-toggle-commands.ts
5418
+ // src/extensions/inline-toggle-commands.ts
5388
5419
  function toggleInline(spec) {
5389
5420
  return (state, dispatch) => {
5390
5421
  if (state.selection.empty) return caretToggle(spec, state, dispatch);
@@ -5466,8 +5497,7 @@ function defineInlineToggle() {
5466
5497
  return union(defineInlineToggleCommands(), defineInlineToggleKeymap());
5467
5498
  }
5468
5499
 
5469
- //#endregion
5470
- //#region src/extensions/link-click.ts
5500
+ // src/extensions/link-click.ts
5471
5501
  const linkClickKey = new PluginKey("meowdown-link-click");
5472
5502
  /**
5473
5503
  * Call `onClick` when the user clicks a rendered Markdown link
@@ -5492,8 +5522,7 @@ function defineLinkClickHandler(getOnClick) {
5492
5522
  });
5493
5523
  }
5494
5524
 
5495
- //#endregion
5496
- //#region src/extensions/link-commands.ts
5525
+ // src/extensions/link-commands.ts
5497
5526
  /**
5498
5527
  * Normalize a typed URL with the existing autolink logic, else keep it verbatim.
5499
5528
  */
@@ -5661,8 +5690,7 @@ function defineLinkEditKeymap(onLinkEdit) {
5661
5690
  return defineKeymap({ "Mod-k": openLinkEdit(onLinkEdit) });
5662
5691
  }
5663
5692
 
5664
- //#endregion
5665
- //#region src/extensions/link-paste.ts
5693
+ // src/extensions/link-paste.ts
5666
5694
  const linkPasteKey = new PluginKey("meowdown-link-paste");
5667
5695
  /**
5668
5696
  * The pasted text as a link `href` when the clipboard holds exactly one URL:
@@ -5704,8 +5732,7 @@ function defineLinkPaste(enabled) {
5704
5732
  })), Priority$1.high);
5705
5733
  }
5706
5734
 
5707
- //#endregion
5708
- //#region src/extensions/list.ts
5735
+ // src/extensions/list.ts
5709
5736
  function serializeListMarker(value) {
5710
5737
  return value === ")" || value === "*" || value === "+" ? value : void 0;
5711
5738
  }
@@ -6092,15 +6119,14 @@ function defineMeowdownList() {
6092
6119
  return union(defineListSpec(), defineMeowdownListPlugins(), defineListCommands(), defineMeowdownListSerializer(), defineListDropIndicator(), defineMeowdownListInputRules(), defineMeowdownListKeymap(), defineListMarkerAttr(), defineListTaskMarkerAttr(), defineListMarkerGapAttr(), defineMeowdownListCommands());
6093
6120
  }
6094
6121
 
6095
- //#endregion
6096
- //#region src/extensions/mark-mode.ts
6122
+ // src/extensions/mark-mode.ts
6097
6123
  function createMarkModePlugin(getMode) {
6098
6124
  return new Plugin({ props: {
6099
6125
  attributes: (state) => ({ "data-mark-mode": getMode(state) }),
6100
- decorations: (state) => {
6126
+ decorations: guardDecorations((state) => {
6101
6127
  const mode = getMode(state);
6102
6128
  return mode === "focus" || mode === "hide" ? computeRevealDecorations(state, mode) : void 0;
6103
- }
6129
+ })
6104
6130
  } });
6105
6131
  }
6106
6132
  function findRevealablePack($pos, mode, direction) {
@@ -6150,8 +6176,7 @@ function defineMarkMode(getMode) {
6150
6176
  return definePlugin(createMarkModePlugin(getMode));
6151
6177
  }
6152
6178
 
6153
- //#endregion
6154
- //#region src/utils/katex.ts
6179
+ // src/utils/katex.ts
6155
6180
  let katexRenderPromise;
6156
6181
  /**
6157
6182
  * Load KaTeX's render function on first use and cache it. Most documents
@@ -6179,8 +6204,7 @@ function renderMathInto(katexRender, element, formula, displayMode) {
6179
6204
  }
6180
6205
  }
6181
6206
 
6182
- //#endregion
6183
- //#region src/extensions/math.ts
6207
+ // src/extensions/math.ts
6184
6208
  /**
6185
6209
  * Renders one inline math unit: a KaTeX preview next to the editable
6186
6210
  * `$formula$` source. CSS decides which of the two is visible; in hide and
@@ -6247,12 +6271,11 @@ var MathMarkView = class {
6247
6271
  function defineMath() {
6248
6272
  return defineMarkView({
6249
6273
  name: "mdMath",
6250
- constructor: (mark, view) => new MathMarkView(mark, view)
6274
+ constructor: guardMarkView((mark, view) => new MathMarkView(mark, view))
6251
6275
  });
6252
6276
  }
6253
6277
 
6254
- //#endregion
6255
- //#region src/extensions/table-column-align.ts
6278
+ // src/extensions/table-column-align.ts
6256
6279
  function parseTableColumnAlign(value) {
6257
6280
  return value === "left" || value === "center" || value === "right" ? value : null;
6258
6281
  }
@@ -6345,7 +6368,7 @@ function unionOfChangedRanges(transactions) {
6345
6368
  function createTableAlignSyncPlugin() {
6346
6369
  return new Plugin({
6347
6370
  key: new PluginKey("table-align-sync"),
6348
- appendTransaction(transactions, _oldState, newState) {
6371
+ appendTransaction: guardAppendTransaction((transactions, _oldState, newState) => {
6349
6372
  if (!transactions.some((transaction) => transaction.docChanged)) return;
6350
6373
  const range = unionOfChangedRanges(transactions);
6351
6374
  if (!range) return;
@@ -6360,7 +6383,7 @@ function createTableAlignSyncPlugin() {
6360
6383
  return false;
6361
6384
  });
6362
6385
  return tr?.docChanged ? tr : void 0;
6363
- }
6386
+ })
6364
6387
  });
6365
6388
  }
6366
6389
  function defineTableAlignSync() {
@@ -6446,8 +6469,7 @@ function defineTableColumnAlign() {
6446
6469
  return union(defineTableCellAlignAttr(), defineTableHeaderCellAlignAttr(), defineTableAlignSync(), defineTableColumnAlignCommands());
6447
6470
  }
6448
6471
 
6449
- //#endregion
6450
- //#region src/extensions/table.ts
6472
+ // src/extensions/table.ts
6451
6473
  /**
6452
6474
  * Whether the selection sits inside a table cell (data or header). Useful for
6453
6475
  * gating block-creating UI, since cells hold inline content only.
@@ -6486,8 +6508,7 @@ function defineTable() {
6486
6508
  return union(defineTableSpec(), defineTableRowSpec(), defineTableCellSpec(), defineTableHeaderCellSpec(), defineTableCellContent(), defineTableColumnAlign(), defineTableEditingPlugin({ allowTableNodeSelection: true }), defineTableCommands(), defineTableDropIndicator(), defineTableKeymap());
6487
6509
  }
6488
6510
 
6489
- //#endregion
6490
- //#region src/extensions/move-block.ts
6511
+ // src/extensions/move-block.ts
6491
6512
  /**
6492
6513
  * The index of the top-level block holding the whole selection, or undefined
6493
6514
  * when the selection spans several top-level blocks or floats between them (a
@@ -6549,8 +6570,7 @@ function defineMoveBlock() {
6549
6570
  });
6550
6571
  }
6551
6572
 
6552
- //#endregion
6553
- //#region src/extensions/pending-replacement.ts
6573
+ // src/extensions/pending-replacement.ts
6554
6574
  const pendingReplacementKey = new PluginKey("meowdownPendingReplacement");
6555
6575
  /**
6556
6576
  * The active pending replacement, or null when there is none.
@@ -6620,11 +6640,11 @@ const pendingReplacementPlugin = new Plugin({
6620
6640
  return value;
6621
6641
  }
6622
6642
  },
6623
- props: { decorations: (state) => {
6643
+ props: { decorations: guardDecorations((state) => {
6624
6644
  const pending = getPendingReplacement(state);
6625
6645
  if (!pending || pending.from >= pending.to) return null;
6626
6646
  return DecorationSet.create(state.doc, [Decoration.inline(pending.from, pending.to, { class: "md-pending-replacement" })]);
6627
- } }
6647
+ }) }
6628
6648
  });
6629
6649
  function startPendingReplacement(options) {
6630
6650
  return (state, dispatch) => {
@@ -6728,27 +6748,29 @@ function definePendingReplacementHandler(handler) {
6728
6748
  const prev = pendingReplacementKey.getState(prevState);
6729
6749
  const next = pendingReplacementKey.getState(view.state);
6730
6750
  if (!next || prev === next) return;
6731
- if (next.pending) {
6732
- if (next.pending !== prev?.pending) handler({
6733
- type: "update",
6734
- pending: next.pending
6751
+ try {
6752
+ if (next.pending) {
6753
+ if (next.pending !== prev?.pending) handler({
6754
+ type: "update",
6755
+ pending: next.pending
6756
+ });
6757
+ } else if (next.ended && next.ended !== prev?.ended) handler({
6758
+ type: "ended",
6759
+ pending: next.ended.pending,
6760
+ outcome: next.ended.outcome
6735
6761
  });
6736
- } else if (next.ended && next.ended !== prev?.ended) handler({
6737
- type: "ended",
6738
- pending: next.ended.pending,
6739
- outcome: next.ended.outcome
6740
- });
6762
+ } catch (error) {
6763
+ reportError(error);
6764
+ }
6741
6765
  } }) }));
6742
6766
  }
6743
6767
 
6744
- //#endregion
6745
- //#region src/extensions/readonly.ts
6768
+ // src/extensions/readonly.ts
6746
6769
  function defineReadonly(getReadonly = () => true) {
6747
6770
  return definePlugin(new Plugin({ props: { editable: (state) => !getReadonly(state) } }));
6748
6771
  }
6749
6772
 
6750
- //#endregion
6751
- //#region src/utils/caret-coords.ts
6773
+ // src/utils/caret-coords.ts
6752
6774
  /**
6753
6775
  * A dimensionless point: the fallback measurement of a hidden (font-size: 0)
6754
6776
  * syntax run. A real caret rect always has line height, and a block-context
@@ -6776,15 +6798,13 @@ function tryCoordsAtPos(view, pos, side) {
6776
6798
  return isPointRect(coords) ? void 0 : coords;
6777
6799
  }
6778
6800
 
6779
- //#endregion
6780
- //#region src/utils/line-break.ts
6801
+ // src/utils/line-break.ts
6781
6802
  function isAfterLineBreak($pos) {
6782
6803
  const { parentOffset, parent } = $pos;
6783
6804
  return parentOffset > 0 && parent.textBetween(parentOffset - 1, parentOffset) === "\n";
6784
6805
  }
6785
6806
 
6786
- //#endregion
6787
- //#region src/extensions/caret-rect.ts
6807
+ // src/extensions/caret-rect.ts
6788
6808
  function findNativeCaretRect(view) {
6789
6809
  const selection = view.dom.ownerDocument.getSelection();
6790
6810
  if (selection == null || selection.rangeCount === 0) return void 0;
@@ -6890,8 +6910,7 @@ function measureCaretScrollRect(view) {
6890
6910
  return findCoordsCaretRect(view) ?? findAtomCaretRect(view);
6891
6911
  }
6892
6912
 
6893
- //#endregion
6894
- //#region src/extensions/scroll-to-selection.ts
6913
+ // src/extensions/scroll-to-selection.ts
6895
6914
  const key$1 = new PluginKey("meowdown-scroll-to-selection");
6896
6915
  function handleScrollToSelection(view) {
6897
6916
  const selection = view.state.selection;
@@ -6993,8 +7012,7 @@ function defineScrollToSelection() {
6993
7012
  }));
6994
7013
  }
6995
7014
 
6996
- //#endregion
6997
- //#region src/extensions/select-doc-boundary.ts
7015
+ // src/extensions/select-doc-boundary.ts
6998
7016
  function selectDocBoundary(direction, extend) {
6999
7017
  return (state, dispatch) => {
7000
7018
  const boundary = direction < 0 ? Selection.atStart(state.doc) : Selection.atEnd(state.doc);
@@ -7021,8 +7039,7 @@ function defineSelectDocBoundary() {
7021
7039
  });
7022
7040
  }
7023
7041
 
7024
- //#endregion
7025
- //#region src/extensions/soft-break.ts
7042
+ // src/extensions/soft-break.ts
7026
7043
  function canHoldSoftBreak($pos) {
7027
7044
  if (!isNodeOfType($pos.parent, "paragraph")) return false;
7028
7045
  const container = $pos.depth > 0 ? $pos.node($pos.depth - 1) : void 0;
@@ -7070,8 +7087,7 @@ function defineSoftBreak() {
7070
7087
  return union(defineCommands({ insertSoftBreak }), defineKeymap({ "Shift-Enter": chainCommands(exitCodeBlockAtEnd, softBreakCommand) }));
7071
7088
  }
7072
7089
 
7073
- //#endregion
7074
- //#region src/extensions/substitution.ts
7090
+ // src/extensions/substitution.ts
7075
7091
  const SUBSTITUTION_RULES = [
7076
7092
  [/<-/, "←"],
7077
7093
  [new RegExp((canUseRegexLookbehind() ? "(?<!-)" : "") + "->"), "→"],
@@ -7159,8 +7175,7 @@ function defineSubstitution(enabled) {
7159
7175
  return union(defineSubstitutionInputRules(enabled), defineSubstitutionUndo(), defineSubstitutionEnterRules(enabled));
7160
7176
  }
7161
7177
 
7162
- //#endregion
7163
- //#region src/extensions/system-substitution-guard.ts
7178
+ // src/extensions/system-substitution-guard.ts
7164
7179
  const EM_DASH = "—";
7165
7180
  /**
7166
7181
  * Block the macOS "smart dashes" rewrite. With the `spellcheck` attribute on,
@@ -7186,8 +7201,7 @@ function defineSystemSubstitutionGuard() {
7186
7201
  return withPriority$1(definePlugin(plugin), Priority$1.highest);
7187
7202
  }
7188
7203
 
7189
- //#endregion
7190
- //#region src/extensions/view-attributes.ts
7204
+ // src/extensions/view-attributes.ts
7191
7205
  /**
7192
7206
  * Add DOM attributes to the editable root. `class` and `style` values from
7193
7207
  * every such extension are combined, so applying this more than once adds
@@ -7197,8 +7211,7 @@ function defineViewAttributes(attributes) {
7197
7211
  return definePlugin(new Plugin({ props: { attributes } }));
7198
7212
  }
7199
7213
 
7200
- //#endregion
7201
- //#region src/utils/clean-text.ts
7214
+ // src/utils/clean-text.ts
7202
7215
  /**
7203
7216
  * Projects a document slice to syntax-clean text, omitting inline markers and
7204
7217
  * hidden link destinations. Set `preserveMathSource` when the result must keep
@@ -7219,8 +7232,7 @@ function cleanTextFromSlice(slice, options = {}) {
7219
7232
  return blocks.join("\n");
7220
7233
  }
7221
7234
 
7222
- //#endregion
7223
- //#region src/extensions/wikilink-trigger.ts
7235
+ // src/extensions/wikilink-trigger.ts
7224
7236
  const WIKILINK_OPEN = "[[";
7225
7237
  /**
7226
7238
  * Inserts the `[[` wikilink trigger at the cursor and opens the wikilink menu.
@@ -7265,8 +7277,7 @@ function defineWikilinkTrigger(enabled) {
7265
7277
  });
7266
7278
  }
7267
7279
 
7268
- //#endregion
7269
- //#region src/extensions/wikilink.ts
7280
+ // src/extensions/wikilink.ts
7270
7281
  /**
7271
7282
  * Render `mdWikilink` as a non-editable label standing in for the raw source.
7272
7283
  * The source stays in `contentDOM` after the label, hidden by `style.css`
@@ -7307,12 +7318,11 @@ function createWikilinkMarkView() {
7307
7318
  function defineWikilink() {
7308
7319
  return defineMarkView({
7309
7320
  name: "mdWikilink",
7310
- constructor: createWikilinkMarkView()
7321
+ constructor: guardMarkView(createWikilinkMarkView())
7311
7322
  });
7312
7323
  }
7313
7324
 
7314
- //#endregion
7315
- //#region src/extensions/x-post-media-click.ts
7325
+ // src/extensions/x-post-media-click.ts
7316
7326
  const xPostMediaClickKey = new PluginKey("meowdown-x-post-media-click");
7317
7327
  /**
7318
7328
  * Call `onClick` when the user activates a photo or video inside an X post
@@ -7330,8 +7340,7 @@ function defineXPostMediaClickHandler(getOnClick) {
7330
7340
  }));
7331
7341
  }
7332
7342
 
7333
- //#endregion
7334
- //#region src/extensions/youtube-video-click.ts
7343
+ // src/extensions/youtube-video-click.ts
7335
7344
  const youTubeVideoClickKey = new PluginKey("meowdown-youtube-video-click");
7336
7345
  /**
7337
7346
  * Call `onClick` when the user activates the poster of a YouTube card.
@@ -7348,8 +7357,7 @@ function defineYouTubeVideoClickHandler(getOnClick) {
7348
7357
  }));
7349
7358
  }
7350
7359
 
7351
- //#endregion
7352
- //#region src/extensions/extension.ts
7360
+ // src/extensions/extension.ts
7353
7361
  function defineEditorExtensionImpl(options) {
7354
7362
  return union(defineMeowdownParagraph(), defineDoc(), defineDocFrontmatterAttr(), defineText(), defineMeowdownBlockquote(), defineMeowdownList(), defineHeading(), defineTable(), defineCodeBlock(), defineMeowdownHorizontalRule(), defineHTMLComment(), defineInlineMarks(), defineEditorConfig(options), defineImage(getEditorConfig), defineFileView(getEditorConfig), defineModClickPrevention(), defineFileClickHandler((state) => getEditorConfig(state).onFileClick), defineImageClickHandler((state) => getEditorConfig(state).onImageClick), defineXPostMediaClickHandler((state) => getEditorConfig(state).onXPostMediaClick), defineYouTubeVideoClickHandler((state) => getEditorConfig(state).onYouTubeVideoClick), defineWikilinkClickHandler((state) => getEditorConfig(state).onWikilinkClick), defineTagClickHandler((state) => getEditorConfig(state).onTagClick), defineLinkClickHandler((state) => getEditorConfig(state).onLinkClick), defineFollowLinkHandler(getEditorConfig), defineExitBoundaryHandler((state) => getEditorConfig(state).onExitBoundary), defineFilePaste(getEditorConfig), defineEmbedPaste((state) => !!getEditorConfig(state).embedPaste), defineLinkPaste((state) => !!getEditorConfig(state).linkPaste), defineBulletAfterHeading((state) => !!getEditorConfig(state).bulletAfterHeading), defineBackspaceEmptyFirstBlock((state) => !!getEditorConfig(state).backspaceDeletesEmptyFirstBlock), defineSubstitution((state) => !!getEditorConfig(state).substitution), defineWikilinkTrigger((state) => !!getEditorConfig(state).wikilinkEnabled), definePlaceholder$1({
7355
7363
  placeholder: (state) => {
@@ -7383,8 +7391,7 @@ function defineEditorExtension(options = {}) {
7383
7391
  return defineEditorExtensionImpl(options);
7384
7392
  }
7385
7393
 
7386
- //#endregion
7387
- //#region src/extensions/schema.ts
7394
+ // src/extensions/schema.ts
7388
7395
  /**
7389
7396
  * The schema shared by every parser and serializer, built once and cached.
7390
7397
  */
@@ -7428,15 +7435,17 @@ function getNodeBuildersForSchema(schema) {
7428
7435
  return builders;
7429
7436
  }
7430
7437
 
7431
- //#endregion
7432
- //#region src/converters/ast-to-pm.ts
7438
+ // src/converters/ast-to-pm.ts
7433
7439
  /**
7434
7440
  * Build nodes with the caller's schema; Markdown inline syntax remains literal text.
7435
7441
  */
7436
7442
  function astToDoc(node, nodes = getNodeBuilders()) {
7437
7443
  const children = node.children?.map((child) => astToDoc(child, nodes)) || [];
7438
7444
  switch (node.type) {
7439
- case "document": return nodes.doc(node.frontmatter == null ? {} : { frontmatter: node.frontmatter }, children);
7445
+ case "document": return nodes.doc({
7446
+ frontmatter: node.frontmatter ?? null,
7447
+ frontmatterTight: !!node.frontmatterTight
7448
+ }, children);
7440
7449
  case "paragraph": return nodes.paragraph(node.value);
7441
7450
  case "heading": return nodes.heading({
7442
7451
  level: node.level,
@@ -7468,8 +7477,7 @@ function astToDoc(node, nodes = getNodeBuilders()) {
7468
7477
  }
7469
7478
  }
7470
7479
 
7471
- //#endregion
7472
- //#region src/converters/md-to-pm.ts
7480
+ // src/converters/md-to-pm.ts
7473
7481
  /**
7474
7482
  * Convert a markdown string into a ProseMirror document node.
7475
7483
  *
@@ -7488,8 +7496,7 @@ function markdownToDoc(markdown, options = {}) {
7488
7496
  return astToDoc(parseMarkdownAst(markdown, options), options.nodes);
7489
7497
  }
7490
7498
 
7491
- //#endregion
7492
- //#region src/extensions/code-block-languages.ts
7499
+ // src/extensions/code-block-languages.ts
7493
7500
  const excludeLanguages = /* @__PURE__ */ new Set([
7494
7501
  "MscGen",
7495
7502
  "Xù",
@@ -7532,8 +7539,7 @@ const codeBlockLanguages = languages.map((language) => ({
7532
7539
  value: language.name.toLowerCase()
7533
7540
  })).filter((language) => !excludeLanguages.has(language.label)).concat(extraLanguages).sort((a, b) => a.label.localeCompare(b.label));
7534
7541
 
7535
- //#endregion
7536
- //#region src/extensions/key-bindings.ts
7542
+ // src/extensions/key-bindings.ts
7537
7543
  /**
7538
7544
  * Human-readable descriptions of the editor's formatting and heading shortcuts.
7539
7545
  */
@@ -7567,8 +7573,7 @@ const EDITOR_KEY_BINDINGS = {
7567
7573
  Escape: "Collapse the selection"
7568
7574
  };
7569
7575
 
7570
- //#endregion
7571
- //#region src/extensions/mark-hover.ts
7576
+ // src/extensions/mark-hover.ts
7572
7577
  const IDLE = { kind: "idle" };
7573
7578
  function getHit(phase) {
7574
7579
  return phase.kind === "active" || phase.kind === "leaving" ? phase.hit : void 0;
@@ -7800,8 +7805,7 @@ function defineMarkHoverHandler(config) {
7800
7805
  }));
7801
7806
  }
7802
7807
 
7803
- //#endregion
7804
- //#region src/extensions/link-hover.ts
7808
+ // src/extensions/link-hover.ts
7805
7809
  const linkHoverKey = new PluginKey("meowdown-link-hover");
7806
7810
  /**
7807
7811
  * Delay before a cold hover enters, in ms.
@@ -7845,14 +7849,12 @@ function dismissLinkHover(state) {
7845
7849
  linkHoverKey.getState(state)?.dismiss();
7846
7850
  }
7847
7851
 
7848
- //#endregion
7849
- //#region src/utils/force-reflow.ts
7852
+ // src/utils/force-reflow.ts
7850
7853
  function forceReflow(element) {
7851
7854
  element.offsetWidth;
7852
7855
  }
7853
7856
 
7854
- //#endregion
7855
- //#region src/extensions/virtual-caret.ts
7857
+ // src/extensions/virtual-caret.ts
7856
7858
  const key = new PluginKey("meowdown-virtual-caret");
7857
7859
  const BLINK_ANIMATIONS = ["md-virtual-caret-blink", "md-virtual-caret-blink2"];
7858
7860
  const DATA_ATTRIBUTE = "data-meowdown-virtual-caret";
@@ -8017,8 +8019,7 @@ function defineVirtualCaret(layer) {
8017
8019
  }));
8018
8020
  }
8019
8021
 
8020
- //#endregion
8021
- //#region src/extensions/wikilink-hover.ts
8022
+ // src/extensions/wikilink-hover.ts
8022
8023
  const wikilinkHoverKey = new PluginKey("meowdown-wikilink-hover");
8023
8024
  /**
8024
8025
  * Delay before a cold hover enters, in ms.
@@ -8058,8 +8059,7 @@ function defineWikilinkHoverHandler(onHoverChange, openDelay = OPEN_DELAY, close
8058
8059
  });
8059
8060
  }
8060
8061
 
8061
- //#endregion
8062
- //#region src/utils/display-text.ts
8062
+ // src/utils/display-text.ts
8063
8063
  function getAtomDisplayText(atom) {
8064
8064
  switch (atom.type.name) {
8065
8065
  case "mdWikilink": {
@@ -8088,8 +8088,7 @@ function getTextblockDisplayText(textblock) {
8088
8088
  return output;
8089
8089
  }
8090
8090
 
8091
- //#endregion
8092
- //#region src/utils/selected-text.ts
8091
+ // src/utils/selected-text.ts
8093
8092
  /**
8094
8093
  * The current selection as Markdown: block structure (list markers, headings,
8095
8094
  * blockquotes) is serialized, and inline Markdown syntax is already literal
@@ -8109,8 +8108,7 @@ function getSelectedText(state) {
8109
8108
  }
8110
8109
  }
8111
8110
 
8112
- //#endregion
8113
- //#region src/utils/virtual-element.ts
8111
+ // src/utils/virtual-element.ts
8114
8112
  function tryHiddenRunCoords(view, pos, side) {
8115
8113
  if (side === 1) {
8116
8114
  const run = getHiddenRunAfter(view.state, pos);
@@ -8149,5 +8147,4 @@ function getVirtualElementFromRange(view, range) {
8149
8147
  };
8150
8148
  }
8151
8149
 
8152
- //#endregion
8153
8150
  export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineBackspaceEmptyFirstBlock, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, defineXPostMediaClickHandler, defineYouTubeVideoClickHandler, dismissLinkHover, docToMarkdown, docToParagraphMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getEditorConfig, getFileKind, getIsComposing, getLinkText, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isMarkOfType, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, paragraphMarkdownToDoc, parsePostEmbedSnapshot, parseWikiEmbed, removeLink, renderMathInto, updateEditorConfig, updateLink, wikiEmbedBasename, withPriority };