@meowdown/core 0.83.2 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { Priority, Priority as Priority$1, canUseRegexLookbehind, createMarkBuilders, createNodeBuilders, defaultBlockAt, defineBaseCommands, defineBaseKeymap, defineClipboardSerializer, defineCommands, defineHistory, defineKeymap, defineMarkSpec, defineMarkView, defineNodeAttr, defineNodeSpec, definePlugin, findMarkRange, getMarkRange, getMarkType, getNodeType, isAllSelection, isApple, isAtBlockStart, isNodeSelection, isTextSelection, setBlockType, toggleNode, union, unsetBlockType, withPriority, withPriority as withPriority$1, withSkipCodeBlock } from "@prosekit/core";
1
+ import { ATOM_SELECTED_CLASS, ATOM_VIEW_CLASS, ATOM_VIEW_CONTENT_CLASS, ATOM_VIEW_PREVIEW_CLASS, CONTENT_CLASS, EMBED_MENU_BUTTON_CLASS, EMBED_RESIZABLE_CLASS, FILE_MARK_CLASS, FILE_VIEW_CLASS, FILE_VIEW_CONTENT_CLASS, FILE_VIEW_ICON_CLASS, FILE_VIEW_NAME_CLASS, FILE_VIEW_PREVIEW_CLASS, FILE_VIEW_SIZE_CLASS, ICON_BUTTON_CLASS, ICON_CLASS, IMAGE_MARK_CLASS, IMAGE_RESIZABLE_CLASS, IMAGE_RESIZE_HANDLE_CLASS, IMAGE_VIEW_CLASS, IMAGE_VIEW_CONTENT_CLASS, IMAGE_VIEW_PREVIEW_CLASS, LINK_TEXT_MARK_CLASS, LINK_TITLE_MARK_CLASS, LINK_URI_MARK_CLASS, MATH_MARK_CLASS, MATH_VIEW_CLASS, MATH_VIEW_CONTENT_CLASS, MATH_VIEW_PREVIEW_CLASS, PACK_MARK_CLASS, PENDING_REPLACEMENT_CLASS, SHOW_CLASS, SYNTAX_MARK_CLASS, TAG_MARK_CLASS, VIRTUAL_CARET_CLASS, VIRTUAL_CARET_LAYER_CLASS, WIKILINK_MARK_CLASS, WIKILINK_VIEW_CLASS, WIKILINK_VIEW_CONTENT_CLASS, WIKILINK_VIEW_LABEL_CLASS, WIKILINK_VIEW_PREVIEW_CLASS, getIconClass } from "./class-names.js";
2
+ import { Priority, Priority as Priority$1, canUseRegexLookbehind, createMarkBuilders, createNodeBuilders, defaultBlockAt, defineBaseCommands, defineBaseKeymap, defineClipboardSerializer, defineCommands, defineDOMEventHandler, defineHistory, defineKeymap, defineMarkSpec, defineMarkView, defineNodeAttr, defineNodeSpec, definePlugin, findMarkRange, getMarkRange, getMarkType, getNodeType, isAllSelection, isApple, isAtBlockStart, isNodeSelection, isTextSelection, setBlockType, toggleNode, union, unsetBlockType, withPriority, withPriority as withPriority$1, withSkipCodeBlock } from "@prosekit/core";
2
3
  import { codeBlockEnterRule, codeBlockInputRule, defineCodeBlockCommands, defineCodeBlockHighlight, defineCodeBlockKeymap, defineCodeBlockPreviewPlugin, defineCodeBlockSpec, isCodeBlockPreviewHiddenDecoration } from "@prosekit/extensions/code-block";
3
4
  import { definePlaceholder, definePlaceholder as definePlaceholder$1 } from "@prosekit/extensions/placeholder";
4
5
  import { defineSearchCommands, defineSearchQuery, defineSearchStatusHandler, getSearchStatus, getSearchStatus as getSearchStatus$1 } from "@prosekit/extensions/search";
@@ -611,7 +612,6 @@ function createCaretSnapPlugin(marks) {
611
612
  })
612
613
  });
613
614
  }
