@meowdown/core 0.81.3 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,7 @@ 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/input-modality.ts
77
76
  const KEYBOARD_MODALITY_KEYS = /* @__PURE__ */ new Set([
78
77
  "ArrowLeft",
79
78
  "ArrowRight",
@@ -146,8 +145,7 @@ function onIsTouchInputChange(listener) {
146
145
  };
147
146
  }
148
147
 
149
- //#endregion
150
- //#region src/utils/transaction.ts
148
+ // src/utils/transaction.ts
151
149
  /**
152
150
  * Whether any of the given transactions are selection transactions directly caused by mouse or touch input.
153
151
  */
@@ -155,8 +153,7 @@ function hasPointerSelectionTransaction(transactions) {
155
153
  return getIsPointerSelection() && transactions.some((tr) => tr.selectionSet && !tr.docChanged);
156
154
  }
157
155
 
158
- //#endregion
159
- //#region src/extensions/editor-config-getter.ts
156
+ // src/extensions/editor-config-getter.ts
160
157
  const defaultConfig = Object.freeze({});
161
158
  const configKey = new PluginKey("meowdown-editor-config");
162
159
  /**
@@ -166,8 +163,7 @@ function getEditorConfig(state) {
166
163
  return configKey.getState(state)?.config ?? defaultConfig;
167
164
  }
168
165
 
169
- //#endregion
170
- //#region src/extensions/mark-mode-config.ts
166
+ // src/extensions/mark-mode-config.ts
171
167
  /**
172
168
  * The active mark mode, defaulting to focus when no configuration is provided.
173
169
  */
@@ -175,8 +171,7 @@ function getMarkMode(state) {
175
171
  return getEditorConfig(state).markMode ?? "focus";
176
172
  }
177
173
 
178
- //#endregion
179
- //#region src/extensions/mark-names.ts
174
+ // src/extensions/mark-names.ts
180
175
  function isMarkOfType(mark, name) {
181
176
  return mark.type.name === name;
182
177
  }
@@ -200,8 +195,7 @@ const ATOM_SOURCE_MARK_NAMES = [
200
195
  "mdFile"
201
196
  ];
202
197
 
203
- //#endregion
204
- //#region src/extensions/hidden-run.ts
198
+ // src/extensions/hidden-run.ts
205
199
  function getCharMarks(state, pos) {
206
200
  if (pos < 0 || pos + 1 > state.doc.content.size) return;
207
201
  const $pos = state.doc.resolve(pos);
@@ -341,8 +335,7 @@ function getUnitMarkerRuns(state, charPos) {
341
335
  return runs;
342
336
  }
343
337
 
344
- //#endregion
345
- //#region src/extensions/mark-range.ts
338
+ // src/extensions/mark-range.ts
346
339
  /**
347
340
  * Returns the resolved position, or `undefined` when it falls outside the
348
341
  * document or in a place that carries no inline marks: marks describe inline
@@ -425,8 +418,7 @@ function getMarkRangeStrictlyAround(state, pos, markNames) {
425
418
  }
426
419
  }
427
420
 
428
- //#endregion
429
- //#region src/extensions/atom-mark-navigation.ts
421
+ // src/extensions/atom-mark-navigation.ts
430
422
  function getActiveMarkNames(marks, state) {
431
423
  const mode = getMarkMode(state);
432
424
  const result = [];
@@ -624,14 +616,12 @@ function defineAtomMarkNavigation({ marks }) {
624
616
  }), Priority$1.high), definePlugin(createCaretSnapPlugin(marks)), definePlugin(createSelectionPlugin(marks)));
625
617
  }
626
618
 
627
- //#endregion
628
- //#region src/extensions/node-names.ts
619
+ // src/extensions/node-names.ts
629
620
  function isNodeOfType(node, name) {
630
621
  return node.type.name === name;
631
622
  }
632
623
 
633
- //#endregion
634
- //#region src/extensions/backspace-empty-first-block.ts
624
+ // src/extensions/backspace-empty-first-block.ts
635
625
  /**
636
626
  * Claim Backspace only when the selection is an empty caret in an empty
637
627
  * paragraph that is the document's first block and has a block after it.
@@ -662,8 +652,7 @@ function defineBackspaceEmptyFirstBlock(enabled) {
662
652
  } }), Priority$1.lowest);
663
653
  }
664
654
 
665
- //#endregion
666
- //#region src/converters/paragraph.ts
655
+ // src/converters/paragraph.ts
667
656
  /**
668
657
  * Build one paragraph without interpreting block-opening Markdown.
669
658
  */
@@ -689,8 +678,7 @@ function docToParagraphMarkdown(doc) {
689
678
  return paragraphs.join("\n").replaceAll(/\n[ \t]*\n+/g, "\n");
690
679
  }
691
680
 
692
- //#endregion
693
- //#region src/extensions/single-paragraph.ts
681
+ // src/extensions/single-paragraph.ts
694
682
  const singleParagraphPluginKey = new PluginKey("single-paragraph");
695
683
  /**
696
684
  * Whether the editor holds one paragraph of inline Markdown (the
@@ -721,8 +709,7 @@ function defineSingleParagraph() {
721
709
  })), Priority$1.highest);
722
710
  }
723
711
 
724
- //#endregion
725
- //#region src/extensions/block-rule.ts
712
+ // src/extensions/block-rule.ts
726
713
  /**
727
714
  * An input rule whose match turns the textblock into, or wraps it in, a block
728
715
  * node. In a single paragraph the match is left as typed text: there is no
@@ -747,14 +734,12 @@ function defineBlockEnterRule(options) {
747
734
  });
748
735
  }
749
736
 
750
- //#endregion
751
- //#region src/extensions/blockquote.ts
737
+ // src/extensions/blockquote.ts
752
738
  function defineMeowdownBlockquote() {
753
739
  return union(defineBlockquoteSpec(), defineBlockquoteCommands(), defineBlockquoteKeymap(), defineBlockInputRule(blockquoteInputRule.regex, createWrappingInputRuleHandler(blockquoteInputRule)));
754
740
  }
755
741
 
756
- //#endregion
757
- //#region src/extensions/bullet-after-heading.ts
742
+ // src/extensions/bullet-after-heading.ts
758
743
  /**
759
744
  * Claim Enter only when the selection is an empty caret at the very end of the
760
745
  * document's first heading (the note's title line). Every other Enter (a later
@@ -788,8 +773,7 @@ function defineBulletAfterHeading(enabled) {
788
773
  } }), Priority$1.high);
789
774
  }
790
775
 
791
- //#endregion
792
- //#region src/extensions/click-below.ts
776
+ // src/extensions/click-below.ts
793
777
  const clickBelowKey = new PluginKey("meowdown-click-below");
794
778
  function acceptsCaretAtEnd(node) {
795
779
  return node.isTextblock && !node.type.spec.code;
@@ -827,8 +811,7 @@ function defineClickBelow() {
827
811
  }));
828
812
  }
829
813
 
830
- //#endregion
831
- //#region src/converters/html-to-md.ts
814
+ // src/converters/html-to-md.ts
832
815
  /**
833
816
  * Convert a `<mark>` element into a `highlight` node. There is no built-in
834
817
  * `hast-util-to-mdast` handler for `<mark>`, so without this the highlight is
@@ -974,8 +957,7 @@ function htmlToMarkdown(html, parser) {
974
957
  return processor.stringify(processor.runSync(hast));
975
958
  }
976
959
 
977
- //#endregion
978
- //#region src/utils/parse-integer.ts
960
+ // src/utils/parse-integer.ts
979
961
  function parseInteger(raw) {
980
962
  if (raw == null) return void 0;
981
963
  const value = Number.parseInt(raw, 10);
@@ -986,8 +968,7 @@ function parsePositiveInteger(raw) {
986
968
  return value != null && value > 0 ? value : void 0;
987
969
  }
988
970
 
989
- //#endregion
990
- //#region src/extensions/inline-runs.ts
971
+ // src/extensions/inline-runs.ts
991
972
  function findAtomMark(marks) {
992
973
  return marks.find((mark) => isMarkOfTypes(mark, ATOM_MARK_NAMES));
993
974
  }
@@ -1029,8 +1010,7 @@ function groupInlineRuns(textblock) {
1029
1010
  return runs;
1030
1011
  }
1031
1012
 
1032
- //#endregion
1033
- //#region src/extensions/clipboard/semantic-inline.ts
1013
+ // src/extensions/clipboard/semantic-inline.ts
1034
1014
  const SEMANTIC_TAGS = {
1035
1015
  mdStrong: "strong",
1036
1016
  mdEm: "em",
@@ -1134,8 +1114,7 @@ function atomUnitToDOM(atom, sourceText) {
1134
1114
  }
1135
1115
  }
1136
1116
 
1137
- //#endregion
1138
- //#region src/extensions/heading.ts
1117
+ // src/extensions/heading.ts
1139
1118
  /**
1140
1119
  * Merge `whitespace: 'pre'` onto the base heading spec. A multi-line setext
1141
1120
  * heading keeps a soft line break as a literal `\n`; without `whitespace: 'pre'`
@@ -1222,8 +1201,7 @@ function defineHeading() {
1222
1201
  return union(defineHeadingSpec(), defineHeadingWhitespace(), defineSetextUnderlineAttr(), defineHeadingClosingHashesAttr(), defineBlockInputRule(headingInputRule.regex, createTextBlockInputRuleHandler(headingInputRule)), defineHeadingCommands(), defineHeadingKeymap());
1223
1202
  }
1224
1203
 
1225
- //#endregion
1226
- //#region src/extensions/paragraph.ts
1204
+ // src/extensions/paragraph.ts
1227
1205
  function defineMeowdownParagraphSpec() {
1228
1206
  return defineNodeSpec({
1229
1207
  name: "paragraph",
@@ -1252,8 +1230,7 @@ function defineMeowdownParagraph() {
1252
1230
  return union(withPriority$1(defineMeowdownParagraphSpec(), Priority$1.highest), defineParagraphCommands(), defineParagraphKeymap());
1253
1231
  }
1254
1232
 
1255
- //#endregion
1256
- //#region src/extensions/clipboard/clipboard-serializer.ts
1233
+ // src/extensions/clipboard/clipboard-serializer.ts
1257
1234
  function withSemanticTextblocks(nodes) {
1258
1235
  return {
1259
1236
  ...nodes,
@@ -1297,8 +1274,7 @@ function getSemanticDOMSerializer(schema) {
1297
1274
  return serializer;
1298
1275
  }
1299
1276
 
1300
- //#endregion
1301
- //#region src/extensions/html-paste.ts
1277
+ // src/extensions/html-paste.ts
1302
1278
  const htmlPasteKey = new PluginKey("meowdown-html-paste");
1303
1279
  /**
1304
1280
  * meowdown's own clipboard HTML, which must skip the markdown conversion and
@@ -1422,8 +1398,7 @@ function defineHTMLPaste() {
1422
1398
  }));
1423
1399
  }
1424
1400
 
1425
- //#endregion
1426
- //#region src/extensions/clipboard/clipboard-parser.ts
1401
+ // src/extensions/clipboard/clipboard-parser.ts
1427
1402
  /**
1428
1403
  * The clipboard parser: schema rules plus the `data-md` rules that restore a
1429
1404
  * textblock's source text from meowdown's own clipboard HTML. Registered as
@@ -1446,8 +1421,7 @@ function defineClipboardParser() {
1446
1421
  });
1447
1422
  }
1448
1423
 
1449
- //#endregion
1450
- //#region src/extensions/clipboard/plain-paste.ts
1424
+ // src/extensions/clipboard/plain-paste.ts
1451
1425
  /**
1452
1426
  * Parse pasted plain text as markdown: `- [ ] task`, `# heading`, fenced code
1453
1427
  * and the other block constructs become real nodes, while inline syntax stays
@@ -1504,8 +1478,7 @@ function definePlainTextPaste() {
1504
1478
  }));
1505
1479
  }
1506
1480
 
1507
- //#endregion
1508
- //#region src/converters/pm-to-ast.ts
1481
+ // src/converters/pm-to-ast.ts
1509
1482
  /**
1510
1483
  * Read persisted block data. Editor marks do not add Markdown delimiters.
1511
1484
  */
@@ -1513,6 +1486,7 @@ function docToAst(node) {
1513
1486
  if (isNodeOfType(node, "doc")) return {
1514
1487
  type: "document",
1515
1488
  frontmatter: node.attrs.frontmatter ?? void 0,
1489
+ frontmatterTight: node.attrs.frontmatterTight ? true : void 0,
1516
1490
  children: readBlocks(node)
1517
1491
  };
1518
1492
  if (isNodeOfType(node, "tableRow")) return readTableRow(node);
@@ -1642,8 +1616,7 @@ function readTableCell(node) {
1642
1616
  };
1643
1617
  }
1644
1618
 
1645
- //#endregion
1646
- //#region src/converters/pm-to-md.ts
1619
+ // src/converters/pm-to-md.ts
1647
1620
  /**
1648
1621
  * Convert persisted editor content to Markdown through the shared block serializer.
1649
1622
  */
@@ -1651,8 +1624,7 @@ function docToMarkdown(node, options = {}) {
1651
1624
  return serializeMarkdownAst(docToAst(node), options);
1652
1625
  }
1653
1626
 
1654
- //#endregion
1655
- //#region src/utils/top-level-block-boundary.ts
1627
+ // src/utils/top-level-block-boundary.ts
1656
1628
  function isAtTopLevelBlockStart($pos) {
1657
1629
  if ($pos.depth === 0) return true;
1658
1630
  if ($pos.parentOffset !== 0) return false;
@@ -1666,8 +1638,7 @@ function isAtTopLevelBlockEnd($pos) {
1666
1638
  return true;
1667
1639
  }
1668
1640
 
1669
- //#endregion
1670
- //#region src/extensions/clipboard/plain-text.ts
1641
+ // src/extensions/clipboard/plain-text.ts
1671
1642
  /**
1672
1643
  * Serialize a slice to Markdown. A block whose content start is not selected is
1673
1644
  * flattened so its opening markers are not synthesized. Incomplete fenced code
@@ -1783,8 +1754,7 @@ function filterTextblock(textblock) {
1783
1754
  return textblock.copy(Fragment.from(parts));
1784
1755
  }
1785
1756
 
1786
- //#endregion
1787
- //#region src/extensions/clipboard/clipboard.ts
1757
+ // src/extensions/clipboard/clipboard.ts
1788
1758
  /**
1789
1759
  * The clipboard pipeline: semantic HTML with `data-md` round-trip attributes
1790
1760
  * plus a markdown-shaped `text/plain` on copy; the matching `data-md` parser,
@@ -1795,8 +1765,7 @@ function defineClipboard() {
1795
1765
  return union(defineSemanticClipboardSerializer(), definePlainTextSerializer(), defineClipboardParser(), defineHTMLPaste(), definePlainTextPaste());
1796
1766
  }
1797
1767
 
1798
- //#endregion
1799
- //#region src/extensions/code-block-highlight.ts
1768
+ // src/extensions/code-block-highlight.ts
1800
1769
  const supportCache = /* @__PURE__ */ new Map();
1801
1770
  const parserCache = /* @__PURE__ */ new Map();
1802
1771
  const LANGUAGE_ALIASES = { math: "latex" };
@@ -1876,8 +1845,7 @@ function getCodeTokens(code, language) {
1876
1845
  return loadLanguageSupport(trimmed).then((loaded) => loaded ? tokenize(code, loaded) : []);
1877
1846
  }
1878
1847
 
1879
- //#endregion
1880
- //#region src/extensions/code-block-exit.ts
1848
+ // src/extensions/code-block-exit.ts
1881
1849
  /**
1882
1850
  * With the caret at the end of a code block, move it into the block below when
1883
1851
  * that block is an empty paragraph, and into a new default block otherwise.
@@ -1908,8 +1876,7 @@ function defineCodeBlockExitKeymap() {
1908
1876
  return defineKeymap({ "Mod-Enter": exitCodeBlockAtEnd });
1909
1877
  }
1910
1878
 
1911
- //#endregion
1912
- //#region src/extensions/code-block.ts
1879
+ // src/extensions/code-block.ts
1913
1880
  function defineFenceStyleAttr() {
1914
1881
  return defineNodeAttr({
1915
1882
  type: "codeBlock",
@@ -1934,6 +1901,29 @@ function defineFenceLengthAttr() {
1934
1901
  }
1935
1902
  });
1936
1903
  }
1904
+ /**
1905
+ * Re-declare the code block `toDOM` with a `data-code-block` root attribute.
1906
+ */
1907
+ function defineCodeBlockDOM() {
1908
+ return defineNodeSpec({
1909
+ name: "codeBlock",
1910
+ toDOM: (node) => {
1911
+ const { language } = node.attrs;
1912
+ return [
1913
+ "pre",
1914
+ {
1915
+ "data-code-block": "",
1916
+ "data-language": language || void 0
1917
+ },
1918
+ [
1919
+ "code",
1920
+ { class: language ? `language-${language}` : void 0 },
1921
+ 0
1922
+ ]
1923
+ ];
1924
+ }
1925
+ });
1926
+ }
1937
1927
  function getTildeFenceAttrs(match) {
1938
1928
  return {
1939
1929
  language: match[1] || "",
@@ -1963,11 +1953,10 @@ function defineDollarFenceEnterRule() {
1963
1953
  });
1964
1954
  }
1965
1955
  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());
1956
+ return union(defineCodeBlockSpec(), defineCodeBlockDOM(), defineCodeBlockKeymap(), defineCodeBlockCommands(), defineBlockInputRule(codeBlockInputRule.regex, createTextBlockInputRuleHandler(codeBlockInputRule)), defineBlockEnterRule(codeBlockEnterRule), defineFenceStyleAttr(), defineFenceLengthAttr(), defineBlockInputRule(tildeFenceInputRule.regex, createTextBlockInputRuleHandler(tildeFenceInputRule)), defineTildeFenceEnterRule(), defineDollarFenceEnterRule(), defineCodeBlockExitKeymap());
1967
1957
  }
1968
1958
 
1969
- //#endregion
1970
- //#region src/extensions/commands.ts
1959
+ // src/extensions/commands.ts
1971
1960
  function selectText(anchor, head) {
1972
1961
  return (state, dispatch) => {
1973
1962
  if (dispatch) {
@@ -2052,8 +2041,7 @@ function defineEditorCommands() {
2052
2041
  });
2053
2042
  }
2054
2043
 
2055
- //#endregion
2056
- //#region src/extensions/cross-editor-drag.ts
2044
+ // src/extensions/cross-editor-drag.ts
2057
2045
  function deleteDraggedContent(view, dragging) {
2058
2046
  const tr = view.state.tr;
2059
2047
  if (dragging.node) {
@@ -2100,8 +2088,7 @@ function defineCrossEditorDrag() {
2100
2088
  return definePlugin(createCrossEditorDragPlugin());
2101
2089
  }
2102
2090
 
2103
- //#endregion
2104
- //#region src/extensions/editor-config.ts
2091
+ // src/extensions/editor-config.ts
2105
2092
  var ConfigController = class {
2106
2093
  constructor(config) {
2107
2094
  this.config = config;
@@ -2113,6 +2100,13 @@ const refreshKeys = /* @__PURE__ */ new Set([
2113
2100
  "placeholder",
2114
2101
  "readOnly",
2115
2102
  "spellCheck",
2103
+ "writingSuggestions",
2104
+ "autoCorrect",
2105
+ "autoCapitalize",
2106
+ "enterKeyHint",
2107
+ "dir",
2108
+ "lang",
2109
+ "ariaLabel",
2116
2110
  "editorClassName"
2117
2111
  ]);
2118
2112
  function updateEditorConfig(editor, patch) {
@@ -2151,16 +2145,14 @@ function defineEditorConfig(initialConfig) {
2151
2145
  }));
2152
2146
  }
2153
2147
 
2154
- //#endregion
2155
- //#region src/extensions/paste.ts
2148
+ // src/extensions/paste.ts
2156
2149
  function getPastedText(event, slice) {
2157
2150
  const fromClipboard = event.clipboardData?.getData("text/plain");
2158
2151
  if (fromClipboard) return fromClipboard;
2159
2152
  return slice.content.textBetween(0, slice.content.size, "\n");
2160
2153
  }
2161
2154
 
2162
- //#endregion
2163
- //#region src/extensions/embed-paste.ts
2155
+ // src/extensions/embed-paste.ts
2164
2156
  const embedPasteKey = new PluginKey("meowdown-embed-paste");
2165
2157
  function detectEmbedUrl(text) {
2166
2158
  const trimmed = text.trim();
@@ -2196,8 +2188,7 @@ function defineEmbedPaste(enabled) {
2196
2188
  }));
2197
2189
  }
2198
2190
 
2199
- //#endregion
2200
- //#region src/extensions/escape-collapse.ts
2191
+ // src/extensions/escape-collapse.ts
2201
2192
  /**
2202
2193
  * Collapses a non-empty selection to a caret at its head. Leaves an empty
2203
2194
  * selection alone so Escape can serve whoever binds it next.
@@ -2217,8 +2208,7 @@ function defineEscapeCollapse() {
2217
2208
  return withPriority$1(defineKeymap({ Escape: collapseSelection }), Priority$1.low);
2218
2209
  }
2219
2210
 
2220
- //#endregion
2221
- //#region src/extensions/exit-boundary.ts
2211
+ // src/extensions/exit-boundary.ts
2222
2212
  const exitBoundaryKey = new PluginKey("meowdown-exit-boundary");
2223
2213
  function canMoveBlockwise(state, direction) {
2224
2214
  const { $anchor, $head } = state.selection;
@@ -2259,8 +2249,7 @@ function defineExitBoundaryHandler(getExitBoundaryHandler) {
2259
2249
  return withPriority$1(definePlugin(createExitBoundaryPlugin(getExitBoundaryHandler)), Priority$1.low);
2260
2250
  }
2261
2251
 
2262
- //#endregion
2263
- //#region src/utils/is-mod-event.ts
2252
+ // src/utils/is-mod-event.ts
2264
2253
  /**
2265
2254
  * Whether the platform's mod key is held on `event`: `Command` on Apple, `Ctrl` elsewhere.
2266
2255
  */
@@ -2268,8 +2257,7 @@ function isModEvent(event) {
2268
2257
  return isApple ? event.metaKey : event.ctrlKey;
2269
2258
  }
2270
2259
 
2271
- //#endregion
2272
- //#region src/extensions/file-click.ts
2260
+ // src/extensions/file-click.ts
2273
2261
  const fileClickKey = new PluginKey("meowdown-file-click");
2274
2262
  function findFileAt(state, pos) {
2275
2263
  const range = getMarkRangeAt(state, pos, "mdFile");
@@ -2310,8 +2298,7 @@ function defineFileClickHandler(getOnClick) {
2310
2298
  }));
2311
2299
  }
