@meowdown/core 0.64.2 → 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 CHANGED
@@ -260,7 +260,7 @@ interface MdMathAttrs {
260
260
  * source reveals around the caret.
261
261
  */
262
262
  type MdPackAttrs = {
263
- key: 'italic' | 'bold' | 'code' | 'del' | 'highlight' | 'autolink';
263
+ key: 'italic' | 'bold' | 'code' | 'del' | 'highlight';
264
264
  data?: null;
265
265
  slot?: 1 | null;
266
266
  revealInFocus: true;
@@ -268,13 +268,25 @@ type MdPackAttrs = {
268
268
  } | {
269
269
  key: 'link';
270
270
  data: {
271
+ form: 'inline' | 'reference';
271
272
  href: string;
272
273
  title: string;
273
- isReference: boolean;
274
+ } | {
275
+ form: 'angle';
276
+ href: string;
274
277
  };
275
278
  slot?: 1 | null;
276
279
  revealInFocus: true;
277
280
  revealInHide?: null;
281
+ } | {
282
+ key: 'link';
283
+ data: {
284
+ form: 'bare';
285
+ href: string;
286
+ };
287
+ slot?: 1 | null;
288
+ revealInFocus?: null;
289
+ revealInHide?: null;
278
290
  } | {
279
291
  key: 'math';
280
292
  data?: null;
@@ -296,7 +308,7 @@ interface PositionRange {
296
308
  }
297
309
  //#endregion
298
310
  //#region src/extensions/get-link-unit-at.d.ts
299
- interface LinkUnit {
311
+ interface LinkUnitBase {
300
312
  /**
301
313
  * Whole inline link, reference link, or autolink range.
302
314
  */
@@ -308,14 +320,6 @@ interface LinkUnit {
308
320
  * whose collapsed glyphs measure at bogus coordinates.
309
321
  */
310
322
  text: PositionRange;
311
- /**
312
- * Interior of `[ ]`. Absent for an autolink.
313
- */
314
- label?: PositionRange;
315
- /**
316
- * Interior of `( )`. What `updateLink` rewrites. Absent for an autolink.
317
- */
318
- dest?: PositionRange;
319
323
  /**
320
324
  * The link URL. Could be an empty string.
321
325
  */
@@ -325,6 +329,25 @@ interface LinkUnit {
325
329
  */
326
330
  title: string;
327
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
+ });
328
351
  /**
329
352
  * The link covering `pos`, with its sub-ranges (`label`, `dest`) and parsed
330
353
  * `href`/`title`. The single query the commands and the hover/click handlers
package/dist/index.js CHANGED
@@ -347,19 +347,14 @@ function resolvePosition(state, pos) {
347
347
  return $pos;
348
348
  }
349
349
  /**
350
- * Returns the run of the first listed mark covering `pos`, or `undefined` when
351
- * none covers it. `attrs` narrows the match to marks whose attributes contain
352
- * it, which is how a caller picks one level out of a nested stack of the same
353
- * 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.
354
353
  */
355
354
  function getMarkRangeAt(state, pos, markName, attrs) {
356
355
  const $pos = resolvePosition(state, pos);
357
356
  if (!$pos) return;
358
- const markNames = Array.isArray(markName) ? markName : [markName];
359
- for (const name of markNames) {
360
- const range = ATOM_MARK_NAMES.includes(name) ? getAtomUnitRange(state, $pos, name) : getMarkRange($pos, name, attrs);
361
- if (range) return range;
362
- }
357
+ return ATOM_MARK_NAMES.includes(markName) ? getAtomUnitRange(state, $pos, markName) : getMarkRange($pos, markName, attrs);
363
358
  }
364
359
  /**
365
360
  * The range of the atom unit touching `$pos`, preferring the child to the
@@ -2848,7 +2843,6 @@ function getGenericPackKey(type) {
2848
2843
  case LEZER_NODE_IDS.InlineCode: return "code";
2849
2844
  case LEZER_NODE_IDS.Strikethrough: return "del";
2850
2845
  case LEZER_NODE_IDS.Highlight: return "highlight";
2851
- case LEZER_NODE_IDS.Autolink: return "autolink";
2852
2846
  default: return;
2853
2847
  }
2854
2848
  }
@@ -2914,6 +2908,7 @@ function walkNode(node, parentMarks, text, marks, out, options, context) {
2914
2908
  case LEZER_NODE_IDS.Wikilink: return walkWikilink(node, parentMarks, text, marks, out);
2915
2909
  case LEZER_NODE_IDS.WikiEmbed: return walkWikiEmbed(node, parentMarks, text, marks, out, options);
2916
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);
2917
2912
  case LEZER_NODE_IDS.URL: return walkURL(node, parentMarks, text, marks, out);
2918
2913
  default: return walkGenericNode(node, parentMarks, text, marks, out, options, context);
2919
2914
  }
@@ -2935,14 +2930,55 @@ function walkGenericNode(node, parentMarks, text, marks, out, options, context)
2935
2930
  else walk(node.children, childMarks, node.from, node.to, text, marks, out, options, context);
2936
2931
  }
2937
2932
  /**
2938
- * A standalone `URL` node is a GFM autolink (the address part of a real
2939
- * `[text](url)` is handled inside `walkResolvedLink`, not here). Linkify the
2940
- * shapes we recognize; anything else keeps the muted `mdLinkUri`.
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`.
2941
2964
  */
2942
2965
  function walkURL(node, parentMarks, text, marks, out) {
2943
2966
  const href = getAutolinkHref(text.slice(node.from, node.to));
2944
- const mark = href ? marks.mdLinkText.create({ href }) : marks.mdLinkUri.create();
2945
- emit(out, node.from, node.to, [...parentMarks, mark]);
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
+ ]);
2946
2982
  }
2947
2983
  function walkLink(node, parentMarks, text, marks, out, options, context) {
2948
2984
  const parts = scanLinkParts(node);
@@ -2966,6 +3002,9 @@ function walkLink(node, parentMarks, text, marks, out, options, context) {
2966
3002
  * Locate the pieces of a `Link` node in Lezer's flat child list:
2967
3003
  * LinkMark `[`, [label children], LinkMark `]`, LinkMark `(`, URL,
2968
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.
2969
3008
  */
2970
3009
  function scanLinkParts(node) {
2971
3010
  let labelFrom = -1;
@@ -2982,7 +3021,7 @@ function scanLinkParts(node) {
2982
3021
  bracketCount++;
2983
3022
  if (bracketCount === 1) labelFrom = child.to;
2984
3023
  if (bracketCount === 2) labelTo = child.from;
2985
- } 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;
2986
3025
  else if (titleNode == null && childType === LEZER_NODE_IDS.LinkTitle) titleNode = child;
2987
3026
  else if (referenceLabelNode == null && childType === LEZER_NODE_IDS.LinkLabel) referenceLabelNode = child;
2988
3027
  }
@@ -3084,9 +3123,9 @@ function walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out
3084
3123
  const pack = createUnitPack(marks, out, parentMarks, node.from, {
3085
3124
  key: "link",
3086
3125
  data: {
3126
+ form: isReference ? "reference" : "inline",
3087
3127
  href,
3088
- title,
3089
- isReference
3128
+ title
3090
3129
  },
3091
3130
  revealInFocus: true
3092
3131
  });
@@ -3118,6 +3157,11 @@ function walkResolvedLink(node, parts, resolution, parentMarks, text, marks, out
3118
3157
  pos = child.to;
3119
3158
  continue;
3120
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
+ }
3121
3165
  const maybeMarkName = MARK_NAME_BY_TYPE_ID.get(child.type);
3122
3166
  const childMarks = maybeMarkName ? [...baseForChild, marks[maybeMarkName].create()] : baseForChild;
3123
3167
  if (child.children.length === 0) emit(out, child.from, child.to, childMarks);
@@ -3709,10 +3753,11 @@ function defineMdMath() {
3709
3753
  }
3710
3754
  /**
3711
3755
  * Wraps a whole inline unit. For a revealable unit (emphasis, strong, code,
3712
- * strikethrough, link, autolink, math) focus mode reveals the unit with one
3713
- * range lookup instead of stitching its punctuation back together; an atom
3714
- * unit (wikilink, image, file) carries it purely as unit identity.
3715
- * `excludes: ''` lets nested units carry two of these marks at once.
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.
3716
3761
  */
3717
3762
  function defineMdPack() {
3718
3763
  return defineMarkSpec({
@@ -4106,67 +4151,66 @@ function lastMarkRunIn(state, range, markName) {
4106
4151
  * the `mdLinkUri` run locates the `( )` body.
4107
4152
  */
4108
4153
  function getLinkUnitAt(state, pos) {
4109
- const linkText = getMarkRangeAt(state, pos, "mdLinkText");
4110
- const pack = getMarkRangeAt(state, pos, "mdPack", { key: "link" }) ?? getMarkRangeAt(state, pos, "mdPack", { key: "autolink" });
4111
- const unit = pack ?? linkText;
4154
+ const unit = getMarkRangeAt(state, pos, "mdPack", { key: "link" });
4112
4155
  if (!unit) return;
4113
- const packAttrs = pack?.mark.attrs;
4114
- const href = (linkText?.mark.attrs)?.href ?? "";
4115
- if (!pack || packAttrs?.key !== "link") {
4116
- const unitRange = {
4117
- from: unit.from,
4118
- to: unit.to
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: ""
4119
4168
  };
4120
- return {
4169
+ case "angle": return {
4170
+ form: "angle",
4121
4171
  unit: unitRange,
4122
- text: packAttrs?.key === "autolink" ? lastMarkRunIn(state, unitRange, "mdLinkText") ?? {
4172
+ text: {
4123
4173
  from: unit.from + 1,
4124
4174
  to: unit.to - 1
4125
- } : unitRange,
4126
- href,
4127
- title: ""
4128
- };
4129
- }
4130
- const linkData = packAttrs.data;
4131
- if (linkData.isReference) {
4132
- const text = linkText == null ? {
4133
- from: unit.from,
4134
- to: unit.to
4135
- } : {
4136
- from: linkText.from + 1,
4137
- to: linkText.to
4138
- };
4139
- return {
4140
- unit: {
4141
- from: unit.from,
4142
- to: unit.to
4143
4175
  },
4144
- text,
4145
- href: linkData.href,
4146
- title: linkData.title
4176
+ href: data.href,
4177
+ title: ""
4147
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
+ }
4148
4213
  }
4149
- const uri = lastMarkRunIn(state, unit, "mdLinkUri");
4150
- const closeBracket = uri ? uri.from - 2 : unit.to - 3;
4151
- const destFrom = uri ? uri.from : unit.to - 1;
4152
- const label = {
4153
- from: unit.from + 1,
4154
- to: closeBracket
4155
- };
4156
- return {
4157
- unit: {
4158
- from: unit.from,
4159
- to: unit.to
4160
- },
4161
- text: label,
4162
- label,
4163
- dest: {
4164
- from: destFrom,
4165
- to: unit.to - 1
4166
- },
4167
- href: linkData.href,
4168
- title: linkData.title
4169
- };
4170
4214
  }
4171
4215
 
4172
4216
  //#endregion
@@ -4226,7 +4270,7 @@ function insertLink({ href, title, wrapText = true } = {}) {
4226
4270
  function updateLink(attrs) {
4227
4271
  return (state, dispatch) => {
4228
4272
  const link = getLinkUnitAt(state, state.selection.from);
4229
- if (!link?.dest) return false;
4273
+ if (link?.form !== "inline") return false;
4230
4274
  const dest = destText(normalizeHref(attrs.href ?? link.href), attrs.title ?? link.title);
4231
4275
  if (dispatch) dispatch(state.tr.insertText(dest, link.dest.from, link.dest.to).scrollIntoView());
4232
4276
  return true;
@@ -4238,7 +4282,7 @@ function updateLink(attrs) {
4238
4282
  function removeLink() {
4239
4283
  return (state, dispatch) => {
4240
4284
  const link = getLinkUnitAt(state, state.selection.from);
4241
- if (!link?.label) return false;
4285
+ if (link?.form !== "inline") return false;
4242
4286
  if (dispatch) dispatch(state.tr.delete(link.label.to, link.unit.to).delete(link.unit.from, link.label.from).scrollIntoView());
4243
4287
  return true;
4244
4288
  };
@@ -4254,7 +4298,7 @@ function openLinkEdit(onLinkEdit) {
4254
4298
  return (state, dispatch, view) => {
4255
4299
  const link = getLinkUnitAt(state, state.selection.from);
4256
4300
  if (link) {
4257
- if (link.label == null || link.dest == null) return false;
4301
+ if (link.form !== "inline") return false;
4258
4302
  if (dispatch && view) {
4259
4303
  const { unit: { from, to } } = link;
4260
4304
  dispatch(state.tr.setSelection(TextSelection.create(state.doc, from, to)).scrollIntoView());
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/core",
3
3
  "type": "module",
4
- "version": "0.64.2",
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.2"
46
+ "@meowdown/markdown": "^0.64.3"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@ocavue/tsconfig": "^0.7.1",