@meowdown/core 0.64.0 → 0.64.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +320 -113
- package/dist/index.js +511 -278
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -83,6 +83,28 @@ 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
|
+
const SYNTAX_MARK_NAMES = /* @__PURE__ */ new Set([
|
|
92
|
+
"mdMark",
|
|
93
|
+
"mdLinkUri",
|
|
94
|
+
"mdLinkTitle"
|
|
95
|
+
]);
|
|
96
|
+
const ATOM_MARK_NAMES = /* @__PURE__ */ new Set([
|
|
97
|
+
"mdWikilink",
|
|
98
|
+
"mdImage",
|
|
99
|
+
"mdFile",
|
|
100
|
+
"mdMath"
|
|
101
|
+
]);
|
|
102
|
+
const ATOM_PACK_KEYS = /* @__PURE__ */ new Set([
|
|
103
|
+
"wikilink",
|
|
104
|
+
"image",
|
|
105
|
+
"file"
|
|
106
|
+
]);
|
|
107
|
+
|
|
86
108
|
//#endregion
|
|
87
109
|
//#region src/extensions/mark-mode.ts
|
|
88
110
|
const markModeKey = new PluginKey("mark-mode");
|
|
@@ -133,8 +155,8 @@ function getMarkMode(state) {
|
|
|
133
155
|
* `getMarkRange` finds the unit, returning the outermost when units nest. One
|
|
134
156
|
* decoration over its range flips the hidden punctuation/url/source visible via
|
|
135
157
|
* 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.
|
|
137
|
-
*
|
|
158
|
+
* either edge (e.g. right after a link's `)`) still reveals it. Atom packs
|
|
159
|
+
* (`ATOM_PACK_KEYS`) and `#tag` never reveal.
|
|
138
160
|
*/
|
|
139
161
|
function computeFocusDecorations(state) {
|
|
140
162
|
return computeRevealDecorations(state, void 0);
|
|
@@ -154,10 +176,164 @@ function computeRevealDecorations(state, packAttrs) {
|
|
|
154
176
|
const $pos = selection.$head;
|
|
155
177
|
const { parent } = $pos;
|
|
156
178
|
if (!parent.isTextblock || parent.type.spec.code) return DecorationSet.empty;
|
|
157
|
-
const range =
|
|
179
|
+
const range = getRevealablePackRange(state, $pos, packAttrs);
|
|
158
180
|
if (!range) return DecorationSet.empty;
|
|
159
181
|
return DecorationSet.create(state.doc, [Decoration.inline(range.from, range.to, { class: "show" })]);
|
|
160
182
|
}
|
|
183
|
+
function isAtomPack(mark) {
|
|
184
|
+
return ATOM_PACK_KEYS.has(mark.attrs.key);
|
|
185
|
+
}
|
|
186
|
+
function getRevealablePackRange(state, $pos, packAttrs) {
|
|
187
|
+
const packType = getMarkType(state.schema, "mdPack");
|
|
188
|
+
const range = getMarkRange($pos, packType, packAttrs);
|
|
189
|
+
if (!range) return;
|
|
190
|
+
if (!isAtomPack(range.mark)) return range;
|
|
191
|
+
if ($pos.parentOffset === 0) return;
|
|
192
|
+
const before = getMarkRange(state.doc.resolve($pos.pos - 1), packType, packAttrs);
|
|
193
|
+
if (!before || before.to !== $pos.pos || isAtomPack(before.mark)) return;
|
|
194
|
+
return before;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/extensions/hidden-run.ts
|
|
199
|
+
function getCharMarks(state, pos) {
|
|
200
|
+
if (pos < 0 || pos + 1 > state.doc.content.size) return;
|
|
201
|
+
const $pos = state.doc.resolve(pos);
|
|
202
|
+
const child = $pos.parent.maybeChild($pos.index());
|
|
203
|
+
if (child == null || !child.isText) return;
|
|
204
|
+
return child.marks;
|
|
205
|
+
}
|
|
206
|
+
function isHiddenChar(state, pos) {
|
|
207
|
+
const marks = getCharMarks(state, pos);
|
|
208
|
+
if (marks == null) return false;
|
|
209
|
+
return marks.some((mark) => SYNTAX_MARK_NAMES.has(mark.type.name));
|
|
210
|
+
}
|
|
211
|
+
function isInsideNonCodeTextblock(state, pos) {
|
|
212
|
+
if (pos < 0 || pos > state.doc.content.size) return false;
|
|
213
|
+
const $pos = state.doc.resolve(pos);
|
|
214
|
+
return $pos.parent.isTextblock && !$pos.parent.type.spec.code;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The maximal contiguous hidden run ending exactly at `pos`, or undefined.
|
|
218
|
+
*/
|
|
219
|
+
function getHiddenRunBefore(state, pos) {
|
|
220
|
+
if (!isInsideNonCodeTextblock(state, pos)) return;
|
|
221
|
+
const blockStart = state.doc.resolve(pos).start();
|
|
222
|
+
let from = pos;
|
|
223
|
+
while (from > blockStart && isHiddenChar(state, from - 1)) from--;
|
|
224
|
+
return from < pos ? {
|
|
225
|
+
from,
|
|
226
|
+
to: pos
|
|
227
|
+
} : void 0;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The maximal contiguous hidden run starting exactly at `pos`, or undefined.
|
|
231
|
+
*/
|
|
232
|
+
function getHiddenRunAfter(state, pos) {
|
|
233
|
+
if (!isInsideNonCodeTextblock(state, pos)) return;
|
|
234
|
+
const blockEnd = state.doc.resolve(pos).end();
|
|
235
|
+
let to = pos;
|
|
236
|
+
while (to < blockEnd && isHiddenChar(state, to)) to++;
|
|
237
|
+
return to > pos ? {
|
|
238
|
+
from: pos,
|
|
239
|
+
to
|
|
240
|
+
} : void 0;
|
|
241
|
+
}
|
|
242
|
+
function isHiddenRunInterior(state, pos) {
|
|
243
|
+
return isHiddenChar(state, pos - 1) && isHiddenChar(state, pos);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The full run around an interior position, or undefined for rest positions.
|
|
247
|
+
*/
|
|
248
|
+
function getHiddenRunAround(state, pos) {
|
|
249
|
+
if (!isHiddenRunInterior(state, pos)) return;
|
|
250
|
+
const before = getHiddenRunBefore(state, pos);
|
|
251
|
+
if (!before) return;
|
|
252
|
+
const after = getHiddenRunAfter(state, pos);
|
|
253
|
+
if (!after) return;
|
|
254
|
+
return {
|
|
255
|
+
from: before.from,
|
|
256
|
+
to: after.to
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function charHasMark(state, pos, mark) {
|
|
260
|
+
const marks = getCharMarks(state, pos);
|
|
261
|
+
return marks != null && mark.isInSet(marks);
|
|
262
|
+
}
|
|
263
|
+
function getInnermostPackRangeAt(state, charPos) {
|
|
264
|
+
const marks = getCharMarks(state, charPos);
|
|
265
|
+
if (marks == null) return;
|
|
266
|
+
const packType = getMarkType(state.schema, "mdPack");
|
|
267
|
+
const packs = marks.filter((mark) => mark.type === packType);
|
|
268
|
+
if (packs.length === 0) return;
|
|
269
|
+
const $pos = state.doc.resolve(charPos);
|
|
270
|
+
const blockStart = $pos.start();
|
|
271
|
+
const blockEnd = $pos.end();
|
|
272
|
+
let innermost;
|
|
273
|
+
for (const pack of packs) {
|
|
274
|
+
let from = charPos;
|
|
275
|
+
while (from > blockStart && charHasMark(state, from - 1, pack)) from--;
|
|
276
|
+
let to = charPos + 1;
|
|
277
|
+
while (to < blockEnd && charHasMark(state, to, pack)) to++;
|
|
278
|
+
if (innermost == null || to - from < innermost.to - innermost.from) innermost = {
|
|
279
|
+
from,
|
|
280
|
+
to
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return innermost;
|
|
284
|
+
}
|
|
285
|
+
function isPackOuterEdge(state, run, edge) {
|
|
286
|
+
const pack = getInnermostPackRangeAt(state, edge === "from" ? run.from : run.to - 1);
|
|
287
|
+
if (pack == null) return false;
|
|
288
|
+
return edge === "from" ? pack.from === run.from : pack.to === run.to;
|
|
289
|
+
}
|
|
290
|
+
function getPointerEdge(state, run, pos) {
|
|
291
|
+
const fromIsOuter = isPackOuterEdge(state, run, "from");
|
|
292
|
+
const toIsOuter = isPackOuterEdge(state, run, "to");
|
|
293
|
+
if (fromIsOuter && !toIsOuter) return run.from;
|
|
294
|
+
if (toIsOuter && !fromIsOuter) return run.to;
|
|
295
|
+
return pos - run.from <= run.to - pos ? run.from : run.to;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* The rest position for a caret that landed at `newPos`. `oldPos` supplies the
|
|
299
|
+
* travel direction for keyboard motion; `isPointer` selects the click rules.
|
|
300
|
+
*/
|
|
301
|
+
function getRestPosition(state, oldPos, newPos, isPointer) {
|
|
302
|
+
if (!isInsideNonCodeTextblock(state, newPos)) return newPos;
|
|
303
|
+
const run = getHiddenRunAround(state, newPos);
|
|
304
|
+
if (run != null) {
|
|
305
|
+
if (!isPointer) return newPos >= oldPos ? run.to : run.from;
|
|
306
|
+
return getPointerEdge(state, run, newPos);
|
|
307
|
+
}
|
|
308
|
+
if (!isPointer) return newPos;
|
|
309
|
+
const runBefore = getHiddenRunBefore(state, newPos);
|
|
310
|
+
if (runBefore != null && isPackOuterEdge(state, runBefore, "from")) return runBefore.from;
|
|
311
|
+
const runAfter = getHiddenRunAfter(state, newPos);
|
|
312
|
+
if (runAfter != null && isPackOuterEdge(state, runAfter, "to")) return runAfter.to;
|
|
313
|
+
return newPos;
|
|
314
|
+
}
|
|
315
|
+
function getCaretTail(state, pos) {
|
|
316
|
+
if (!isInsideNonCodeTextblock(state, pos)) return;
|
|
317
|
+
const hiddenBefore = isHiddenChar(state, pos - 1);
|
|
318
|
+
const hiddenAfter = isHiddenChar(state, pos);
|
|
319
|
+
if (hiddenBefore === hiddenAfter) return;
|
|
320
|
+
return hiddenAfter ? "left" : "right";
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The leading and trailing hidden runs of the innermost unit whose marker
|
|
324
|
+
* character sits at `charPos`, trailing first so callers can delete them in
|
|
325
|
+
* order without remapping. A fully hidden unit yields one run.
|
|
326
|
+
*/
|
|
327
|
+
function getUnitMarkerRuns(state, charPos) {
|
|
328
|
+
const pack = getInnermostPackRangeAt(state, charPos);
|
|
329
|
+
if (pack == null) return [];
|
|
330
|
+
const leading = getHiddenRunAfter(state, pack.from);
|
|
331
|
+
const trailing = getHiddenRunBefore(state, pack.to);
|
|
332
|
+
const runs = [];
|
|
333
|
+
if (trailing != null) runs.push(trailing);
|
|
334
|
+
if (leading != null && (trailing == null || leading.from !== trailing.from)) runs.push(leading);
|
|
335
|
+
return runs;
|
|
336
|
+
}
|
|
161
337
|
|
|
162
338
|
//#endregion
|
|
163
339
|
//#region src/extensions/mark-range.ts
|
|
@@ -185,10 +361,30 @@ function getMarkRangeAt(state, pos, markName, attrs) {
|
|
|
185
361
|
const markNames = Array.isArray(markName) ? markName : [markName];
|
|
186
362
|
for (const name of markNames) {
|
|
187
363
|
const range = getMarkRange($pos, name, attrs);
|
|
188
|
-
if (range) return range;
|
|
364
|
+
if (range) return ATOM_MARK_NAMES.has(name) ? clampToUnitRange(state, $pos, range) : range;
|
|
189
365
|
}
|
|
190
366
|
}
|
|
191
367
|
/**
|
|
368
|
+
* Two identical adjacent units carry equal atom marks (only their packs differ,
|
|
369
|
+
* by `slot`), so `getMarkRange`'s eq-based expansion runs across both. Clamp
|
|
370
|
+
* the range to the matched child's own unit: the innermost pack covering its
|
|
371
|
+
* first character. A child without a pack (a document not produced by the
|
|
372
|
+
* inline parser) keeps the unclamped range.
|
|
373
|
+
*/
|
|
374
|
+
function clampToUnitRange(state, $pos, range) {
|
|
375
|
+
const parent = $pos.parent;
|
|
376
|
+
const after = parent.childAfter($pos.parentOffset);
|
|
377
|
+
const matched = after.node && range.mark.isInSet(after.node.marks) ? after : parent.childBefore($pos.parentOffset);
|
|
378
|
+
if (!matched.node) return range;
|
|
379
|
+
const pack = getInnermostPackRangeAt(state, $pos.start() + matched.offset);
|
|
380
|
+
if (!pack) return range;
|
|
381
|
+
return {
|
|
382
|
+
from: Math.max(range.from, pack.from),
|
|
383
|
+
to: Math.min(range.to, pack.to),
|
|
384
|
+
mark: range.mark
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
192
388
|
* Returns the run ending exactly at `pos`, the one immediately to its left.
|
|
193
389
|
* Probes from inside the left neighbour (`pos - 1`): probing `pos` itself
|
|
194
390
|
* cannot see that run when another run starts exactly there, because
|
|
@@ -470,7 +666,9 @@ const highlightToMarkdown = (node, _parent, state, info) => {
|
|
|
470
666
|
function isCheckboxInput(node) {
|
|
471
667
|
return node.tagName === "input" && node.properties.type === "checkbox";
|
|
472
668
|
}
|
|
473
|
-
/**
|
|
669
|
+
/**
|
|
670
|
+
* The first checkbox `<input>` before any nested list, if any.
|
|
671
|
+
*/
|
|
474
672
|
function findCheckbox(node) {
|
|
475
673
|
for (const child of node.children) {
|
|
476
674
|
if (child.type !== "element") continue;
|
|
@@ -518,7 +716,9 @@ function normalizeTaskItem(node) {
|
|
|
518
716
|
children: [input, ...content]
|
|
519
717
|
};
|
|
520
718
|
}
|
|
521
|
-
/**
|
|
719
|
+
/**
|
|
720
|
+
* `li` handler that recognizes ProseMirror-style task items, then delegates.
|
|
721
|
+
*/
|
|
522
722
|
const taskAwareListItem = (state, element) => {
|
|
523
723
|
return defaultHandlers.li(state, normalizeTaskItem(element) ?? element);
|
|
524
724
|
};
|
|
@@ -567,7 +767,9 @@ function createProcessor() {
|
|
|
567
767
|
}).freeze();
|
|
568
768
|
}
|
|
569
769
|
const getProcessor = once(createProcessor);
|
|
570
|
-
/**
|
|
770
|
+
/**
|
|
771
|
+
* Convert HTML into Markdown text.
|
|
772
|
+
*/
|
|
571
773
|
function htmlToMarkdown(html) {
|
|
572
774
|
return String(getProcessor().processSync(html));
|
|
573
775
|
}
|
|
@@ -584,43 +786,36 @@ function parsePositiveInteger(raw) {
|
|
|
584
786
|
return value != null && value > 0 ? value : void 0;
|
|
585
787
|
}
|
|
586
788
|
|
|
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
789
|
//#endregion
|
|
605
790
|
//#region src/extensions/inline-runs.ts
|
|
606
791
|
function findAtomMark(marks) {
|
|
607
792
|
return marks.find((mark) => ATOM_MARK_NAMES.has(mark.type.name));
|
|
608
793
|
}
|
|
794
|
+
function findOwnPackMark(marks) {
|
|
795
|
+
return marks.findLast((mark) => isMarkOfType(mark, "mdPack"));
|
|
796
|
+
}
|
|
609
797
|
function hasSyntaxMark(marks) {
|
|
610
798
|
return marks.some((mark) => SYNTAX_MARK_NAMES.has(mark.type.name));
|
|
611
799
|
}
|
|
612
800
|
/**
|
|
613
801
|
* Group a textblock's text nodes into atom units and plain runs. A unit's
|
|
614
|
-
* text nodes
|
|
615
|
-
*
|
|
802
|
+
* text nodes carry equal packs, while an identical neighbour's pack differs
|
|
803
|
+
* by `slot`, so pack equality splits adjacent same-attrs units.
|
|
616
804
|
*/
|
|
617
805
|
function groupInlineRuns(textblock) {
|
|
618
806
|
const runs = [];
|
|
807
|
+
let previousPack;
|
|
619
808
|
textblock.forEach((child) => {
|
|
620
|
-
if (!child.isText || !child.text)
|
|
809
|
+
if (!child.isText || !child.text) {
|
|
810
|
+
previousPack = void 0;
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
621
813
|
const atom = findAtomMark(child.marks);
|
|
814
|
+
const pack = findOwnPackMark(child.marks);
|
|
622
815
|
const last = runs.at(-1);
|
|
623
|
-
|
|
816
|
+
const continuesUnit = atom != null && last?.atom != null && pack != null && previousPack != null && pack.eq(previousPack);
|
|
817
|
+
previousPack = pack;
|
|
818
|
+
if (continuesUnit && last != null) {
|
|
624
819
|
last.text += child.text;
|
|
625
820
|
last.children.push(child);
|
|
626
821
|
return;
|
|
@@ -702,7 +897,9 @@ function syncOpenWrappers(open, next, out) {
|
|
|
702
897
|
});
|
|
703
898
|
}
|
|
704
899
|
}
|
|
705
|
-
/**
|
|
900
|
+
/**
|
|
901
|
+
* A soft break (a literal `\n` in the source) renders as `<br>`.
|
|
902
|
+
*/
|
|
706
903
|
function appendTextWithBreaks(parent, text) {
|
|
707
904
|
const lines = text.split("\n");
|
|
708
905
|
for (const [index, line] of lines.entries()) {
|
|
@@ -758,7 +955,9 @@ function defineHeadingWhitespace() {
|
|
|
758
955
|
whitespace: "pre"
|
|
759
956
|
});
|
|
760
957
|
}
|
|
761
|
-
/**
|
|
958
|
+
/**
|
|
959
|
+
* The clipboard DOM of a heading: semantic inline content plus `data-md`.
|
|
960
|
+
*/
|
|
762
961
|
function headingClipboardDOM(node) {
|
|
763
962
|
const attrs = node.attrs;
|
|
764
963
|
return semanticTextblockDOM(`h${attrs.level}`, node, {
|
|
@@ -766,7 +965,9 @@ function headingClipboardDOM(node) {
|
|
|
766
965
|
"data-closing-hashes": attrs.closingHashes != null ? String(attrs.closingHashes) : void 0
|
|
767
966
|
});
|
|
768
967
|
}
|
|
769
|
-
/**
|
|
968
|
+
/**
|
|
969
|
+
* The clipboard parse rules restoring a heading's source text from `data-md`.
|
|
970
|
+
*/
|
|
770
971
|
function headingFromDOM() {
|
|
771
972
|
return [
|
|
772
973
|
1,
|
|
@@ -839,11 +1040,15 @@ function defineMeowdownParagraphSpec() {
|
|
|
839
1040
|
}
|
|
840
1041
|
});
|
|
841
1042
|
}
|
|
842
|
-
/**
|
|
1043
|
+
/**
|
|
1044
|
+
* The clipboard DOM of a paragraph: semantic inline content plus `data-md`.
|
|
1045
|
+
*/
|
|
843
1046
|
function paragraphClipboardDOM(node) {
|
|
844
1047
|
return semanticTextblockDOM("p", node);
|
|
845
1048
|
}
|
|
846
|
-
/**
|
|
1049
|
+
/**
|
|
1050
|
+
* The clipboard parse rules restoring a paragraph's source text from `data-md`.
|
|
1051
|
+
*/
|
|
847
1052
|
function paragraphFromDOM() {
|
|
848
1053
|
return [createSourceTextRule("p", "paragraph")];
|
|
849
1054
|
}
|
|
@@ -968,7 +1173,9 @@ function appendText(output, text) {
|
|
|
968
1173
|
output.trailingNewlines = text.length - index;
|
|
969
1174
|
}
|
|
970
1175
|
}
|
|
971
|
-
/**
|
|
1176
|
+
/**
|
|
1177
|
+
* Recursive worker of {@link extractStyledPlainText}.
|
|
1178
|
+
*/
|
|
972
1179
|
function appendNodeText(node, output, insideLine) {
|
|
973
1180
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
974
1181
|
appendText(output, node.nodeValue ?? "");
|
|
@@ -1103,7 +1310,9 @@ function definePlainTextPaste() {
|
|
|
1103
1310
|
|
|
1104
1311
|
//#endregion
|
|
1105
1312
|
//#region src/utils/backticks.ts
|
|
1106
|
-
/**
|
|
1313
|
+
/**
|
|
1314
|
+
* Length of the longest run of `charCode` in `text`, at least `min`.
|
|
1315
|
+
*/
|
|
1107
1316
|
function longestCharRun(text, charCode, min = 0) {
|
|
1108
1317
|
let longest = min;
|
|
1109
1318
|
let run = 0;
|
|
@@ -1113,7 +1322,9 @@ function longestCharRun(text, charCode, min = 0) {
|
|
|
1113
1322
|
} else run = 0;
|
|
1114
1323
|
return longest;
|
|
1115
1324
|
}
|
|
1116
|
-
/**
|
|
1325
|
+
/**
|
|
1326
|
+
* Length of the longest run of backticks in `text`, at least `min`.
|
|
1327
|
+
*/
|
|
1117
1328
|
function longestBacktickRun(text, min = 0) {
|
|
1118
1329
|
return longestCharRun(text, 96, min);
|
|
1119
1330
|
}
|
|
@@ -1157,7 +1368,9 @@ function emitFrontmatter(body, out) {
|
|
|
1157
1368
|
out.write("---");
|
|
1158
1369
|
out.closeBlock();
|
|
1159
1370
|
}
|
|
1160
|
-
/**
|
|
1371
|
+
/**
|
|
1372
|
+
* Heading prefixes indexed by level (1..6). Index 0 is a sentinel.
|
|
1373
|
+
*/
|
|
1161
1374
|
const HEADING_PREFIX = [
|
|
1162
1375
|
"",
|
|
1163
1376
|
"# ",
|
|
@@ -1227,7 +1440,9 @@ var MdOut = class {
|
|
|
1227
1440
|
this.emitDeferredBlankLine();
|
|
1228
1441
|
this.deferredBlankPrefix = this.linePrefix;
|
|
1229
1442
|
}
|
|
1230
|
-
/**
|
|
1443
|
+
/**
|
|
1444
|
+
* End the current block; the next write gets a blank line before it.
|
|
1445
|
+
*/
|
|
1231
1446
|
closeBlock() {
|
|
1232
1447
|
if (this.atLineStart && this.pendingFirst !== null) {
|
|
1233
1448
|
this.emitDeferredBlankLine();
|
|
@@ -1449,7 +1664,9 @@ function toIndentedCode(code) {
|
|
|
1449
1664
|
for (let i = 0; i < lines.length; i++) if (lines[i] !== "") lines[i] = ` ${lines[i]}`;
|
|
1450
1665
|
return lines.join("\n");
|
|
1451
1666
|
}
|
|
1452
|
-
/**
|
|
1667
|
+
/**
|
|
1668
|
+
* Whether any content line would read as a closing `$$` fence.
|
|
1669
|
+
*/
|
|
1453
1670
|
function hasDollarFenceLine(code) {
|
|
1454
1671
|
return code.split("\n").some((line) => line.trim() === "$$");
|
|
1455
1672
|
}
|
|
@@ -1614,7 +1831,9 @@ function definePlainTextSerializer() {
|
|
|
1614
1831
|
} }
|
|
1615
1832
|
}));
|
|
1616
1833
|
}
|
|
1617
|
-
/**
|
|
1834
|
+
/**
|
|
1835
|
+
* Drop the inline text a hide-mode editor never shows.
|
|
1836
|
+
*/
|
|
1618
1837
|
function stripHiddenInline(slice) {
|
|
1619
1838
|
return new Slice(mapFragment(slice.content), slice.openStart, slice.openEnd);
|
|
1620
1839
|
}
|
|
@@ -1999,141 +2218,6 @@ function executeCommand(view, command) {
|
|
|
1999
2218
|
return command(view.state, view.dispatch, view);
|
|
2000
2219
|
}
|
|
2001
2220
|
|
|
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
2221
|
//#endregion
|
|
2138
2222
|
//#region src/extensions/hidden-run-caret.ts
|
|
2139
2223
|
const snapKey = new PluginKey("meowdown-hidden-run-snap");
|
|
@@ -2494,11 +2578,15 @@ function parseMagicComment(comment) {
|
|
|
2494
2578
|
function toPositiveNumber(value) {
|
|
2495
2579
|
if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.round(value);
|
|
2496
2580
|
}
|
|
2497
|
-
/**
|
|
2581
|
+
/**
|
|
2582
|
+
* The canonical comment meowdown writes for the metadata.
|
|
2583
|
+
*/
|
|
2498
2584
|
function formatMagicComment(magic) {
|
|
2499
2585
|
return `<!-- ${JSON.stringify(magic)} -->`;
|
|
2500
2586
|
}
|
|
2501
|
-
/**
|
|
2587
|
+
/**
|
|
2588
|
+
* Drop a trailing magic comment from the source text.
|
|
2589
|
+
*/
|
|
2502
2590
|
function stripMagicComment(source) {
|
|
2503
2591
|
return source.replace(TRAILING_MAGIC_COMMENT_RE, "");
|
|
2504
2592
|
}
|
|
@@ -2619,7 +2707,9 @@ function positiveInteger(value) {
|
|
|
2619
2707
|
const parsed = Number.parseInt(value, 10);
|
|
2620
2708
|
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
2621
2709
|
}
|
|
2622
|
-
/**
|
|
2710
|
+
/**
|
|
2711
|
+
* Parse `![[target]]`, `![[target|alias]]`, `![[target|width]]`, or `![[target|widthxheight]]`.
|
|
2712
|
+
*/
|
|
2623
2713
|
function parseWikiEmbed(source) {
|
|
2624
2714
|
const inner = source.replace(/^!\[\[/, "").replace(/\]\]$/, "");
|
|
2625
2715
|
const pipe = inner.lastIndexOf("|");
|
|
@@ -2653,11 +2743,15 @@ function parseWikiEmbed(source) {
|
|
|
2653
2743
|
height
|
|
2654
2744
|
};
|
|
2655
2745
|
}
|
|
2656
|
-
/**
|
|
2746
|
+
/**
|
|
2747
|
+
* Rewrite a wiki image embed with a persisted display size.
|
|
2748
|
+
*/
|
|
2657
2749
|
function formatSizedWikiEmbed(target, width, height) {
|
|
2658
2750
|
return `![[${target}|${Math.round(width)}x${Math.round(height)}]]`;
|
|
2659
2751
|
}
|
|
2660
|
-
/**
|
|
2752
|
+
/**
|
|
2753
|
+
* Last path component of a target, with a note heading/block fragment removed.
|
|
2754
|
+
*/
|
|
2661
2755
|
function wikiEmbedBasename(target) {
|
|
2662
2756
|
const path = target.split(/[?#]/, 1)[0];
|
|
2663
2757
|
const segment = path.split(/[/\\]/).findLast(Boolean) ?? path;
|
|
@@ -2670,7 +2764,9 @@ function wikiEmbedBasename(target) {
|
|
|
2670
2764
|
|
|
2671
2765
|
//#endregion
|
|
2672
2766
|
//#region src/extensions/wikilink.ts
|
|
2673
|
-
/**
|
|
2767
|
+
/**
|
|
2768
|
+
* Splits `[[target]]`/`[[target|alias]]` into its target and display label (the alias, or empty).
|
|
2769
|
+
*/
|
|
2674
2770
|
function parseWikilink(text) {
|
|
2675
2771
|
const inner = text.replace(/^\[\[/, "").replace(/\]\]$/, "");
|
|
2676
2772
|
const pipe = inner.indexOf("|");
|
|
@@ -2769,7 +2865,28 @@ function inlineTextToMarkChunksWithContext(marks, text, options, context) {
|
|
|
2769
2865
|
walk(elements, [], 0, text.length, text, marks, out, options, context);
|
|
2770
2866
|
return out;
|
|
2771
2867
|
}
|
|
2772
|
-
/**
|
|
2868
|
+
/**
|
|
2869
|
+
* The pack of one unit starting at `from`. When the chunk ending exactly there
|
|
2870
|
+
* closes a unit whose pack equals this one, create it with `slot: 1` instead:
|
|
2871
|
+
* equal packs would let ProseMirror merge the two units into one text node,
|
|
2872
|
+
* one mark run and one mark view. The neighbour's own pack sits at the same
|
|
2873
|
+
* depth, right after `parentMarks`; at any other depth that position holds a
|
|
2874
|
+
* different mark (or nothing) and never compares equal.
|
|
2875
|
+
*/
|
|
2876
|
+
function createUnitPack(marks, out, parentMarks, from, attrs) {
|
|
2877
|
+
const pack = marks.mdPack.create(attrs);
|
|
2878
|
+
const previous = out.at(-1);
|
|
2879
|
+
if (previous == null || previous[1] !== from) return pack;
|
|
2880
|
+
const neighbourPack = previous[2][parentMarks.length];
|
|
2881
|
+
if (neighbourPack == null || !neighbourPack.eq(pack)) return pack;
|
|
2882
|
+
return marks.mdPack.create({
|
|
2883
|
+
...attrs,
|
|
2884
|
+
slot: 1
|
|
2885
|
+
});
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2888
|
+
* Drop the surrounding `"" '' ()` delimiters of a `LinkTitle` slice and unescape.
|
|
2889
|
+
*/
|
|
2773
2890
|
function unquoteTitle(raw) {
|
|
2774
2891
|
return raw.slice(1, -1).replaceAll(/\\(.)/g, "$1");
|
|
2775
2892
|
}
|
|
@@ -2813,7 +2930,7 @@ function walkGenericNode(node, parentMarks, text, marks, out, options, context)
|
|
|
2813
2930
|
else if (type === LEZER_NODE_IDS.Strikethrough) packKey = "strike";
|
|
2814
2931
|
else if (type === LEZER_NODE_IDS.Highlight) packKey = "highlight";
|
|
2815
2932
|
else if (type === LEZER_NODE_IDS.Autolink) packKey = "autolink";
|
|
2816
|
-
const base = packKey ? [...parentMarks, marks.
|
|
2933
|
+
const base = packKey ? [...parentMarks, createUnitPack(marks, out, parentMarks, node.from, { key: packKey })] : parentMarks;
|
|
2817
2934
|
const maybeMarkName = MARK_NAME_BY_TYPE_ID.get(type);
|
|
2818
2935
|
const childMarks = maybeMarkName ? [...base, marks[maybeMarkName].create()] : base;
|
|
2819
2936
|
if (node.children.length === 0) emit(out, node.from, node.to, childMarks);
|
|
@@ -2836,9 +2953,13 @@ function walkLink(node, parentMarks, text, marks, out, options, context) {
|
|
|
2836
2953
|
walkUnresolvedLink(node, parentMarks, text, marks, out, options, context);
|
|
2837
2954
|
return;
|
|
2838
2955
|
}
|
|
2839
|
-
const
|
|
2840
|
-
if (
|
|
2841
|
-
emit(out, node.from, node.to,
|
|
2956
|
+
const fileMark = claimFileLink(parts, resolution, text, marks, options);
|
|
2957
|
+
if (fileMark) {
|
|
2958
|
+
emit(out, node.from, node.to, [
|
|
2959
|
+
...parentMarks,
|
|
2960
|
+
createUnitPack(marks, out, parentMarks, node.from, { key: "file" }),
|
|
2961
|
+
fileMark
|
|
2962
|
+
]);
|
|
2842
2963
|
return;
|
|
2843
2964
|
}
|
|
2844
2965
|
walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out, options, context);
|
|
@@ -2902,7 +3023,9 @@ function walkUnresolvedLink(node, parentMarks, text, marks, out, options, contex
|
|
|
2902
3023
|
return child.type !== LEZER_NODE_IDS.LinkMark && child.type !== LEZER_NODE_IDS.LinkLabel;
|
|
2903
3024
|
}), parentMarks, node.from, node.to, text, marks, out, options, context);
|
|
2904
3025
|
}
|
|
2905
|
-
/**
|
|
3026
|
+
/**
|
|
3027
|
+
* The last path segment of `href` (query/hash stripped), decoded when possible.
|
|
3028
|
+
*/
|
|
2906
3029
|
function hrefBasename(href) {
|
|
2907
3030
|
const path = href.split(/[?#]/, 1)[0];
|
|
2908
3031
|
const segment = path.split(/[/\\]/).findLast(Boolean) ?? path;
|
|
@@ -2913,12 +3036,12 @@ function hrefBasename(href) {
|
|
|
2913
3036
|
}
|
|
2914
3037
|
}
|
|
2915
3038
|
/**
|
|
2916
|
-
* The
|
|
2917
|
-
* claimed as a file, or `undefined` when the link
|
|
2918
|
-
* resolver is never consulted for a link without a
|
|
2919
|
-
* destination.
|
|
3039
|
+
* The `mdFile` mark for a whole inline or resolved reference link that the
|
|
3040
|
+
* host's `resolveFileLink` claimed as a file, or `undefined` when the link
|
|
3041
|
+
* stays a regular link. The resolver is never consulted for a link without a
|
|
3042
|
+
* closed label or a non-empty destination.
|
|
2920
3043
|
*/
|
|
2921
|
-
function claimFileLink(parts, resolution,
|
|
3044
|
+
function claimFileLink(parts, resolution, text, marks, options) {
|
|
2922
3045
|
const resolveFileLink = options?.resolveFileLink;
|
|
2923
3046
|
if (!resolveFileLink) return void 0;
|
|
2924
3047
|
const { labelFrom, labelTo } = parts;
|
|
@@ -2932,11 +3055,11 @@ function claimFileLink(parts, resolution, parentMarks, text, marks, options) {
|
|
|
2932
3055
|
title
|
|
2933
3056
|
})) return void 0;
|
|
2934
3057
|
const name = label || hrefBasename(href);
|
|
2935
|
-
return
|
|
3058
|
+
return marks.mdFile.create({
|
|
2936
3059
|
href,
|
|
2937
3060
|
name,
|
|
2938
3061
|
title
|
|
2939
|
-
})
|
|
3062
|
+
});
|
|
2940
3063
|
}
|
|
2941
3064
|
/**
|
|
2942
3065
|
* Special walker for `Link` nodes.
|
|
@@ -2968,7 +3091,7 @@ function walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out
|
|
|
2968
3091
|
href,
|
|
2969
3092
|
title
|
|
2970
3093
|
};
|
|
2971
|
-
const pack = marks.
|
|
3094
|
+
const pack = createUnitPack(marks, out, parentMarks, node.from, {
|
|
2972
3095
|
key: "link",
|
|
2973
3096
|
data
|
|
2974
3097
|
});
|
|
@@ -3038,15 +3161,19 @@ function walkImage(node, parentMarks, text, marks, out, options, context, traili
|
|
|
3038
3161
|
const width = trailing?.magic.width ?? null;
|
|
3039
3162
|
const height = trailing?.magic.height ?? null;
|
|
3040
3163
|
const to = trailing?.to ?? node.to;
|
|
3041
|
-
emit(out, node.from, to, [
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3164
|
+
emit(out, node.from, to, [
|
|
3165
|
+
...parentMarks,
|
|
3166
|
+
createUnitPack(marks, out, parentMarks, node.from, { key: "image" }),
|
|
3167
|
+
marks.mdImage.create({
|
|
3168
|
+
src,
|
|
3169
|
+
alt,
|
|
3170
|
+
title,
|
|
3171
|
+
width,
|
|
3172
|
+
height,
|
|
3173
|
+
syntax: null,
|
|
3174
|
+
wikiTarget: null
|
|
3175
|
+
})
|
|
3176
|
+
]);
|
|
3050
3177
|
}
|
|
3051
3178
|
/**
|
|
3052
3179
|
* Special walker for inline math `$formula$`/`$$formula$$`.
|
|
@@ -3065,7 +3192,7 @@ function walkMath(node, parentMarks, text, marks, out) {
|
|
|
3065
3192
|
const formula = text.slice(markNodes[0].to, markNodes[1].from);
|
|
3066
3193
|
const base = [
|
|
3067
3194
|
...parentMarks,
|
|
3068
|
-
marks.
|
|
3195
|
+
createUnitPack(marks, out, parentMarks, node.from, { key: "math" }),
|
|
3069
3196
|
marks.mdMath.create({ formula })
|
|
3070
3197
|
];
|
|
3071
3198
|
emit(out, node.from, markNodes[0].to, [...base, marks.mdMark.create()]);
|
|
@@ -3077,10 +3204,14 @@ function walkMath(node, parentMarks, text, marks, out) {
|
|
|
3077
3204
|
*/
|
|
3078
3205
|
function walkWikilink(node, parentMarks, text, marks, out) {
|
|
3079
3206
|
const { target, display } = parseWikilink(text.slice(node.from, node.to));
|
|
3080
|
-
emit(out, node.from, node.to, [
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3207
|
+
emit(out, node.from, node.to, [
|
|
3208
|
+
...parentMarks,
|
|
3209
|
+
createUnitPack(marks, out, parentMarks, node.from, { key: "wikilink" }),
|
|
3210
|
+
marks.mdWikilink.create({
|
|
3211
|
+
target,
|
|
3212
|
+
display
|
|
3213
|
+
})
|
|
3214
|
+
]);
|
|
3084
3215
|
}
|
|
3085
3216
|
/**
|
|
3086
3217
|
* Resolve `![[target]]` into one of Meowdown's existing source-backed atoms.
|
|
@@ -3097,33 +3228,45 @@ function walkWikiEmbed(node, parentMarks, text, marks, out, options) {
|
|
|
3097
3228
|
if (resolution.kind === "image") {
|
|
3098
3229
|
const src = resolution.src ?? embed.target;
|
|
3099
3230
|
const alt = (resolution.alt ?? embed.display) || wikiEmbedBasename(embed.target);
|
|
3100
|
-
emit(out, node.from, node.to, [
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3231
|
+
emit(out, node.from, node.to, [
|
|
3232
|
+
...parentMarks,
|
|
3233
|
+
createUnitPack(marks, out, parentMarks, node.from, { key: "image" }),
|
|
3234
|
+
marks.mdImage.create({
|
|
3235
|
+
src,
|
|
3236
|
+
alt,
|
|
3237
|
+
title: "",
|
|
3238
|
+
width: embed.width,
|
|
3239
|
+
height: embed.height,
|
|
3240
|
+
syntax: "wikiEmbed",
|
|
3241
|
+
wikiTarget: embed.target
|
|
3242
|
+
})
|
|
3243
|
+
]);
|
|
3109
3244
|
return;
|
|
3110
3245
|
}
|
|
3111
3246
|
if (resolution.kind === "file") {
|
|
3112
3247
|
const href = resolution.href ?? embed.target;
|
|
3113
3248
|
const name = (resolution.name ?? embed.display) || wikiEmbedBasename(embed.target);
|
|
3114
|
-
emit(out, node.from, node.to, [
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3249
|
+
emit(out, node.from, node.to, [
|
|
3250
|
+
...parentMarks,
|
|
3251
|
+
createUnitPack(marks, out, parentMarks, node.from, { key: "file" }),
|
|
3252
|
+
marks.mdFile.create({
|
|
3253
|
+
href,
|
|
3254
|
+
name,
|
|
3255
|
+
title: resolution.title ?? ""
|
|
3256
|
+
})
|
|
3257
|
+
]);
|
|
3119
3258
|
return;
|
|
3120
3259
|
}
|
|
3121
3260
|
const target = resolution.target ?? embed.target;
|
|
3122
3261
|
const display = resolution.display ?? embed.display;
|
|
3123
|
-
emit(out, node.from, node.to, [
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3262
|
+
emit(out, node.from, node.to, [
|
|
3263
|
+
...parentMarks,
|
|
3264
|
+
createUnitPack(marks, out, parentMarks, node.from, { key: "wikilink" }),
|
|
3265
|
+
marks.mdWikilink.create({
|
|
3266
|
+
target,
|
|
3267
|
+
display
|
|
3268
|
+
})
|
|
3269
|
+
]);
|
|
3127
3270
|
}
|
|
3128
3271
|
/**
|
|
3129
3272
|
* Push `[from, to, marks]` to `out`, coalescing with the previous chunk
|
|
@@ -3513,7 +3656,9 @@ function defineMdTag() {
|
|
|
3513
3656
|
parseDOM: [{ tag: "span.md-tag" }]
|
|
3514
3657
|
});
|
|
3515
3658
|
}
|
|
3516
|
-
/**
|
|
3659
|
+
/**
|
|
3660
|
+
* Covers the whole `[[target]]`/`[[target|alias]]` source.
|
|
3661
|
+
*/
|
|
3517
3662
|
function defineMdWikilink() {
|
|
3518
3663
|
return defineMarkSpec({
|
|
3519
3664
|
name: "mdWikilink",
|
|
@@ -3547,7 +3692,9 @@ function defineMdFile() {
|
|
|
3547
3692
|
parseDOM: [{ tag: "span.md-file" }]
|
|
3548
3693
|
});
|
|
3549
3694
|
}
|
|
3550
|
-
/**
|
|
3695
|
+
/**
|
|
3696
|
+
* Covers the whole `$formula$` source, dollars included.
|
|
3697
|
+
*/
|
|
3551
3698
|
function defineMdMath() {
|
|
3552
3699
|
return defineMarkSpec({
|
|
3553
3700
|
name: "mdMath",
|
|
@@ -3562,9 +3709,10 @@ function defineMdMath() {
|
|
|
3562
3709
|
});
|
|
3563
3710
|
}
|
|
3564
3711
|
/**
|
|
3565
|
-
* Wraps a whole
|
|
3566
|
-
* link, autolink,
|
|
3567
|
-
*
|
|
3712
|
+
* Wraps a whole inline unit. For a revealable unit (emphasis, strong, code,
|
|
3713
|
+
* strikethrough, link, autolink, math) focus mode reveals the unit with one
|
|
3714
|
+
* range lookup instead of stitching its punctuation back together; an atom
|
|
3715
|
+
* unit (wikilink, image, file) carries it purely as unit identity.
|
|
3568
3716
|
* `excludes: ''` lets nested units carry two of these marks at once.
|
|
3569
3717
|
*/
|
|
3570
3718
|
function defineMdPack() {
|
|
@@ -3574,7 +3722,8 @@ function defineMdPack() {
|
|
|
3574
3722
|
inclusive: false,
|
|
3575
3723
|
attrs: {
|
|
3576
3724
|
key: {},
|
|
3577
|
-
data: { default: null }
|
|
3725
|
+
data: { default: null },
|
|
3726
|
+
slot: { default: null }
|
|
3578
3727
|
},
|
|
3579
3728
|
toDOM: (mark) => {
|
|
3580
3729
|
return [
|
|
@@ -3624,7 +3773,9 @@ const MARKER_IDS = /* @__PURE__ */ new Set([
|
|
|
3624
3773
|
LEZER_NODE_IDS.StrikethroughMark,
|
|
3625
3774
|
LEZER_NODE_IDS.HighlightMark
|
|
3626
3775
|
]);
|
|
3627
|
-
/**
|
|
3776
|
+
/**
|
|
3777
|
+
* The opening and closing delimiter tokens of a toggleable node.
|
|
3778
|
+
*/
|
|
3628
3779
|
function delimiters(node) {
|
|
3629
3780
|
return [node.children[0], node.children.at(-1)];
|
|
3630
3781
|
}
|
|
@@ -3672,7 +3823,9 @@ function engulf(nodes, from, to) {
|
|
|
3672
3823
|
}
|
|
3673
3824
|
return [from, to];
|
|
3674
3825
|
}
|
|
3675
|
-
/**
|
|
3826
|
+
/**
|
|
3827
|
+
* Shrink [from, to] so it starts and ends on non-whitespace.
|
|
3828
|
+
*/
|
|
3676
3829
|
function trimRange(text, from, to) {
|
|
3677
3830
|
while (from < to && isSpaceChar(text.charCodeAt(from))) from++;
|
|
3678
3831
|
while (to > from && isSpaceChar(text.charCodeAt(to - 1))) to--;
|
|
@@ -3832,7 +3985,9 @@ function caretPlan(text, pos, spec) {
|
|
|
3832
3985
|
pos
|
|
3833
3986
|
};
|
|
3834
3987
|
}
|
|
3835
|
-
/**
|
|
3988
|
+
/**
|
|
3989
|
+
* Whether `pos` sits where inserted syntax could not parse: inside an atom or inside another span's delimiters.
|
|
3990
|
+
*/
|
|
3836
3991
|
function insideAtom(nodes, pos) {
|
|
3837
3992
|
for (const node of nodes) if (node.from < pos && pos < node.to) {
|
|
3838
3993
|
const content = nestableContent(node);
|
|
@@ -4014,12 +4169,16 @@ function getLinkUnitAt(state, pos) {
|
|
|
4014
4169
|
|
|
4015
4170
|
//#endregion
|
|
4016
4171
|
//#region src/extensions/link-commands.ts
|
|
4017
|
-
/**
|
|
4172
|
+
/**
|
|
4173
|
+
* Normalize a typed URL with the existing autolink logic, else keep it verbatim.
|
|
4174
|
+
*/
|
|
4018
4175
|
function normalizeHref(raw) {
|
|
4019
4176
|
const value = raw.trim();
|
|
4020
4177
|
return value ? getAutolinkHref(value) ?? value : "";
|
|
4021
4178
|
}
|
|
4022
|
-
/**
|
|
4179
|
+
/**
|
|
4180
|
+
* The `( ... )` body for a link: the href plus an optional CommonMark title.
|
|
4181
|
+
*/
|
|
4023
4182
|
function destText(href, title) {
|
|
4024
4183
|
return href + (title ? ` "${title.replaceAll(/(["\\])/g, String.raw`\$1`)}"` : "");
|
|
4025
4184
|
}
|
|
@@ -4059,7 +4218,9 @@ function insertLink({ href, title, wrapText = true } = {}) {
|
|
|
4059
4218
|
return true;
|
|
4060
4219
|
};
|
|
4061
4220
|
}
|
|
4062
|
-
/**
|
|
4221
|
+
/**
|
|
4222
|
+
* Rewrite the `( ... )` of the link at the caret/selection.
|
|
4223
|
+
*/
|
|
4063
4224
|
function updateLink(attrs) {
|
|
4064
4225
|
return (state, dispatch) => {
|
|
4065
4226
|
const link = getLinkUnitAt(state, state.selection.from);
|
|
@@ -4069,7 +4230,9 @@ function updateLink(attrs) {
|
|
|
4069
4230
|
return true;
|
|
4070
4231
|
};
|
|
4071
4232
|
}
|
|
4072
|
-
/**
|
|
4233
|
+
/**
|
|
4234
|
+
* Unwrap the link at the caret: keep the label text, drop the syntax.
|
|
4235
|
+
*/
|
|
4073
4236
|
function removeLink() {
|
|
4074
4237
|
return (state, dispatch) => {
|
|
4075
4238
|
const link = getLinkUnitAt(state, state.selection.from);
|
|
@@ -4290,26 +4453,38 @@ const listInputRules = [
|
|
|
4290
4453
|
function defineMeowdownListInputRules() {
|
|
4291
4454
|
return union(listInputRules.map(defineInputRule));
|
|
4292
4455
|
}
|
|
4293
|
-
/**
|
|
4456
|
+
/**
|
|
4457
|
+
* Circle checkbox task: a `task` list item with a `+` marker.
|
|
4458
|
+
*/
|
|
4294
4459
|
function wrapInCircleTask() {
|
|
4295
4460
|
return wrapInList({
|
|
4296
4461
|
kind: "task",
|
|
4297
4462
|
marker: "+"
|
|
4298
4463
|
});
|
|
4299
4464
|
}
|
|
4300
|
-
/**
|
|
4465
|
+
/**
|
|
4466
|
+
* Square checkbox task: a `task` list item with the canonical `-` marker.
|
|
4467
|
+
*/
|
|
4301
4468
|
function wrapInSquareTask() {
|
|
4302
4469
|
return wrapInList({
|
|
4303
4470
|
kind: "task",
|
|
4304
4471
|
marker: null
|
|
4305
4472
|
});
|
|
4306
4473
|
}
|
|
4307
|
-
/**
|
|
4474
|
+
/**
|
|
4475
|
+
* The attributes of the list item whose own content holds the selection, if
|
|
4476
|
+
* any. A continuation block deeper inside an item (a paragraph after the
|
|
4477
|
+
* item's first block) is not the item's content, so it reports no list: the
|
|
4478
|
+
* cycle commands then wrap it into a new nested list instead of reading the
|
|
4479
|
+
* ancestor's kind.
|
|
4480
|
+
*/
|
|
4308
4481
|
function getListAttrsAtSelection(state) {
|
|
4309
|
-
const {
|
|
4482
|
+
const { selection } = state;
|
|
4483
|
+
if (isNodeSelection(selection) && isNodeOfType(selection.node, "list")) return selection.node.attrs;
|
|
4484
|
+
const { $from } = selection;
|
|
4310
4485
|
for (let depth = $from.depth; depth > 0; depth--) {
|
|
4311
4486
|
const node = $from.node(depth);
|
|
4312
|
-
if (isNodeOfType(node, "list")) return node.attrs;
|
|
4487
|
+
if (isNodeOfType(node, "list")) return $from.index(depth) === 0 ? node.attrs : null;
|
|
4313
4488
|
}
|
|
4314
4489
|
return null;
|
|
4315
4490
|
}
|
|
@@ -4340,18 +4515,18 @@ function cycleCheckableList() {
|
|
|
4340
4515
|
function cycleBulletOrderedList() {
|
|
4341
4516
|
return (state, dispatch, view) => {
|
|
4342
4517
|
const attrs = getListAttrsAtSelection(state);
|
|
4343
|
-
|
|
4518
|
+
if (attrs?.kind === "bullet" || attrs?.kind === "ordered") return toggleList({
|
|
4344
4519
|
kind: "ordered",
|
|
4345
4520
|
marker: null,
|
|
4346
4521
|
checked: false,
|
|
4347
4522
|
collapsed: false
|
|
4348
|
-
}
|
|
4523
|
+
})(state, dispatch, view);
|
|
4524
|
+
return wrapInList({
|
|
4349
4525
|
kind: "bullet",
|
|
4350
4526
|
marker: null,
|
|
4351
4527
|
checked: false,
|
|
4352
4528
|
collapsed: false
|
|
4353
|
-
};
|
|
4354
|
-
return toggleList(next)(state, dispatch, view);
|
|
4529
|
+
})(state, dispatch, view);
|
|
4355
4530
|
};
|
|
4356
4531
|
}
|
|
4357
4532
|
function toggleListCollapsed() {
|
|
@@ -4575,7 +4750,9 @@ var MathMarkView = class {
|
|
|
4575
4750
|
});
|
|
4576
4751
|
}
|
|
4577
4752
|
};
|
|
4578
|
-
/**
|
|
4753
|
+
/**
|
|
4754
|
+
* Inline math rendering: a KaTeX preview on the `mdMath` mark.
|
|
4755
|
+
*/
|
|
4579
4756
|
function defineMath() {
|
|
4580
4757
|
return defineMarkView({
|
|
4581
4758
|
name: "mdMath",
|
|
@@ -4884,7 +5061,9 @@ function defineMoveBlock() {
|
|
|
4884
5061
|
//#endregion
|
|
4885
5062
|
//#region src/extensions/pending-replacement.ts
|
|
4886
5063
|
const pendingReplacementKey = new PluginKey("meowdownPendingReplacement");
|
|
4887
|
-
/**
|
|
5064
|
+
/**
|
|
5065
|
+
* The active pending replacement, or null when there is none.
|
|
5066
|
+
*/
|
|
4888
5067
|
function getPendingReplacement(state) {
|
|
4889
5068
|
return pendingReplacementKey.getState(state)?.pending ?? null;
|
|
4890
5069
|
}
|
|
@@ -5026,7 +5205,9 @@ function definePendingReplacementCommands() {
|
|
|
5026
5205
|
discardPendingReplacement
|
|
5027
5206
|
});
|
|
5028
5207
|
}
|
|
5029
|
-
/**
|
|
5208
|
+
/**
|
|
5209
|
+
* Accept on Mod-Enter and discard on Escape, only while a replacement is pending.
|
|
5210
|
+
*/
|
|
5030
5211
|
function definePendingReplacementKeymap() {
|
|
5031
5212
|
return defineKeymap({
|
|
5032
5213
|
"Mod-Enter": acceptPendingReplacement(),
|
|
@@ -5343,22 +5524,30 @@ function defineEditorExtension(options = {}) {
|
|
|
5343
5524
|
|
|
5344
5525
|
//#endregion
|
|
5345
5526
|
//#region src/extensions/schema.ts
|
|
5346
|
-
/**
|
|
5527
|
+
/**
|
|
5528
|
+
* The schema shared by every parser and serializer, built once and cached.
|
|
5529
|
+
*/
|
|
5347
5530
|
const getSharedSchema = /* @__PURE__ */ once(() => {
|
|
5348
5531
|
const schema = defineEditorExtension().schema;
|
|
5349
5532
|
if (schema == null) throw new Error("Unexpected empty schema");
|
|
5350
5533
|
return schema;
|
|
5351
5534
|
});
|
|
5352
|
-
/**
|
|
5535
|
+
/**
|
|
5536
|
+
* Typed node builders bound to the shared schema.
|
|
5537
|
+
*/
|
|
5353
5538
|
const getNodeBuilders = /* @__PURE__ */ once(() => {
|
|
5354
5539
|
return createNodeBuilders(getSharedSchema());
|
|
5355
5540
|
});
|
|
5356
|
-
/**
|
|
5541
|
+
/**
|
|
5542
|
+
* Typed mark builders bound to the shared schema.
|
|
5543
|
+
*/
|
|
5357
5544
|
const getMarkBuilders = /* @__PURE__ */ once(() => {
|
|
5358
5545
|
return createMarkBuilders(getSharedSchema());
|
|
5359
5546
|
});
|
|
5360
5547
|
const MARK_BUILDERS_CACHE_KEY = "meowdown_mark_builders";
|
|
5361
|
-
/**
|
|
5548
|
+
/**
|
|
5549
|
+
* Typed mark builders bound to a specific schema, cached per schema.
|
|
5550
|
+
*/
|
|
5362
5551
|
function getMarkBuildersForSchema(schema) {
|
|
5363
5552
|
const cached = schema.cached[MARK_BUILDERS_CACHE_KEY];
|
|
5364
5553
|
if (cached) return cached;
|
|
@@ -5367,7 +5556,9 @@ function getMarkBuildersForSchema(schema) {
|
|
|
5367
5556
|
return builders;
|
|
5368
5557
|
}
|
|
5369
5558
|
const NODE_BUILDERS_CACHE_KEY = "meowdown_node_builders";
|
|
5370
|
-
/**
|
|
5559
|
+
/**
|
|
5560
|
+
* Typed node builders bound to a specific schema, cached per schema.
|
|
5561
|
+
*/
|
|
5371
5562
|
function getNodeBuildersForSchema(schema) {
|
|
5372
5563
|
const cached = schema.cached[NODE_BUILDERS_CACHE_KEY];
|
|
5373
5564
|
if (cached) return cached;
|
|
@@ -5512,7 +5703,9 @@ function convertHeading(nodes, cursor, text, level, isSetext) {
|
|
|
5512
5703
|
closingHashes
|
|
5513
5704
|
}, content);
|
|
5514
5705
|
}
|
|
5515
|
-
/**
|
|
5706
|
+
/**
|
|
5707
|
+
* Count the `=` / `-` characters in a setext underline run.
|
|
5708
|
+
*/
|
|
5516
5709
|
function countUnderlineChars(text, from, to) {
|
|
5517
5710
|
if (from < 0) return 0;
|
|
5518
5711
|
let count = 0;
|
|
@@ -5522,7 +5715,9 @@ function countUnderlineChars(text, from, to) {
|
|
|
5522
5715
|
}
|
|
5523
5716
|
return count;
|
|
5524
5717
|
}
|
|
5525
|
-
/**
|
|
5718
|
+
/**
|
|
5719
|
+
* Count the `#` characters between `from` and `to`.
|
|
5720
|
+
*/
|
|
5526
5721
|
function countHashChars(text, from, to) {
|
|
5527
5722
|
if (from < 0) return 0;
|
|
5528
5723
|
let count = 0;
|
|
@@ -5540,7 +5735,9 @@ function measureContentColumn(text, from) {
|
|
|
5540
5735
|
for (let index = lineStart; index < from; index++) col += text.charCodeAt(index) === 9 ? 4 - col % 4 : 1;
|
|
5541
5736
|
return col;
|
|
5542
5737
|
}
|
|
5543
|
-
/**
|
|
5738
|
+
/**
|
|
5739
|
+
* Drop a line's leading whitespace up to `column`, counting a tab as `4 - col % 4` columns.
|
|
5740
|
+
*/
|
|
5544
5741
|
function sliceColumn(line, column) {
|
|
5545
5742
|
let col = 0;
|
|
5546
5743
|
let index = 0;
|
|
@@ -5632,7 +5829,9 @@ function convertList(nodes, cursor, text, kind) {
|
|
|
5632
5829
|
}
|
|
5633
5830
|
return items;
|
|
5634
5831
|
}
|
|
5635
|
-
/**
|
|
5832
|
+
/**
|
|
5833
|
+
* The marker style at `cursor`, plus the start number of an ordered item.
|
|
5834
|
+
*/
|
|
5636
5835
|
function readListMark(cursor, text, kind) {
|
|
5637
5836
|
if (kind === "ordered") {
|
|
5638
5837
|
const delimiterCode = text.charCodeAt(cursor.to - 1);
|
|
@@ -5859,7 +6058,9 @@ function canonicalizeTableRow(line) {
|
|
|
5859
6058
|
function normalizeLine(line) {
|
|
5860
6059
|
return canonicalizeTableRow(line) ?? collapseWhitespace(line);
|
|
5861
6060
|
}
|
|
5862
|
-
/**
|
|
6061
|
+
/**
|
|
6062
|
+
* Classify how `markdown` survives the editor's parse-then-serialize round trip.
|
|
6063
|
+
*/
|
|
5863
6064
|
function checkRoundTrip(markdown, options = {}) {
|
|
5864
6065
|
const doc = markdownToDoc(markdown, { frontmatter: options.frontmatter });
|
|
5865
6066
|
const serialized = docToMarkdown(doc, { frontmatter: options.frontmatter });
|
|
@@ -6036,7 +6237,9 @@ function applyTweetHeight(iframe, height) {
|
|
|
6036
6237
|
const YOUTUBE_HOSTS = /^(?:www\.|m\.)?(?:youtube\.com|youtube-nocookie\.com)$/i;
|
|
6037
6238
|
const YOUTU_BE_HOST = /^(?:www\.)?youtu\.be$/i;
|
|
6038
6239
|
const VIDEO_ID = /^[\w-]{11}$/;
|
|
6039
|
-
/**
|
|
6240
|
+
/**
|
|
6241
|
+
* Extract `{ videoId, startSeconds? }` from any watch/shorts/embed/live/`youtu.be` URL.
|
|
6242
|
+
*/
|
|
6040
6243
|
function parseYouTube(src) {
|
|
6041
6244
|
let url;
|
|
6042
6245
|
try {
|
|
@@ -6059,7 +6262,9 @@ function parseYouTube(src) {
|
|
|
6059
6262
|
startSeconds
|
|
6060
6263
|
};
|
|
6061
6264
|
}
|
|
6062
|
-
/**
|
|
6265
|
+
/**
|
|
6266
|
+
* `90`, `90s`, `1m30s`, `1h2m3s` to seconds.
|
|
6267
|
+
*/
|
|
6063
6268
|
function parseStartSeconds(value) {
|
|
6064
6269
|
if (/^\d+$/.test(value)) return Number(value);
|
|
6065
6270
|
const matched = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/.exec(value);
|
|
@@ -6085,7 +6290,9 @@ const matchYouTube = (src) => {
|
|
|
6085
6290
|
//#endregion
|
|
6086
6291
|
//#region src/extensions/embed.ts
|
|
6087
6292
|
const EMBED_MATCHERS = [matchYouTube, matchTweet];
|
|
6088
|
-
/**
|
|
6293
|
+
/**
|
|
6294
|
+
* Detect a tweet/YouTube embed in an image `src`, or `undefined` for a plain image.
|
|
6295
|
+
*/
|
|
6089
6296
|
function matchEmbed(src) {
|
|
6090
6297
|
for (const match of EMBED_MATCHERS) {
|
|
6091
6298
|
const descriptor = match(src);
|
|
@@ -6170,7 +6377,9 @@ function createExitBoundaryPlugin(onExitBoundary) {
|
|
|
6170
6377
|
} }
|
|
6171
6378
|
});
|
|
6172
6379
|
}
|
|
6173
|
-
/**
|
|
6380
|
+
/**
|
|
6381
|
+
* Call `onExitBoundary` when an arrow key press would leave the document boundary.
|
|
6382
|
+
*/
|
|
6174
6383
|
function defineExitBoundaryHandler(onExitBoundary) {
|
|
6175
6384
|
return withPriority$1(definePlugin(createExitBoundaryPlugin(onExitBoundary)), Priority$1.low);
|
|
6176
6385
|
}
|
|
@@ -6253,7 +6462,9 @@ function takePastedFiles(data, options) {
|
|
|
6253
6462
|
const defaultOnFileSaveError = (error) => {
|
|
6254
6463
|
console.error("[meowdown] failed to save pasted file:", error);
|
|
6255
6464
|
};
|
|
6256
|
-
/**
|
|
6465
|
+
/**
|
|
6466
|
+
* Escape `\`, `[`, and `]` so a filename stays inside its `[text]` label.
|
|
6467
|
+
*/
|
|
6257
6468
|
function escapeLinkText(name) {
|
|
6258
6469
|
return name.replaceAll(/[\\[\]]/g, String.raw`\$&`);
|
|
6259
6470
|
}
|
|
@@ -6337,7 +6548,9 @@ function formatFileSize(bytes) {
|
|
|
6337
6548
|
|
|
6338
6549
|
//#endregion
|
|
6339
6550
|
//#region src/extensions/file-view.ts
|
|
6340
|
-
/**
|
|
6551
|
+
/**
|
|
6552
|
+
* `data-file-kind` values by file extension, for host CSS theming.
|
|
6553
|
+
*/
|
|
6341
6554
|
const FILE_KIND_BY_EXTENSION = /* @__PURE__ */ new Map([
|
|
6342
6555
|
["pdf", "pdf"],
|
|
6343
6556
|
["zip", "archive"],
|
|
@@ -6368,7 +6581,9 @@ const FILE_KIND_BY_EXTENSION = /* @__PURE__ */ new Map([
|
|
|
6368
6581
|
["txt", "text"],
|
|
6369
6582
|
["md", "text"]
|
|
6370
6583
|
]);
|
|
6371
|
-
/**
|
|
6584
|
+
/**
|
|
6585
|
+
* Classify a file destination for the pill's `data-file-kind` attribute.
|
|
6586
|
+
*/
|
|
6372
6587
|
function getFileKind(href) {
|
|
6373
6588
|
const path = href.split(/[?#]/, 1)[0];
|
|
6374
6589
|
const dot = path.lastIndexOf(".");
|
|
@@ -6377,7 +6592,9 @@ function getFileKind(href) {
|
|
|
6377
6592
|
return FILE_KIND_BY_EXTENSION.get(extension) ?? "generic";
|
|
6378
6593
|
}
|
|
6379
6594
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
6380
|
-
/**
|
|
6595
|
+
/**
|
|
6596
|
+
* A minimal document-outline icon, drawn in `currentColor`.
|
|
6597
|
+
*/
|
|
6381
6598
|
function buildFileIcon() {
|
|
6382
6599
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
6383
6600
|
svg.setAttribute("class", "md-file-view-icon");
|
|
@@ -6546,7 +6763,9 @@ function defineTagClickHandler(onClick) {
|
|
|
6546
6763
|
//#endregion
|
|
6547
6764
|
//#region src/extensions/wikilink-click.ts
|
|
6548
6765
|
const wikilinkClickKey = new PluginKey("meowdown-wikilink-click");
|
|
6549
|
-
/**
|
|
6766
|
+
/**
|
|
6767
|
+
* Exported for tests.
|
|
6768
|
+
*/
|
|
6550
6769
|
function findWikilinkAt(state, pos) {
|
|
6551
6770
|
const range = getMarkRangeAt(state, pos, "mdWikilink");
|
|
6552
6771
|
if (!range) return;
|
|
@@ -6676,7 +6895,9 @@ function findImageForPreview(view, preview) {
|
|
|
6676
6895
|
if (!content) return;
|
|
6677
6896
|
return findImageAt(view.state, view.posAtDOM(content, 0));
|
|
6678
6897
|
}
|
|
6679
|
-
/**
|
|
6898
|
+
/**
|
|
6899
|
+
* Fingers wander a little during a tap; past this it is a scroll or a drag.
|
|
6900
|
+
*/
|
|
6680
6901
|
const TAP_MOVE_TOLERANCE = 10;
|
|
6681
6902
|
function findTouch(touches, identifier) {
|
|
6682
6903
|
return Array.from(touches).find((touch) => touch.identifier === identifier);
|
|
@@ -6767,7 +6988,9 @@ function defineImageClickHandler(onClick) {
|
|
|
6767
6988
|
|
|
6768
6989
|
//#endregion
|
|
6769
6990
|
//#region src/extensions/image.ts
|
|
6770
|
-
/**
|
|
6991
|
+
/**
|
|
6992
|
+
* Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it.
|
|
6993
|
+
*/
|
|
6771
6994
|
function defaultResolveImageUrl(src) {
|
|
6772
6995
|
return /^https?:\/\//i.test(src) ? src : void 0;
|
|
6773
6996
|
}
|
|
@@ -6843,7 +7066,9 @@ function rewriteMagicComment(view, range, patch, addToHistory) {
|
|
|
6843
7066
|
if (!addToHistory) transaction.setMeta("addToHistory", false);
|
|
6844
7067
|
view.dispatch(transaction);
|
|
6845
7068
|
}
|
|
6846
|
-
/**
|
|
7069
|
+
/**
|
|
7070
|
+
* Persist a resized width and height into the trailing magic comment.
|
|
7071
|
+
*/
|
|
6847
7072
|
function commitImageSize(view, content, rawWidth, rawHeight) {
|
|
6848
7073
|
const pos = view.posAtDOM(content, 0);
|
|
6849
7074
|
const range = getMarkRangeAt(view.state, pos, "mdImage");
|
|
@@ -6928,7 +7153,9 @@ var ImageMarkView = class {
|
|
|
6928
7153
|
ignoreMutation(mutation) {
|
|
6929
7154
|
return !this.#contentDOM.contains(mutation.target);
|
|
6930
7155
|
}
|
|
6931
|
-
/**
|
|
7156
|
+
/**
|
|
7157
|
+
* Build the inline preview for the image `src`: an embed iframe or a resizable `<img>`.
|
|
7158
|
+
*/
|
|
6932
7159
|
#renderPreview() {
|
|
6933
7160
|
const { src } = this.#attrs;
|
|
6934
7161
|
const embed = matchEmbed(src);
|
|
@@ -7038,7 +7265,9 @@ function defineImage(options = {}) {
|
|
|
7038
7265
|
|
|
7039
7266
|
//#endregion
|
|
7040
7267
|
//#region src/extensions/key-bindings.ts
|
|
7041
|
-
/**
|
|
7268
|
+
/**
|
|
7269
|
+
* Human-readable descriptions of the editor's formatting and heading shortcuts.
|
|
7270
|
+
*/
|
|
7042
7271
|
const EDITOR_KEY_BINDINGS = {
|
|
7043
7272
|
"Mod-b": "Bold",
|
|
7044
7273
|
"Mod-i": "Italic",
|
|
@@ -7390,7 +7619,9 @@ function defineSubstitutionEnterRules() {
|
|
|
7390
7619
|
});
|
|
7391
7620
|
}));
|
|
7392
7621
|
}
|
|
7393
|
-
/**
|
|
7622
|
+
/**
|
|
7623
|
+
* Apply the editor's automatic plain-text substitutions.
|
|
7624
|
+
*/
|
|
7394
7625
|
function defineSubstitution() {
|
|
7395
7626
|
return union(defineSubstitutionInputRules(), defineSubstitutionUndo(), defineSubstitutionEnterRules());
|
|
7396
7627
|
}
|
|
@@ -7454,7 +7685,9 @@ if (typeof window !== "undefined") {
|
|
|
7454
7685
|
function getIsTouchInput() {
|
|
7455
7686
|
return lastIsTouchInput;
|
|
7456
7687
|
}
|
|
7457
|
-
/**
|
|
7688
|
+
/**
|
|
7689
|
+
* Calls `listener` whenever {@link getIsTouchInput} may report a new value.
|
|
7690
|
+
*/
|
|
7458
7691
|
function onIsTouchInputChange(listener) {
|
|
7459
7692
|
listeners.add(listener);
|
|
7460
7693
|
return () => {
|