2312
2300
 
2313
- //#endregion
2314
- //#region src/extensions/file-paste.ts
2301
+ // src/extensions/file-paste.ts
2315
2302
  const filePasteKey = new PluginKey("file-paste");
2316
2303
  const IMAGE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
2317
2304
  "avif",
@@ -2414,8 +2401,7 @@ function defineFilePaste(getOptions) {
2414
2401
  return withPriority$1(definePlugin(createFilePastePlugin(getOptions)), Priority$1.high);
2415
2402
  }
2416
2403
 
2417
- //#endregion
2418
- //#region src/utils/format-file-size.ts
2404
+ // src/utils/format-file-size.ts
2419
2405
  const UNITS = [
2420
2406
  "KB",
2421
2407
  "MB",
@@ -2438,8 +2424,7 @@ function formatFileSize(bytes) {
2438
2424
  return `${value < 9.95 ? Math.round(value * 10) / 10 : Math.round(value)} ${unit}`;
2439
2425
  }
2440
2426
 
2441
- //#endregion
2442
- //#region src/extensions/file-view.ts
2427
+ // src/extensions/file-view.ts
2443
2428
  /**
2444
2429
  * `data-file-kind` values by file extension, for host CSS theming.
2445
2430
  */
@@ -2593,8 +2578,7 @@ function defineFileView(getOptions) {
2593
2578
  });
