@meowdown/core 0.64.0 → 0.64.2

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +340 -117
  2. package/dist/index.js +577 -342
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -83,6 +83,31 @@ function hasPointerSelectionTransaction(transactions) {
83
83
  return transactions.some(isPointerSelectionTransaction);
84
84
  }
85
85
 
86
+ //#endregion
87
+ //#region src/extensions/mark-names.ts
88
+ function isMarkOfType(mark, name) {
89
+ return mark.type.name === name;
90
+ }
91
+ function isMarkOfTypes(mark, names) {
92
+ return names.includes(mark.type.name);
93
+ }
94
+ const SYNTAX_MARK_NAMES = [
95
+ "mdMark",
96
+ "mdLinkUri",
97
+ "mdLinkTitle"
98
+ ];
99
+ const ATOM_MARK_NAMES = [
100
+ "mdWikilink",
101
+ "mdImage",
102
+ "mdFile",
103
+ "mdMath"
104
+ ];
105
+ const ATOM_SOURCE_MARK_NAMES = [
106
+ "mdImage",
107
+ "mdWikilink",
108
+ "mdFile"
109
+ ];
110
+
86
111
  //#endregion
87
112
  //#region src/extensions/mark-mode.ts
88
113
  const markModeKey = new PluginKey("mark-mode");
@@ -101,9 +126,8 @@ function createMarkModePlugin(initialMode) {
101
126
  return { "data-mark-mode": getCurrentMarkMode(state) ?? initialMode };
102
127
  },
103
128
  decorations: (state) => {
104
- const mode = getCurrentMarkMode(state);
105
- if (mode === "focus") return computeFocusDecorations(state);
106
- if (mode === "hide") return computeMathRevealDecorations(state);
129
+ const mode = getCurrentMarkMode(state) ?? initialMode;
130
+ return mode === "focus" || mode === "hide" ? computeRevealDecorations(state, mode) : void 0;
107
131
  }
108
132
  }
109
133
  });
@@ -115,9 +139,6 @@ function setMarkMode(mode) {
115
139
  return true;
116
140
  };
117
141
  }
118
- function defineMarkMode(mode) {
119
- return union(definePlugin(createMarkModePlugin(mode)), defineCommands({ setMarkMode }));
120
- }
121
142
  /**
122
143
  * The active mark mode. `defineEditorExtension` always applies
123
144
  * `defineMarkMode`, so this is `undefined` only for a state built without it.
@@ -125,38 +146,190 @@ function defineMarkMode(mode) {
125
146
  function getMarkMode(state) {
126
147
  return markModeKey.getState(state);
127
148
  }
149
+ function findRevealablePack($pos, mode, direction) {
150
+ const { parent } = $pos;
151
+ if (!parent.isTextblock || parent.type.spec.code) return;
152
+ const node = direction === -1 ? $pos.nodeBefore : $pos.nodeAfter;
153
+ for (const mark of node?.marks ?? []) if (isMarkOfType(mark, "mdPack")) {
154
+ const attrs = mark.attrs;
155
+ if (mode === "focus" && attrs.revealInFocus || mode === "hide" && attrs.revealInHide) return mark;
156
+ }
157
+ }
128
158
  /**
129
- * In focus mode, reveal the markdown syntax of the inline unit under the caret.
159
+ * Reveal the markdown syntax of the inline units the selection touches: the
160
+ * unit before its start and the unit after its end, which for a caret are
161
+ * the two units meeting at it.
130
162
  *
131
- * Every revealable unit (emphasis, strong, code, strikethrough, link, autolink,
132
- * image) carries one `mdPack` mark spanning it, so a single boundary-inclusive
133
- * `getMarkRange` finds the unit, returning the outermost when units nest. One
134
- * decoration over its range flips the hidden punctuation/url/source visible via
135
- * the `.show` CSS rule. Because the range covers the whole unit, a caret at
136
- * either edge (e.g. right after a link's `)`) still reveals it. Wikilink and
137
- * `#tag` carry no `mdPack`, so they never reveal.
163
+ * Every unit carries one `mdPack` mark spanning it, so one `getMarkRange`
164
+ * per probed pack expands it to its whole unit, and one decoration over
165
+ * each range flips the hidden punctuation/url/source visible via the `.show`
166
+ * CSS rule. The two probes land on the same unit when the selection sits
167
+ * inside one (equal ranges dedupe to one decoration), and on two units at a
168
+ * shared boundary, so the characters an edit would touch are never hidden.
169
+ * `#tag` carries no pack and never reveals.
170
+ */
171
+ function computeRevealDecorations(state, mode) {
172
+ const { $from, $to } = state.selection;
173
+ const packBefore = findRevealablePack($from, mode, -1);
174
+ const packAfter = findRevealablePack($to, mode, 1);
175
+ const ranges = [];
176
+ for (const [$pos, pack] of [[$from, packBefore], [$to, packAfter]]) {
177
+ if (!pack) continue;
178
+ const range = getMarkRange($pos, "mdPack", pack.attrs);
179
+ if (!range) continue;
180
+ if (ranges.some((r) => r.from === range.from && r.to === range.to)) continue;
181
+ ranges.push({
182
+ from: range.from,
183
+ to: range.to
184
+ });
185
+ }
186
+ if (ranges.length === 0) return;
187
+ const decorations = ranges.map((range) => Decoration.inline(range.from, range.to, { class: "show" }));
188
+ return DecorationSet.create(state.doc, decorations);
189
+ }
190
+ function defineMarkMode(mode) {
191
+ return union(definePlugin(createMarkModePlugin(mode)), defineCommands({ setMarkMode }));
192
+ }
193
+
194
+ //#endregion
195
+ //#region src/extensions/hidden-run.ts
196
+ function getCharMarks(state, pos) {
197
+ if (pos < 0 || pos + 1 > state.doc.content.size) return;
198
+ const $pos = state.doc.resolve(pos);
199
+ const child = $pos.parent.maybeChild($pos.index());
200
+ if (child == null || !child.isText) return;
201
+ return child.marks;
202
+ }
203
+ function isHiddenChar(state, pos) {
204
+ const marks = getCharMarks(state, pos);
205
+ if (marks == null) return false;
206
+ return marks.some((mark) => isMarkOfTypes(mark, SYNTAX_MARK_NAMES));
207
+ }
208
+ function isInsideNonCodeTextblock(state, pos) {
209
+ if (pos < 0 || pos > state.doc.content.size) return false;
210
+ const $pos = state.doc.resolve(pos);
211
+ return $pos.parent.isTextblock && !$pos.parent.type.spec.code;
212
+ }
213
+ /**
214
+ * The maximal contiguous hidden run ending exactly at `pos`, or undefined.
138
215
  */
