@meowdown/core 0.64.1 → 0.64.3
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 +65 -26
- package/dist/index.js +224 -178
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -246,31 +246,59 @@ interface MdMathAttrs {
|
|
|
246
246
|
formula: string;
|
|
247
247
|
}
|
|
248
248
|
/**
|
|
249
|
-
*
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*/
|
|
262
|
-
type MdPackAttrs =
|
|
249
|
+
* Content-derived identity of one inline syntax unit.
|
|
250
|
+
*
|
|
251
|
+
* - `key`: the unit's kind. It keeps adjacent same-kind units apart and stays
|
|
252
|
+
* stable when unrelated text in the block is edited, so editing one unit
|
|
253
|
+
* never re-marks the others.
|
|
254
|
+
* - `data`: the unit's parsed data, read off the mark instead of
|
|
255
|
+
* re-parsing the text.
|
|
256
|
+
* - `slot`: `1` when the pack would otherwise equal the pack of the unit
|
|
257
|
+
* ending exactly where this one starts; equal packs would merge the two
|
|
258
|
+
* units into one mark run and one mark view.
|
|
259
|
+
* - `revealInFocus`/`revealInHide`: the mark modes in which the unit's
|
|
260
|
+
* source reveals around the caret.
|
|
261
|
+
*/
|
|
262
|
+
type MdPackAttrs = {
|
|
263
|
+
key: 'italic' | 'bold' | 'code' | 'del' | 'highlight';
|
|
264
|
+
data?: null;
|
|
265
|
+
slot?: 1 | null;
|
|
266
|
+
revealInFocus: true;
|
|
267
|
+
revealInHide?: null;
|
|
268
|
+
} | {
|
|
263
269
|
key: 'link';
|
|
264
270
|
data: {
|
|
271
|
+
form: 'inline' | 'reference';
|
|
265
272
|
href: string;
|
|
266
273
|
title: string;
|
|
267
|
-
|
|
274
|
+
} | {
|
|
275
|
+
form: 'angle';
|
|
276
|
+
href: string;
|
|
277
|
+
};
|
|
278
|
+
slot?: 1 | null;
|
|
279
|
+
revealInFocus: true;
|
|
280
|
+
revealInHide?: null;
|
|
281
|
+
} | {
|
|
282
|
+
key: 'link';
|
|
283
|
+
data: {
|
|
284
|
+
form: 'bare';
|
|
285
|
+
href: string;
|
|
268
286
|
};
|
|
287
|
+
slot?: 1 | null;
|
|
288
|
+
revealInFocus?: null;
|
|
289
|
+
revealInHide?: null;
|
|
290
|
+
} | {
|
|
291
|
+
key: 'math';
|
|
292
|
+
data?: null;
|
|
293
|
+
slot?: 1 | null;
|
|
294
|
+
revealInFocus: true;
|
|
295
|
+
revealInHide: true;
|
|
269
296
|
} | {
|
|
270
|
-
key:
|
|
297
|
+
key: 'wikilink' | 'image' | 'file';
|
|
271
298
|
data?: null;
|
|
272
|
-
}) & {
|
|
273
299
|
slot?: 1 | null;
|
|
300
|
+
revealInFocus?: null;
|
|
301
|
+
revealInHide?: null;
|
|
274
302
|
};
|
|
275
303
|
//#endregion
|
|
276
304
|
//#region src/utils/range.d.ts
|
|
@@ -280,7 +308,7 @@ interface PositionRange {
|
|
|
280
308
|
}
|
|
281
309
|
//#endregion
|
|
282
310
|
//#region src/extensions/get-link-unit-at.d.ts
|
|
283
|
-
interface
|
|
311
|
+
interface LinkUnitBase {
|
|
284
312
|
/**
|
|
285
313
|
* Whole inline link, reference link, or autolink range.
|
|
286
314
|
*/
|
|
@@ -292,14 +320,6 @@ interface LinkUnit {
|
|
|
292
320
|
* whose collapsed glyphs measure at bogus coordinates.
|
|
293
321
|
*/
|
|
294
322
|
text: PositionRange;
|
|
295
|
-
/**
|
|
296
|
-
* Interior of `[ ]`. Absent for an autolink.
|
|
297
|
-
*/
|
|
298
|
-
label?: PositionRange;
|
|
299
|
-
/**
|
|
300
|
-
* Interior of `( )`. What `updateLink` rewrites. Absent for an autolink.
|
|
301
|
-
*/
|
|
302
|
-
dest?: PositionRange;
|
|
303
323
|
/**
|
|
304
324
|
* The link URL. Could be an empty string.
|
|
305
325
|
*/
|
|
@@ -309,6 +329,25 @@ interface LinkUnit {
|
|
|
309
329
|
*/
|
|
310
330
|
title: string;
|
|
311
331
|
}
|
|
332
|
+
type LinkUnit = (LinkUnitBase & {
|
|
333
|
+
form: 'inline';
|
|
334
|
+
/**
|
|
335
|
+
* Interior of `[ ]`.
|
|
336
|
+
*/
|
|
337
|
+
label: PositionRange;
|
|
338
|
+
/**
|
|
339
|
+
* Interior of `( )`. What `updateLink` rewrites.
|
|
340
|
+
*/
|
|
341
|
+
dest: PositionRange;
|
|
342
|
+
}) | (LinkUnitBase & {
|
|
343
|
+
/**
|
|
344
|
+
* A reference link or autolink resolves an href but has no editable
|
|
345
|
+
* label/dest.
|
|
346
|
+
*/
|
|
347
|
+
form: 'reference' | 'angle' | 'bare';
|
|
348
|
+
label?: undefined;
|
|
349
|
+
dest?: undefined;
|
|
350
|
+
});
|
|
312
351
|
/**
|
|
313
352
|
* The link covering `pos`, with its sub-ranges (`label`, `dest`) and parsed
|
|
314
353
|
* `href`/`title`. The single query the commands and the hover/click handlers
|
package/dist/index.js
CHANGED
|
@@ -88,22 +88,25 @@ function hasPointerSelectionTransaction(transactions) {
|
|
|
88
88
|
function isMarkOfType(mark, name) {
|
|
89
89
|
return mark.type.name === name;
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
function isMarkOfTypes(mark, names) {
|
|
92
|
+
return names.includes(mark.type.name);
|
|
93
|
+
}
|
|
94
|
+
const SYNTAX_MARK_NAMES = [
|
|
92
95
|
"mdMark",
|
|
93
96
|
"mdLinkUri",
|
|
94
97
|
"mdLinkTitle"
|
|
95
|
-
]
|
|
96
|
-
const ATOM_MARK_NAMES =
|
|
98
|
+
];
|
|
99
|
+
const ATOM_MARK_NAMES = [
|
|
97
100
|
"mdWikilink",
|
|
98
101
|
"mdImage",
|
|
99
102
|
"mdFile",
|
|
100
103
|
"mdMath"
|
|
101
|
-
]
|
|
102
|
-
const
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
]
|
|
104
|
+
];
|
|
105
|
+
const ATOM_SOURCE_MARK_NAMES = [
|
|
106
|
+
"mdImage",
|
|
107
|
+
"mdWikilink",
|
|
108
|
+
"mdFile"
|
|
109
|
+
];
|
|
107
110
|
|
|
108
111
|
//#endregion
|
|
109
112
|
//#region src/extensions/mark-mode.ts
|
|
@@ -123,9 +126,8 @@ function createMarkModePlugin(initialMode) {
|
|
|
123
126
|
return { "data-mark-mode": getCurrentMarkMode(state) ?? initialMode };
|
|
124
127
|
},
|
|
125
128
|
decorations: (state) => {
|
|
126
|
-
const mode = getCurrentMarkMode(state);
|
|
127
|
-
|
|
128
|
-
if (mode === "hide") return computeMathRevealDecorations(state);
|
|
129
|
+
const mode = getCurrentMarkMode(state) ?? initialMode;
|
|
130
|
+
return mode === "focus" || mode === "hide" ? computeRevealDecorations(state, mode) : void 0;
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
});
|
|
@@ -137,9 +139,6 @@ function setMarkMode(mode) {
|
|
|
137
139
|
return true;
|
|
138
140
|
};
|
|
139
141
|
}
|
|
140
|
-
function defineMarkMode(mode) {
|
|
141
|
-
return union(definePlugin(createMarkModePlugin(mode)), defineCommands({ setMarkMode }));
|
|
142
|
-
}
|
|
143
142
|
/**
|
|
144
143
|
* The active mark mode. `defineEditorExtension` always applies
|
|
145
144
|
* `defineMarkMode`, so this is `undefined` only for a state built without it.
|
|
@@ -147,51 +146,49 @@ function defineMarkMode(mode) {
|
|
|
147
146
|
function getMarkMode(state) {
|
|
148
147
|
return markModeKey.getState(state);
|
|
149
148
|
}
|
|
150
|
-
|
|
151
|
-
* In focus mode, reveal the markdown syntax of the inline unit under the caret.
|
|
152
|
-
*
|
|
153
|
-
* Every revealable unit (emphasis, strong, code, strikethrough, link, autolink,
|
|
154
|
-
* image) carries one `mdPack` mark spanning it, so a single boundary-inclusive
|
|
155
|
-
* `getMarkRange` finds the unit, returning the outermost when units nest. One
|
|
156
|
-
* decoration over its range flips the hidden punctuation/url/source visible via
|
|
157
|
-
* the `.show` CSS rule. Because the range covers the whole unit, a caret at
|
|
158
|
-
* either edge (e.g. right after a link's `)`) still reveals it. Atom packs
|
|
159
|
-
* (`ATOM_PACK_KEYS`) and `#tag` never reveal.
|
|
160
|
-
*/
|
|
161
|
-
function computeFocusDecorations(state) {
|
|
162
|
-
return computeRevealDecorations(state, void 0);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* In hide mode, reveal only the math unit under the caret. A math unit hides
|
|
166
|
-
* its whole source (content included), so it is the one construct that must
|
|
167
|
-
* still reveal in hide mode to stay editable; everything else follows the
|
|
168
|
-
* hide-mode contract and never reveals.
|
|
169
|
-
*/
|
|
170
|
-
function computeMathRevealDecorations(state) {
|
|
171
|
-
return computeRevealDecorations(state, { key: "math" });
|
|
172
|
-
}
|
|
173
|
-
function computeRevealDecorations(state, packAttrs) {
|
|
174
|
-
const { selection } = state;
|
|
175
|
-
if (!selection.empty) return DecorationSet.empty;
|
|
176
|
-
const $pos = selection.$head;
|
|
149
|
+
function findRevealablePack($pos, mode, direction) {
|
|
177
150
|
const { parent } = $pos;
|
|
178
|
-
if (!parent.isTextblock || parent.type.spec.code) return
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
181
|
-
|
|
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
|
+
}
|
|
182
157
|
}
|
|
183
|
-
|
|
184
|
-
|
|
158
|
+
/**
|
|
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.
|
|
162
|
+
*
|
|
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);
|
|
185
189
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
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;
|
|
190
|
+
function defineMarkMode(mode) {
|
|
191
|
+
return union(definePlugin(createMarkModePlugin(mode)), defineCommands({ setMarkMode }));
|
|
195
192
|
}
|
|
196
193
|
|
|
197
194
|
//#endregion
|
|
@@ -206,7 +203,7 @@ function getCharMarks(state, pos) {
|
|
|
206
203
|
function isHiddenChar(state, pos) {
|
|
207
204
|
const marks = getCharMarks(state, pos);
|
|
208
205
|
if (marks == null) return false;
|
|
209
|
-
return marks.some((mark) =>
|
|
206
|
+
return marks.some((mark) => isMarkOfTypes(mark, SYNTAX_MARK_NAMES));
|
|
210
207
|
}
|
|
211
208
|
function isInsideNonCodeTextblock(state, pos) {
|
|
212
209
|
if (pos < 0 || pos > state.doc.content.size) return false;
|
|
@@ -350,38 +347,35 @@ function resolvePosition(state, pos) {
|
|
|
350
347
|
return $pos;
|
|
351
348
|
}
|
|
352
349
|
/**
|
|
353
|
-
* Returns the run of the
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
* mark.
|
|
350
|
+
* Returns the run of the mark covering `pos`, or `undefined` when none covers
|
|
351
|
+
* it. `attrs` narrows the match to marks whose attributes contain it, which
|
|
352
|
+
* is how a caller picks one level out of a nested stack of the same mark.
|
|
357
353
|
*/
|
|
358
354
|
function getMarkRangeAt(state, pos, markName, attrs) {
|
|
359
355
|
const $pos = resolvePosition(state, pos);
|
|
360
356
|
if (!$pos) return;
|
|
361
|
-
|
|
362
|
-
for (const name of markNames) {
|
|
363
|
-
const range = getMarkRange($pos, name, attrs);
|
|
364
|
-
if (range) return ATOM_MARK_NAMES.has(name) ? clampToUnitRange(state, $pos, range) : range;
|
|
365
|
-
}
|
|
357
|
+
return ATOM_MARK_NAMES.includes(markName) ? getAtomUnitRange(state, $pos, markName) : getMarkRange($pos, markName, attrs);
|
|
366
358
|
}
|
|
367
359
|
/**
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* inline parser)
|
|
360
|
+
* The range of the atom unit touching `$pos`, preferring the child to the
|
|
361
|
+
* right like `getMarkRange`. Two identical adjacent units carry equal atom
|
|
362
|
+
* marks, so eq-based expansion would run across both; the unit boundary comes
|
|
363
|
+
* from the innermost pack instead, which `slot` keeps distinct. A text node
|
|
364
|
+
* without a pack (a document not produced by the inline parser) falls back to
|
|
365
|
+
* the eq-based range. Does not support `attrs` filtering.
|
|
373
366
|
*/
|
|
374
|
-
function
|
|
367
|
+
function getAtomUnitRange(state, $pos, name) {
|
|
375
368
|
const parent = $pos.parent;
|
|
376
369
|
const after = parent.childAfter($pos.parentOffset);
|
|
377
|
-
const matched = after.node &&
|
|
378
|
-
|
|
370
|
+
const matched = after.node && after.node.marks.some((mark) => isMarkOfType(mark, name)) ? after : parent.childBefore($pos.parentOffset);
|
|
371
|
+
const mark = matched.node?.marks.find((candidate) => isMarkOfType(candidate, name));
|
|
372
|
+
if (!matched.node || mark == null) return;
|
|
379
373
|
const pack = getInnermostPackRangeAt(state, $pos.start() + matched.offset);
|
|
380
|
-
if (!pack) return
|
|
374
|
+
if (!pack) return getMarkRange($pos, name);
|
|
381
375
|
return {
|
|
382
|
-
from:
|
|
383
|
-
to:
|
|
384
|
-
mark
|
|
376
|
+
from: pack.from,
|
|
377
|
+
to: pack.to,
|
|
378
|
+
mark
|
|
385
379
|
};
|
|
386
380
|
}
|
|
387
381
|
/**
|
|
@@ -424,15 +418,6 @@ function getMarkRangeStrictlyAround(state, pos, markNames) {
|
|
|
424
418
|
|
|
425
419
|
//#endregion
|
|
426
420
|
//#region src/extensions/atom-mark-navigation.ts
|
|
427
|
-
/**
|
|
428
|
-
* The source marks whose mark views hide the raw text behind a rendered
|
|
429
|
-
* preview (`.md-atom-view-preview`) and act as one caret stop.
|
|
430
|
-
*/
|
|
431
|
-
const ATOM_SOURCE_MARK_NAMES = [
|
|
432
|
-
"mdImage",
|
|
433
|
-
"mdWikilink",
|
|
434
|
-
"mdFile"
|
|
435
|
-
];
|
|
436
421
|
function getActiveMarkNames(marks, state) {
|
|
437
422
|
const mode = getMarkMode(state);
|
|
438
423
|
if (!mode) return [];
|
|
@@ -452,7 +437,7 @@ function getSelectedRange(state, markNames) {
|
|
|
452
437
|
*/
|
|
453
438
|
function getSelectedAtomRange(state) {
|
|
454
439
|
if (!getMarkMode(state)) return;
|
|
455
|
-
return getSelectedRange(state,
|
|
440
|
+
return getSelectedRange(state, ATOM_SOURCE_MARK_NAMES);
|
|
456
441
|
}
|
|
457
442
|
function findSelectionAcrossBlockBoundary(state, pos, markNames, direction) {
|
|
458
443
|
const $pos = state.doc.resolve(pos);
|
|
@@ -789,13 +774,13 @@ function parsePositiveInteger(raw) {
|
|
|
789
774
|
//#endregion
|
|
790
775
|
//#region src/extensions/inline-runs.ts
|
|
791
776
|
function findAtomMark(marks) {
|
|
792
|
-
return marks.find((mark) =>
|
|
777
|
+
return marks.find((mark) => isMarkOfTypes(mark, ATOM_MARK_NAMES));
|
|
793
778
|
}
|
|
794
779
|
function findOwnPackMark(marks) {
|
|
795
780
|
return marks.findLast((mark) => isMarkOfType(mark, "mdPack"));
|
|
796
781
|
}
|
|
797
782
|
function hasSyntaxMark(marks) {
|
|
798
|
-
return marks.some((mark) =>
|
|
783
|
+
return marks.some((mark) => isMarkOfTypes(mark, SYNTAX_MARK_NAMES));
|
|
799
784
|
}
|
|
800
785
|
/**
|
|
801
786
|
* Group a textblock's text nodes into atom units and plain runs. A unit's
|
|
@@ -2851,6 +2836,16 @@ const MARK_NAME_BY_TYPE_ID = /* @__PURE__ */ new Map([
|
|
|
2851
2836
|
[LEZER_NODE_IDS.Hashtag, "mdTag"],
|
|
2852
2837
|
[LEZER_NODE_IDS.WikilinkMark, "mdMark"]
|
|
2853
2838
|
]);
|
|
2839
|
+
function getGenericPackKey(type) {
|
|
2840
|
+
switch (type) {
|
|
2841
|
+
case LEZER_NODE_IDS.Emphasis: return "italic";
|
|
2842
|
+
case LEZER_NODE_IDS.StrongEmphasis: return "bold";
|
|
2843
|
+
case LEZER_NODE_IDS.InlineCode: return "code";
|
|
2844
|
+
case LEZER_NODE_IDS.Strikethrough: return "del";
|
|
2845
|
+
case LEZER_NODE_IDS.Highlight: return "highlight";
|
|
2846
|
+
default: return;
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2854
2849
|
/**
|
|
2855
2850
|
* Walk a textblock's inline content and produce a list of mark chunks
|
|
2856
2851
|
* with positions relative to the start of `text` (i.e. zero-based).
|
|
@@ -2913,6 +2908,7 @@ function walkNode(node, parentMarks, text, marks, out, options, context) {
|
|
|
2913
2908
|
case LEZER_NODE_IDS.Wikilink: return walkWikilink(node, parentMarks, text, marks, out);
|
|
2914
2909
|
case LEZER_NODE_IDS.WikiEmbed: return walkWikiEmbed(node, parentMarks, text, marks, out, options);
|
|
2915
2910
|
case LEZER_NODE_IDS.InlineMath: return walkMath(node, parentMarks, text, marks, out);
|
|
2911
|
+
case LEZER_NODE_IDS.Autolink: return walkAutolink(node, parentMarks, text, marks, out);
|
|
2916
2912
|
case LEZER_NODE_IDS.URL: return walkURL(node, parentMarks, text, marks, out);
|
|
2917
2913
|
default: return walkGenericNode(node, parentMarks, text, marks, out, options, context);
|
|
2918
2914
|
}
|
|
@@ -2922,29 +2918,67 @@ function walkNode(node, parentMarks, text, marks, out, options, context) {
|
|
|
2922
2918
|
* syntax marks, then recurses into its children.
|
|
2923
2919
|
*/
|
|
2924
2920
|
function walkGenericNode(node, parentMarks, text, marks, out, options, context) {
|
|
2925
|
-
const
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
else if (type === LEZER_NODE_IDS.Autolink) packKey = "autolink";
|
|
2933
|
-
const base = packKey ? [...parentMarks, createUnitPack(marks, out, parentMarks, node.from, { key: packKey })] : parentMarks;
|
|
2934
|
-
const maybeMarkName = MARK_NAME_BY_TYPE_ID.get(type);
|
|
2921
|
+
const packKey = getGenericPackKey(node.type);
|
|
2922
|
+
const packMark = packKey && createUnitPack(marks, out, parentMarks, node.from, {
|
|
2923
|
+
key: packKey,
|
|
2924
|
+
revealInFocus: true
|
|
2925
|
+
});
|
|
2926
|
+
const base = packMark ? [...parentMarks, packMark] : parentMarks;
|
|
2927
|
+
const maybeMarkName = MARK_NAME_BY_TYPE_ID.get(node.type);
|
|
2935
2928
|
const childMarks = maybeMarkName ? [...base, marks[maybeMarkName].create()] : base;
|
|
2936
2929
|
if (node.children.length === 0) emit(out, node.from, node.to, childMarks);
|
|
2937
2930
|
else walk(node.children, childMarks, node.from, node.to, text, marks, out, options, context);
|
|
2938
2931
|
}
|
|
2939
2932
|
/**
|
|
2940
|
-
*
|
|
2941
|
-
*
|
|
2942
|
-
*
|
|
2933
|
+
* Special walker for an angle autolink `<url>`. The unit's `href` lives on
|
|
2934
|
+
* its pack, computed from the `URL` child (`''` when the address is not
|
|
2935
|
+
* linkable, in which case the URL keeps the muted `mdLinkUri`). The children
|
|
2936
|
+
* are only the `<`/`>` marks and the `URL`, consumed right here: the `URL`
|
|
2937
|
+
* is a component of this unit, never a unit of its own.
|
|
2938
|
+
*/
|
|
2939
|
+
function walkAutolink(node, parentMarks, text, marks, out) {
|
|
2940
|
+
const urlNode = node.children.find((child) => child.type === LEZER_NODE_IDS.URL);
|
|
2941
|
+
const href = urlNode ? getAutolinkHref(text.slice(urlNode.from, urlNode.to)) ?? "" : "";
|
|
2942
|
+
const base = [...parentMarks, createUnitPack(marks, out, parentMarks, node.from, {
|
|
2943
|
+
key: "link",
|
|
2944
|
+
data: {
|
|
2945
|
+
form: "angle",
|
|
2946
|
+
href
|
|
2947
|
+
},
|
|
2948
|
+
revealInFocus: true
|
|
2949
|
+
})];
|
|
2950
|
+
let pos = node.from;
|
|
2951
|
+
for (const child of node.children) {
|
|
2952
|
+
if (child.from > pos) emit(out, pos, child.from, base);
|
|
2953
|
+
const childMark = child.type === LEZER_NODE_IDS.URL ? href ? marks.mdLinkText.create({ href }) : marks.mdLinkUri.create() : marks.mdMark.create();
|
|
2954
|
+
emit(out, child.from, child.to, [...base, childMark]);
|
|
2955
|
+
pos = child.to;
|
|
2956
|
+
}
|
|
2957
|
+
if (pos < node.to) emit(out, pos, node.to, base);
|
|
2958
|
+
}
|
|
2959
|
+
/**
|
|
2960
|
+
* A `URL` node reaching this walker is a standalone GFM autolink: the address
|
|
2961
|
+
* part of a `[text](url)` link is consumed by `walkResolvedLink`, and an
|
|
2962
|
+
* angle autolink's URL by `walkAutolink`. Linkify the shapes we recognize,
|
|
2963
|
+
* packing each as its own unit; anything else keeps the muted `mdLinkUri`.
|
|
2943
2964
|
*/
|
|
2944
2965
|
function walkURL(node, parentMarks, text, marks, out) {
|
|
2945
2966
|
const href = getAutolinkHref(text.slice(node.from, node.to));
|
|
2946
|
-
|
|
2947
|
-
|
|
2967
|
+
if (!href) {
|
|
2968
|
+
emit(out, node.from, node.to, [...parentMarks, marks.mdLinkUri.create()]);
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
emit(out, node.from, node.to, [
|
|
2972
|
+
...parentMarks,
|
|
2973
|
+
createUnitPack(marks, out, parentMarks, node.from, {
|
|
2974
|
+
key: "link",
|
|
2975
|
+
data: {
|
|
2976
|
+
form: "bare",
|
|
2977
|
+
href
|
|
2978
|
+
}
|
|
2979
|
+
}),
|
|
2980
|
+
marks.mdLinkText.create({ href })
|
|
2981
|
+
]);
|
|
2948
2982
|
}
|
|
2949
2983
|
function walkLink(node, parentMarks, text, marks, out, options, context) {
|
|
2950
2984
|
const parts = scanLinkParts(node);
|
|
@@ -2968,6 +3002,9 @@ function walkLink(node, parentMarks, text, marks, out, options, context) {
|
|
|
2968
3002
|
* Locate the pieces of a `Link` node in Lezer's flat child list:
|
|
2969
3003
|
* LinkMark `[`, [label children], LinkMark `]`, LinkMark `(`, URL,
|
|
2970
3004
|
* optional LinkTitle, LinkMark `)`.
|
|
3005
|
+
*
|
|
3006
|
+
* An autolink inside the label also emits a `URL` child, so only a `URL`
|
|
3007
|
+
* after the second `LinkMark` (the `]` closing the label) is the destination.
|
|
2971
3008
|
*/
|
|
2972
3009
|
function scanLinkParts(node) {
|
|
2973
3010
|
let labelFrom = -1;
|
|
@@ -2984,7 +3021,7 @@ function scanLinkParts(node) {
|
|
|
2984
3021
|
bracketCount++;
|
|
2985
3022
|
if (bracketCount === 1) labelFrom = child.to;
|
|
2986
3023
|
if (bracketCount === 2) labelTo = child.from;
|
|
2987
|
-
} else if (urlNode == null && childType === LEZER_NODE_IDS.URL) urlNode = child;
|
|
3024
|
+
} else if (urlNode == null && bracketCount >= 2 && childType === LEZER_NODE_IDS.URL) urlNode = child;
|
|
2988
3025
|
else if (titleNode == null && childType === LEZER_NODE_IDS.LinkTitle) titleNode = child;
|
|
2989
3026
|
else if (referenceLabelNode == null && childType === LEZER_NODE_IDS.LinkLabel) referenceLabelNode = child;
|
|
2990
3027
|
}
|
|
@@ -3083,17 +3120,14 @@ function walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out
|
|
|
3083
3120
|
const { href, title, isReference } = resolution;
|
|
3084
3121
|
const linkTextMark = marks.mdLinkText.create({ href });
|
|
3085
3122
|
const inLabel = (pos) => labelEnd >= 0 && pos < labelEnd;
|
|
3086
|
-
const data = isReference ? {
|
|
3087
|
-
href,
|
|
3088
|
-
title,
|
|
3089
|
-
reference: true
|
|
3090
|
-
} : {
|
|
3091
|
-
href,
|
|
3092
|
-
title
|
|
3093
|
-
};
|
|
3094
3123
|
const pack = createUnitPack(marks, out, parentMarks, node.from, {
|
|
3095
3124
|
key: "link",
|
|
3096
|
-
data
|
|
3125
|
+
data: {
|
|
3126
|
+
form: isReference ? "reference" : "inline",
|
|
3127
|
+
href,
|
|
3128
|
+
title
|
|
3129
|
+
},
|
|
3130
|
+
revealInFocus: true
|
|
3097
3131
|
});
|
|
3098
3132
|
const base = [...parentMarks, pack];
|
|
3099
3133
|
let pos = node.from;
|
|
@@ -3123,6 +3157,11 @@ function walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out
|
|
|
3123
3157
|
pos = child.to;
|
|
3124
3158
|
continue;
|
|
3125
3159
|
}
|
|
3160
|
+
if (child.type === LEZER_NODE_IDS.URL && inLabel(child.from)) {
|
|
3161
|
+
emit(out, child.from, child.to, baseForChild);
|
|
3162
|
+
pos = child.to;
|
|
3163
|
+
continue;
|
|
3164
|
+
}
|
|
3126
3165
|
const maybeMarkName = MARK_NAME_BY_TYPE_ID.get(child.type);
|
|
3127
3166
|
const childMarks = maybeMarkName ? [...baseForChild, marks[maybeMarkName].create()] : baseForChild;
|
|
3128
3167
|
if (child.children.length === 0) emit(out, child.from, child.to, childMarks);
|
|
@@ -3192,7 +3231,11 @@ function walkMath(node, parentMarks, text, marks, out) {
|
|
|
3192
3231
|
const formula = text.slice(markNodes[0].to, markNodes[1].from);
|
|
3193
3232
|
const base = [
|
|
3194
3233
|
...parentMarks,
|
|
3195
|
-
createUnitPack(marks, out, parentMarks, node.from, {
|
|
3234
|
+
createUnitPack(marks, out, parentMarks, node.from, {
|
|
3235
|
+
key: "math",
|
|
3236
|
+
revealInFocus: true,
|
|
3237
|
+
revealInHide: true
|
|
3238
|
+
}),
|
|
3196
3239
|
marks.mdMath.create({ formula })
|
|
3197
3240
|
];
|
|
3198
3241
|
emit(out, node.from, markNodes[0].to, [...base, marks.mdMark.create()]);
|
|
@@ -3710,10 +3753,11 @@ function defineMdMath() {
|
|
|
3710
3753
|
}
|
|
3711
3754
|
/**
|
|
3712
3755
|
* Wraps a whole inline unit. For a revealable unit (emphasis, strong, code,
|
|
3713
|
-
* strikethrough, link,
|
|
3714
|
-
* range lookup instead of stitching its punctuation back together;
|
|
3715
|
-
* unit (wikilink, image, file) carries
|
|
3716
|
-
* `excludes: ''` lets nested units carry two of these
|
|
3756
|
+
* strikethrough, inline and angle link, math) focus mode reveals the unit
|
|
3757
|
+
* with one range lookup instead of stitching its punctuation back together;
|
|
3758
|
+
* a unit that never reveals (wikilink, image, file, bare autolink) carries
|
|
3759
|
+
* it as unit identity. `excludes: ''` lets nested units carry two of these
|
|
3760
|
+
* marks at once.
|
|
3717
3761
|
*/
|
|
3718
3762
|
function defineMdPack() {
|
|
3719
3763
|
return defineMarkSpec({
|
|
@@ -3723,7 +3767,9 @@ function defineMdPack() {
|
|
|
3723
3767
|
attrs: {
|
|
3724
3768
|
key: {},
|
|
3725
3769
|
data: { default: null },
|
|
3726
|
-
slot: { default: null }
|
|
3770
|
+
slot: { default: null },
|
|
3771
|
+
revealInFocus: { default: null },
|
|
3772
|
+
revealInHide: { default: null }
|
|
3727
3773
|
},
|
|
3728
3774
|
toDOM: (mark) => {
|
|
3729
3775
|
return [
|
|
@@ -4105,66 +4151,66 @@ function lastMarkRunIn(state, range, markName) {
|
|
|
4105
4151
|
* the `mdLinkUri` run locates the `( )` body.
|
|
4106
4152
|
*/
|
|
4107
4153
|
function getLinkUnitAt(state, pos) {
|
|
4108
|
-
const
|
|
4109
|
-
const pack = getMarkRangeAt(state, pos, "mdPack", { key: "link" }) ?? getMarkRangeAt(state, pos, "mdPack", { key: "autolink" });
|
|
4110
|
-
const unit = pack ?? linkText;
|
|
4154
|
+
const unit = getMarkRangeAt(state, pos, "mdPack", { key: "link" });
|
|
4111
4155
|
if (!unit) return;
|
|
4112
|
-
const
|
|
4113
|
-
const
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4156
|
+
const { data } = unit.mark.attrs;
|
|
4157
|
+
const unitRange = {
|
|
4158
|
+
from: unit.from,
|
|
4159
|
+
to: unit.to
|
|
4160
|
+
};
|
|
4161
|
+
switch (data.form) {
|
|
4162
|
+
case "bare": return {
|
|
4163
|
+
form: "bare",
|
|
4164
|
+
unit: unitRange,
|
|
4165
|
+
text: unitRange,
|
|
4166
|
+
href: data.href,
|
|
4167
|
+
title: ""
|
|
4118
4168
|
};
|
|
4119
|
-
return {
|
|
4169
|
+
case "angle": return {
|
|
4170
|
+
form: "angle",
|
|
4120
4171
|
unit: unitRange,
|
|
4121
|
-
text:
|
|
4172
|
+
text: {
|
|
4122
4173
|
from: unit.from + 1,
|
|
4123
4174
|
to: unit.to - 1
|
|
4124
|
-
} : unitRange,
|
|
4125
|
-
href,
|
|
4126
|
-
title: ""
|
|
4127
|
-
};
|
|
4128
|
-
}
|
|
4129
|
-
if (packAttrs.data.reference === true) {
|
|
4130
|
-
const text = linkText == null ? {
|
|
4131
|
-
from: unit.from,
|
|
4132
|
-
to: unit.to
|
|
4133
|
-
} : {
|
|
4134
|
-
from: linkText.from + 1,
|
|
4135
|
-
to: linkText.to
|
|
4136
|
-
};
|
|
4137
|
-
return {
|
|
4138
|
-
unit: {
|
|
4139
|
-
from: unit.from,
|
|
4140
|
-
to: unit.to
|
|
4141
4175
|
},
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
title: packAttrs.data.title
|
|
4176
|
+
href: data.href,
|
|
4177
|
+
title: ""
|
|
4145
4178
|
};
|
|
4179
|
+
case "reference": {
|
|
4180
|
+
const linkText = getMarkRangeAt(state, pos, "mdLinkText");
|
|
4181
|
+
return {
|
|
4182
|
+
form: "reference",
|
|
4183
|
+
unit: unitRange,
|
|
4184
|
+
text: linkText == null ? unitRange : {
|
|
4185
|
+
from: linkText.from + 1,
|
|
4186
|
+
to: linkText.to
|
|
4187
|
+
},
|
|
4188
|
+
href: data.href,
|
|
4189
|
+
title: data.title
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
case "inline": {
|
|
4193
|
+
const uri = lastMarkRunIn(state, unitRange, "mdLinkUri");
|
|
4194
|
+
const closeBracket = uri ? uri.from - 2 : unit.to - 3;
|
|
4195
|
+
const destFrom = uri ? uri.from : unit.to - 1;
|
|
4196
|
+
const label = {
|
|
4197
|
+
from: unit.from + 1,
|
|
4198
|
+
to: closeBracket
|
|
4199
|
+
};
|
|
4200
|
+
return {
|
|
4201
|
+
form: "inline",
|
|
4202
|
+
unit: unitRange,
|
|
4203
|
+
text: label,
|
|
4204
|
+
label,
|
|
4205
|
+
dest: {
|
|
4206
|
+
from: destFrom,
|
|
4207
|
+
to: unit.to - 1
|
|
4208
|
+
},
|
|
4209
|
+
href: data.href,
|
|
4210
|
+
title: data.title
|
|
4211
|
+
};
|
|
4212
|
+
}
|
|
4146
4213
|
}
|
|
4147
|
-
const uri = lastMarkRunIn(state, unit, "mdLinkUri");
|
|
4148
|
-
const closeBracket = uri ? uri.from - 2 : unit.to - 3;
|
|
4149
|
-
const destFrom = uri ? uri.from : unit.to - 1;
|
|
4150
|
-
const label = {
|
|
4151
|
-
from: unit.from + 1,
|
|
4152
|
-
to: closeBracket
|
|
4153
|
-
};
|
|
4154
|
-
return {
|
|
4155
|
-
unit: {
|
|
4156
|
-
from: unit.from,
|
|
4157
|
-
to: unit.to
|
|
4158
|
-
},
|
|
4159
|
-
text: label,
|
|
4160
|
-
label,
|
|
4161
|
-
dest: {
|
|
4162
|
-
from: destFrom,
|
|
4163
|
-
to: unit.to - 1
|
|
4164
|
-
},
|
|
4165
|
-
href: packAttrs.data.href,
|
|
4166
|
-
title: packAttrs.data.title
|
|
4167
|
-
};
|
|
4168
4214
|
}
|
|
4169
4215
|
|
|
4170
4216
|
//#endregion
|
|
@@ -4224,7 +4270,7 @@ function insertLink({ href, title, wrapText = true } = {}) {
|
|
|
4224
4270
|
function updateLink(attrs) {
|
|
4225
4271
|
return (state, dispatch) => {
|
|
4226
4272
|
const link = getLinkUnitAt(state, state.selection.from);
|
|
4227
|
-
if (
|
|
4273
|
+
if (link?.form !== "inline") return false;
|
|
4228
4274
|
const dest = destText(normalizeHref(attrs.href ?? link.href), attrs.title ?? link.title);
|
|
4229
4275
|
if (dispatch) dispatch(state.tr.insertText(dest, link.dest.from, link.dest.to).scrollIntoView());
|
|
4230
4276
|
return true;
|
|
@@ -4236,7 +4282,7 @@ function updateLink(attrs) {
|
|
|
4236
4282
|
function removeLink() {
|
|
4237
4283
|
return (state, dispatch) => {
|
|
4238
4284
|
const link = getLinkUnitAt(state, state.selection.from);
|
|
4239
|
-
if (
|
|
4285
|
+
if (link?.form !== "inline") return false;
|
|
4240
4286
|
if (dispatch) dispatch(state.tr.delete(link.label.to, link.unit.to).delete(link.unit.from, link.label.from).scrollIntoView());
|
|
4241
4287
|
return true;
|
|
4242
4288
|
};
|
|
@@ -4252,7 +4298,7 @@ function openLinkEdit(onLinkEdit) {
|
|
|
4252
4298
|
return (state, dispatch, view) => {
|
|
4253
4299
|
const link = getLinkUnitAt(state, state.selection.from);
|
|
4254
4300
|
if (link) {
|
|
4255
|
-
if (link.
|
|
4301
|
+
if (link.form !== "inline") return false;
|
|
4256
4302
|
if (dispatch && view) {
|
|
4257
4303
|
const { unit: { from, to } } = link;
|
|
4258
4304
|
dispatch(state.tr.setSelection(TextSelection.create(state.doc, from, to)).scrollIntoView());
|
|
@@ -7900,7 +7946,7 @@ function cleanTextFromSlice(slice, options = {}) {
|
|
|
7900
7946
|
blockNode.descendants((textNode) => {
|
|
7901
7947
|
if (!textNode.isText || !textNode.text) return true;
|
|
7902
7948
|
const textNodeMarks = textNode.marks.map((mark) => mark.type.name);
|
|
7903
|
-
if (!(textNodeMarks.some((markName) => SYNTAX_MARK_NAMES.
|
|
7949
|
+
if (!(textNodeMarks.some((markName) => SYNTAX_MARK_NAMES.includes(markName)) && !(options.preserveMathSource && textNodeMarks.includes("mdMath")))) parts.push(textNode.text);
|
|
7904
7950
|
return false;
|
|
7905
7951
|
});
|
|
7906
7952
|
blocks.push(parts.join(""));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meowdown/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.64.
|
|
4
|
+
"version": "0.64.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"remark-stringify": "^11.0.0",
|
|
44
44
|
"unicode-by-name": "^0.2.0",
|
|
45
45
|
"unified": "^11.0.5",
|
|
46
|
-
"@meowdown/markdown": "^0.64.
|
|
46
|
+
"@meowdown/markdown": "^0.64.3"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@ocavue/tsconfig": "^0.7.1",
|