2594
2579
  }
2595
2580
 
2596
- //#endregion
2597
- //#region src/extensions/find.ts
2581
+ // src/extensions/find.ts
2598
2582
  const revealActiveMatch = new Plugin({ props: { decorations: (state) => {
2599
2583
  const { from, to, empty } = state.selection;
2600
2584
  if (empty || getSearchStatus$1(state).active === 0) return;
@@ -2610,8 +2594,7 @@ function defineFind() {
2610
2594
  return union(defineSearchQuery(), defineSearchCommands(), definePlugin(revealActiveMatch));
2611
2595
  }
2612
2596
 
2613
- //#endregion
2614
- //#region src/extensions/get-link-unit-at.ts
2597
+ // src/extensions/get-link-unit-at.ts
2615
2598
  /**
2616
2599
  * The last text run carrying `markName` inside `range`. "Last" so a linked
2617
2600
  * image's inner url/title (which comes first) never shadows the link's own.
@@ -2705,8 +2688,7 @@ function getLinkUnitAt(state, pos) {
2705
2688
  }
2706
2689
  }
2707
2690
 
2708
- //#endregion
2709
- //#region src/extensions/image-click.ts
2691
+ // src/extensions/image-click.ts
2710
2692
  const imageClickKey = new PluginKey("meowdown-image-click");
2711
2693
  /**
2712
2694
  * The image preview wrapper around `target`, or nothing when the wrapper holds
@@ -2843,8 +2825,7 @@ function defineImageClickHandler(getOnClick) {
2843
2825
  }));
2844
2826
  }
2845
2827
 
2846
- //#endregion
2847
- //#region src/extensions/mark-click.ts
2828
+ // src/extensions/mark-click.ts
2848
2829
  /**
2849
2830
  * Shared click plumbing for text-backed marks (wikilinks, Markdown links, tags):
2850
2831
  * a click anywhere on the rendered mark fires `onClick`.
@@ -2865,8 +2846,7 @@ function defineMarkClickHandler(config) {
2865
2846
  }));
2866
2847
  }
2867
2848
 
2868
- //#endregion
2869
- //#region src/extensions/tag-click.ts
2849
+ // src/extensions/tag-click.ts
2870
2850
  const tagClickKey = new PluginKey("meowdown-tag-click");
2871
2851
  /**
2872
2852
  * The tag covering `pos`, found via the `mdTag` run. The tag name is read from
@@ -2907,8 +2887,7 @@ function defineTagClickHandler(getOnClick) {
2907
2887
  });
2908
2888
  }
2909
2889
 
2910
- //#endregion
2911
- //#region src/extensions/wikilink-click.ts
2890
+ // src/extensions/wikilink-click.ts
2912
2891
  const wikilinkClickKey = new PluginKey("meowdown-wikilink-click");
2913
2892
  /**
2914
2893
  * Exported for tests.
@@ -2959,8 +2938,7 @@ function defineWikilinkClickHandler(getOnClick) {
2959
2938
  });
2960
2939
  }
2961
2940
 
2962
- //#endregion
2963
- //#region src/extensions/follow-link.ts
2941
+ // src/extensions/follow-link.ts
2964
2942
  const followLinkKey = new PluginKey("meowdown-follow-link");
2965
2943
  function createFollowLinkPlugin(getHandlers) {
2966
2944
  return new Plugin({
@@ -3052,27 +3030,29 @@ function defineFollowLinkHandler(getHandlers) {
3052
3030
  return withPriority$1(definePlugin(createFollowLinkPlugin(getHandlers)), Priority$1.high);
3053
3031
  }
3054
3032
 
3055
- //#endregion
3056
- //#region src/extensions/frontmatter.ts
3033
+ // src/extensions/frontmatter.ts
3057
3034
  /**
3058
- * Stores YAML frontmatter as a non-rendered attribute on the root `doc` node.
3035
+ * Stores YAML frontmatter as a non-rendered attribute on the root `doc` node,
3036
+ * plus whether the first block starts right under its closing fence.
3059
3037
  */
3060
3038
  function defineDocFrontmatterAttr() {
3061
- return defineNodeAttr({
3039
+ return union(defineNodeAttr({
3062
3040
  type: "doc",
3063
3041
  attr: "frontmatter",
3064
3042
  default: null
3065
- });
3043
+ }), defineNodeAttr({
3044
+ type: "doc",
3045
+ attr: "frontmatterTight",
3046
+ default: false
3047
+ }));
3066
3048
  }
3067
3049
 
3068
- //#endregion
3069
- //#region src/utils/execute-command.ts
3050
+ // src/utils/execute-command.ts
3070
3051
  function executeCommand(view, command) {
3071
3052
  return command(view.state, view.dispatch, view);
3072
3053
  }
3073
3054
 
3074
- //#endregion
3075
- //#region src/extensions/hidden-run-caret.ts
3055
+ // src/extensions/hidden-run-caret.ts
3076
3056
  const snapKey = new PluginKey("meowdown-hidden-run-snap");
3077
3057
  const beforeInputKey = new PluginKey("meowdown-hidden-run-beforeinput");
3078
3058
  function createSnapPlugin() {
@@ -3148,8 +3128,7 @@ function defineHiddenRunCaret() {
3148
3128
  }), Priority$1.highest));
3149
3129
  }
3150
3130
 
3151
- //#endregion
3152
- //#region src/extensions/horizontal-rule.ts
3131
+ // src/extensions/horizontal-rule.ts
3153
3132
  function defineHorizontalRuleMarkerAttr() {
3154
3133
  return defineNodeAttr({
3155
3134
  type: "horizontalRule",
@@ -3163,8 +3142,7 @@ function defineMeowdownHorizontalRule() {
3163
3142
  return union(defineHorizontalRuleSpec(), defineHorizontalRuleCommands(), defineHorizontalRuleMarkerAttr(), defineBlockInputRule(horizontalRuleInputRule.regex, horizontalRuleInputRule.handler));
3164
3143
  }
3165
3144
 
3166
- //#endregion
3167
- //#region src/utils/non-prose-attrs.ts
3145
+ // src/utils/non-prose-attrs.ts
3168
3146
  const NON_PROSE_ATTRS = {
3169
3147
  spellcheck: "false",
3170
3148
  autocorrect: "off",
@@ -3172,8 +3150,7 @@ const NON_PROSE_ATTRS = {
3172
3150
  writingsuggestions: "false"
3173
3151
  };
3174
3152
 
3175
- //#endregion
3176
- //#region src/extensions/html-comment.ts
3153
+ // src/extensions/html-comment.ts
3177
3154
  /**
3178
3155
  * A block-level HTML comment (`<!-- ... -->`) as an invisible, atomic node.
3179
3156
  *
@@ -3212,8 +3189,7 @@ function defineHTMLComment() {
3212
3189
  });
3213
3190
  }
3214
3191
 
3215
- //#endregion
3216
- //#region src/utils/to-positive-number.ts
3192
+ // src/utils/to-positive-number.ts
3217
3193
  /**
3218
3194
  * The value rounded to an integer, or nothing unless it is a finite number
3219
3195
  * above zero.
@@ -3222,8 +3198,7 @@ function toPositiveNumber(value) {
3222
3198
  if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.round(value);
3223
3199
  }
3224
3200
 
3225
- //#endregion
3226
- //#region src/extensions/magic-comment.ts
3201
+ // src/extensions/magic-comment.ts
3227
3202
  const MAGIC_COMMENT_RE = /^<!--\s*(\{[\s\S]*?\})\s*-->/;
3228
3203
  const TRAILING_MAGIC_COMMENT_RE = /(?:<!--\s*\{[\s\S]*?\}\s*-->)+$/;
3229
3204
  /**
@@ -3270,8 +3245,7 @@ function stripMagicComment(source) {
3270
3245
  return source.replace(TRAILING_MAGIC_COMMENT_RE, "");
3271
3246
  }
3272
3247
 
3273
- //#endregion
3274
- //#region src/extensions/post-embed.ts
3248
+ // src/extensions/post-embed.ts
3275
3249
  /**
3276
3250
  * Wrap a loader in a 64-entry LRU keyed by URL. A settled URL answers
3277
3251
  * synchronously, so a revisited document renders its cards in the first
@@ -3332,8 +3306,7 @@ function parsePostEmbedSnapshot(value) {
3332
3306
  return result.success ? result.output : void 0;
3333
3307
  }
3334
3308
 
3335
- //#endregion
3336
- //#region src/extensions/wiki-embed.ts
3309
+ // src/extensions/wiki-embed.ts
3337
3310
  const WIKI_EMBED_SIZE = /^(\d+)(?:x(\d+))?$/i;
3338
3311
  function positiveInteger(value) {
3339
3312
  if (!value) return null;
@@ -3395,8 +3368,7 @@ function wikiEmbedBasename(target) {
3395
3368
  }
3396
3369
  }
3397
3370
 
3398
- //#endregion
3399
- //#region src/extensions/image.ts
3371
+ // src/extensions/image.ts
3400
3372
  /**
3401
3373
  * Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it.
3402
3374
  */
@@ -3759,8 +3731,7 @@ function defineImage(getOptions) {
3759
3731
  });
3760
3732
  }
3761
3733
 
3762
- //#endregion
3763
- //#region src/extensions/mark-chunk.ts
3734
+ // src/extensions/mark-chunk.ts
3764
3735
  function markChunkToJSON(chunk) {
3765
3736
  const [from, to, marks] = chunk;
3766
3737
  return [
@@ -3778,8 +3749,7 @@ function markChunkFromJSON(schema, json) {
3778
3749
  ];
3779
3750
  }
3780
3751
 
3781
- //#endregion
3782
- //#region src/extensions/marks-equal.ts
3752
+ // src/extensions/marks-equal.ts
3783
3753
  /**
3784
3754
  * Position-wise equality on two mark arrays. Returns `true` only when
3785
3755
  * the arrays have the same length and `a[i].eq(b[i])` for every index.
@@ -3791,8 +3761,7 @@ function marksEqual(a, b) {
3791
3761
  return true;
3792
3762
  }
3793
3763
 
3794
- //#endregion
3795
- //#region src/extensions/batch-set-mark-step.ts
3764
+ // src/extensions/batch-set-mark-step.ts
3796
3765
  const SPARSE_CHUNK_LIMIT = 32;
3797
3766
  /**
3798
3767
  * Apply a small batch by visiting each chunk's narrow range independently.
@@ -3961,8 +3930,7 @@ var BatchSetMarkStep = class BatchSetMarkStep extends Step {
3961
3930
  Step.jsonID("batchSetMark", BatchSetMarkStep);
3962
3931
  const emptyBatchSetMarkStep = new BatchSetMarkStep([]);
3963
3932
 
3964
- //#endregion
3965
- //#region src/extensions/reference-links.ts
3933
+ // src/extensions/reference-links.ts
3966
3934
  const MAX_DEFINITION_LENGTH = 1024;
3967
3935
  function normalizeReferenceLabel(label) {
3968
3936
  return normalizeIdentifier(label);
@@ -4069,8 +4037,7 @@ function updateReferenceDefinitions(previous, transaction, doc) {
4069
4037
  return collectReferenceDefinitions(doc);
4070
4038
  }
4071
4039
 
4072
- //#endregion
4073
- //#region src/extensions/inline-text-to-mark-chunks.ts
4040
+ // src/extensions/inline-text-to-mark-chunks.ts
4074
4041
  /**
4075
4042
  * Lookup from Lezer node type id to the ProseMirror mark.
4076
4043
  *
@@ -4643,8 +4610,7 @@ function emit(out, from, to, marks) {
4643
4610
  ]);
4644
4611
  }
4645
4612
 
4646
- //#endregion
4647
- //#region src/extensions/inline-mark-plugin.ts
4613
+ // src/extensions/inline-mark-plugin.ts
4648
4614
  const META_KEY = "inline-marks-applied";
4649
4615
  const TRIGGER_KEY = "inline-marks-trigger";
4650
4616
  const RESTYLE_KEY = "inline-marks-restyle";
@@ -4870,8 +4836,7 @@ function defineInlineMarkPlugin(getOptions) {
4870
4836
  return definePlugin(createInlineMarkPlugin(getOptions));
4871
4837
  }
4872
4838
 
4873
- //#endregion
4874
- //#region src/extensions/inline-marks.ts
4839
+ // src/extensions/inline-marks.ts
4875
4840
  function defineMdImage() {
4876
4841
  return defineMarkSpec({
4877
4842
  name: "mdImage",
@@ -5100,8 +5065,7 @@ function defineInlineMarks() {
5100
5065
  return union(defineMdMark(), defineMdEm(), defineMdStrong(), defineMdCode(), defineMdLinkText(), defineMdLinkUri(), defineMdLinkTitle(), defineMdDel(), defineMdHighlight(), defineMdTag(), defineMdWikilink(), defineMdImage(), defineMdFile(), defineMdMath(), defineMdPack());
5101
5066
  }
5102
5067
 
5103
- //#endregion
5104
- //#region src/utils/backticks.ts
5068
+ // src/utils/backticks.ts
5105
5069
  /**
5106
5070
  * Length of the longest run of `charCode` in `text`, at least `min`.
5107
5071
  */
@@ -5121,8 +5085,7 @@ function longestBacktickRun(text, min = 0) {
5121
5085
  return longestCharRun(text, 96, min);
5122
5086
  }
5123
5087
 
5124
- //#endregion
5125
- //#region src/extensions/inline-toggle.ts
5088
+ // src/extensions/inline-toggle.ts
5126
5089
  const TOGGLE_SPECS = {
5127
5090
  em: {
5128
5091
  node: LEZER_NODE_IDS.Emphasis,
@@ -5376,8 +5339,7 @@ function insideAtom(nodes, pos) {
5376
5339
  return false;
5377
5340
  }
5378
5341
 
5379
- //#endregion
5380
- //#region src/extensions/inline-toggle-commands.ts
5342
+ // src/extensions/inline-toggle-commands.ts
5381
5343
  function toggleInline(spec) {
5382
5344
  return (state, dispatch) => {
5383
5345
  if (state.selection.empty) return caretToggle(spec, state, dispatch);
@@ -5459,8 +5421,7 @@ function defineInlineToggle() {
5459
5421
  return union(defineInlineToggleCommands(), defineInlineToggleKeymap());
5460
5422
  }
5461
5423
 
5462
- //#endregion
5463
- //#region src/extensions/link-click.ts
5424
+ // src/extensions/link-click.ts
5464
5425
  const linkClickKey = new PluginKey("meowdown-link-click");
5465
5426
  /**
5466
5427
  * Call `onClick` when the user clicks a rendered Markdown link
@@ -5485,8 +5446,7 @@ function defineLinkClickHandler(getOnClick) {
5485
5446
  });
5486
5447
  }
5487
5448
 
5488
- //#endregion
5489
- //#region src/extensions/link-commands.ts
5449
+ // src/extensions/link-commands.ts
5490
5450
  /**
5491
5451
  * Normalize a typed URL with the existing autolink logic, else keep it verbatim.
5492
5452
  */
@@ -5654,8 +5614,7 @@ function defineLinkEditKeymap(onLinkEdit) {
5654
5614
  return defineKeymap({ "Mod-k": openLinkEdit(onLinkEdit) });
5655
5615
  }
5656
5616
 
5657
- //#endregion
5658
- //#region src/extensions/link-paste.ts
5617
+ // src/extensions/link-paste.ts
5659
5618
  const linkPasteKey = new PluginKey("meowdown-link-paste");
5660
5619
  /**
5661
5620
  * The pasted text as a link `href` when the clipboard holds exactly one URL:
@@ -5697,8 +5656,7 @@ function defineLinkPaste(enabled) {
5697
5656
  })), Priority$1.high);
5698
5657
  }
5699
5658
 
5700
- //#endregion
5701
- //#region src/extensions/list.ts
5659
+ // src/extensions/list.ts
5702
5660
  function serializeListMarker(value) {
5703
5661
  return value === ")" || value === "*" || value === "+" ? value : void 0;
5704
5662
  }
@@ -6085,8 +6043,7 @@ function defineMeowdownList() {
6085
6043
  return union(defineListSpec(), defineMeowdownListPlugins(), defineListCommands(), defineMeowdownListSerializer(), defineListDropIndicator(), defineMeowdownListInputRules(), defineMeowdownListKeymap(), defineListMarkerAttr(), defineListTaskMarkerAttr(), defineListMarkerGapAttr(), defineMeowdownListCommands());
6086
6044
  }
6087
6045
 
6088
- //#endregion
6089
- //#region src/extensions/mark-mode.ts
6046
+ // src/extensions/mark-mode.ts
6090
6047
  function createMarkModePlugin(getMode) {
6091
6048
  return new Plugin({ props: {
6092
6049
  attributes: (state) => ({ "data-mark-mode": getMode(state) }),
@@ -6143,8 +6100,7 @@ function defineMarkMode(getMode) {
6143
6100
  return definePlugin(createMarkModePlugin(getMode));
6144
6101
  }
6145
6102
 
6146
- //#endregion
6147
- //#region src/utils/katex.ts
6103
+ // src/utils/katex.ts
6148
6104
  let katexRenderPromise;
6149
6105
  /**
6150
6106
  * Load KaTeX's render function on first use and cache it. Most documents
@@ -6172,8 +6128,7 @@ function renderMathInto(katexRender, element, formula, displayMode) {
6172
6128
  }
6173
6129
  }
6174
6130
 
6175
- //#endregion
6176
- //#region src/extensions/math.ts
6131
+ // src/extensions/math.ts
6177
6132
  /**
6178
6133
  * Renders one inline math unit: a KaTeX preview next to the editable
6179
6134
  * `$formula$` source. CSS decides which of the two is visible; in hide and
@@ -6244,8 +6199,7 @@ function defineMath() {
6244
6199
  });
6245
6200
  }
6246
6201
 
6247
- //#endregion
6248
- //#region src/extensions/table-column-align.ts
6202
+ // src/extensions/table-column-align.ts
6249
6203
  function parseTableColumnAlign(value) {
6250
6204
  return value === "left" || value === "center" || value === "right" ? value : null;
6251
6205
  }
@@ -6439,8 +6393,7 @@ function defineTableColumnAlign() {
6439
6393
  return union(defineTableCellAlignAttr(), defineTableHeaderCellAlignAttr(), defineTableAlignSync(), defineTableColumnAlignCommands());
6440
6394
  }
6441
6395
 
6442
- //#endregion
6443
- //#region src/extensions/table.ts
6396
+ // src/extensions/table.ts
6444
6397
  /**
6445
6398
  * Whether the selection sits inside a table cell (data or header). Useful for
6446
6399
  * gating block-creating UI, since cells hold inline content only.
@@ -6479,8 +6432,7 @@ function defineTable() {
6479
6432
  return union(defineTableSpec(), defineTableRowSpec(), defineTableCellSpec(), defineTableHeaderCellSpec(), defineTableCellContent(), defineTableColumnAlign(), defineTableEditingPlugin({ allowTableNodeSelection: true }), defineTableCommands(), defineTableDropIndicator(), defineTableKeymap());
6480
6433
  }
6481
6434
 
6482
- //#endregion
6483
- //#region src/extensions/move-block.ts
6435
+ // src/extensions/move-block.ts
6484
6436
  /**
6485
6437
  * The index of the top-level block holding the whole selection, or undefined
6486
6438
  * when the selection spans several top-level blocks or floats between them (a
@@ -6542,8 +6494,7 @@ function defineMoveBlock() {
6542
6494
  });
6543
6495
  }
6544
6496
 
6545
- //#endregion
6546
- //#region src/extensions/pending-replacement.ts
6497
+ // src/extensions/pending-replacement.ts
6547
6498
  const pendingReplacementKey = new PluginKey("meowdownPendingReplacement");
6548
6499
  /**
6549
6500
  * The active pending replacement, or null when there is none.
@@ -6734,14 +6685,12 @@ function definePendingReplacementHandler(handler) {
6734
6685
  } }) }));
6735
6686
  }
6736
6687
 
6737
- //#endregion
6738
- //#region src/extensions/readonly.ts
6688
+ // src/extensions/readonly.ts
6739
6689
  function defineReadonly(getReadonly = () => true) {
6740
6690
  return definePlugin(new Plugin({ props: { editable: (state) => !getReadonly(state) } }));
6741
6691
  }
6742
6692
 
6743
- //#endregion
6744
- //#region src/utils/caret-coords.ts
6693
+ // src/utils/caret-coords.ts
6745
6694
  /**
6746
6695
  * A dimensionless point: the fallback measurement of a hidden (font-size: 0)
6747
6696
  * syntax run. A real caret rect always has line height, and a block-context
@@ -6769,15 +6718,13 @@ function tryCoordsAtPos(view, pos, side) {
6769
6718
  return isPointRect(coords) ? void 0 : coords;
6770
6719
  }
6771
6720
 
6772
- //#endregion
6773
- //#region src/utils/line-break.ts
6721
+ // src/utils/line-break.ts
6774
6722
  function isAfterLineBreak($pos) {
6775
6723
  const { parentOffset, parent } = $pos;
6776
6724
  return parentOffset > 0 && parent.textBetween(parentOffset - 1, parentOffset) === "\n";
6777
6725
  }
6778
6726
 
6779
- //#endregion
6780
- //#region src/extensions/caret-rect.ts
6727
+ // src/extensions/caret-rect.ts
6781
6728
  function findNativeCaretRect(view) {
6782
6729
  const selection = view.dom.ownerDocument.getSelection();
6783
6730
  if (selection == null || selection.rangeCount === 0) return void 0;
@@ -6883,8 +6830,7 @@ function measureCaretScrollRect(view) {
6883
6830
  return findCoordsCaretRect(view) ?? findAtomCaretRect(view);
6884
6831
  }
6885
6832
 
6886
- //#endregion
6887
- //#region src/extensions/scroll-to-selection.ts
6833
+ // src/extensions/scroll-to-selection.ts
6888
6834
  const key$1 = new PluginKey("meowdown-scroll-to-selection");
6889
6835
  function handleScrollToSelection(view) {
6890
6836
  const selection = view.state.selection;
@@ -6986,8 +6932,7 @@ function defineScrollToSelection() {
6986
6932
  }));
6987
6933
  }
6988
6934
 
6989
- //#endregion
6990
- //#region src/extensions/select-doc-boundary.ts
6935
+ // src/extensions/select-doc-boundary.ts
6991
6936
  function selectDocBoundary(direction, extend) {
6992
6937
  return (state, dispatch) => {
6993
6938
  const boundary = direction < 0 ? Selection.atStart(state.doc) : Selection.atEnd(state.doc);
@@ -7014,8 +6959,7 @@ function defineSelectDocBoundary() {
7014
6959
  });
7015
6960
  }
7016
6961
 
7017
- //#endregion
7018
- //#region src/extensions/soft-break.ts
6962
+ // src/extensions/soft-break.ts
7019
6963
  function canHoldSoftBreak($pos) {
7020
6964
  if (!isNodeOfType($pos.parent, "paragraph")) return false;
7021
6965
  const container = $pos.depth > 0 ? $pos.node($pos.depth - 1) : void 0;
@@ -7063,8 +7007,7 @@ function defineSoftBreak() {
7063
7007
  return union(defineCommands({ insertSoftBreak }), defineKeymap({ "Shift-Enter": chainCommands(exitCodeBlockAtEnd, softBreakCommand) }));
7064
7008
  }
7065
7009
 
7066
- //#endregion
7067
- //#region src/extensions/substitution.ts
7010
+ // src/extensions/substitution.ts
7068
7011
  const SUBSTITUTION_RULES = [
7069
7012
  [/<-/, "←"],
7070
7013
  [new RegExp((canUseRegexLookbehind() ? "(?<!-)" : "") + "->"), "→"],
@@ -7152,8 +7095,7 @@ function defineSubstitution(enabled) {
7152
7095
  return union(defineSubstitutionInputRules(enabled), defineSubstitutionUndo(), defineSubstitutionEnterRules(enabled));
7153
7096
  }
7154
7097
 
7155
- //#endregion
7156
- //#region src/extensions/system-substitution-guard.ts
7098
+ // src/extensions/system-substitution-guard.ts
7157
7099
  const EM_DASH = "—";
7158
7100
  /**
7159
7101
  * Block the macOS "smart dashes" rewrite. With the `spellcheck` attribute on,
@@ -7179,8 +7121,7 @@ function defineSystemSubstitutionGuard() {
7179
7121
  return withPriority$1(definePlugin(plugin), Priority$1.highest);
7180
7122
  }
7181
7123
 
7182
- //#endregion
7183
- //#region src/extensions/view-attributes.ts
7124
+ // src/extensions/view-attributes.ts
7184
7125
  /**
7185
7126
  * Add DOM attributes to the editable root. `class` and `style` values from
7186
7127
  * every such extension are combined, so applying this more than once adds
@@ -7190,8 +7131,7 @@ function defineViewAttributes(attributes) {
7190
7131
  return definePlugin(new Plugin({ props: { attributes } }));
7191
7132
  }
7192
7133
 
7193
- //#endregion
7194
- //#region src/utils/clean-text.ts
7134
+ // src/utils/clean-text.ts
7195
7135
  /**
7196
7136
  * Projects a document slice to syntax-clean text, omitting inline markers and
7197
7137
  * hidden link destinations. Set `preserveMathSource` when the result must keep
@@ -7212,8 +7152,7 @@ function cleanTextFromSlice(slice, options = {}) {
7212
7152
  return blocks.join("\n");
7213
7153
  }
7214
7154
 
7215
- //#endregion
7216
- //#region src/extensions/wikilink-trigger.ts
7155
+ // src/extensions/wikilink-trigger.ts
7217
7156
  const WIKILINK_OPEN = "[[";
7218
7157
  /**
7219
7158
  * Inserts the `[[` wikilink trigger at the cursor and opens the wikilink menu.
@@ -7258,8 +7197,7 @@ function defineWikilinkTrigger(enabled) {
7258
7197
  });
7259
7198
  }
7260
7199
 
7261
- //#endregion
7262
- //#region src/extensions/wikilink.ts
7200
+ // src/extensions/wikilink.ts
7263
7201
  /**
7264
7202
  * Render `mdWikilink` as a non-editable label standing in for the raw source.
7265
7203
  * The source stays in `contentDOM` after the label, hidden by `style.css`
@@ -7304,8 +7242,7 @@ function defineWikilink() {
7304
7242
  });
7305
7243
  }
7306
7244
 
7307
- //#endregion
7308
- //#region src/extensions/x-post-media-click.ts
7245
+ // src/extensions/x-post-media-click.ts
7309
7246
  const xPostMediaClickKey = new PluginKey("meowdown-x-post-media-click");
7310
7247
  /**
7311
7248
  * Call `onClick` when the user activates a photo or video inside an X post
@@ -7323,8 +7260,7 @@ function defineXPostMediaClickHandler(getOnClick) {
7323
7260
  }));
7324
7261
  }
7325
7262
 
7326
- //#endregion
7327
- //#region src/extensions/youtube-video-click.ts
7263
+ // src/extensions/youtube-video-click.ts
7328
7264
  const youTubeVideoClickKey = new PluginKey("meowdown-youtube-video-click");
7329
7265
  /**
7330
7266
  * Call `onClick` when the user activates the poster of a YouTube card.
@@ -7341,8 +7277,7 @@ function defineYouTubeVideoClickHandler(getOnClick) {
7341
7277
  }));
7342
7278
  }
7343
7279
 
7344
- //#endregion
7345
- //#region src/extensions/extension.ts
7280
+ // src/extensions/extension.ts
7346
7281
  function defineEditorExtensionImpl(options) {
7347
7282
  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({
7348
7283
  placeholder: (state) => {
@@ -7351,10 +7286,17 @@ function defineEditorExtensionImpl(options) {
7351
7286
  },
7352
7287
  strategy: "doc"
7353
7288
  }), defineReadonly((state) => !!getEditorConfig(state).readOnly), defineViewAttributes((state) => {
7354
- const { editorClassName, spellCheck } = getEditorConfig(state);
7289
+ const config = getEditorConfig(state);
7355
7290
  const attributes = { class: "meowdown-content" };
7356
- if (editorClassName) attributes.class += ` ${editorClassName}`;
7357
- if (spellCheck != null) attributes.spellcheck = String(spellCheck);
7291
+ if (config.editorClassName) attributes.class += ` ${config.editorClassName}`;
7292
+ if (config.spellCheck != null) attributes.spellcheck = String(config.spellCheck);
7293
+ if (config.writingSuggestions != null) attributes.writingsuggestions = String(config.writingSuggestions);
7294
+ if (config.autoCorrect != null) attributes.autocorrect = config.autoCorrect ? "on" : "off";
7295
+ if (config.autoCapitalize != null) attributes.autocapitalize = config.autoCapitalize;
7296
+ if (config.enterKeyHint != null) attributes.enterkeyhint = config.enterKeyHint;
7297
+ if (config.dir != null) attributes.dir = config.dir;
7298
+ if (config.lang != null) attributes.lang = config.lang;
7299
+ if (config.ariaLabel != null) attributes["aria-label"] = config.ariaLabel;
7358
7300
  return attributes;
7359
7301
  }), defineCodeBlockSyntaxHighlight(), defineCrossEditorDrag(), defineClickBelow(), defineEscapeCollapse(), defineSoftBreak(), defineMoveBlock(), defineSelectDocBoundary(), defineSingleParagraph(), defineInlineMarkPlugin(getEditorConfig), defineInlineToggle(), defineLinkCommands(), defineWikilink(), defineMath(), defineMarkMode(getMarkMode), defineClipboard(), defineScrollToSelection(), defineHiddenRunCaret(), defineSystemSubstitutionGuard(), defineAtomMarkNavigation({ marks: ATOM_SOURCE_MARK_NAMES.map((name) => ({
7360
7302
  name,
@@ -7369,8 +7311,7 @@ function defineEditorExtension(options = {}) {
7369
7311
  return defineEditorExtensionImpl(options);
7370
7312
  }
7371
7313
 
7372
- //#endregion
7373
- //#region src/extensions/schema.ts
7314
+ // src/extensions/schema.ts
7374
7315
  /**
7375
7316
  * The schema shared by every parser and serializer, built once and cached.
7376
7317
  */
@@ -7414,15 +7355,17 @@ function getNodeBuildersForSchema(schema) {
7414
7355
  return builders;
7415
7356
  }
7416
7357
 
7417
- //#endregion
7418
- //#region src/converters/ast-to-pm.ts
7358
+ // src/converters/ast-to-pm.ts
7419
7359
  /**
7420
7360
  * Build nodes with the caller's schema; Markdown inline syntax remains literal text.
7421
7361
  */
7422
7362
  function astToDoc(node, nodes = getNodeBuilders()) {
7423
7363
  const children = node.children?.map((child) => astToDoc(child, nodes)) || [];
7424
7364
  switch (node.type) {
7425
- case "document": return nodes.doc(node.frontmatter == null ? {} : { frontmatter: node.frontmatter }, children);
7365
+ case "document": return nodes.doc({
7366
+ frontmatter: node.frontmatter ?? null,
7367
+ frontmatterTight: !!node.frontmatterTight
7368
+ }, children);
7426
7369
  case "paragraph": return nodes.paragraph(node.value);
7427
7370
  case "heading": return nodes.heading({
7428
7371
  level: node.level,
@@ -7454,8 +7397,7 @@ function astToDoc(node, nodes = getNodeBuilders()) {
7454
7397
  }
7455
7398
  }
7456
7399
 
7457
- //#endregion
7458
- //#region src/converters/md-to-pm.ts
7400
+ // src/converters/md-to-pm.ts
7459
7401
  /**
7460
7402
  * Convert a markdown string into a ProseMirror document node.
7461
7403
  *
@@ -7474,8 +7416,7 @@ function markdownToDoc(markdown, options = {}) {
7474
7416
  return astToDoc(parseMarkdownAst(markdown, options), options.nodes);
7475
7417
  }
7476
7418
 
7477
- //#endregion
7478
- //#region src/extensions/code-block-languages.ts
7419
+ // src/extensions/code-block-languages.ts
7479
7420
  const excludeLanguages = /* @__PURE__ */ new Set([
7480
7421
  "MscGen",
7481
7422
  "Xù",
@@ -7518,8 +7459,7 @@ const codeBlockLanguages = languages.map((language) => ({
7518
7459
  value: language.name.toLowerCase()
7519
7460
  })).filter((language) => !excludeLanguages.has(language.label)).concat(extraLanguages).sort((a, b) => a.label.localeCompare(b.label));
7520
7461
 
7521
- //#endregion
7522
- //#region src/extensions/key-bindings.ts
7462
+ // src/extensions/key-bindings.ts
7523
7463
  /**
7524
7464
  * Human-readable descriptions of the editor's formatting and heading shortcuts.
7525
7465
  */
@@ -7553,8 +7493,7 @@ const EDITOR_KEY_BINDINGS = {
7553
7493
  Escape: "Collapse the selection"
7554
7494
  };
7555
7495
 
7556
- //#endregion
7557
- //#region src/extensions/mark-hover.ts
7496
+ // src/extensions/mark-hover.ts
7558
7497
  const IDLE = { kind: "idle" };
7559
7498
  function getHit(phase) {
7560
7499
  return phase.kind === "active" || phase.kind === "leaving" ? phase.hit : void 0;
@@ -7786,8 +7725,7 @@ function defineMarkHoverHandler(config) {
7786
7725
  }));
7787
7726
  }
7788
7727
 
7789
- //#endregion
7790
- //#region src/extensions/link-hover.ts
7728
+ // src/extensions/link-hover.ts
7791
7729
  const linkHoverKey = new PluginKey("meowdown-link-hover");
7792
7730
  /**
7793
7731
  * Delay before a cold hover enters, in ms.
@@ -7831,14 +7769,12 @@ function dismissLinkHover(state) {
7831
7769
  linkHoverKey.getState(state)?.dismiss();
7832
7770
  }
7833
7771
 
7834
- //#endregion
7835
- //#region src/utils/force-reflow.ts
7772
+ // src/utils/force-reflow.ts
7836
7773
  function forceReflow(element) {
7837
7774
  element.offsetWidth;
7838
7775
  }
7839
7776
 
7840
- //#endregion
7841
- //#region src/extensions/virtual-caret.ts
7777
+ // src/extensions/virtual-caret.ts
7842
7778
  const key = new PluginKey("meowdown-virtual-caret");
7843
7779
  const BLINK_ANIMATIONS = ["md-virtual-caret-blink", "md-virtual-caret-blink2"];
7844
7780
  const DATA_ATTRIBUTE = "data-meowdown-virtual-caret";
@@ -8003,8 +7939,7 @@ function defineVirtualCaret(layer) {
8003
7939
  }));
8004
7940
  }
8005
7941
 
8006
- //#endregion
8007
- //#region src/extensions/wikilink-hover.ts
7942
+ // src/extensions/wikilink-hover.ts
8008
7943
  const wikilinkHoverKey = new PluginKey("meowdown-wikilink-hover");
8009
7944
  /**
8010
7945
  * Delay before a cold hover enters, in ms.
@@ -8044,8 +7979,7 @@ function defineWikilinkHoverHandler(onHoverChange, openDelay = OPEN_DELAY, close
8044
7979
  });
8045
7980
  }
8046
7981
 
8047
- //#endregion
8048
- //#region src/utils/display-text.ts
7982
+ // src/utils/display-text.ts
8049
7983
  function getAtomDisplayText(atom) {
8050
7984
  switch (atom.type.name) {
8051
7985
  case "mdWikilink": {
@@ -8074,8 +8008,7 @@ function getTextblockDisplayText(textblock) {
8074
8008
  return output;
8075
8009
  }
8076
8010
 
8077
- //#endregion
8078
- //#region src/utils/selected-text.ts
8011
+ // src/utils/selected-text.ts
8079
8012
  /**
8080
8013
  * The current selection as Markdown: block structure (list markers, headings,
8081
8014
  * blockquotes) is serialized, and inline Markdown syntax is already literal
@@ -8095,8 +8028,7 @@ function getSelectedText(state) {
8095
8028
  }
8096
8029
  }
8097
8030
 
8098
- //#endregion
8099
- //#region src/utils/virtual-element.ts
8031
+ // src/utils/virtual-element.ts
8100
8032
  function tryHiddenRunCoords(view, pos, side) {
8101
8033
  if (side === 1) {
8102
8034
  const run = getHiddenRunAfter(view.state, pos);
@@ -8135,5 +8067,4 @@ function getVirtualElementFromRange(view, range) {
8135
8067
  };
8136
8068
  }
8137
8069
 
8138
- //#endregion
8139
8070
  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 };