614
- const SELECTED_CLASS = "md-atom-selected";
615
615
  function createSelectionPlugin(marks) {
616
616
  return new Plugin({
617
617
  key: new PluginKey("atom-mark-selection"),
@@ -620,12 +620,12 @@ function createSelectionPlugin(marks) {
620
620
  if (markNames.length === 0) return;
621
621
  if (isNodeSelection(state.selection)) return;
622
622
  const range = getSelectedRange(state, markNames);
623
- if (range) return DecorationSet.create(state.doc, [Decoration.inline(range.from, range.to, { class: SELECTED_CLASS })]);
623
+ if (range) return DecorationSet.create(state.doc, [Decoration.inline(range.from, range.to, { class: ATOM_SELECTED_CLASS })]);
624
624
  const { from, to, empty } = state.selection;
625
625
  if (empty) return null;
626
626
  const decorations = [];
627
627
  state.doc.nodesBetween(from, to, (node, pos) => {
628
- if (node.marks.some((mark) => markNames.includes(mark.type.name))) decorations.push(Decoration.inline(pos, pos + node.nodeSize, { class: SELECTED_CLASS }));
628
+ if (node.marks.some((mark) => markNames.includes(mark.type.name))) decorations.push(Decoration.inline(pos, pos + node.nodeSize, { class: ATOM_SELECTED_CLASS }));
629
629
  });
630
630
  return DecorationSet.create(state.doc, decorations);
631
631
  }) }
@@ -1990,6 +1990,7 @@ function defineCodeBlock() {
1990
1990
  }
1991
1991
 
1992
1992
  // src/extensions/commands.ts
1993
+ /** @internal */
1993
1994
  function selectText(anchor, head) {
1994
1995
  return (state, dispatch) => {
1995
1996
  if (dispatch) {
@@ -1999,6 +2000,7 @@ function selectText(anchor, head) {
1999
2000
  return true;
2000
2001
  };
2001
2002
  }
2003
+ /** @internal */
2002
2004
  function selectTextBetween($anchor, $head, bias) {
2003
2005
  return (state, dispatch) => {
2004
2006
  if (dispatch) {
@@ -2008,6 +2010,7 @@ function selectTextBetween($anchor, $head, bias) {
2008
2010
  return true;
2009
2011
  };
2010
2012
  }
2013
+ /** @internal */
2011
2014
  function insertMarkdown(markdown) {
2012
2015
  return (state, dispatch) => {
2013
2016
  if (!markdown.trim()) return false;
@@ -2032,6 +2035,8 @@ function insertMarkdown(markdown) {
2032
2035
  * `insertText`. When a non-space character sits right before the caret, a
2033
2036
  * space is inserted first so the trigger can match, like a user would type
2034
2037
  * it. In a code block, where no menu can open, the command does nothing.
2038
+ *
2039
+ * @internal
2035
2040
  */
2036
2041
  function insertTrigger(text) {
2037
2042
  return (state, dispatch) => {
@@ -2053,16 +2058,40 @@ function insertTrigger(text) {
2053
2058
  * Turns the current block into plain text, peeling one layer per call: a
2054
2059
  * non-paragraph textblock becomes a paragraph, then a paragraph in a list
2055
2060
  * loses its marker, then a paragraph in a blockquote lifts out of it.
2061
+ *
2062
+ * @internal
2056
2063
  */
2057
2064
  function turnIntoText() {
2058
2065
  return chainCommands(setBlockType({ type: "paragraph" }), unwrapList(), lift);
2059
2066
  }
2067
+ /** @internal */
2060
2068
  function scrollIntoView() {
2061
2069
  return (state, dispatch) => {
2062
2070
  if (dispatch) dispatch(state.tr.scrollIntoView());
2063
2071
  return true;
2064
2072
  };
2065
2073
  }
2074
+ /**
2075
+ * Deletes the block at `pos` and puts the caret where it was. A block that is
2076
+ * the only child of its parent gives way to an empty default block (usually a
2077
+ * paragraph), since the parent cannot be left empty.
2078
+ *
2079
+ * @internal
2080
+ */
2081
+ function deleteBlock(pos) {
2082
+ return (state, dispatch) => {
2083
+ const $pos = state.doc.resolve(pos);
2084
+ const node = $pos.nodeAfter;
2085
+ if (!node) return false;
2086
+ if (dispatch) {
2087
+ const end = pos + node.nodeSize;
2088
+ const tr = $pos.parent.childCount === 1 ? state.tr.replaceWith(pos, end, $pos.parent.type.contentMatch.defaultType?.createAndFill() ?? []) : state.tr.delete(pos, end);
2089
+ tr.setSelection(TextSelection.near(tr.doc.resolve(Math.min(pos, tr.doc.content.size)), 1));
2090
+ dispatch(tr.scrollIntoView());
2091
+ }
2092
+ return true;
2093
+ };
2094
+ }
2066
2095
  function defineEditorCommands() {
2067
2096
  return defineCommands({
2068
2097
  insertMarkdown,
@@ -2282,6 +2311,32 @@ function defineExitBoundaryHandler(getExitBoundaryHandler) {
2282
2311
  return withPriority$1(definePlugin(createExitBoundaryPlugin(getExitBoundaryHandler)), Priority$1.low);
2283
2312
  }
2284
2313
 
2314
+ // src/utils/atom-view.ts
2315
+ /**
2316
+ * The atom mark view (an image, file, or wiki link) whose source holds
2317
+ * `insidePos`, a position inside the unit rather than on its edges.
2318
+ */
2319
+ function findAtomViewAt(view, insidePos) {
2320
+ const { node } = view.domAtPos(insidePos, 0);
2321
+ return (isElementLike(node) ? node : node.parentElement)?.closest(`.${ATOM_VIEW_CLASS}`);
2322
+ }
2323
+ /**
2324
+ * The rendered preview of the atom mark view whose source holds `insidePos`.
2325
+ */
2326
+ function findAtomPreviewElement(view, insidePos) {
2327
+ return findAtomViewAt(view, insidePos)?.querySelector(`.${ATOM_VIEW_PREVIEW_CLASS}`);
2328
+ }
2329
+ /**
2330
+ * The document position of the source of the atom mark view that holds
2331
+ * `element`. Read it from the hidden content holder, not from an event's
2332
+ * position: an event on the non-editable preview lands on the unit boundary,
2333
+ * where a mark lookup would pick the next adjacent unit.
2334
+ */
2335
+ function getAtomViewSourcePos(view, element) {
2336
+ const content = element.closest(`.${ATOM_VIEW_CLASS}`)?.querySelector(`.${ATOM_VIEW_CONTENT_CLASS}`);
2337
+ return content ? view.posAtDOM(content, 0) : void 0;
2338
+ }
2339
+
2285
2340
  // src/utils/is-mod-event.ts
2286
2341
  /**
2287
2342
  * Whether the platform's mod key is held on `event`: `Command` on Apple, `Ctrl` elsewhere.
@@ -2314,11 +2369,11 @@ function defineFileClickHandler(getOnClick) {
2314
2369
  props: { handleClick: (view, _pos, event) => {
2315
2370
  const handler = getOnClick?.(view.state);
2316
2371
  if (!handler) return false;
2317
- const preview = event.target?.closest?.(".md-file-view-preview");
2372
+ const preview = event.target?.closest?.(`.${FILE_VIEW_PREVIEW_CLASS}`);
2318
2373
  if (!preview) return false;
2319
- const content = preview.closest(".md-file-view")?.querySelector(".md-file-view-content");
2320
- if (!content) return false;
2321
- const hit = findFileAt(view.state, view.posAtDOM(content, 0));
2374
+ const pos = getAtomViewSourcePos(view, preview);
2375
+ if (pos == null) return false;
2376
+ const hit = findFileAt(view.state, pos);
2322
2377
  if (!hit) return false;
2323
2378
  handler({
2324
2379
  href: hit.href,
@@ -2546,7 +2601,7 @@ const SVG_NS = "http://www.w3.org/2000/svg";
2546
2601
  */
2547
2602
  function buildFileIcon() {
2548
2603
  const svg = document.createElementNS(SVG_NS, "svg");
2549
- svg.setAttribute("class", "md-file-view-icon");
2604
+ svg.setAttribute("class", FILE_VIEW_ICON_CLASS);
2550
2605
  svg.setAttribute("viewBox", "0 0 24 24");
2551
2606
  svg.setAttribute("aria-hidden", "true");
2552
2607
  for (const shape of ["M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", "M14 2v4a2 2 0 0 0 2 2h4"]) {
@@ -2572,9 +2627,9 @@ var FileMarkView = class {
2572
2627
  constructor(mark, options) {
2573
2628
  this.#attrs = mark.attrs;
2574
2629
  this.#dom = document.createElement("span");
2575
- this.#dom.className = "md-file-view md-atom-view";
2630
+ this.#dom.className = `${FILE_VIEW_CLASS} ${ATOM_VIEW_CLASS}`;
2576
2631
  this.#preview = document.createElement("span");
2577
- this.#preview.className = "md-file-view-preview md-atom-view-preview";
2632
+ this.#preview.className = `${FILE_VIEW_PREVIEW_CLASS} ${ATOM_VIEW_PREVIEW_CLASS}`;
2578
2633
  this.#preview.contentEditable = "false";
2579
2634
  this.#preview.dataset.testid = "file-pill";
2580
2635
  this.#preview.dataset.fileKind = getFileKind(this.#attrs.href);
@@ -2582,15 +2637,15 @@ var FileMarkView = class {
2582
2637
  this.#dom.appendChild(this.#preview);
2583
2638
  this.#preview.appendChild(buildFileIcon());
2584
2639
  this.#nameElement = document.createElement("span");
2585
- this.#nameElement.className = "md-file-view-name";
2640
+ this.#nameElement.className = FILE_VIEW_NAME_CLASS;
2586
2641
  this.#nameElement.textContent = this.#attrs.name;
2587
2642
  this.#preview.appendChild(this.#nameElement);
2588
2643
  this.#sizeElement = document.createElement("span");
2589
- this.#sizeElement.className = "md-file-view-size";
2644
+ this.#sizeElement.className = FILE_VIEW_SIZE_CLASS;
2590
2645
  this.#sizeElement.dataset.testid = "file-pill-size";
2591
2646
  this.#preview.appendChild(this.#sizeElement);
2592
2647
  this.#contentDOM = document.createElement("span");
2593
- this.#contentDOM.className = "md-file-view-content md-atom-view-content";
2648
+ this.#contentDOM.className = `${FILE_VIEW_CONTENT_CLASS} ${ATOM_VIEW_CONTENT_CLASS}`;
2594
2649
  this.#dom.appendChild(this.#contentDOM);
2595
2650
  this.#loadFileInfo(options?.resolveFileInfo);
2596
2651
  }
@@ -2654,7 +2709,7 @@ function defineFileView(getOptions) {
2654
2709
  const revealActiveMatch = new Plugin({ props: { decorations: guardDecorations((state) => {
2655
2710
  const { from, to, empty } = state.selection;
2656
2711
  if (empty || getSearchStatus$1(state).active === 0) return;
2657
- return DecorationSet.create(state.doc, [Decoration.inline(from, to, { class: "show" })]);
2712
+ return DecorationSet.create(state.doc, [Decoration.inline(from, to, { class: SHOW_CLASS })]);
2658
2713
  }) } });
2659
2714
  /**
2660
2715
  * Find over the editor's text. `setSearchQuery` highlights every match and
@@ -2764,37 +2819,39 @@ function getLinkUnitAt(state, pos) {
2764
2819
  const imageClickKey = new PluginKey("meowdown-image-click");
2765
2820
  /**
2766
2821
  * The image preview wrapper around `target`, or nothing when the wrapper holds
2767
- * a post-embed card: its links and controls have their own click behavior.
2822
+ * a post-embed card (its links and controls have their own click behavior) or
2823
+ * `target` is the preview's menu button.
2768
2824
  */
2769
2825
  function getClosestImagePreview(target) {
2770
2826
  if (!(target instanceof HTMLElement)) return;
2771
- const preview = target.closest(".md-image-view-preview");
2827
+ if (target.closest(`.${"md-embed-menu-button"}`)) return;
2828
+ const preview = target.closest(`.${IMAGE_VIEW_PREVIEW_CLASS}`);
2772
2829
  if (!preview || preview.dataset.postEmbed != null) return;
2773
2830
  return preview;
2774
2831
  }
2775
2832
  function getPreviewImage(preview) {
2776
- return preview.querySelector("img") ?? void 0;
2833
+ return preview.querySelector("img");
2777
2834
  }
2778
2835
  function findImageAt(state, pos) {
2779
2836
  const range = getMarkRangeAt(state, pos, "mdImage");
2780
2837
  if (!range) return;
2781
- const { src, alt } = range.mark.attrs;
2838
+ const { from, to, mark } = range;
2839
+ const { src, alt, syntax } = mark.attrs;
2782
2840
  return {
2783
- from: range.from,
2784
- to: range.to,
2841
+ from,
2842
+ to,
2785
2843
  src,
2786
- alt
2844
+ alt,
2845
+ syntax,
2846
+ kind: matchEmbed(src) ?? "image"
2787
2847
  };
2788
2848
  }
2789
2849
  /**
2790
- * Resolve the image hit for a preview element via its content holder, not the
2791
- * event's document position: an event on the non-editable preview lands on the
2792
- * run boundary, where `getMarkRange` would pick the next adjacent image.
2850
+ * The image whose rendered preview, menu button, or other element is `element`.
2793
2851
  */
2794
- function findImageForPreview(view, preview) {
2795
- const content = preview.closest(".md-image-view")?.querySelector(".md-image-view-content");
2796
- if (!content) return;
2797
- return findImageAt(view.state, view.posAtDOM(content, 0));
2852
+ function findImageForElement(view, element) {
2853
+ const pos = getAtomViewSourcePos(view, element);
2854
+ return pos == null ? void 0 : findImageAt(view.state, pos);
2798
2855
  }
2799
2856
  /**
2800
2857
  * Fingers wander a little during a tap; past this it is a scroll or a drag.
@@ -2830,12 +2887,12 @@ function defineImageClickHandler(getOnClick) {
2830
2887
  const preview = getClosestImagePreview(event.target);
2831
2888
  if (!preview) return false;
2832
2889
  event.preventDefault();
2833
- const hit = findImageForPreview(view, preview);
2890
+ const hit = findImageForElement(view, preview);
2834
2891
  if (hit) handler({
2835
2892
  src: hit.src,
2836
2893
  alt: hit.alt,
2837
2894
  event,
2838
- element: getPreviewImage(preview),
2895
+ element: getPreviewImage(preview) ?? void 0,
2839
2896
  mod: isModEvent(event)
2840
2897
  });
2841
2898
  return true;
@@ -2854,7 +2911,7 @@ function defineImageClickHandler(getOnClick) {
2854
2911
  pendingTaps.delete(view);
2855
2912
  if (event.touches.length !== 1) return false;
2856
2913
  if (!getClosestImagePreview(event.target)) return false;
2857
- if (event.target instanceof HTMLElement && event.target.closest(".md-image-resize-handle")) return false;
2914
+ if (event.target instanceof HTMLElement && event.target.closest(`.${"md-image-resize-handle"}`)) return false;
2858
2915
  const touch = event.changedTouches[0];
2859
2916
  if (!touch) return false;
2860
2917
  pendingTaps.set(view, {
@@ -2882,13 +2939,13 @@ function defineImageClickHandler(getOnClick) {
2882
2939
  if (!handler) return false;
2883
2940
  const preview = getClosestImagePreview(event.target);
2884
2941
  if (!preview) return false;
2885
- const hit = findImageForPreview(view, preview);
2942
+ const hit = findImageForElement(view, preview);
2886
2943
  if (!hit) return false;
2887
2944
  handler({
2888
2945
  src: hit.src,
2889
2946
  alt: hit.alt,
2890
2947
  event,
2891
- element: getPreviewImage(preview),
2948
+ element: getPreviewImage(preview) ?? void 0,
2892
2949
  mod: isModEvent(event)
2893
2950
  });
2894
2951
  return true;
@@ -2945,7 +3002,7 @@ function defineTagClickHandler(getOnClick) {
2945
3002
  return defineMarkClickHandler({
2946
3003
  key: tagClickKey,
2947
3004
  enabled: (state) => !!getOnClick?.(state),
2948
- selector: ".md-tag",
3005
+ selector: `.${TAG_MARK_CLASS}`,
2949
3006
  preventDefault: false,
2950
3007
  findPayloadAt: (state, pos) => findTagAt(state, pos)?.tag,
2951
3008
  onClick: (tag, event, state) => {
@@ -2982,9 +3039,8 @@ function findWikilinkAt(state, pos) {
2982
3039
  * next adjacent mark; the hidden content holder has the exact source position.
2983
3040
  */
2984
3041
  function findWikilinkForElement(view, element) {
2985
- const content = element.closest(".md-wikilink-view")?.querySelector(".md-wikilink-view-content");
2986
- if (!content) return;
2987
- return findWikilinkAt(view.state, view.posAtDOM(content, 0));
3042
+ const pos = getAtomViewSourcePos(view, element);
3043
+ return pos == null ? void 0 : findWikilinkAt(view.state, pos);
2988
3044
  }
2989
3045
  /**
2990
3046
  * Call `onClick` when the user clicks a rendered wikilink label, or presses
@@ -2995,7 +3051,7 @@ function defineWikilinkClickHandler(getOnClick) {
2995
3051
  return defineMarkClickHandler({
2996
3052
  key: wikilinkClickKey,
2997
3053
  enabled: (state) => !!getOnClick?.(state),
2998
- selector: ".md-wikilink-view-preview",
3054
+ selector: `.${WIKILINK_VIEW_PREVIEW_CLASS}`,
2999
3055
  preventDefault: false,
3000
3056
  findPayloadAt: (state, pos) => findWikilinkAt(state, pos)?.target,
3001
3057
  findPayloadForElement: (view, element) => findWikilinkForElement(view, element)?.target,
@@ -3442,6 +3498,32 @@ function wikiEmbedBasename(target) {
3442
3498
 
3443
3499
  // src/extensions/image.ts
3444
3500
  /**
3501
+ * The menu button of an image or card, handled by `defineEmbedMenuHandler`.
3502
+ * It stays out of the tab order: `Mod-/` opens the menu from the keyboard.
3503
+ */
3504
+ function createEmbedMenuButton() {
3505
+ const button = document.createElement("button");
3506
+ button.type = "button";
3507
+ button.className = `${EMBED_MENU_BUTTON_CLASS} ${ICON_BUTTON_CLASS}`;
3508
+ button.tabIndex = -1;
3509
+ button.setAttribute("aria-label", "More");
3510
+ button.setAttribute("aria-haspopup", "menu");
3511
+ button.title = `More (${isApple ? "⌘/" : "Ctrl+/"})`;
3512
+ button.dataset.testid = "embed-menu";
3513
+ const icon = document.createElement("span");
3514
+ icon.className = `${ICON_CLASS} ${getIconClass("ellipsis")}`;
3515
+ icon.setAttribute("aria-hidden", "true");
3516
+ button.appendChild(icon);
3517
+ return button;
3518
+ }
3519
+ /**
3520
+ * The YouTube video saved in a mark's `snapshot` attr, or `null` to fetch it.
3521
+ */
3522
+ function getSavedYouTubeVideo(snapshot) {
3523
+ const saved = snapshot == null ? void 0 : parsePostEmbedSnapshot(snapshot);
3524
+ return saved?.kind === "youtube-video" ? saved.data : null;
3525
+ }
3526
+ /**
3445
3527
  * Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it.
3446
3528
  */
3447
3529
  function defaultResolveImageUrl(src) {
@@ -3566,6 +3648,7 @@ var ImageMarkView = class {
3566
3648
  #attrs;
3567
3649
  #resizableRoot;
3568
3650
  #image;
3651
+ #youtube;
3569
3652
  #destroyed = false;
3570
3653
  constructor(mark, view, options) {
3571
3654
  this.#attrs = mark.attrs;
@@ -3575,9 +3658,9 @@ var ImageMarkView = class {
3575
3658
  this.#mediaUrlProtocols = options.mediaUrlProtocols;
3576
3659
  this.#resolveYouTubeVideo = options.resolveYouTubeVideo ?? defaultResolveYouTubeVideo;
3577
3660
  this.#dom = document.createElement("span");
3578
- this.#dom.className = "md-image-view md-atom-view";
3661
+ this.#dom.className = `${IMAGE_VIEW_CLASS} ${ATOM_VIEW_CLASS}`;
3579
3662
  this.#contentDOM = document.createElement("span");
3580
- this.#contentDOM.className = "md-image-view-content md-atom-view-content";
3663
+ this.#contentDOM.className = `${IMAGE_VIEW_CONTENT_CLASS} ${ATOM_VIEW_CONTENT_CLASS}`;
3581
3664
  this.#contentDOM.dataset.testid = "image-source";
3582
3665
  for (const [name, value] of Object.entries(NON_PROSE_ATTRS)) this.#contentDOM.setAttribute(name, value);
3583
3666
  this.#dom.appendChild(this.#contentDOM);
@@ -3592,8 +3675,9 @@ var ImageMarkView = class {
3592
3675
  update(mark) {
3593
3676
  const next = mark.attrs;
3594
3677
  const previous = this.#attrs;
3595
- if (next.src !== previous.src || next.snapshot !== previous.snapshot) return false;
3678
+ if (next.src !== previous.src) return false;
3596
3679
  this.#attrs = next;
3680
+ if (this.#youtube && next.snapshot !== previous.snapshot) this.#youtube.data = getSavedYouTubeVideo(next.snapshot);
3597
3681
  if (this.#image && next.alt !== previous.alt) this.#image.alt = next.alt;
3598
3682
  if (this.#resizableRoot && (next.width !== previous.width || next.height !== previous.height)) {
3599
3683
  if (this.#image) applyImageDisplaySize(this.#resizableRoot, this.#image, next.width, next.height);
@@ -3620,6 +3704,7 @@ var ImageMarkView = class {
3620
3704
  wrapper.dataset.testid = `${kind}-embed`;
3621
3705
  wrapper.dataset.postEmbed = kind;
3622
3706
  wrapper.appendChild(this.#buildPostEmbed(kind, src));
3707
+ wrapper.appendChild(createEmbedMenuButton());
3623
3708
  this.#insertPreview(wrapper);
3624
3709
  return;
3625
3710
  }
@@ -3653,7 +3738,7 @@ var ImageMarkView = class {
3653
3738
  }
3654
3739
  #createWrapper() {
3655
3740
  const wrapper = document.createElement("span");
3656
- wrapper.className = "md-image-view-preview md-atom-view-preview";
3741
+ wrapper.className = `${IMAGE_VIEW_PREVIEW_CLASS} ${ATOM_VIEW_PREVIEW_CLASS}`;
3657
3742
  return wrapper;
3658
3743
  }
3659
3744
  /**
@@ -3671,13 +3756,13 @@ var ImageMarkView = class {
3671
3756
  const wrapper = this.#createWrapper();
3672
3757
  wrapper.dataset.testid = "image-preview";
3673
3758
  wrapper.appendChild(this.#buildResizableImage(url));
3759
+ wrapper.appendChild(createEmbedMenuButton());
3674
3760
  return wrapper;
3675
3761
  }
3676
3762
  /**
3677
3763
  * Resolve X cards from their URL; YouTube cards may reuse a saved snapshot.
3678
3764
  */
3679
3765
  #buildPostEmbed(kind, src) {
3680
- const saved = this.#attrs.snapshot == null ? void 0 : parsePostEmbedSnapshot(this.#attrs.snapshot);
3681
3766
  if (kind === "x-post") {
3682
3767
  registerXPost();
3683
3768
  const element = document.createElement("meowdown-embed-x");
@@ -3686,13 +3771,17 @@ var ImageMarkView = class {
3686
3771
  element.url = src;
3687
3772
  return element;
3688
3773
  }
3689
- registerYouTubeVideo();
3690
- const element = document.createElement("meowdown-embed-youtube");
3691
- element.playback = "inline";
3692
- element.data = saved?.kind === "youtube-video" ? saved.data : null;
3693
- element.resolver = this.#persisting(kind, this.#resolveYouTubeVideo);
3694
- element.url = src;
3695
- return this.#buildResizableVideo(element);
3774
+ if (kind === "youtube-video") {
3775
+ registerYouTubeVideo();
3776
+ const element = document.createElement("meowdown-embed-youtube");
3777
+ element.playback = "inline";
3778
+ element.data = getSavedYouTubeVideo(this.#attrs.snapshot);
3779
+ element.resolver = this.#persisting(kind, this.#resolveYouTubeVideo);
3780
+ element.url = src;
3781
+ this.#youtube = element;
3782
+ return this.#buildResizableVideo(element);
3783
+ }
3784
+ throw new Error(`Unexpected embed kind ${kind}`);
3696
3785
  }
3697
3786
  /**
3698
3787
  * Wrap a resolver so its first valid answer is persisted under `kind`.
@@ -3722,12 +3811,12 @@ var ImageMarkView = class {
3722
3811
  registerResizableRootElement();
3723
3812
  registerResizableHandleElement();
3724
3813
  const root = document.createElement("prosekit-resizable-root");
3725
- root.className = "md-embed-resizable";
3814
+ root.className = EMBED_RESIZABLE_CLASS;
3726
3815
  root.dataset.testid = "embed-resizable";
3727
3816
  applySize(root, this.#attrs.width, null);
3728
3817
  root.appendChild(element);
3729
3818
  const handle = document.createElement("prosekit-resizable-handle");
3730
- handle.className = "md-image-resize-handle";
3819
+ handle.className = IMAGE_RESIZE_HANDLE_CLASS;
3731
3820
  handle.setAttribute("position", "bottom-right");
3732
3821
  handle.addEventListener("click", (event) => event.stopPropagation());
3733
3822
  root.appendChild(handle);
@@ -3748,12 +3837,12 @@ var ImageMarkView = class {
3748
3837
  registerResizableRootElement();
3749
3838
  registerResizableHandleElement();
3750
3839
  const root = document.createElement("prosekit-resizable-root");
3751
- root.className = "md-image-resizable";
3840
+ root.className = IMAGE_RESIZABLE_CLASS;
3752
3841
  root.dataset.testid = "image-resizable";
3753
3842
  root.setAttribute("data-loading", "");
3754
3843
  applySize(root, this.#attrs.width, this.#attrs.height);
3755
3844
  const handle = document.createElement("prosekit-resizable-handle");
3756
- handle.className = "md-image-resize-handle";
3845
+ handle.className = IMAGE_RESIZE_HANDLE_CLASS;
3757
3846
  handle.setAttribute("position", "bottom-right");
3758
3847
  handle.addEventListener("click", (event) => event.stopPropagation());
3759
3848
  root.appendChild(handle);
@@ -4929,10 +5018,10 @@ function defineMdImage() {
4929
5018
  },
4930
5019
  toDOM: () => [
4931
5020
  "span",
4932
- { class: "md-image" },
5021
+ { class: IMAGE_MARK_CLASS },
4933
5022
  0
4934
5023
  ],
4935
- parseDOM: [{ tag: "span.md-image" }]
5024
+ parseDOM: [{ tag: `span.${IMAGE_MARK_CLASS}` }]
4936
5025
  });
4937
5026
  }
4938
5027
  /**
@@ -4944,10 +5033,10 @@ function defineMdMark() {
4944
5033
  inclusive: false,
4945
5034
  toDOM: () => [
4946
5035
  "span",
4947
- { class: "md-mark" },
5036
+ { class: SYNTAX_MARK_CLASS },
4948
5037
  0
4949
5038
  ],
4950
- parseDOM: [{ tag: "span.md-mark" }]
5039
+ parseDOM: [{ tag: `span.${SYNTAX_MARK_CLASS}` }]
4951
5040
  });
4952
5041
  }
4953
5042
  function defineMdEm() {
@@ -4983,7 +5072,7 @@ function defineMdLinkText() {
4983
5072
  toDOM: (mark) => [
4984
5073
  "a",
4985
5074
  {
4986
- class: "md-link",
5075
+ class: LINK_TEXT_MARK_CLASS,
4987
5076
  href: mark.attrs.href
4988
5077
  },
4989
5078
  0
@@ -5003,12 +5092,12 @@ function defineMdLinkUri() {
5003
5092
  toDOM: () => [
5004
5093
  "span",
5005
5094
  {
5006
- class: "md-link-uri",
5095
+ class: LINK_URI_MARK_CLASS,
5007
5096
  ...NON_PROSE_ATTRS
5008
5097
  },
5009
5098
  0
5010
5099
  ],
5011
- parseDOM: [{ tag: "span.md-link-uri" }]
5100
+ parseDOM: [{ tag: `span.${LINK_URI_MARK_CLASS}` }]
5012
5101
  });
5013
5102
  }
5014
5103
  function defineMdLinkTitle() {
@@ -5017,10 +5106,10 @@ function defineMdLinkTitle() {
5017
5106
  inclusive: false,
5018
5107
  toDOM: () => [
5019
5108
  "span",
5020
- { class: "md-link-title" },
5109
+ { class: LINK_TITLE_MARK_CLASS },
5021
5110
  0
5022
5111
  ],
5023
- parseDOM: [{ tag: "span.md-link-title" }]
5112
+ parseDOM: [{ tag: `span.${LINK_TITLE_MARK_CLASS}` }]
5024
5113
  });
5025
5114
  }
5026
5115
  function defineMdDel() {
@@ -5046,10 +5135,10 @@ function defineMdTag() {
5046
5135
  name: "mdTag",
5047
5136
  toDOM: () => [
5048
5137
  "span",
5049
- { class: "md-tag" },
5138
+ { class: TAG_MARK_CLASS },
5050
5139
  0
5051
5140
  ],
5052
- parseDOM: [{ tag: "span.md-tag" }]
5141
+ parseDOM: [{ tag: `span.${TAG_MARK_CLASS}` }]
5053
5142
  });
5054
5143
  }
5055
5144
  /**
@@ -5065,10 +5154,10 @@ function defineMdWikilink() {
5065
5154
  },
5066
5155
  toDOM: () => [
5067
5156
  "span",
5068
- { class: "md-wikilink" },
5157
+ { class: WIKILINK_MARK_CLASS },
5069
5158
  0
5070
5159
  ],
5071
- parseDOM: [{ tag: "span.md-wikilink" }]
5160
+ parseDOM: [{ tag: `span.${WIKILINK_MARK_CLASS}` }]
5072
5161
  });
5073
5162
  }
5074
5163
  function defineMdFile() {
@@ -5082,10 +5171,10 @@ function defineMdFile() {
5082
5171
  },
5083
5172
  toDOM: () => [
5084
5173
  "span",
5085
- { class: "md-file" },
5174
+ { class: FILE_MARK_CLASS },
5086
5175
  0
5087
5176
  ],
5088
- parseDOM: [{ tag: "span.md-file" }]
5177
+ parseDOM: [{ tag: `span.${FILE_MARK_CLASS}` }]
5089
5178
  });
5090
5179
  }
5091
5180
  /**
@@ -5098,10 +5187,10 @@ function defineMdMath() {
5098
5187
  attrs: { formula: { default: "" } },
5099
5188
  toDOM: () => [
5100
5189
  "span",
5101
- { class: "md-math" },
5190
+ { class: MATH_MARK_CLASS },
5102
5191
  0
5103
5192
  ],
5104
- parseDOM: [{ tag: "span.md-math" }]
5193
+ parseDOM: [{ tag: `span.${MATH_MARK_CLASS}` }]
5105
5194
  });
5106
5195
  }
5107
5196
  /**
@@ -5125,16 +5214,17 @@ function defineMdPack() {
5125
5214
  revealInHide: { default: null }
5126
5215
  },
5127
5216
  toDOM: (mark) => {
5217
+ const attrs = mark.attrs;
5128
5218
  return [
5129
5219
  "span",
5130
5220
  {
5131
- class: "md-pack",
5132
- "data-key": mark.attrs.key
5221
+ class: PACK_MARK_CLASS,
5222
+ "data-key": attrs.key
5133
5223
  },
5134
5224
  0
5135
5225
  ];
5136
5226
  },
5137
- parseDOM: [{ tag: "span.md-pack" }]
5227
+ parseDOM: [{ tag: `span.${PACK_MARK_CLASS}` }]
5138
5228
  });
5139
5229
  }
5140
5230
  function defineInlineMarks() {
@@ -5508,7 +5598,7 @@ function defineLinkClickHandler(getOnClick) {
5508
5598
  return defineMarkClickHandler({
5509
5599
  key: linkClickKey,
5510
5600
  enabled: (state) => !!getOnClick?.(state),
5511
- selector: ".md-link",
5601
+ selector: `.${LINK_TEXT_MARK_CLASS}`,
5512
5602
  preventDefault: true,
5513
5603
  findPayloadAt: (state, pos) => getLinkUnitAt(state, pos)?.href,
5514
5604
  onClick: (href, event, state) => {
@@ -6168,7 +6258,7 @@ function computeRevealDecorations(state, mode) {
6168
6258
  }
6169
6259
  if (ranges.length === 0) return;
6170
6260
  const decorations = ranges.map((range) => {
6171
- return Decoration.inline(range.from, range.to, { class: "show" });
6261
+ return Decoration.inline(range.from, range.to, { class: SHOW_CLASS });
6172
6262
  });
6173
6263
  return DecorationSet.create(state.doc, decorations);
6174
6264
  }
@@ -6220,9 +6310,9 @@ var MathMarkView = class {
6220
6310
  constructor(mark, view) {
6221
6311
  this.#formula = mark.attrs.formula;
6222
6312
  this.#dom = document.createElement("span");
6223
- this.#dom.className = "md-math-view";
6313
+ this.#dom.className = MATH_VIEW_CLASS;
6224
6314
  this.#preview = document.createElement("span");
6225
- this.#preview.className = "md-math-view-preview";
6315
+ this.#preview.className = MATH_VIEW_PREVIEW_CLASS;
6226
6316
  this.#preview.dataset.testid = "math-preview";
6227
6317
  this.#preview.contentEditable = "false";
6228
6318
  this.#preview.addEventListener("mousedown", (event) => {
@@ -6234,7 +6324,7 @@ var MathMarkView = class {
6234
6324
  view.focus();
6235
6325
  });
6236
6326
  this.#contentDOM = document.createElement("span");
6237
- this.#contentDOM.className = "md-math-view-content";
6327
+ this.#contentDOM.className = MATH_VIEW_CONTENT_CLASS;
6238
6328
  for (const [name, value] of Object.entries(NON_PROSE_ATTRS)) this.#contentDOM.setAttribute(name, value);
6239
6329
  this.#dom.appendChild(this.#preview);
6240
6330
  this.#dom.appendChild(this.#contentDOM);
@@ -6643,7 +6733,7 @@ const pendingReplacementPlugin = new Plugin({
6643
6733
  props: { decorations: guardDecorations((state) => {
6644
6734
  const pending = getPendingReplacement(state);
6645
6735
  if (!pending || pending.from >= pending.to) return null;
6646
- return DecorationSet.create(state.doc, [Decoration.inline(pending.from, pending.to, { class: "md-pending-replacement" })]);
6736
+ return DecorationSet.create(state.doc, [Decoration.inline(pending.from, pending.to, { class: PENDING_REPLACEMENT_CLASS })]);
6647
6737
  }) }
6648
6738
  });
6649
6739
  function startPendingReplacement(options) {
@@ -6896,10 +6986,6 @@ function findAtomEdgeRect(view, pos, side) {
6896
6986
  return side === 1 ? fragments[0] : fragments[fragments.length - 1];
6897
6987
  }
6898
6988
  }
6899
- function findAtomPreviewElement(view, insidePos) {
6900
- const { node } = view.domAtPos(insidePos, 0);
6901
- return (node instanceof Element ? node : node.parentElement)?.closest(".md-atom-view")?.querySelector(".md-atom-view-preview") ?? void 0;
6902
- }
6903
6989
  /**
6904
6990
  * The caret rect for scroll targeting: the head-anchored subset of the
6905
6991
  * geometry the virtual caret draws. Skips the native-selection measurement
@@ -7288,19 +7374,19 @@ function createWikilinkMarkView() {
7288
7374
  return (mark) => {
7289
7375
  const attrs = mark.attrs;
7290
7376
  const dom = document.createElement("span");
7291
- dom.className = "md-wikilink-view md-atom-view";
7377
+ dom.className = `${WIKILINK_VIEW_CLASS} ${ATOM_VIEW_CLASS}`;
7292
7378
  const preview = document.createElement("span");
7293
- preview.className = "md-wikilink-view-preview md-atom-view-preview";
7379
+ preview.className = `${WIKILINK_VIEW_PREVIEW_CLASS} ${ATOM_VIEW_PREVIEW_CLASS}`;
7294
7380
  preview.contentEditable = "false";
7295
7381
  preview.dataset.testid = "wikilink";
7296
7382
  dom.appendChild(preview);
7297
7383
  const label = document.createElement("span");
7298
- label.className = "md-wikilink-view-label";
7384
+ label.className = WIKILINK_VIEW_LABEL_CLASS;
7299
7385
  label.contentEditable = "false";
7300
7386
  label.textContent = attrs.display || attrs.target;
7301
7387
  preview.appendChild(label);
7302
7388
  const contentDOM = document.createElement("span");
7303
- contentDOM.className = "md-wikilink-view-content md-atom-view-content";
7389
+ contentDOM.className = `${WIKILINK_VIEW_CONTENT_CLASS} ${ATOM_VIEW_CONTENT_CLASS}`;
7304
7390
  dom.appendChild(contentDOM);
7305
7391
  return {
7306
7392
  dom,
@@ -7367,7 +7453,7 @@ function defineEditorExtensionImpl(options) {
7367
7453
  strategy: "doc"
7368
7454
  }), defineReadonly((state) => !!getEditorConfig(state).readOnly), defineViewAttributes((state) => {
7369
7455
  const config = getEditorConfig(state);
7370
- const attributes = { class: "meowdown-content" };
7456
+ const attributes = { class: CONTENT_CLASS };
7371
7457
  if (config.editorClassName) attributes.class += ` ${config.editorClassName}`;
7372
7458
  if (config.spellCheck != null) attributes.spellcheck = String(config.spellCheck);
7373
7459
  if (config.writingSuggestions != null) attributes.writingsuggestions = String(config.writingSuggestions);
@@ -7539,6 +7625,92 @@ const codeBlockLanguages = languages.map((language) => ({
7539
7625
  value: language.name.toLowerCase()
7540
7626
  })).filter((language) => !excludeLanguages.has(language.label)).concat(extraLanguages).sort((a, b) => a.label.localeCompare(b.label));
7541
7627
 
7628
+ // src/extensions/embed-menu.ts
7629
+ /**
7630
+ * The image or card that the selection covers exactly, as arrow keys select
7631
+ * it.
7632
+ */
7633
+ function getSelectedEmbed(state) {
7634
+ const { from, to, empty } = state.selection;
7635
+ if (empty) return;
7636
+ const embed = findImageAt(state, from);
7637
+ return embed && embed.from === from && embed.to === to ? embed : void 0;
7638
+ }
7639
+ /**
7640
+ * Replace the X post or YouTube card at `pos` with an ordinary link and put
7641
+ * the caret after it. A card without alt text becomes its bare URL, which
7642
+ * renders as an autolink; otherwise the `![alt](src "title")` source loses its
7643
+ * `!`. The trailing magic comment (size and saved snapshot) is dropped. Images
7644
+ * and `![[target]]` embeds are not converted.
7645
+ */
7646
+ function convertEmbedToLink(pos) {
7647
+ return (state, dispatch) => {
7648
+ const embed = findImageAt(state, pos);
7649
+ if (!embed || embed.kind === "image" || embed.syntax != null) return false;
7650
+ if (dispatch) {
7651
+ const source = stripMagicComment(state.doc.textBetween(embed.from, embed.to));
7652
+ dispatch(replaceEmbed(state, embed, embed.alt ? source.slice(1) : embed.src));
7653
+ }
7654
+ return true;
7655
+ };
7656
+ }
7657
+ /**
7658
+ * Delete the image or card at `pos`, with its source and magic comment, and
7659
+ * put the caret where it was.
7660
+ */
7661
+ function deleteEmbed(pos) {
7662
+ return (state, dispatch) => {
7663
+ const embed = findImageAt(state, pos);
7664
+ if (!embed) return false;
7665
+ if (dispatch) dispatch(replaceEmbed(state, embed, ""));
7666
+ return true;
7667
+ };
7668
+ }
7669
+ /**
7670
+ * Replace the source of `embed` with `text`, which may be empty, and put the
7671
+ * caret after the text.
7672
+ */
7673
+ function replaceEmbed(state, embed, text) {
7674
+ const { from, to } = embed;
7675
+ const tr = text ? state.tr.insertText(text, from, to) : state.tr.delete(from, to);
7676
+ return tr.setSelection(TextSelection.create(tr.doc, from + text.length)).scrollIntoView();
7677
+ }
7678
+ /**
7679
+ * The menu button of the card at `pos`, if the card is rendered.
7680
+ */
7681
+ function getEmbedMenuButton(view, pos) {
7682
+ return findAtomViewAt(view, pos + 1)?.querySelector(`.${EMBED_MENU_BUTTON_CLASS}`);
7683
+ }
7684
+ function getButtonTarget(target) {
7685
+ if (!isElementLike(target)) return;
7686
+ return target.closest(`.${EMBED_MENU_BUTTON_CLASS}`);
7687
+ }
7688
+ /**
7689
+ * Call `onOpen` when the user clicks the menu button of an image, X post card,
7690
+ * or YouTube card, or presses `Mod-/` with one selected.
7691
+ */
7692
+ function defineEmbedMenuHandler(onOpen) {
7693
+ const openSelectedEmbed = (state, dispatch, view) => {
7694
+ const embed = getSelectedEmbed(state);
7695
+ const button = embed && view && getEmbedMenuButton(view, embed.from);
7696
+ if (!embed || !button) return false;
7697
+ if (dispatch) onOpen(embed, button);
7698
+ return true;
7699
+ };
7700
+ return union(defineDOMEventHandler("mousedown", (_view, event) => {
7701
+ if (!getButtonTarget(event.target)) return false;
7702
+ event.preventDefault();
7703
+ return true;
7704
+ }), defineDOMEventHandler("click", (view, event) => {
7705
+ const button = getButtonTarget(event.target);
7706
+ if (!button) return false;
7707
+ event.preventDefault();
7708
+ const embed = findImageForElement(view, button);
7709
+ if (embed) onOpen(embed, button);
7710
+ return true;
7711
+ }), defineKeymap({ "Mod-/": openSelectedEmbed }));
7712
+ }
7713
+
7542
7714
  // src/extensions/key-bindings.ts
7543
7715
  /**
7544
7716
  * Human-readable descriptions of the editor's formatting and heading shortcuts.
@@ -7558,6 +7730,7 @@ const EDITOR_KEY_BINDINGS = {
7558
7730
  "Mod-Alt-5": "Heading 5",
7559
7731
  "Mod-Alt-6": "Heading 6",
7560
7732
  "Mod-.": "Fold or unfold a bullet",
7733
+ "Mod-/": "Open the code block or embed menu",
7561
7734
  "Shift-Enter": "Insert a line break, or leave a code block from its end",
7562
7735
  "Mod-Enter": "Follow the link under the caret, or cycle a checkbox task",
7563
7736
  "Mod-Shift-Enter": "Cycle a circle checkbox task",
@@ -7826,7 +7999,7 @@ const CLOSE_DELAY$1 = 100;
7826
7999
  function defineLinkHoverHandler(onHoverChange, { canLeave } = {}) {
7827
8000
  return defineMarkHoverHandler({
7828
8001
  key: linkHoverKey,
7829
- selector: ".md-link",
8002
+ selector: `.${LINK_TEXT_MARK_CLASS}`,
7830
8003
  openDelay: OPEN_DELAY$1,
7831
8004
  closeDelay: CLOSE_DELAY$1,
7832
8005
  tap: true,
@@ -7890,9 +8063,9 @@ var VirtualCaretView = class {
7890
8063
  this.#view = view;
7891
8064
  this.#document = view.dom.ownerDocument;
7892
8065
  this.#layer = layer;
7893
- this.#layer.classList.add("md-virtual-caret-layer");
8066
+ this.#layer.classList.add(VIRTUAL_CARET_LAYER_CLASS);
7894
8067
  this.#caret = this.#layer.appendChild(this.#document.createElement("div"));
7895
- this.#caret.className = "md-virtual-caret";
8068
+ this.#caret.className = VIRTUAL_CARET_CLASS;
7896
8069
  this.#caret.dataset.testid = "virtual-caret";
7897
8070
  this.#document.addEventListener("selectionchange", this.#requestReposition);
7898
8071
  this.#unsubscribeModality = onIsTouchInputChange(this.#requestReposition);
@@ -7916,7 +8089,7 @@ var VirtualCaretView = class {
7916
8089
  this.#view.dom.removeEventListener("blur", this.#handleBlur);
7917
8090
  this.#resizeObserver?.disconnect();
7918
8091
  this.#caret.remove();
7919
- this.#layer.classList.remove("md-virtual-caret-layer");
8092
+ this.#layer.classList.remove(VIRTUAL_CARET_LAYER_CLASS);
7920
8093
  delete this.#layer.dataset.focused;
7921
8094
  this.#view.dom.removeAttribute(DATA_ATTRIBUTE);
7922
8095
  }
@@ -8043,7 +8216,7 @@ const CLOSE_DELAY = 100;
8043
8216
  function defineWikilinkHoverHandler(onHoverChange, openDelay = OPEN_DELAY, closeDelay = CLOSE_DELAY) {
8044
8217
  return defineMarkHoverHandler({
8045
8218
  key: wikilinkHoverKey,
8046
- selector: ".md-wikilink-view-preview",
8219
+ selector: `.${WIKILINK_VIEW_PREVIEW_CLASS}`,
8047
8220
  openDelay,
8048
8221
  closeDelay,
8049
8222
  tap: false,
@@ -8147,4 +8320,4 @@ function getVirtualElementFromRange(view, range) {
8147
8320
  };
8148
8321
  }
8149
8322
 
8150
- export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineBackspaceEmptyFirstBlock, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, defineXPostMediaClickHandler, defineYouTubeVideoClickHandler, dismissLinkHover, docToMarkdown, docToParagraphMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getEditorConfig, getFileKind, getIsComposing, getLinkText, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isMarkOfType, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, paragraphMarkdownToDoc, parsePostEmbedSnapshot, parseWikiEmbed, removeLink, renderMathInto, updateEditorConfig, updateLink, wikiEmbedBasename, withPriority };
8323
+ export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, convertEmbedToLink, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineBackspaceEmptyFirstBlock, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedMenuHandler, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, defineXPostMediaClickHandler, defineYouTubeVideoClickHandler, deleteBlock, deleteEmbed, dismissLinkHover, docToMarkdown, docToParagraphMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getEditorConfig, getFileKind, getIsComposing, getLinkText, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, insertMarkdown, insertTrigger, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isMarkOfType, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, paragraphMarkdownToDoc, parsePostEmbedSnapshot, parseWikiEmbed, removeLink, renderMathInto, scrollIntoView, selectText, selectTextBetween, turnIntoText, updateEditorConfig, updateLink, wikiEmbedBasename, withPriority };