139
- function computeFocusDecorations(state) {
140
- return computeRevealDecorations(state, void 0);
216
+ function getHiddenRunBefore(state, pos) {
217
+ if (!isInsideNonCodeTextblock(state, pos)) return;
218
+ const blockStart = state.doc.resolve(pos).start();
219
+ let from = pos;
220
+ while (from > blockStart && isHiddenChar(state, from - 1)) from--;
221
+ return from < pos ? {
222
+ from,
223
+ to: pos
224
+ } : void 0;
141
225
  }
142
226
  /**
143
- * In hide mode, reveal only the math unit under the caret. A math unit hides
144
- * its whole source (content included), so it is the one construct that must
145
- * still reveal in hide mode to stay editable; everything else follows the
146
- * hide-mode contract and never reveals.
227
+ * The maximal contiguous hidden run starting exactly at `pos`, or undefined.
147
228
  */
148
- function computeMathRevealDecorations(state) {
149
- return computeRevealDecorations(state, { key: "math" });
229
+ function getHiddenRunAfter(state, pos) {
230
+ if (!isInsideNonCodeTextblock(state, pos)) return;
231
+ const blockEnd = state.doc.resolve(pos).end();
232
+ let to = pos;
233
+ while (to < blockEnd && isHiddenChar(state, to)) to++;
234
+ return to > pos ? {
235
+ from: pos,
236
+ to
237
+ } : void 0;
150
238
  }
151
- function computeRevealDecorations(state, packAttrs) {
152
- const { selection } = state;
153
- if (!selection.empty) return DecorationSet.empty;
154
- const $pos = selection.$head;
155
- const { parent } = $pos;
156
- if (!parent.isTextblock || parent.type.spec.code) return DecorationSet.empty;
157
- const range = getMarkRange($pos, getMarkType(state.schema, "mdPack"), packAttrs);
158
- if (!range) return DecorationSet.empty;
159
- return DecorationSet.create(state.doc, [Decoration.inline(range.from, range.to, { class: "show" })]);
239
+ function isHiddenRunInterior(state, pos) {
240
+ return isHiddenChar(state, pos - 1) && isHiddenChar(state, pos);
241
+ }
242
+ /**
243
+ * The full run around an interior position, or undefined for rest positions.
244
+ */
245
+ function getHiddenRunAround(state, pos) {
246
+ if (!isHiddenRunInterior(state, pos)) return;
247
+ const before = getHiddenRunBefore(state, pos);
248
+ if (!before) return;
249
+ const after = getHiddenRunAfter(state, pos);
250
+ if (!after) return;
251
+ return {
252
+ from: before.from,
253
+ to: after.to
254
+ };
255
+ }
256
+ function charHasMark(state, pos, mark) {
257
+ const marks = getCharMarks(state, pos);
258
+ return marks != null && mark.isInSet(marks);
259
+ }
260
+ function getInnermostPackRangeAt(state, charPos) {
261
+ const marks = getCharMarks(state, charPos);
262
+ if (marks == null) return;
263
+ const packType = getMarkType(state.schema, "mdPack");
264
+ const packs = marks.filter((mark) => mark.type === packType);
265
+ if (packs.length === 0) return;
266
+ const $pos = state.doc.resolve(charPos);
267
+ const blockStart = $pos.start();
268
+ const blockEnd = $pos.end();
269
+ let innermost;
270
+ for (const pack of packs) {
271
+ let from = charPos;
272
+ while (from > blockStart && charHasMark(state, from - 1, pack)) from--;
273
+ let to = charPos + 1;
274
+ while (to < blockEnd && charHasMark(state, to, pack)) to++;
275
+ if (innermost == null || to - from < innermost.to - innermost.from) innermost = {
276
+ from,
277
+ to
278
+ };
279
+ }
280
+ return innermost;
281
+ }
282
+ function isPackOuterEdge(state, run, edge) {
283
+ const pack = getInnermostPackRangeAt(state, edge === "from" ? run.from : run.to - 1);
284
+ if (pack == null) return false;
285
+ return edge === "from" ? pack.from === run.from : pack.to === run.to;
286
+ }
287
+ function getPointerEdge(state, run, pos) {
288
+ const fromIsOuter = isPackOuterEdge(state, run, "from");
289
+ const toIsOuter = isPackOuterEdge(state, run, "to");
290
+ if (fromIsOuter && !toIsOuter) return run.from;
291
+ if (toIsOuter && !fromIsOuter) return run.to;
292
+ return pos - run.from <= run.to - pos ? run.from : run.to;
293
+ }
294
+ /**
295
+ * The rest position for a caret that landed at `newPos`. `oldPos` supplies the
296
+ * travel direction for keyboard motion; `isPointer` selects the click rules.
297
+ */
298
+ function getRestPosition(state, oldPos, newPos, isPointer) {
299
+ if (!isInsideNonCodeTextblock(state, newPos)) return newPos;
300
+ const run = getHiddenRunAround(state, newPos);
301
+ if (run != null) {
302
+ if (!isPointer) return newPos >= oldPos ? run.to : run.from;
303
+ return getPointerEdge(state, run, newPos);
304
+ }
305
+ if (!isPointer) return newPos;
306
+ const runBefore = getHiddenRunBefore(state, newPos);
307
+ if (runBefore != null && isPackOuterEdge(state, runBefore, "from")) return runBefore.from;
308
+ const runAfter = getHiddenRunAfter(state, newPos);
309
+ if (runAfter != null && isPackOuterEdge(state, runAfter, "to")) return runAfter.to;
310
+ return newPos;
311
+ }
312
+ function getCaretTail(state, pos) {
313
+ if (!isInsideNonCodeTextblock(state, pos)) return;
314
+ const hiddenBefore = isHiddenChar(state, pos - 1);
315
+ const hiddenAfter = isHiddenChar(state, pos);
316
+ if (hiddenBefore === hiddenAfter) return;
317
+ return hiddenAfter ? "left" : "right";
318
+ }
319
+ /**
320
+ * The leading and trailing hidden runs of the innermost unit whose marker
321
+ * character sits at `charPos`, trailing first so callers can delete them in
322
+ * order without remapping. A fully hidden unit yields one run.
323
+ */
324
+ function getUnitMarkerRuns(state, charPos) {
325
+ const pack = getInnermostPackRangeAt(state, charPos);
326
+ if (pack == null) return [];
327
+ const leading = getHiddenRunAfter(state, pack.from);
328
+ const trailing = getHiddenRunBefore(state, pack.to);
329
+ const runs = [];
330
+ if (trailing != null) runs.push(trailing);
331
+ if (leading != null && (trailing == null || leading.from !== trailing.from)) runs.push(leading);
332
+ return runs;
160
333
  }
161
334
 
162
335
  //#endregion
@@ -184,11 +357,33 @@ function getMarkRangeAt(state, pos, markName, attrs) {
184
357
  if (!$pos) return;
185
358
  const markNames = Array.isArray(markName) ? markName : [markName];
186
359
  for (const name of markNames) {
187
- const range = getMarkRange($pos, name, attrs);
360
+ const range = ATOM_MARK_NAMES.includes(name) ? getAtomUnitRange(state, $pos, name) : getMarkRange($pos, name, attrs);
188
361
  if (range) return range;
189
362
  }
190
363
  }
191
364
  /**
365
+ * The range of the atom unit touching `$pos`, preferring the child to the
366
+ * right like `getMarkRange`. Two identical adjacent units carry equal atom
367
+ * marks, so eq-based expansion would run across both; the unit boundary comes
368
+ * from the innermost pack instead, which `slot` keeps distinct. A text node
369
+ * without a pack (a document not produced by the inline parser) falls back to
370
+ * the eq-based range. Does not support `attrs` filtering.
371
+ */
372
+ function getAtomUnitRange(state, $pos, name) {
373
+ const parent = $pos.parent;
374
+ const after = parent.childAfter($pos.parentOffset);
375
+ const matched = after.node && after.node.marks.some((mark) => isMarkOfType(mark, name)) ? after : parent.childBefore($pos.parentOffset);
376
+ const mark = matched.node?.marks.find((candidate) => isMarkOfType(candidate, name));
377
+ if (!matched.node || mark == null) return;
378
+ const pack = getInnermostPackRangeAt(state, $pos.start() + matched.offset);
379
+ if (!pack) return getMarkRange($pos, name);
380
+ return {
381
+ from: pack.from,
382
+ to: pack.to,
383
+ mark
384
+ };
385
+ }
386
+ /**
192
387
  * Returns the run ending exactly at `pos`, the one immediately to its left.
193
388
  * Probes from inside the left neighbour (`pos - 1`): probing `pos` itself
194
389
  * cannot see that run when another run starts exactly there, because
@@ -228,15 +423,6 @@ function getMarkRangeStrictlyAround(state, pos, markNames) {
228
423
 
229
424
  //#endregion
230
425
  //#region src/extensions/atom-mark-navigation.ts
231
- /**
232
- * The source marks whose mark views hide the raw text behind a rendered
233
- * preview (`.md-atom-view-preview`) and act as one caret stop.
234
- */
235
- const ATOM_SOURCE_MARK_NAMES = [
236
- "mdImage",
237
- "mdWikilink",
238
- "mdFile"
239
- ];
240
426
  function getActiveMarkNames(marks, state) {
241
427
  const mode = getMarkMode(state);
242
428
  if (!mode) return [];
@@ -256,7 +442,7 @@ function getSelectedRange(state, markNames) {
256
442
  */
257
443
  function getSelectedAtomRange(state) {
258
444
  if (!getMarkMode(state)) return;
259
- return getSelectedRange(state, [...ATOM_SOURCE_MARK_NAMES]);
445
+ return getSelectedRange(state, ATOM_SOURCE_MARK_NAMES);
260
446
  }
261
447
  function findSelectionAcrossBlockBoundary(state, pos, markNames, direction) {
262
448
  const $pos = state.doc.resolve(pos);
@@ -470,7 +656,9 @@ const highlightToMarkdown = (node, _parent, state, info) => {
470
656
  function isCheckboxInput(node) {
471
657
  return node.tagName === "input" && node.properties.type === "checkbox";
472
658
  }
473
- /** The first checkbox `<input>` before any nested list, if any. */
659
+ /**
660
+ * The first checkbox `<input>` before any nested list, if any.
661
+ */
474
662
  function findCheckbox(node) {
475
663
  for (const child of node.children) {
476
664
  if (child.type !== "element") continue;
@@ -518,7 +706,9 @@ function normalizeTaskItem(node) {
518
706
  children: [input, ...content]
519
707
  };
520
708
  }
521
- /** `li` handler that recognizes ProseMirror-style task items, then delegates. */
709
+ /**
710
+ * `li` handler that recognizes ProseMirror-style task items, then delegates.
711
+ */
522
712
  const taskAwareListItem = (state, element) => {
523
713
  return defaultHandlers.li(state, normalizeTaskItem(element) ?? element);
524
714
  };
@@ -567,7 +757,9 @@ function createProcessor() {
567
757
  }).freeze();
568
758
  }
569
759
  const getProcessor = once(createProcessor);
570
- /** Convert HTML into Markdown text. */
760
+ /**
761
+ * Convert HTML into Markdown text.
762
+ */
571
763
  function htmlToMarkdown(html) {
572
764
  return String(getProcessor().processSync(html));
573
765
  }
@@ -584,43 +776,36 @@ function parsePositiveInteger(raw) {
584
776
  return value != null && value > 0 ? value : void 0;
585
777
  }
586
778
 
587
- //#endregion
588
- //#region src/extensions/mark-names.ts
589
- function isMarkOfType(mark, name) {
590
- return mark.type.name === name;
591
- }
592
- const SYNTAX_MARK_NAMES = /* @__PURE__ */ new Set([
593
- "mdMark",
594
- "mdLinkUri",
595
- "mdLinkTitle"
596
- ]);
597
- const ATOM_MARK_NAMES = /* @__PURE__ */ new Set([
598
- "mdWikilink",
599
- "mdImage",
600
- "mdFile",
601
- "mdMath"
602
- ]);
603
-
604
779
  //#endregion
605
780
  //#region src/extensions/inline-runs.ts
606
781
  function findAtomMark(marks) {
607
- return marks.find((mark) => ATOM_MARK_NAMES.has(mark.type.name));
782
+ return marks.find((mark) => isMarkOfTypes(mark, ATOM_MARK_NAMES));
783
+ }
784
+ function findOwnPackMark(marks) {
785
+ return marks.findLast((mark) => isMarkOfType(mark, "mdPack"));
608
786
  }
609
787
  function hasSyntaxMark(marks) {
610
- return marks.some((mark) => SYNTAX_MARK_NAMES.has(mark.type.name));
788
+ return marks.some((mark) => isMarkOfTypes(mark, SYNTAX_MARK_NAMES));
611
789
  }
612
790
  /**
613
791
  * Group a textblock's text nodes into atom units and plain runs. A unit's
614
- * text nodes share one mark instance (the inline parser creates each unit
615
- * mark once), so instance identity splits adjacent same-attrs units.
792
+ * text nodes carry equal packs, while an identical neighbour's pack differs
793
+ * by `slot`, so pack equality splits adjacent same-attrs units.
616
794
  */
617
795
  function groupInlineRuns(textblock) {
618
796
  const runs = [];
797
+ let previousPack;
619
798
  textblock.forEach((child) => {
620
- if (!child.isText || !child.text) return;
799
+ if (!child.isText || !child.text) {
800
+ previousPack = void 0;
801
+ return;
802
+ }
621
803
  const atom = findAtomMark(child.marks);
804
+ const pack = findOwnPackMark(child.marks);
622
805
  const last = runs.at(-1);
623
- if (atom != null && last != null && last.atom === atom) {
806
+ const continuesUnit = atom != null && last?.atom != null && pack != null && previousPack != null && pack.eq(previousPack);
807
+ previousPack = pack;
808
+ if (continuesUnit && last != null) {
624
809
  last.text += child.text;
625
810
  last.children.push(child);
626
811
  return;
@@ -702,7 +887,9 @@ function syncOpenWrappers(open, next, out) {
702
887
  });
703
888
  }
704
889
  }
705
- /** A soft break (a literal `\n` in the source) renders as `<br>`. */
890
+ /**
891
+ * A soft break (a literal `\n` in the source) renders as `<br>`.
892
+ */
706
893
  function appendTextWithBreaks(parent, text) {
707
894
  const lines = text.split("\n");
708
895
  for (const [index, line] of lines.entries()) {
@@ -758,7 +945,9 @@ function defineHeadingWhitespace() {
758
945
  whitespace: "pre"
759
946
  });
760
947
  }
761
- /** The clipboard DOM of a heading: semantic inline content plus `data-md`. */
948
+ /**
949
+ * The clipboard DOM of a heading: semantic inline content plus `data-md`.
950
+ */
762
951
  function headingClipboardDOM(node) {
763
952
  const attrs = node.attrs;
764
953
  return semanticTextblockDOM(`h${attrs.level}`, node, {
@@ -766,7 +955,9 @@ function headingClipboardDOM(node) {
766
955
  "data-closing-hashes": attrs.closingHashes != null ? String(attrs.closingHashes) : void 0
767
956
  });
768
957
  }
769
- /** The clipboard parse rules restoring a heading's source text from `data-md`. */
958
+ /**
959
+ * The clipboard parse rules restoring a heading's source text from `data-md`.
960
+ */
770
961
  function headingFromDOM() {
771
962
  return [
772
963
  1,
@@ -839,11 +1030,15 @@ function defineMeowdownParagraphSpec() {
839
1030
  }
840
1031
  });
841
1032
  }
842
- /** The clipboard DOM of a paragraph: semantic inline content plus `data-md`. */
1033
+ /**
1034
+ * The clipboard DOM of a paragraph: semantic inline content plus `data-md`.
1035
+ */
843
1036
  function paragraphClipboardDOM(node) {
844
1037
  return semanticTextblockDOM("p", node);
845
1038
  }
846
- /** The clipboard parse rules restoring a paragraph's source text from `data-md`. */
1039
+ /**
1040
+ * The clipboard parse rules restoring a paragraph's source text from `data-md`.
1041
+ */
847
1042
  function paragraphFromDOM() {
848
1043
  return [createSourceTextRule("p", "paragraph")];
849
1044
  }
@@ -968,7 +1163,9 @@ function appendText(output, text) {
968
1163
  output.trailingNewlines = text.length - index;
969
1164
  }
970
1165
  }
971
- /** Recursive worker of {@link extractStyledPlainText}. */
1166
+ /**
1167
+ * Recursive worker of {@link extractStyledPlainText}.
1168
+ */
972
1169
  function appendNodeText(node, output, insideLine) {
973
1170
  if (node.nodeType === Node.TEXT_NODE) {
974
1171
  appendText(output, node.nodeValue ?? "");
@@ -1103,7 +1300,9 @@ function definePlainTextPaste() {
1103
1300
 
1104
1301
  //#endregion
1105
1302
  //#region src/utils/backticks.ts
1106
- /** Length of the longest run of `charCode` in `text`, at least `min`. */
1303
+ /**
1304
+ * Length of the longest run of `charCode` in `text`, at least `min`.
1305
+ */
1107
1306
  function longestCharRun(text, charCode, min = 0) {
1108
1307
  let longest = min;
1109
1308
  let run = 0;
@@ -1113,7 +1312,9 @@ function longestCharRun(text, charCode, min = 0) {
1113
1312
  } else run = 0;
1114
1313
  return longest;
1115
1314
  }
1116
- /** Length of the longest run of backticks in `text`, at least `min`. */
1315
+ /**
1316
+ * Length of the longest run of backticks in `text`, at least `min`.
1317
+ */
1117
1318
  function longestBacktickRun(text, min = 0) {
1118
1319
  return longestCharRun(text, 96, min);
1119
1320
  }
@@ -1157,7 +1358,9 @@ function emitFrontmatter(body, out) {
1157
1358
  out.write("---");
1158
1359
  out.closeBlock();
1159
1360
  }
1160
- /** Heading prefixes indexed by level (1..6). Index 0 is a sentinel. */
1361
+ /**
1362
+ * Heading prefixes indexed by level (1..6). Index 0 is a sentinel.
1363
+ */
1161
1364
  const HEADING_PREFIX = [
1162
1365
  "",
1163
1366
  "# ",
@@ -1227,7 +1430,9 @@ var MdOut = class {
1227
1430
  this.emitDeferredBlankLine();
1228
1431
  this.deferredBlankPrefix = this.linePrefix;
1229
1432
  }
1230
- /** End the current block; the next write gets a blank line before it. */
1433
+ /**
1434
+ * End the current block; the next write gets a blank line before it.
1435
+ */
1231
1436
  closeBlock() {
1232
1437
  if (this.atLineStart && this.pendingFirst !== null) {
1233
1438
  this.emitDeferredBlankLine();
@@ -1449,7 +1654,9 @@ function toIndentedCode(code) {
1449
1654
  for (let i = 0; i < lines.length; i++) if (lines[i] !== "") lines[i] = ` ${lines[i]}`;
1450
1655
  return lines.join("\n");
1451
1656
  }
1452
- /** Whether any content line would read as a closing `$$` fence. */
1657
+ /**
1658
+ * Whether any content line would read as a closing `$$` fence.
1659
+ */
1453
1660
  function hasDollarFenceLine(code) {
1454
1661
  return code.split("\n").some((line) => line.trim() === "$$");
1455
1662
  }
@@ -1614,7 +1821,9 @@ function definePlainTextSerializer() {
1614
1821
  } }
1615
1822
  }));
1616
1823
  }
1617
- /** Drop the inline text a hide-mode editor never shows. */
1824
+ /**
1825
+ * Drop the inline text a hide-mode editor never shows.
1826
+ */
1618
1827
  function stripHiddenInline(slice) {
1619
1828
  return new Slice(mapFragment(slice.content), slice.openStart, slice.openEnd);
1620
1829
  }
@@ -1999,141 +2208,6 @@ function executeCommand(view, command) {
1999
2208
  return command(view.state, view.dispatch, view);
2000
2209
  }
2001
2210
 
2002
- //#endregion
2003
- //#region src/extensions/hidden-run.ts
2004
- function getCharMarks(state, pos) {
2005
- if (pos < 0 || pos + 1 > state.doc.content.size) return;
2006
- const $pos = state.doc.resolve(pos);
2007
- const child = $pos.parent.maybeChild($pos.index());
2008
- if (child == null || !child.isText) return;
2009
- return child.marks;
2010
- }
2011
- function isHiddenChar(state, pos) {
2012
- const marks = getCharMarks(state, pos);
2013
- if (marks == null) return false;
2014
- return marks.some((mark) => SYNTAX_MARK_NAMES.has(mark.type.name));
2015
- }
2016
- function isInsideNonCodeTextblock(state, pos) {
2017
- if (pos < 0 || pos > state.doc.content.size) return false;
2018
- const $pos = state.doc.resolve(pos);
2019
- return $pos.parent.isTextblock && !$pos.parent.type.spec.code;
2020
- }
2021
- /** The maximal contiguous hidden run ending exactly at `pos`, or undefined. */
2022
- function getHiddenRunBefore(state, pos) {
2023
- if (!isInsideNonCodeTextblock(state, pos)) return;
2024
- const blockStart = state.doc.resolve(pos).start();
2025
- let from = pos;
2026
- while (from > blockStart && isHiddenChar(state, from - 1)) from--;
2027
- return from < pos ? {
2028
- from,
2029
- to: pos
2030
- } : void 0;
2031
- }
2032
- /** The maximal contiguous hidden run starting exactly at `pos`, or undefined. */
2033
- function getHiddenRunAfter(state, pos) {
2034
- if (!isInsideNonCodeTextblock(state, pos)) return;
2035
- const blockEnd = state.doc.resolve(pos).end();
2036
- let to = pos;
2037
- while (to < blockEnd && isHiddenChar(state, to)) to++;
2038
- return to > pos ? {
2039
- from: pos,
2040
- to
2041
- } : void 0;
2042
- }
2043
- function isHiddenRunInterior(state, pos) {
2044
- return isHiddenChar(state, pos - 1) && isHiddenChar(state, pos);
2045
- }
2046
- /** The full run around an interior position, or undefined for rest positions. */
2047
- function getHiddenRunAround(state, pos) {
2048
- if (!isHiddenRunInterior(state, pos)) return;
2049
- const before = getHiddenRunBefore(state, pos);
2050
- if (!before) return;
2051
- const after = getHiddenRunAfter(state, pos);
2052
- if (!after) return;
2053
- return {
2054
- from: before.from,
2055
- to: after.to
2056
- };
2057
- }
2058
- function charHasMark(state, pos, mark) {
2059
- const marks = getCharMarks(state, pos);
2060
- return marks != null && mark.isInSet(marks);
2061
- }
2062
- function getInnermostPackRangeAt(state, charPos) {
2063
- const marks = getCharMarks(state, charPos);
2064
- if (marks == null) return;
2065
- const packType = getMarkType(state.schema, "mdPack");
2066
- const packs = marks.filter((mark) => mark.type === packType);
2067
- if (packs.length === 0) return;
2068
- const $pos = state.doc.resolve(charPos);
2069
- const blockStart = $pos.start();
2070
- const blockEnd = $pos.end();
2071
- let innermost;
2072
- for (const pack of packs) {
2073
- let from = charPos;
2074
- while (from > blockStart && charHasMark(state, from - 1, pack)) from--;
2075
- let to = charPos + 1;
2076
- while (to < blockEnd && charHasMark(state, to, pack)) to++;
2077
- if (innermost == null || to - from < innermost.to - innermost.from) innermost = {
2078
- from,
2079
- to
2080
- };
2081
- }
2082
- return innermost;
2083
- }
2084
- function isPackOuterEdge(state, run, edge) {
2085
- const pack = getInnermostPackRangeAt(state, edge === "from" ? run.from : run.to - 1);
2086
- if (pack == null) return false;
2087
- return edge === "from" ? pack.from === run.from : pack.to === run.to;
2088
- }
2089
- function getPointerEdge(state, run, pos) {
2090
- const fromIsOuter = isPackOuterEdge(state, run, "from");
2091
- const toIsOuter = isPackOuterEdge(state, run, "to");
2092
- if (fromIsOuter && !toIsOuter) return run.from;
2093
- if (toIsOuter && !fromIsOuter) return run.to;
2094
- return pos - run.from <= run.to - pos ? run.from : run.to;
2095
- }
2096
- /**
2097
- * The rest position for a caret that landed at `newPos`. `oldPos` supplies the
2098
- * travel direction for keyboard motion; `isPointer` selects the click rules.
2099
- */
2100
- function getRestPosition(state, oldPos, newPos, isPointer) {
2101
- if (!isInsideNonCodeTextblock(state, newPos)) return newPos;
2102
- const run = getHiddenRunAround(state, newPos);
2103
- if (run != null) {
2104
- if (!isPointer) return newPos >= oldPos ? run.to : run.from;
2105
- return getPointerEdge(state, run, newPos);
2106
- }
2107
- if (!isPointer) return newPos;
2108
- const runBefore = getHiddenRunBefore(state, newPos);
2109
- if (runBefore != null && isPackOuterEdge(state, runBefore, "from")) return runBefore.from;
2110
- const runAfter = getHiddenRunAfter(state, newPos);
2111
- if (runAfter != null && isPackOuterEdge(state, runAfter, "to")) return runAfter.to;
2112
- return newPos;
2113
- }
2114
- function getCaretTail(state, pos) {
2115
- if (!isInsideNonCodeTextblock(state, pos)) return;
2116
- const hiddenBefore = isHiddenChar(state, pos - 1);
2117
- const hiddenAfter = isHiddenChar(state, pos);
2118
- if (hiddenBefore === hiddenAfter) return;
2119
- return hiddenAfter ? "left" : "right";
2120
- }
2121
- /**
2122
- * The leading and trailing hidden runs of the innermost unit whose marker
2123
- * character sits at `charPos`, trailing first so callers can delete them in
2124
- * order without remapping. A fully hidden unit yields one run.
2125
- */
2126
- function getUnitMarkerRuns(state, charPos) {
2127
- const pack = getInnermostPackRangeAt(state, charPos);
2128
- if (pack == null) return [];
2129
- const leading = getHiddenRunAfter(state, pack.from);
2130
- const trailing = getHiddenRunBefore(state, pack.to);
2131
- const runs = [];
2132
- if (trailing != null) runs.push(trailing);
2133
- if (leading != null && (trailing == null || leading.from !== trailing.from)) runs.push(leading);
2134
- return runs;
2135
- }
2136
-
2137
2211
  //#endregion
2138
2212
  //#region src/extensions/hidden-run-caret.ts
2139
2213
  const snapKey = new PluginKey("meowdown-hidden-run-snap");
@@ -2494,11 +2568,15 @@ function parseMagicComment(comment) {
2494
2568
  function toPositiveNumber(value) {
2495
2569
  if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.round(value);
2496
2570
  }
2497
- /** The canonical comment meowdown writes for the metadata. */
2571
+ /**
2572
+ * The canonical comment meowdown writes for the metadata.
2573
+ */
2498
2574
  function formatMagicComment(magic) {
2499
2575
  return `<!-- ${JSON.stringify(magic)} -->`;
2500
2576
  }
2501
- /** Drop a trailing magic comment from the source text. */
2577
+ /**
2578
+ * Drop a trailing magic comment from the source text.
2579
+ */
2502
2580
  function stripMagicComment(source) {
2503
2581
  return source.replace(TRAILING_MAGIC_COMMENT_RE, "");
2504
2582
  }
@@ -2619,7 +2697,9 @@ function positiveInteger(value) {
2619
2697
  const parsed = Number.parseInt(value, 10);
2620
2698
  return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
2621
2699
  }
2622
- /** Parse `![[target]]`, `![[target|alias]]`, `![[target|width]]`, or `![[target|widthxheight]]`. */
2700
+ /**
2701
+ * Parse `![[target]]`, `![[target|alias]]`, `![[target|width]]`, or `![[target|widthxheight]]`.
2702
+ */
2623
2703
  function parseWikiEmbed(source) {
2624
2704
  const inner = source.replace(/^!\[\[/, "").replace(/\]\]$/, "");
2625
2705
  const pipe = inner.lastIndexOf("|");
@@ -2653,11 +2733,15 @@ function parseWikiEmbed(source) {
2653
2733
  height
2654
2734
  };
2655
2735
  }
2656
- /** Rewrite a wiki image embed with a persisted display size. */
2736
+ /**
2737
+ * Rewrite a wiki image embed with a persisted display size.
2738
+ */
2657
2739
  function formatSizedWikiEmbed(target, width, height) {
2658
2740
  return `![[${target}|${Math.round(width)}x${Math.round(height)}]]`;
2659
2741
  }
2660
- /** Last path component of a target, with a note heading/block fragment removed. */
2742
+ /**
2743
+ * Last path component of a target, with a note heading/block fragment removed.
2744
+ */
2661
2745
  function wikiEmbedBasename(target) {
2662
2746
  const path = target.split(/[?#]/, 1)[0];
2663
2747
  const segment = path.split(/[/\\]/).findLast(Boolean) ?? path;
@@ -2670,7 +2754,9 @@ function wikiEmbedBasename(target) {
2670
2754
 
2671
2755
  //#endregion
2672
2756
  //#region src/extensions/wikilink.ts
2673
- /** Splits `[[target]]`/`[[target|alias]]` into its target and display label (the alias, or empty). */
2757
+ /**
2758
+ * Splits `[[target]]`/`[[target|alias]]` into its target and display label (the alias, or empty).
2759
+ */
2674
2760
  function parseWikilink(text) {
2675
2761
  const inner = text.replace(/^\[\[/, "").replace(/\]\]$/, "");
2676
2762
  const pipe = inner.indexOf("|");
@@ -2755,6 +2841,17 @@ const MARK_NAME_BY_TYPE_ID = /* @__PURE__ */ new Map([
2755
2841
  [LEZER_NODE_IDS.Hashtag, "mdTag"],
2756
2842
  [LEZER_NODE_IDS.WikilinkMark, "mdMark"]
2757
2843
  ]);
2844
+ function getGenericPackKey(type) {
2845
+ switch (type) {
2846
+ case LEZER_NODE_IDS.Emphasis: return "italic";
2847
+ case LEZER_NODE_IDS.StrongEmphasis: return "bold";
2848
+ case LEZER_NODE_IDS.InlineCode: return "code";
2849
+ case LEZER_NODE_IDS.Strikethrough: return "del";
2850
+ case LEZER_NODE_IDS.Highlight: return "highlight";
2851
+ case LEZER_NODE_IDS.Autolink: return "autolink";
2852
+ default: return;
2853
+ }
2854
+ }
2758
2855
  /**
2759
2856
  * Walk a textblock's inline content and produce a list of mark chunks
2760
2857
  * with positions relative to the start of `text` (i.e. zero-based).
@@ -2769,7 +2866,28 @@ function inlineTextToMarkChunksWithContext(marks, text, options, context) {
2769
2866
  walk(elements, [], 0, text.length, text, marks, out, options, context);
2770
2867
  return out;
2771
2868
  }
2772
- /** Drop the surrounding `"" '' ()` delimiters of a `LinkTitle` slice and unescape. */
2869
+ /**
2870
+ * The pack of one unit starting at `from`. When the chunk ending exactly there
2871
+ * closes a unit whose pack equals this one, create it with `slot: 1` instead:
2872
+ * equal packs would let ProseMirror merge the two units into one text node,
2873
+ * one mark run and one mark view. The neighbour's own pack sits at the same
2874
+ * depth, right after `parentMarks`; at any other depth that position holds a
2875
+ * different mark (or nothing) and never compares equal.
2876
+ */
2877
+ function createUnitPack(marks, out, parentMarks, from, attrs) {
2878
+ const pack = marks.mdPack.create(attrs);
2879
+ const previous = out.at(-1);
2880
+ if (previous == null || previous[1] !== from) return pack;
2881
+ const neighbourPack = previous[2][parentMarks.length];
2882
+ if (neighbourPack == null || !neighbourPack.eq(pack)) return pack;
2883
+ return marks.mdPack.create({
2884
+ ...attrs,
2885
+ slot: 1
2886
+ });
2887
+ }
2888
+ /**
2889
+ * Drop the surrounding `"" '' ()` delimiters of a `LinkTitle` slice and unescape.
2890
+ */
2773
2891
  function unquoteTitle(raw) {
2774
2892
  return raw.slice(1, -1).replaceAll(/\\(.)/g, "$1");
2775
2893
  }
@@ -2805,16 +2923,13 @@ function walkNode(node, parentMarks, text, marks, out, options, context) {
2805
2923
  * syntax marks, then recurses into its children.
2806
2924
  */
2807
2925
  function walkGenericNode(node, parentMarks, text, marks, out, options, context) {
2808
- const type = node.type;
2809
- let packKey;
2810
- if (type === LEZER_NODE_IDS.Emphasis) packKey = "italic";
2811
- else if (type === LEZER_NODE_IDS.StrongEmphasis) packKey = "bold";
2812
- else if (type === LEZER_NODE_IDS.InlineCode) packKey = "code";
2813
- else if (type === LEZER_NODE_IDS.Strikethrough) packKey = "strike";
2814
- else if (type === LEZER_NODE_IDS.Highlight) packKey = "highlight";
2815
- else if (type === LEZER_NODE_IDS.Autolink) packKey = "autolink";
2816
- const base = packKey ? [...parentMarks, marks.mdPack.create({ key: packKey })] : parentMarks;
2817
- const maybeMarkName = MARK_NAME_BY_TYPE_ID.get(type);
2926
+ const packKey = getGenericPackKey(node.type);
2927
+ const packMark = packKey && createUnitPack(marks, out, parentMarks, node.from, {
2928
+ key: packKey,
2929
+ revealInFocus: true
2930
+ });
2931
+ const base = packMark ? [...parentMarks, packMark] : parentMarks;
2932
+ const maybeMarkName = MARK_NAME_BY_TYPE_ID.get(node.type);
2818
2933
  const childMarks = maybeMarkName ? [...base, marks[maybeMarkName].create()] : base;
2819
2934
  if (node.children.length === 0) emit(out, node.from, node.to, childMarks);
2820
2935
  else walk(node.children, childMarks, node.from, node.to, text, marks, out, options, context);
@@ -2836,9 +2951,13 @@ function walkLink(node, parentMarks, text, marks, out, options, context) {
2836
2951
  walkUnresolvedLink(node, parentMarks, text, marks, out, options, context);
2837
2952
  return;
2838
2953
  }
2839
- const fileMarks = claimFileLink(parts, resolution, parentMarks, text, marks, options);
2840
- if (fileMarks) {
2841
- emit(out, node.from, node.to, fileMarks);
2954
+ const fileMark = claimFileLink(parts, resolution, text, marks, options);
2955
+ if (fileMark) {
2956
+ emit(out, node.from, node.to, [
2957
+ ...parentMarks,
2958
+ createUnitPack(marks, out, parentMarks, node.from, { key: "file" }),
2959
+ fileMark
2960
+ ]);
2842
2961
  return;
2843
2962
  }
2844
2963
  walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out, options, context);
@@ -2902,7 +3021,9 @@ function walkUnresolvedLink(node, parentMarks, text, marks, out, options, contex
2902
3021
  return child.type !== LEZER_NODE_IDS.LinkMark && child.type !== LEZER_NODE_IDS.LinkLabel;
2903
3022
  }), parentMarks, node.from, node.to, text, marks, out, options, context);
2904
3023
  }
2905
- /** The last path segment of `href` (query/hash stripped), decoded when possible. */
3024
+ /**
3025
+ * The last path segment of `href` (query/hash stripped), decoded when possible.
3026
+ */
2906
3027
  function hrefBasename(href) {
2907
3028
  const path = href.split(/[?#]/, 1)[0];
2908
3029
  const segment = path.split(/[/\\]/).findLast(Boolean) ?? path;
@@ -2913,12 +3034,12 @@ function hrefBasename(href) {
2913
3034
  }
2914
3035
  }
2915
3036
  /**
2916
- * The marks for a whole inline or resolved reference link that the host's `resolveFileLink`
2917
- * claimed as a file, or `undefined` when the link stays a regular link. The
2918
- * resolver is never consulted for a link without a closed label or a non-empty
2919
- * destination.
3037
+ * The `mdFile` mark for a whole inline or resolved reference link that the
3038
+ * host's `resolveFileLink` claimed as a file, or `undefined` when the link
3039
+ * stays a regular link. The resolver is never consulted for a link without a
3040
+ * closed label or a non-empty destination.
2920
3041
  */
2921
- function claimFileLink(parts, resolution, parentMarks, text, marks, options) {
3042
+ function claimFileLink(parts, resolution, text, marks, options) {
2922
3043
  const resolveFileLink = options?.resolveFileLink;
2923
3044
  if (!resolveFileLink) return void 0;
2924
3045
  const { labelFrom, labelTo } = parts;
@@ -2932,11 +3053,11 @@ function claimFileLink(parts, resolution, parentMarks, text, marks, options) {
2932
3053
  title
2933
3054
  })) return void 0;
2934
3055
  const name = label || hrefBasename(href);
2935
- return [...parentMarks, marks.mdFile.create({
3056
+ return marks.mdFile.create({
2936
3057
  href,
2937
3058
  name,
2938
3059
  title
2939
- })];
3060
+ });
2940
3061
  }
2941
3062
  /**
2942
3063
  * Special walker for `Link` nodes.
@@ -2960,17 +3081,14 @@ function walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out
2960
3081
  const { href, title, isReference } = resolution;
2961
3082
  const linkTextMark = marks.mdLinkText.create({ href });
2962
3083
  const inLabel = (pos) => labelEnd >= 0 && pos < labelEnd;
2963
- const data = isReference ? {
2964
- href,
2965
- title,
2966
- reference: true
2967
- } : {
2968
- href,
2969
- title
2970
- };
2971
- const pack = marks.mdPack.create({
3084
+ const pack = createUnitPack(marks, out, parentMarks, node.from, {
2972
3085
  key: "link",
2973
- data
3086
+ data: {
3087
+ href,
3088
+ title,
3089
+ isReference
3090
+ },
3091
+ revealInFocus: true
2974
3092
  });
2975
3093
  const base = [...parentMarks, pack];
2976
3094
  let pos = node.from;
@@ -3038,15 +3156,19 @@ function walkImage(node, parentMarks, text, marks, out, options, context, traili
3038
3156
  const width = trailing?.magic.width ?? null;
3039
3157
  const height = trailing?.magic.height ?? null;
3040
3158
  const to = trailing?.to ?? node.to;
3041
- emit(out, node.from, to, [...parentMarks, marks.mdImage.create({
3042
- src,
3043
- alt,
3044
- title,
3045
- width,
3046
- height,
3047
- syntax: null,
3048
- wikiTarget: null
3049
- })]);
3159
+ emit(out, node.from, to, [
3160
+ ...parentMarks,
3161
+ createUnitPack(marks, out, parentMarks, node.from, { key: "image" }),
3162
+ marks.mdImage.create({
3163
+ src,
3164
+ alt,
3165
+ title,
3166
+ width,
3167
+ height,
3168
+ syntax: null,
3169
+ wikiTarget: null
3170
+ })
3171
+ ]);
3050
3172
  }
3051
3173
  /**
3052
3174
  * Special walker for inline math `$formula$`/`$$formula$$`.
@@ -3065,7 +3187,11 @@ function walkMath(node, parentMarks, text, marks, out) {
3065
3187
  const formula = text.slice(markNodes[0].to, markNodes[1].from);
3066
3188
  const base = [
3067
3189
  ...parentMarks,
3068
- marks.mdPack.create({ key: "math" }),
3190
+ createUnitPack(marks, out, parentMarks, node.from, {
3191
+ key: "math",
3192
+ revealInFocus: true,
3193
+ revealInHide: true
3194
+ }),
3069
3195
  marks.mdMath.create({ formula })
3070
3196
  ];
3071
3197
  emit(out, node.from, markNodes[0].to, [...base, marks.mdMark.create()]);
@@ -3077,10 +3203,14 @@ function walkMath(node, parentMarks, text, marks, out) {
3077
3203
  */
3078
3204
  function walkWikilink(node, parentMarks, text, marks, out) {
3079
3205
  const { target, display } = parseWikilink(text.slice(node.from, node.to));
3080
- emit(out, node.from, node.to, [...parentMarks, marks.mdWikilink.create({
3081
- target,
3082
- display
3083
- })]);
3206
+ emit(out, node.from, node.to, [
3207
+ ...parentMarks,
3208
+ createUnitPack(marks, out, parentMarks, node.from, { key: "wikilink" }),
3209
+ marks.mdWikilink.create({
3210
+ target,
3211
+ display
3212
+ })
3213
+ ]);
3084
3214
  }
3085
3215
  /**
3086
3216
  * Resolve `![[target]]` into one of Meowdown's existing source-backed atoms.
@@ -3097,33 +3227,45 @@ function walkWikiEmbed(node, parentMarks, text, marks, out, options) {
3097
3227
  if (resolution.kind === "image") {
3098
3228
  const src = resolution.src ?? embed.target;
3099
3229
  const alt = (resolution.alt ?? embed.display) || wikiEmbedBasename(embed.target);
3100
- emit(out, node.from, node.to, [...parentMarks, marks.mdImage.create({
3101
- src,
3102
- alt,
3103
- title: "",
3104
- width: embed.width,
3105
- height: embed.height,
3106
- syntax: "wikiEmbed",
3107
- wikiTarget: embed.target
3108
- })]);
3230
+ emit(out, node.from, node.to, [
3231
+ ...parentMarks,
3232
+ createUnitPack(marks, out, parentMarks, node.from, { key: "image" }),
3233
+ marks.mdImage.create({
3234
+ src,
3235
+ alt,
3236
+ title: "",
3237
+ width: embed.width,
3238
+ height: embed.height,
3239
+ syntax: "wikiEmbed",
3240
+ wikiTarget: embed.target
3241
+ })
3242
+ ]);
3109
3243
  return;
3110
3244
  }
3111
3245
  if (resolution.kind === "file") {
3112
3246
  const href = resolution.href ?? embed.target;
3113
3247
  const name = (resolution.name ?? embed.display) || wikiEmbedBasename(embed.target);
3114
- emit(out, node.from, node.to, [...parentMarks, marks.mdFile.create({
3115
- href,
3116
- name,
3117
- title: resolution.title ?? ""
3118
- })]);
3248
+ emit(out, node.from, node.to, [
3249
+ ...parentMarks,
3250
+ createUnitPack(marks, out, parentMarks, node.from, { key: "file" }),
3251
+ marks.mdFile.create({
3252
+ href,
3253
+ name,
3254
+ title: resolution.title ?? ""
3255
+ })
3256
+ ]);
3119
3257
  return;
3120
3258
  }
3121
3259
  const target = resolution.target ?? embed.target;
3122
3260
  const display = resolution.display ?? embed.display;
3123
- emit(out, node.from, node.to, [...parentMarks, marks.mdWikilink.create({
3124
- target,
3125
- display
3126
- })]);
3261
+ emit(out, node.from, node.to, [
3262
+ ...parentMarks,
3263
+ createUnitPack(marks, out, parentMarks, node.from, { key: "wikilink" }),
3264
+ marks.mdWikilink.create({
3265
+ target,
3266
+ display
3267
+ })
3268
+ ]);
3127
3269
  }
3128
3270
  /**
3129
3271
  * Push `[from, to, marks]` to `out`, coalescing with the previous chunk
@@ -3513,7 +3655,9 @@ function defineMdTag() {
3513
3655
  parseDOM: [{ tag: "span.md-tag" }]
3514
3656
  });
3515
3657
  }
3516
- /** Covers the whole `[[target]]`/`[[target|alias]]` source. */
3658
+ /**
3659
+ * Covers the whole `[[target]]`/`[[target|alias]]` source.
3660
+ */
3517
3661
  function defineMdWikilink() {
3518
3662
  return defineMarkSpec({
3519
3663
  name: "mdWikilink",
@@ -3547,7 +3691,9 @@ function defineMdFile() {
3547
3691
  parseDOM: [{ tag: "span.md-file" }]
3548
3692
  });
3549
3693
  }
3550
- /** Covers the whole `$formula$` source, dollars included. */
3694
+ /**
3695
+ * Covers the whole `$formula$` source, dollars included.
3696
+ */
3551
3697
  function defineMdMath() {
3552
3698
  return defineMarkSpec({
3553
3699
  name: "mdMath",
@@ -3562,9 +3708,10 @@ function defineMdMath() {
3562
3708
  });
3563
3709
  }
3564
3710
  /**
3565
- * Wraps a whole revealable inline unit (emphasis, strong, code, strikethrough,
3566
- * link, autolink, image) so focus mode can reveal the unit with one
3567
- * `getMarkRange` lookup instead of stitching its punctuation back together.
3711
+ * Wraps a whole inline unit. For a revealable unit (emphasis, strong, code,
3712
+ * strikethrough, link, autolink, math) focus mode reveals the unit with one
3713
+ * range lookup instead of stitching its punctuation back together; an atom
3714
+ * unit (wikilink, image, file) carries it purely as unit identity.
3568
3715
  * `excludes: ''` lets nested units carry two of these marks at once.
3569
3716
  */
3570
3717
  function defineMdPack() {
@@ -3574,7 +3721,10 @@ function defineMdPack() {
3574
3721
  inclusive: false,
3575
3722
  attrs: {
3576
3723
  key: {},
3577
- data: { default: null }
3724
+ data: { default: null },
3725
+ slot: { default: null },
3726
+ revealInFocus: { default: null },
3727
+ revealInHide: { default: null }
3578
3728
  },
3579
3729
  toDOM: (mark) => {
3580
3730
  return [
@@ -3624,7 +3774,9 @@ const MARKER_IDS = /* @__PURE__ */ new Set([
3624
3774
  LEZER_NODE_IDS.StrikethroughMark,
3625
3775
  LEZER_NODE_IDS.HighlightMark
3626
3776
  ]);
3627
- /** The opening and closing delimiter tokens of a toggleable node. */
3777
+ /**
3778
+ * The opening and closing delimiter tokens of a toggleable node.
3779
+ */
3628
3780
  function delimiters(node) {
3629
3781
  return [node.children[0], node.children.at(-1)];
3630
3782
  }
@@ -3672,7 +3824,9 @@ function engulf(nodes, from, to) {
3672
3824
  }
3673
3825
  return [from, to];
3674
3826
  }
3675
- /** Shrink [from, to] so it starts and ends on non-whitespace. */
3827
+ /**
3828
+ * Shrink [from, to] so it starts and ends on non-whitespace.
3829
+ */
3676
3830
  function trimRange(text, from, to) {
3677
3831
  while (from < to && isSpaceChar(text.charCodeAt(from))) from++;
3678
3832
  while (to > from && isSpaceChar(text.charCodeAt(to - 1))) to--;
@@ -3832,7 +3986,9 @@ function caretPlan(text, pos, spec) {
3832
3986
  pos
3833
3987
  };
3834
3988
  }
3835
- /** Whether `pos` sits where inserted syntax could not parse: inside an atom or inside another span's delimiters. */
3989
+ /**
3990
+ * Whether `pos` sits where inserted syntax could not parse: inside an atom or inside another span's delimiters.
3991
+ */
3836
3992
  function insideAtom(nodes, pos) {
3837
3993
  for (const node of nodes) if (node.from < pos && pos < node.to) {
3838
3994
  const content = nestableContent(node);
@@ -3971,7 +4127,8 @@ function getLinkUnitAt(state, pos) {
3971
4127
  title: ""
3972
4128
  };
3973
4129
  }
3974
- if (packAttrs.data.reference === true) {
4130
+ const linkData = packAttrs.data;
4131
+ if (linkData.isReference) {
3975
4132
  const text = linkText == null ? {
3976
4133
  from: unit.from,
3977
4134
  to: unit.to
@@ -3985,8 +4142,8 @@ function getLinkUnitAt(state, pos) {
3985
4142
  to: unit.to
3986
4143
  },
3987
4144
  text,
3988
- href: packAttrs.data.href,
3989
- title: packAttrs.data.title
4145
+ href: linkData.href,
4146
+ title: linkData.title
3990
4147
  };
3991
4148
  }
3992
4149
  const uri = lastMarkRunIn(state, unit, "mdLinkUri");
@@ -4007,19 +4164,23 @@ function getLinkUnitAt(state, pos) {
4007
4164
  from: destFrom,
4008
4165
  to: unit.to - 1
4009
4166
  },
4010
- href: packAttrs.data.href,
4011
- title: packAttrs.data.title
4167
+ href: linkData.href,
4168
+ title: linkData.title
4012
4169
  };
4013
4170
  }
4014
4171
 
4015
4172
  //#endregion
4016
4173
  //#region src/extensions/link-commands.ts
4017
- /** Normalize a typed URL with the existing autolink logic, else keep it verbatim. */
4174
+ /**
4175
+ * Normalize a typed URL with the existing autolink logic, else keep it verbatim.
4176
+ */
4018
4177
  function normalizeHref(raw) {
4019
4178
  const value = raw.trim();
4020
4179
  return value ? getAutolinkHref(value) ?? value : "";
4021
4180
  }
4022
- /** The `( ... )` body for a link: the href plus an optional CommonMark title. */
4181
+ /**
4182
+ * The `( ... )` body for a link: the href plus an optional CommonMark title.
4183
+ */
4023
4184
  function destText(href, title) {
4024
4185
  return href + (title ? ` "${title.replaceAll(/(["\\])/g, String.raw`\$1`)}"` : "");
4025
4186
  }
@@ -4059,7 +4220,9 @@ function insertLink({ href, title, wrapText = true } = {}) {
4059
4220
  return true;
4060
4221
  };
4061
4222
  }
4062
- /** Rewrite the `( ... )` of the link at the caret/selection. */
4223
+ /**
4224
+ * Rewrite the `( ... )` of the link at the caret/selection.
4225
+ */
4063
4226
  function updateLink(attrs) {
4064
4227
  return (state, dispatch) => {
4065
4228
  const link = getLinkUnitAt(state, state.selection.from);
@@ -4069,7 +4232,9 @@ function updateLink(attrs) {
4069
4232
  return true;
4070
4233
  };
4071
4234
  }
4072
- /** Unwrap the link at the caret: keep the label text, drop the syntax. */
4235
+ /**
4236
+ * Unwrap the link at the caret: keep the label text, drop the syntax.
4237
+ */
4073
4238
  function removeLink() {
4074
4239
  return (state, dispatch) => {
4075
4240
  const link = getLinkUnitAt(state, state.selection.from);
@@ -4290,26 +4455,38 @@ const listInputRules = [
4290
4455
  function defineMeowdownListInputRules() {
4291
4456
  return union(listInputRules.map(defineInputRule));
4292
4457
  }
4293
- /** Circle checkbox task: a `task` list item with a `+` marker. */
4458
+ /**
4459
+ * Circle checkbox task: a `task` list item with a `+` marker.
4460
+ */
4294
4461
  function wrapInCircleTask() {
4295
4462
  return wrapInList({
4296
4463
  kind: "task",
4297
4464
  marker: "+"
4298
4465
  });
4299
4466
  }
4300
- /** Square checkbox task: a `task` list item with the canonical `-` marker. */
4467
+ /**
4468
+ * Square checkbox task: a `task` list item with the canonical `-` marker.
4469
+ */
4301
4470
  function wrapInSquareTask() {
4302
4471
  return wrapInList({
4303
4472
  kind: "task",
4304
4473
  marker: null
4305
4474
  });
4306
4475
  }
4307
- /** The attributes of the closest list node enclosing the selection, if any. */
4476
+ /**
4477
+ * The attributes of the list item whose own content holds the selection, if
4478
+ * any. A continuation block deeper inside an item (a paragraph after the
4479
+ * item's first block) is not the item's content, so it reports no list: the
4480
+ * cycle commands then wrap it into a new nested list instead of reading the
4481
+ * ancestor's kind.
4482
+ */
4308
4483
  function getListAttrsAtSelection(state) {
4309
- const { $from } = state.selection;
4484
+ const { selection } = state;
4485
+ if (isNodeSelection(selection) && isNodeOfType(selection.node, "list")) return selection.node.attrs;
4486
+ const { $from } = selection;
4310
4487
  for (let depth = $from.depth; depth > 0; depth--) {
4311
4488
  const node = $from.node(depth);
4312
- if (isNodeOfType(node, "list")) return node.attrs;
4489
+ if (isNodeOfType(node, "list")) return $from.index(depth) === 0 ? node.attrs : null;
4313
4490
  }
4314
4491
  return null;
4315
4492
  }
@@ -4340,18 +4517,18 @@ function cycleCheckableList() {
4340
4517
  function cycleBulletOrderedList() {
4341
4518
  return (state, dispatch, view) => {
4342
4519
  const attrs = getListAttrsAtSelection(state);
4343
- const next = attrs?.kind === "bullet" || attrs?.kind === "ordered" ? {
4520
+ if (attrs?.kind === "bullet" || attrs?.kind === "ordered") return toggleList({
4344
4521
  kind: "ordered",
4345
4522
  marker: null,
4346
4523
  checked: false,
4347
4524
  collapsed: false
4348
- } : {
4525
+ })(state, dispatch, view);
4526
+ return wrapInList({
4349
4527
  kind: "bullet",
4350
4528
  marker: null,
4351
4529
  checked: false,
4352
4530
  collapsed: false
4353
- };
4354
- return toggleList(next)(state, dispatch, view);
4531
+ })(state, dispatch, view);
4355
4532
  };
4356
4533
  }
4357
4534
  function toggleListCollapsed() {
@@ -4575,7 +4752,9 @@ var MathMarkView = class {
4575
4752
  });
4576
4753
  }
4577
4754
  };
4578
- /** Inline math rendering: a KaTeX preview on the `mdMath` mark. */
4755
+ /**
4756
+ * Inline math rendering: a KaTeX preview on the `mdMath` mark.
4757
+ */
4579
4758
  function defineMath() {
4580
4759
  return defineMarkView({
4581
4760
  name: "mdMath",
@@ -4884,7 +5063,9 @@ function defineMoveBlock() {
4884
5063
  //#endregion
4885
5064
  //#region src/extensions/pending-replacement.ts
4886
5065
  const pendingReplacementKey = new PluginKey("meowdownPendingReplacement");
4887
- /** The active pending replacement, or null when there is none. */
5066
+ /**
5067
+ * The active pending replacement, or null when there is none.
5068
+ */
4888
5069
  function getPendingReplacement(state) {
4889
5070
  return pendingReplacementKey.getState(state)?.pending ?? null;
4890
5071
  }
@@ -5026,7 +5207,9 @@ function definePendingReplacementCommands() {
5026
5207
  discardPendingReplacement
5027
5208
  });
5028
5209
  }
5029
- /** Accept on Mod-Enter and discard on Escape, only while a replacement is pending. */
5210
+ /**
5211
+ * Accept on Mod-Enter and discard on Escape, only while a replacement is pending.
5212
+ */
5030
5213
  function definePendingReplacementKeymap() {
5031
5214
  return defineKeymap({
5032
5215
  "Mod-Enter": acceptPendingReplacement(),
@@ -5343,22 +5526,30 @@ function defineEditorExtension(options = {}) {
5343
5526
 
5344
5527
  //#endregion
5345
5528
  //#region src/extensions/schema.ts
5346
- /** The schema shared by every parser and serializer, built once and cached. */
5529
+ /**
5530
+ * The schema shared by every parser and serializer, built once and cached.
5531
+ */
5347
5532
  const getSharedSchema = /* @__PURE__ */ once(() => {
5348
5533
  const schema = defineEditorExtension().schema;
5349
5534
  if (schema == null) throw new Error("Unexpected empty schema");
5350
5535
  return schema;
5351
5536
  });
5352
- /** Typed node builders bound to the shared schema. */
5537
+ /**
5538
+ * Typed node builders bound to the shared schema.
5539
+ */
5353
5540
  const getNodeBuilders = /* @__PURE__ */ once(() => {
5354
5541
  return createNodeBuilders(getSharedSchema());
5355
5542
  });
5356
- /** Typed mark builders bound to the shared schema. */
5543
+ /**
5544
+ * Typed mark builders bound to the shared schema.
5545
+ */
5357
5546
  const getMarkBuilders = /* @__PURE__ */ once(() => {
5358
5547
  return createMarkBuilders(getSharedSchema());
5359
5548
  });
5360
5549
  const MARK_BUILDERS_CACHE_KEY = "meowdown_mark_builders";
5361
- /** Typed mark builders bound to a specific schema, cached per schema. */
5550
+ /**
5551
+ * Typed mark builders bound to a specific schema, cached per schema.
5552
+ */
5362
5553
  function getMarkBuildersForSchema(schema) {
5363
5554
  const cached = schema.cached[MARK_BUILDERS_CACHE_KEY];
5364
5555
  if (cached) return cached;
@@ -5367,7 +5558,9 @@ function getMarkBuildersForSchema(schema) {
5367
5558
  return builders;
5368
5559
  }
5369
5560
  const NODE_BUILDERS_CACHE_KEY = "meowdown_node_builders";
5370
- /** Typed node builders bound to a specific schema, cached per schema. */
5561
+ /**
5562
+ * Typed node builders bound to a specific schema, cached per schema.
5563
+ */
5371
5564
  function getNodeBuildersForSchema(schema) {
5372
5565
  const cached = schema.cached[NODE_BUILDERS_CACHE_KEY];
5373
5566
  if (cached) return cached;
@@ -5512,7 +5705,9 @@ function convertHeading(nodes, cursor, text, level, isSetext) {
5512
5705
  closingHashes
5513
5706
  }, content);
5514
5707
  }
5515
- /** Count the `=` / `-` characters in a setext underline run. */
5708
+ /**
5709
+ * Count the `=` / `-` characters in a setext underline run.
5710
+ */
5516
5711
  function countUnderlineChars(text, from, to) {
5517
5712
  if (from < 0) return 0;
5518
5713
  let count = 0;
@@ -5522,7 +5717,9 @@ function countUnderlineChars(text, from, to) {
5522
5717
  }
5523
5718
  return count;
5524
5719
  }
5525
- /** Count the `#` characters between `from` and `to`. */
5720
+ /**
5721
+ * Count the `#` characters between `from` and `to`.
5722
+ */
5526
5723
  function countHashChars(text, from, to) {
5527
5724
  if (from < 0) return 0;
5528
5725
  let count = 0;
@@ -5540,7 +5737,9 @@ function measureContentColumn(text, from) {
5540
5737
  for (let index = lineStart; index < from; index++) col += text.charCodeAt(index) === 9 ? 4 - col % 4 : 1;
5541
5738
  return col;
5542
5739
  }
5543
- /** Drop a line's leading whitespace up to `column`, counting a tab as `4 - col % 4` columns. */
5740
+ /**
5741
+ * Drop a line's leading whitespace up to `column`, counting a tab as `4 - col % 4` columns.
5742
+ */
5544
5743
  function sliceColumn(line, column) {
5545
5744
  let col = 0;
5546
5745
  let index = 0;
@@ -5632,7 +5831,9 @@ function convertList(nodes, cursor, text, kind) {
5632
5831
  }
5633
5832
  return items;
5634
5833
  }
5635
- /** The marker style at `cursor`, plus the start number of an ordered item. */
5834
+ /**
5835
+ * The marker style at `cursor`, plus the start number of an ordered item.
5836
+ */
5636
5837
  function readListMark(cursor, text, kind) {
5637
5838
  if (kind === "ordered") {
5638
5839
  const delimiterCode = text.charCodeAt(cursor.to - 1);
@@ -5859,7 +6060,9 @@ function canonicalizeTableRow(line) {
5859
6060
  function normalizeLine(line) {
5860
6061
  return canonicalizeTableRow(line) ?? collapseWhitespace(line);
5861
6062
  }
5862
- /** Classify how `markdown` survives the editor's parse-then-serialize round trip. */
6063
+ /**
6064
+ * Classify how `markdown` survives the editor's parse-then-serialize round trip.
6065
+ */
5863
6066
  function checkRoundTrip(markdown, options = {}) {
5864
6067
  const doc = markdownToDoc(markdown, { frontmatter: options.frontmatter });
5865
6068
  const serialized = docToMarkdown(doc, { frontmatter: options.frontmatter });
@@ -6036,7 +6239,9 @@ function applyTweetHeight(iframe, height) {
6036
6239
  const YOUTUBE_HOSTS = /^(?:www\.|m\.)?(?:youtube\.com|youtube-nocookie\.com)$/i;
6037
6240
  const YOUTU_BE_HOST = /^(?:www\.)?youtu\.be$/i;
6038
6241
  const VIDEO_ID = /^[\w-]{11}$/;
6039
- /** Extract `{ videoId, startSeconds? }` from any watch/shorts/embed/live/`youtu.be` URL. */
6242
+ /**
6243
+ * Extract `{ videoId, startSeconds? }` from any watch/shorts/embed/live/`youtu.be` URL.
6244
+ */
6040
6245
  function parseYouTube(src) {
6041
6246
  let url;
6042
6247
  try {
@@ -6059,7 +6264,9 @@ function parseYouTube(src) {
6059
6264
  startSeconds
6060
6265
  };
6061
6266
  }
6062
- /** `90`, `90s`, `1m30s`, `1h2m3s` to seconds. */
6267
+ /**
6268
+ * `90`, `90s`, `1m30s`, `1h2m3s` to seconds.
6269
+ */
6063
6270
  function parseStartSeconds(value) {
6064
6271
  if (/^\d+$/.test(value)) return Number(value);
6065
6272
  const matched = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/.exec(value);
@@ -6085,7 +6292,9 @@ const matchYouTube = (src) => {
6085
6292
  //#endregion
6086
6293
  //#region src/extensions/embed.ts
6087
6294
  const EMBED_MATCHERS = [matchYouTube, matchTweet];
6088
- /** Detect a tweet/YouTube embed in an image `src`, or `undefined` for a plain image. */
6295
+ /**
6296
+ * Detect a tweet/YouTube embed in an image `src`, or `undefined` for a plain image.
6297
+ */
6089
6298
  function matchEmbed(src) {
6090
6299
  for (const match of EMBED_MATCHERS) {
6091
6300
  const descriptor = match(src);
@@ -6170,7 +6379,9 @@ function createExitBoundaryPlugin(onExitBoundary) {
6170
6379
  } }
6171
6380
  });
6172
6381
  }
6173
- /** Call `onExitBoundary` when an arrow key press would leave the document boundary. */
6382
+ /**
6383
+ * Call `onExitBoundary` when an arrow key press would leave the document boundary.
6384
+ */
6174
6385
  function defineExitBoundaryHandler(onExitBoundary) {
6175
6386
  return withPriority$1(definePlugin(createExitBoundaryPlugin(onExitBoundary)), Priority$1.low);
6176
6387
  }
@@ -6253,7 +6464,9 @@ function takePastedFiles(data, options) {
6253
6464
  const defaultOnFileSaveError = (error) => {
6254
6465
  console.error("[meowdown] failed to save pasted file:", error);
6255
6466
  };
6256
- /** Escape `\`, `[`, and `]` so a filename stays inside its `[text]` label. */
6467
+ /**
6468
+ * Escape `\`, `[`, and `]` so a filename stays inside its `[text]` label.
6469
+ */
6257
6470
  function escapeLinkText(name) {
6258
6471
  return name.replaceAll(/[\\[\]]/g, String.raw`\$&`);
6259
6472
  }
@@ -6337,7 +6550,9 @@ function formatFileSize(bytes) {
6337
6550
 
6338
6551
  //#endregion
6339
6552
  //#region src/extensions/file-view.ts
6340
- /** `data-file-kind` values by file extension, for host CSS theming. */
6553
+ /**
6554
+ * `data-file-kind` values by file extension, for host CSS theming.
6555
+ */
6341
6556
  const FILE_KIND_BY_EXTENSION = /* @__PURE__ */ new Map([
6342
6557
  ["pdf", "pdf"],
6343
6558
  ["zip", "archive"],
@@ -6368,7 +6583,9 @@ const FILE_KIND_BY_EXTENSION = /* @__PURE__ */ new Map([
6368
6583
  ["txt", "text"],
6369
6584
  ["md", "text"]
6370
6585
  ]);
6371
- /** Classify a file destination for the pill's `data-file-kind` attribute. */
6586
+ /**
6587
+ * Classify a file destination for the pill's `data-file-kind` attribute.
6588
+ */
6372
6589
  function getFileKind(href) {
6373
6590
  const path = href.split(/[?#]/, 1)[0];
6374
6591
  const dot = path.lastIndexOf(".");
@@ -6377,7 +6594,9 @@ function getFileKind(href) {
6377
6594
  return FILE_KIND_BY_EXTENSION.get(extension) ?? "generic";
6378
6595
  }
6379
6596
  const SVG_NS = "http://www.w3.org/2000/svg";
6380
- /** A minimal document-outline icon, drawn in `currentColor`. */
6597
+ /**
6598
+ * A minimal document-outline icon, drawn in `currentColor`.
6599
+ */
6381
6600
  function buildFileIcon() {
6382
6601
  const svg = document.createElementNS(SVG_NS, "svg");
6383
6602
  svg.setAttribute("class", "md-file-view-icon");
@@ -6546,7 +6765,9 @@ function defineTagClickHandler(onClick) {
6546
6765
  //#endregion
6547
6766
  //#region src/extensions/wikilink-click.ts
6548
6767
  const wikilinkClickKey = new PluginKey("meowdown-wikilink-click");
6549
- /** Exported for tests. */
6768
+ /**
6769
+ * Exported for tests.
6770
+ */
6550
6771
  function findWikilinkAt(state, pos) {
6551
6772
  const range = getMarkRangeAt(state, pos, "mdWikilink");
6552
6773
  if (!range) return;
@@ -6676,7 +6897,9 @@ function findImageForPreview(view, preview) {
6676
6897
  if (!content) return;
6677
6898
  return findImageAt(view.state, view.posAtDOM(content, 0));
6678
6899
  }
6679
- /** Fingers wander a little during a tap; past this it is a scroll or a drag. */
6900
+ /**
6901
+ * Fingers wander a little during a tap; past this it is a scroll or a drag.
6902
+ */
6680
6903
  const TAP_MOVE_TOLERANCE = 10;
6681
6904
  function findTouch(touches, identifier) {
6682
6905
  return Array.from(touches).find((touch) => touch.identifier === identifier);
@@ -6767,7 +6990,9 @@ function defineImageClickHandler(onClick) {
6767
6990
 
6768
6991
  //#endregion
6769
6992
  //#region src/extensions/image.ts
6770
- /** Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it. */
6993
+ /**
6994
+ * Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it.
6995
+ */
6771
6996
  function defaultResolveImageUrl(src) {
6772
6997
  return /^https?:\/\//i.test(src) ? src : void 0;
6773
6998
  }
@@ -6843,7 +7068,9 @@ function rewriteMagicComment(view, range, patch, addToHistory) {
6843
7068
  if (!addToHistory) transaction.setMeta("addToHistory", false);
6844
7069
  view.dispatch(transaction);
6845
7070
  }
6846
- /** Persist a resized width and height into the trailing magic comment. */
7071
+ /**
7072
+ * Persist a resized width and height into the trailing magic comment.
7073
+ */
6847
7074
  function commitImageSize(view, content, rawWidth, rawHeight) {
6848
7075
  const pos = view.posAtDOM(content, 0);
6849
7076
  const range = getMarkRangeAt(view.state, pos, "mdImage");
@@ -6928,7 +7155,9 @@ var ImageMarkView = class {
6928
7155
  ignoreMutation(mutation) {
6929
7156
  return !this.#contentDOM.contains(mutation.target);
6930
7157
  }
6931
- /** Build the inline preview for the image `src`: an embed iframe or a resizable `<img>`. */
7158
+ /**
7159
+ * Build the inline preview for the image `src`: an embed iframe or a resizable `<img>`.
7160
+ */
6932
7161
  #renderPreview() {
6933
7162
  const { src } = this.#attrs;
6934
7163
  const embed = matchEmbed(src);
@@ -7038,7 +7267,9 @@ function defineImage(options = {}) {
7038
7267
 
7039
7268
  //#endregion
7040
7269
  //#region src/extensions/key-bindings.ts
7041
- /** Human-readable descriptions of the editor's formatting and heading shortcuts. */
7270
+ /**
7271
+ * Human-readable descriptions of the editor's formatting and heading shortcuts.
7272
+ */
7042
7273
  const EDITOR_KEY_BINDINGS = {
7043
7274
  "Mod-b": "Bold",
7044
7275
  "Mod-i": "Italic",
@@ -7390,7 +7621,9 @@ function defineSubstitutionEnterRules() {
7390
7621
  });
7391
7622
  }));
7392
7623
  }
7393
- /** Apply the editor's automatic plain-text substitutions. */
7624
+ /**
7625
+ * Apply the editor's automatic plain-text substitutions.
7626
+ */
7394
7627
  function defineSubstitution() {
7395
7628
  return union(defineSubstitutionInputRules(), defineSubstitutionUndo(), defineSubstitutionEnterRules());
7396
7629
  }
@@ -7454,7 +7687,9 @@ if (typeof window !== "undefined") {
7454
7687
  function getIsTouchInput() {
7455
7688
  return lastIsTouchInput;
7456
7689
  }
7457
- /** Calls `listener` whenever {@link getIsTouchInput} may report a new value. */
7690
+ /**
7691
+ * Calls `listener` whenever {@link getIsTouchInput} may report a new value.
7692
+ */
7458
7693
  function onIsTouchInputChange(listener) {
7459
7694
  listeners.add(listener);
7460
7695
  return () => {
@@ -7667,7 +7902,7 @@ function cleanTextFromSlice(slice, options = {}) {
7667
7902
  blockNode.descendants((textNode) => {
7668
7903
  if (!textNode.isText || !textNode.text) return true;
7669
7904
  const textNodeMarks = textNode.marks.map((mark) => mark.type.name);
7670
- if (!(textNodeMarks.some((markName) => SYNTAX_MARK_NAMES.has(markName)) && !(options.preserveMathSource && textNodeMarks.includes("mdMath")))) parts.push(textNode.text);
7905
+ if (!(textNodeMarks.some((markName) => SYNTAX_MARK_NAMES.includes(markName)) && !(options.preserveMathSource && textNodeMarks.includes("mdMath")))) parts.push(textNode.text);
7671
7906
  return false;
7672
7907
  });
7673
7908
  blocks.push(parts.join(""));