@meowdown/react 0.66.0 → 0.67.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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactElement, ReactNode, Ref } from "react";
2
- import { AcceptPendingReplacementOptions, ExitBoundaryHandler, FileClickHandler, FileInfoResolver, FileLinkResolver, FilePasteOptions, FileViewOptions, ImageClickHandler, ImageOptions, LinkClickHandler, LinkCopyHandler, ListMarker, MarkMode, PendingReplacement, PendingReplacementOutcome, PlaceholderOptions, SearchStatusHandler, StartPendingReplacementOptions, TagClickHandler, TypedEditor, WikiEmbedResolver, WikilinkClickHandler, WikilinkHoverHit } from "@meowdown/core";
2
+ import { AcceptPendingReplacementOptions, ExitBoundaryHandler, FileClickHandler, FileInfoResolver, FileLinkResolver, FilePasteOptions, FileViewOptions, ImageClickHandler, ImageOptions, LinkClickHandler, LinkCopyHandler, LinkPreview, LinkPreviewResolver, LinkPreviewResolver as LinkPreviewResolver$1, ListMarker, MarkMode, PendingReplacement, PendingReplacementOutcome, PlaceholderOptions, SearchStatusHandler, StartPendingReplacementOptions, TagClickHandler, TypedEditor, WikiEmbedResolver, WikilinkClickHandler, WikilinkHoverHit } from "@meowdown/core";
3
3
  import { SelectionJSON, SelectionJSON as SelectionJSON$1 } from "@prosekit/core";
4
4
  import { useEditor, useExtension, useKeymap } from "@prosekit/react";
5
5
  //#region src/utils/date-format.d.ts
@@ -347,6 +347,13 @@ interface EditorProps {
347
347
  * for a toast. Pass a stable function (e.g. from `useCallback`).
348
348
  */
349
349
  onLinkCopy?: LinkCopyHandler;
350
+ /**
351
+ * Resolves optional display metadata for the link popup. Failures should be
352
+ * represented by `undefined`; the popup keeps its URL and actions available.
353
+ * May be called repeatedly for the same `href`, so cache expensive work in
354
+ * the host. Pass a stable function (e.g. from `useCallback`).
355
+ */
356
+ resolveLinkPreview?: LinkPreviewResolver$1;
350
357
  /**
351
358
  * Called with the tag name (without the leading `#`) on click of a rendered
352
359
  * `#tag`, or on `Mod-Enter` with the caret on one. Pass a stable function
@@ -508,7 +515,7 @@ interface EditorProps {
508
515
  *
509
516
  * Callbacks and resolvers should be stable; pass them via `useCallback`.
510
517
  */
511
- export declare function MeowdownEditor({ mode, initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution, frontmatter, blockHandle, caretGlide, placeholder, readOnly, spellCheck, searchQuery, onSearchChange, timeFormat, editorClassName, wrapperClassName, handleRef, children }: EditorProps): ReactElement;
518
+ export declare function MeowdownEditor({ mode, initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution, frontmatter, blockHandle, caretGlide, placeholder, readOnly, spellCheck, searchQuery, onSearchChange, timeFormat, editorClassName, wrapperClassName, handleRef, children }: EditorProps): ReactElement;
512
519
  //#endregion
513
520
  //#region src/components/markdown-view.d.ts
514
521
  /**
@@ -654,4 +661,4 @@ interface WikilinkHoverCardProps {
654
661
  */
655
662
  export declare function WikilinkHoverCard({ children, className }: WikilinkHoverCardProps): ReactNode;
656
663
  //#endregion
657
- export { type EditorHandle, type EditorMode, type EditorProps, type EditorStateSnapshot, type MarkdownViewProps, type PendingReplacementResolveHandler, type SelectionHint, type SelectionJSON, type SelectionMenuContext, type SelectionMenuItem, type SelectionMenuSearchHandler, type SlashMenuItem, type SlashMenuSearchHandler, type TagItem, type TagSearchHandler, type TaskClickHandler, type TaskClickPayload, type TimeFormat, type WikilinkHoverCardProps, type WikilinkItem, type WikilinkSearchHandler, useEditor, useExtension, useKeymap };
664
+ export { type EditorHandle, type EditorMode, type EditorProps, type EditorStateSnapshot, type LinkPreview, type LinkPreviewResolver, type MarkdownViewProps, type PendingReplacementResolveHandler, type SelectionHint, type SelectionJSON, type SelectionMenuContext, type SelectionMenuItem, type SelectionMenuSearchHandler, type SlashMenuItem, type SlashMenuSearchHandler, type TagItem, type TagSearchHandler, type TaskClickHandler, type TaskClickPayload, type TimeFormat, type WikilinkHoverCardProps, type WikilinkItem, type WikilinkSearchHandler, useEditor, useExtension, useKeymap };
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { clsx } from "clsx/lite";
2
2
  import { Fragment, cloneElement, createElement, useCallback, useDeferredValue, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
3
- import { buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, getCodeTokens, getFileKind, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunksWithContext, isCodeBlockPreviewHiddenDecoration, isModEvent, isNodeOfType, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, renderMathInto } from "@meowdown/core";
3
+ import { buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, getCodeTokens, getFileKind, getLinkText, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunksWithContext, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isModEvent, isNodeOfType, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, normalizeHref, renderMathInto } from "@meowdown/core";
4
4
  import { clamp } from "@ocavue/utils";
5
5
  import { canUseRegexLookbehind, createEditor, defineDocChangeHandler, defineUpdateHandler, isTextSelection, union } from "@prosekit/core";
6
6
  import { Selection, TextSelection } from "@prosekit/pm/state";
7
7
  import { ProseKit, defineReactNodeView, useEditor, useEditor as useEditor$1, useEditorDerivedValue, useExtension, useExtension as useExtension$1, useKeymap } from "@prosekit/react";
8
8
  import GithubSlugger from "github-slugger";
9
9
  import { Combobox } from "@base-ui/react/combobox";
10
- import { CheckIcon, ChevronsUpDownIcon, CopyIcon, GripHorizontalIcon, GripVerticalIcon, PencilIcon, SparklesIcon, UnlinkIcon } from "lucide-react";
10
+ import { CheckIcon, ChevronsUpDownIcon, CopyIcon, Globe2Icon, GripHorizontalIcon, GripVerticalIcon, PencilIcon, SparklesIcon, UnlinkIcon } from "lucide-react";
11
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
12
  import { BlockHandleDraggable, BlockHandlePopup, BlockHandlePositioner, BlockHandleRoot } from "@prosekit/react/block-handle";
13
13
  import { DropIndicator } from "@prosekit/react/drop-indicator";
@@ -499,70 +499,85 @@ function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick,
499
499
  return null;
500
500
  }
501
501
 
502
- //#endregion
503
- //#region src/hooks/use-delayed-flag.ts
504
- /**
505
- * Delay before the flag opens, in ms.
506
- */
507
- const OPEN_DELAY$1 = 400;
508
- /**
509
- * Grace before the flag closes, in ms. The window lets a pointer travel from
510
- * the hovered link onto the popover it anchors.
511
- */
512
- const CLOSE_DELAY$1 = 300;
513
- /**
514
- * Mirrors `value` into a boolean that flips true `openDelay`ms after `value`
515
- * becomes true and false `closeDelay`ms after it becomes false, cancelling any
516
- * pending flip on each change.
517
- */
518
- function useDelayedFlag(value, openDelay = OPEN_DELAY$1, closeDelay = CLOSE_DELAY$1) {
519
- const [flag, setFlag] = useState(false);
520
- const timerRef = useRef(void 0);
521
- useEffect(() => {
522
- clearTimeout(timerRef.current);
523
- timerRef.current = setTimeout(() => setFlag(value), value ? openDelay : closeDelay);
524
- return () => clearTimeout(timerRef.current);
525
- }, [
526
- value,
527
- openDelay,
528
- closeDelay
529
- ]);
530
- return flag;
531
- }
532
-
533
502
  //#endregion
534
503
  //#region src/components/link-menu.module.css
535
504
  var link_menu_module_default = {
505
+ "Actions": "meow_Actions_IjcqKG",
536
506
  "Button": "meow_Button_IjcqKG",
507
+ "Description": "meow_Description_IjcqKG",
508
+ "Field": "meow_Field_IjcqKG",
537
509
  "Form": "meow_Form_IjcqKG",
510
+ "FormActions": "meow_FormActions_IjcqKG",
511
+ "Host": "meow_Host_IjcqKG",
512
+ "Icon": "meow_Icon_IjcqKG",
538
513
  "Input": "meow_Input_IjcqKG",
539
514
  "Popup": "meow_Popup_IjcqKG",
540
515
  "Positioner": "meow_Positioner_IjcqKG",
516
+ "Preview": "meow_Preview_IjcqKG",
517
+ "PreviewBody": "meow_PreviewBody_IjcqKG",
518
+ "pulse": "meow_pulse_IjcqKG",
519
+ "RemoveButton": "meow_RemoveButton_IjcqKG",
520
+ "ReplaceButton": "meow_ReplaceButton_IjcqKG",
521
+ "ReplaceRow": "meow_ReplaceRow_IjcqKG",
541
522
  "Row": "meow_Row_IjcqKG",
542
- "SrOnly": "meow_SrOnly_IjcqKG",
543
- "Url": "meow_Url_IjcqKG"
523
+ "SaveButton": "meow_SaveButton_IjcqKG",
524
+ "Skeleton": "meow_Skeleton_IjcqKG",
525
+ "SkeletonLine": "meow_SkeletonLine_IjcqKG",
526
+ "SkeletonLineShort": "meow_SkeletonLineShort_IjcqKG",
527
+ "Title": "meow_Title_IjcqKG",
528
+ "Url": "meow_Url_IjcqKG",
529
+ "UseTitleButton": "meow_UseTitleButton_IjcqKG"
544
530
  };
545
531
 
546
532
  //#endregion
547
533
  //#region src/components/link-menu.tsx
548
- /**
549
- * Select the link unit so the text-backed commands target it, and keep the
550
- * editor focused so its virtual selection stays visible behind the popover.
551
- */
534
+ function useLinkPreview(href, resolveLinkPreview) {
535
+ const [settled, setSettled] = useState();
536
+ useEffect(() => {
537
+ if (!href || !resolveLinkPreview || !isWebHref(href)) return;
538
+ let stale = false;
539
+ Promise.resolve().then(() => resolveLinkPreview(href)).then((preview) => {
540
+ if (!stale) setSettled({
541
+ href,
542
+ result: preview ? {
543
+ status: "resolved",
544
+ preview
545
+ } : { status: "failed" }
546
+ });
547
+ }).catch(() => {
548
+ if (!stale) setSettled({
549
+ href,
550
+ result: { status: "failed" }
551
+ });
552
+ });
553
+ return () => {
554
+ stale = true;
555
+ };
556
+ }, [href, resolveLinkPreview]);
557
+ if (!href || !resolveLinkPreview || !isWebHref(href)) return { status: "idle" };
558
+ return settled?.href === href ? settled.result : { status: "loading" };
559
+ }
560
+ function isWebHref(href) {
561
+ try {
562
+ const protocol = new URL(href).protocol;
563
+ return protocol === "https:" || protocol === "http:";
564
+ } catch {
565
+ return false;
566
+ }
567
+ }
552
568
  function selectLinkUnit(editor, link) {
553
569
  editor.commands.selectText(link.unit.from, link.unit.to);
554
570
  editor.focus();
555
571
  }
556
- /**
557
- * A Base UI popover anchored at `anchor`. Base UI dismisses it on an outside
558
- * press or Escape, both routed through `onClose`.
559
- */
560
- function LinkPopover({ anchor, onClose, onPopupHover, children }) {
572
+ function LinkPopover({ anchor, open, onClose, onCloseComplete, onPopupHover, children }) {
561
573
  return /* @__PURE__ */ jsx(Popover.Root, {
562
- open: true,
574
+ open,
563
575
  onOpenChange: (open) => {
564
576
  if (!open) onClose();
565
577
  },
578
+ onOpenChangeComplete: (open) => {
579
+ if (!open) onCloseComplete?.();
580
+ },
566
581
  children: /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
567
582
  anchor,
568
583
  side: "bottom",
@@ -580,31 +595,29 @@ function LinkPopover({ anchor, onClose, onPopupHover, children }) {
580
595
  }) })
581
596
  });
582
597
  }
583
- /**
584
- * The hover preview: the url plus copy, edit, and remove actions.
585
- */
586
- function LinkInfoContent({ href, onLinkClick, onLinkCopy, onEdit, onRemove }) {
598
+ function LinkAnchor({ href, className, onLinkClick, children }) {
599
+ return /* @__PURE__ */ jsx("a", {
600
+ className,
601
+ href,
602
+ title: href,
603
+ target: "_blank",
604
+ rel: "noopener noreferrer",
605
+ onClick: (event) => {
606
+ if (!onLinkClick) return;
607
+ event.preventDefault();
608
+ onLinkClick({
609
+ href,
610
+ event: event.nativeEvent,
611
+ mod: isModEvent(event)
612
+ });
613
+ },
614
+ children
615
+ });
616
+ }
617
+ function LinkActions({ href, onLinkCopy, onEdit, onRemove }) {
587
618
  return /* @__PURE__ */ jsxs("div", {
588
- className: link_menu_module_default.Row,
589
- "data-testid": "link-popover-read",
619
+ className: link_menu_module_default.Actions,
590
620
  children: [
591
- /* @__PURE__ */ jsx("a", {
592
- className: link_menu_module_default.Url,
593
- href,
594
- title: href,
595
- target: "_blank",
596
- rel: "noopener noreferrer",
597
- onClick: (event) => {
598
- if (!onLinkClick) return;
599
- event.preventDefault();
600
- onLinkClick({
601
- href,
602
- event: event.nativeEvent,
603
- mod: isModEvent(event)
604
- });
605
- },
606
- children: href
607
- }),
608
621
  /* @__PURE__ */ jsx(CopyButton, {
609
622
  getText: () => href,
610
623
  label: "Copy link",
@@ -630,150 +643,274 @@ function LinkInfoContent({ href, onLinkClick, onLinkCopy, onEdit, onRemove }) {
630
643
  ]
631
644
  });
632
645
  }
633
- /**
634
- * The url and title form, opened by `Mod-k` or the preview's edit button.
635
- */
636
- function LinkEditContent({ link, onSubmit }) {
637
- const hrefInputRef = useRef(null);
638
- const titleInputRef = useRef(null);
639
- const href = link ? link.href : "";
640
- const title = link ? link.title : "";
646
+ function PreviewIcon({ preview }) {
647
+ const [failed, setFailed] = useState(false);
648
+ if (!preview.iconSrc || failed) return /* @__PURE__ */ jsx(Globe2Icon, { "aria-hidden": "true" });
649
+ return /* @__PURE__ */ jsx("img", {
650
+ src: preview.iconSrc,
651
+ alt: "",
652
+ onError: () => setFailed(true)
653
+ });
654
+ }
655
+ function getHostname(href) {
656
+ try {
657
+ return new URL(href).hostname;
658
+ } catch {
659
+ return href;
660
+ }
661
+ }
662
+ function LinkInfoContent({ href, previewState, onLinkClick, onLinkCopy, onEdit, onRemove, onUseTitle }) {
663
+ if (previewState.status === "resolved") {
664
+ const { preview } = previewState;
665
+ return /* @__PURE__ */ jsxs("div", {
666
+ "data-testid": "link-popover-read",
667
+ children: [
668
+ /* @__PURE__ */ jsxs("div", {
669
+ className: link_menu_module_default.Preview,
670
+ children: [
671
+ /* @__PURE__ */ jsx("div", {
672
+ className: link_menu_module_default.Icon,
673
+ children: /* @__PURE__ */ jsx(PreviewIcon, { preview }, preview.iconSrc)
674
+ }),
675
+ /* @__PURE__ */ jsxs("div", {
676
+ className: link_menu_module_default.PreviewBody,
677
+ children: [/* @__PURE__ */ jsx(LinkAnchor, {
678
+ href,
679
+ className: link_menu_module_default.Title,
680
+ onLinkClick,
681
+ children: preview.title
682
+ }), /* @__PURE__ */ jsx("div", {
683
+ className: link_menu_module_default.Host,
684
+ children: getHostname(href)
685
+ })]
686
+ }),
687
+ /* @__PURE__ */ jsx(LinkActions, {
688
+ href,
689
+ onLinkCopy,
690
+ onEdit,
691
+ onRemove
692
+ })
693
+ ]
694
+ }),
695
+ preview.description && /* @__PURE__ */ jsx("p", {
696
+ className: link_menu_module_default.Description,
697
+ children: preview.description
698
+ }),
699
+ onUseTitle && /* @__PURE__ */ jsxs("div", {
700
+ className: link_menu_module_default.ReplaceRow,
701
+ children: [
702
+ /* @__PURE__ */ jsx(SparklesIcon, { "aria-hidden": "true" }),
703
+ /* @__PURE__ */ jsx("span", { children: "Replace URL with its title?" }),
704
+ /* @__PURE__ */ jsx("button", {
705
+ type: "button",
706
+ className: link_menu_module_default.ReplaceButton,
707
+ "aria-label": "Replace URL with its title",
708
+ onClick: () => onUseTitle(preview.title),
709
+ children: "Yes"
710
+ })
711
+ ]
712
+ })
713
+ ]
714
+ });
715
+ }
716
+ return /* @__PURE__ */ jsxs("div", {
717
+ "data-testid": "link-popover-read",
718
+ children: [/* @__PURE__ */ jsxs("div", {
719
+ className: link_menu_module_default.Row,
720
+ children: [/* @__PURE__ */ jsx(LinkAnchor, {
721
+ href,
722
+ className: link_menu_module_default.Url,
723
+ onLinkClick,
724
+ children: href
725
+ }), /* @__PURE__ */ jsx(LinkActions, {
726
+ href,
727
+ onLinkCopy,
728
+ onEdit,
729
+ onRemove
730
+ })]
731
+ }), previewState.status === "loading" && /* @__PURE__ */ jsxs("div", {
732
+ className: link_menu_module_default.Skeleton,
733
+ "data-testid": "link-popover-loading",
734
+ role: "status",
735
+ "aria-label": "Loading link preview",
736
+ children: [/* @__PURE__ */ jsx("div", { className: link_menu_module_default.SkeletonLine }), /* @__PURE__ */ jsx("div", { className: link_menu_module_default.SkeletonLineShort })]
737
+ })]
738
+ });
739
+ }
740
+ function LinkEditContent({ edit, resolveLinkPreview, onSubmit, onRemove }) {
741
+ const [text, setText] = useState(edit.text);
742
+ const [href, setHref] = useState(edit.link?.href ?? "");
743
+ const [debouncedHref, setDebouncedHref] = useState(href.trim());
744
+ const textInputRef = useRef(null);
745
+ const previewHref = normalizeHref(debouncedHref);
746
+ const previewState = useLinkPreview(previewHref || void 0, resolveLinkPreview);
747
+ const canUseTitle = !!edit.link && debouncedHref === href.trim() && isLinkTextForHref(text, previewHref);
641
748
  useEffect(() => {
642
- hrefInputRef.current?.focus();
749
+ const timer = window.setTimeout(() => setDebouncedHref(href.trim()), 400);
750
+ return () => window.clearTimeout(timer);
751
+ }, [href]);
752
+ useEffect(() => {
753
+ textInputRef.current?.focus();
643
754
  }, []);
644
755
  return /* @__PURE__ */ jsxs("form", {
645
756
  className: link_menu_module_default.Form,
646
757
  "data-testid": "link-popover-edit",
647
758
  onSubmit: (event) => {
648
759
  event.preventDefault();
649
- onSubmit(hrefInputRef.current?.value || "", titleInputRef.current?.value || "");
760
+ if (text.trim() && href.trim()) onSubmit(text, href);
650
761
  },
651
762
  children: [
652
- /* @__PURE__ */ jsx("input", {
653
- ref: hrefInputRef,
654
- className: link_menu_module_default.Input,
655
- defaultValue: href,
656
- placeholder: "Paste link...",
657
- "data-testid": "link-popover-input"
763
+ /* @__PURE__ */ jsxs("label", {
764
+ className: link_menu_module_default.Field,
765
+ children: [/* @__PURE__ */ jsx("span", { children: "Text" }), /* @__PURE__ */ jsx("input", {
766
+ ref: textInputRef,
767
+ className: link_menu_module_default.Input,
768
+ value: text,
769
+ required: true,
770
+ "data-testid": "link-popover-text-input",
771
+ onChange: (event) => setText(event.target.value)
772
+ })]
658
773
  }),
659
- /* @__PURE__ */ jsx("input", {
660
- ref: titleInputRef,
661
- className: link_menu_module_default.Input,
662
- defaultValue: title,
663
- placeholder: "Title (optional)"
774
+ /* @__PURE__ */ jsxs("label", {
775
+ className: link_menu_module_default.Field,
776
+ children: [/* @__PURE__ */ jsx("span", { children: "Link" }), /* @__PURE__ */ jsx("input", {
777
+ className: link_menu_module_default.Input,
778
+ value: href,
779
+ required: true,
780
+ placeholder: "Paste link...",
781
+ "data-testid": "link-popover-input",
782
+ onChange: (event) => setHref(event.target.value)
783
+ })]
664
784
  }),
665
- /* @__PURE__ */ jsx("button", {
666
- type: "submit",
667
- className: link_menu_module_default.SrOnly,
668
- "data-testid": "link-popover-submit",
669
- children: "Save"
785
+ /* @__PURE__ */ jsxs("div", {
786
+ className: link_menu_module_default.FormActions,
787
+ children: [
788
+ onRemove && /* @__PURE__ */ jsx("button", {
789
+ type: "button",
790
+ className: link_menu_module_default.RemoveButton,
791
+ onClick: onRemove,
792
+ children: "Remove link"
793
+ }),
794
+ canUseTitle && previewState.status === "resolved" && /* @__PURE__ */ jsx("button", {
795
+ type: "button",
796
+ className: link_menu_module_default.UseTitleButton,
797
+ onClick: () => setText(previewState.preview.title),
798
+ children: "Use page title"
799
+ }),
800
+ /* @__PURE__ */ jsx("button", {
801
+ type: "submit",
802
+ className: link_menu_module_default.SaveButton,
803
+ "data-testid": "link-popover-submit",
804
+ children: "Save"
805
+ })
806
+ ]
670
807
  })
671
808
  ]
672
809
  });
673
810
  }
674
- /**
675
- * Owns both link triggers and shows one popover at a time:
676
- *
677
- * - hovering a link opens a read-only preview that follows the pointer;
678
- * - `Mod-k` (or the preview's edit button) opens an edit form that stays until
679
- * it is submitted, dismissed with Escape, or pressed outside.
680
- */
681
- function LinkMenu({ onLinkClick, onLinkCopy }) {
811
+ function LinkMenu({ onLinkClick, onLinkCopy, resolveLinkPreview, readOnly = false }) {
682
812
  const editor = useEditor$1();
683
- const [hover, setHover] = useState();
684
- const [onLink, setOnLink] = useState(false);
685
- const [overPopup, setOverPopup] = useState(false);
813
+ const [hit, setHit] = useState();
686
814
  const [edit, setEdit] = useState();
687
- const hoverOpen = useDelayedFlag(onLink || overPopup);
688
- const linkHoverExtension = useMemo(() => {
689
- return defineLinkHoverHandler((hit) => {
690
- setOnLink(!!hit);
691
- if (hit) setHover(hit.payload);
692
- });
693
- }, []);
815
+ const [editOpen, setEditOpen] = useState(false);
816
+ const [displayedRead, setDisplayedRead] = useState();
817
+ const overPopupRef = useRef(false);
818
+ const linkHoverExtension = useMemo(() => defineLinkHoverHandler((nextHit) => {
819
+ setHit(nextHit?.payload);
820
+ if (nextHit) setDisplayedRead(nextHit.payload);
821
+ }, { canLeave: () => !overPopupRef.current }), []);
694
822
  useExtension$1(linkHoverExtension);
695
- const linkEditExtension = useMemo(() => {
696
- return defineLinkEditKeymap((options) => {
697
- setEdit(options);
698
- });
699
- }, []);
823
+ const linkEditExtension = useMemo(() => readOnly ? null : defineLinkEditKeymap((options) => {
824
+ setEdit(options);
825
+ setEditOpen(true);
826
+ setHit(void 0);
827
+ }), [readOnly]);
700
828
  useExtension$1(linkEditExtension);
701
- const closeHover = useCallback(() => {
702
- setOnLink(false);
703
- setOverPopup(false);
704
- setHover(void 0);
829
+ const closeRead = useCallback(() => {
830
+ overPopupRef.current = false;
831
+ setHit(void 0);
705
832
  }, []);
706
833
  const closeEdit = useCallback(() => {
707
- setEdit(void 0);
708
- closeHover();
709
- editor.focus();
710
- }, [editor, closeHover]);
711
- let rangeFrom;
712
- let rangeTo;
713
- if (edit) {
714
- const anchorRange = edit.link?.text ?? edit;
715
- rangeFrom = anchorRange.from;
716
- rangeTo = anchorRange.to;
717
- } else if (hover) {
718
- const anchorRange = hover.text;
719
- rangeFrom = anchorRange.from;
720
- rangeTo = anchorRange.to;
721
- }
834
+ setEditOpen(false);
835
+ closeRead();
836
+ }, [closeRead]);
837
+ const requestedRead = hit;
838
+ const previewState = useLinkPreview(displayedRead?.href, resolveLinkPreview);
839
+ const range = edit ? edit.link?.text ?? edit : displayedRead?.text;
722
840
  const anchor = useMemo(() => {
723
- if (rangeFrom == null || rangeTo == null) return;
724
- return getVirtualElementFromRange(editor.view, {
725
- from: rangeFrom,
726
- to: rangeTo
727
- });
728
- }, [
729
- rangeFrom,
730
- rangeTo,
731
- editor
732
- ]);
733
- if (edit) return /* @__PURE__ */ jsx(LinkPopover, {
841
+ if (!range) return;
842
+ return getVirtualElementFromRange(editor.view, range);
843
+ }, [range, editor]);
844
+ if (edit && !readOnly) return /* @__PURE__ */ jsx(LinkPopover, {
734
845
  anchor,
846
+ open: editOpen,
735
847
  onClose: closeEdit,
848
+ onCloseComplete: () => {
849
+ setEdit(void 0);
850
+ editor.focus();
851
+ },
736
852
  children: /* @__PURE__ */ jsx(LinkEditContent, {
737
- link: edit.link,
738
- onSubmit: (href, title) => {
739
- if (edit.link) {
740
- if (href.trim()) editor.commands.updateLink({
741
- href,
742
- title
743
- });
744
- else editor.commands.removeLink();
745
- } else if (href.trim()) editor.commands.insertLink({
853
+ edit,
854
+ resolveLinkPreview,
855
+ onRemove: edit.link ? () => {
856
+ editor.commands.removeLink();
857
+ closeEdit();
858
+ } : void 0,
859
+ onSubmit: (text, href) => {
860
+ if (edit.link) editor.commands.updateLink({
861
+ text: text === edit.text ? void 0 : text,
746
862
  href,
747
- title
863
+ title: edit.link.title
864
+ });
865
+ else editor.commands.insertLink({
866
+ text,
867
+ href
748
868
  });
749
869
  closeEdit();
750
870
  }
751
871
  })
752
872
  });
753
- if (hoverOpen && hover) {
754
- const link = hover;
755
- const editable = link.form === "inline";
873
+ if (displayedRead) {
874
+ const mutable = !readOnly && displayedRead.form !== "reference";
875
+ const text = getLinkText(editor.view.state, displayedRead);
876
+ const canUseTitle = mutable && isLinkTextForHref(text, displayedRead.href);
877
+ const editLink = () => {
878
+ selectLinkUnit(editor, displayedRead);
879
+ setEdit({
880
+ from: displayedRead.unit.from,
881
+ to: displayedRead.unit.to,
882
+ link: displayedRead,
883
+ text
884
+ });
885
+ setEditOpen(true);
886
+ closeRead();
887
+ };
888
+ const removeLink = () => {
889
+ selectLinkUnit(editor, displayedRead);
890
+ editor.commands.removeLink();
891
+ closeRead();
892
+ };
756
893
  return /* @__PURE__ */ jsx(LinkPopover, {
757
894
  anchor,
758
- onClose: closeHover,
759
- onPopupHover: setOverPopup,
895
+ open: requestedRead !== void 0,
896
+ onClose: closeRead,
897
+ onCloseComplete: () => {
898
+ if (!requestedRead) setDisplayedRead(void 0);
899
+ },
900
+ onPopupHover: (over) => {
901
+ overPopupRef.current = over;
902
+ },
760
903
  children: /* @__PURE__ */ jsx(LinkInfoContent, {
761
- href: link.href,
904
+ href: displayedRead.href,
905
+ previewState,
762
906
  onLinkClick,
763
907
  onLinkCopy,
764
- onEdit: editable ? () => {
765
- selectLinkUnit(editor, link);
766
- setEdit({
767
- from: link.unit.from,
768
- to: link.unit.to,
769
- link
770
- });
771
- closeHover();
772
- } : void 0,
773
- onRemove: editable ? () => {
774
- selectLinkUnit(editor, link);
775
- editor.commands.removeLink();
776
- closeHover();
908
+ onEdit: mutable ? editLink : void 0,
909
+ onRemove: mutable ? removeLink : void 0,
910
+ onUseTitle: canUseTitle ? (title) => {
911
+ selectLinkUnit(editor, displayedRead);
912
+ editor.commands.updateLink({ text: title });
913
+ closeRead();
777
914
  } : void 0
778
915
  })
779
916
  });
@@ -922,6 +1059,37 @@ function PendingReplacementPreview({ actions, onResolve }) {
922
1059
  });
923
1060
  }
924
1061
 
1062
+ //#endregion
1063
+ //#region src/hooks/use-delayed-flag.ts
1064
+ /**
1065
+ * Delay before the flag opens, in ms.
1066
+ */
1067
+ const OPEN_DELAY$1 = 300;
1068
+ /**
1069
+ * Grace before the flag closes, in ms. The window lets a pointer travel from
1070
+ * the hovered link onto the popover it anchors.
1071
+ */
1072
+ const CLOSE_DELAY$1 = 100;
1073
+ /**
1074
+ * Mirrors `value` into a boolean that flips true `openDelay`ms after `value`
1075
+ * becomes true and false `closeDelay`ms after it becomes false, cancelling any
1076
+ * pending flip on each change.
1077
+ */
1078
+ function useDelayedFlag(value, openDelay = OPEN_DELAY$1, closeDelay = CLOSE_DELAY$1) {
1079
+ const [flag, setFlag] = useState(false);
1080
+ const timerRef = useRef(void 0);
1081
+ useEffect(() => {
1082
+ clearTimeout(timerRef.current);
1083
+ timerRef.current = setTimeout(() => setFlag(value), value ? openDelay : closeDelay);
1084
+ return () => clearTimeout(timerRef.current);
1085
+ }, [
1086
+ value,
1087
+ openDelay,
1088
+ closeDelay
1089
+ ]);
1090
+ return flag;
1091
+ }
1092
+
925
1093
  //#endregion
926
1094
  //#region src/components/selection-menu.module.css
927
1095
  var selection_menu_module_default = {
@@ -1727,7 +1895,7 @@ function findHeadingPosition(doc, fragment) {
1727
1895
  });
1728
1896
  return match;
1729
1897
  }
1730
- function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution = true, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, searchQuery = "", onSearchChange, timeFormat, editorClassName, ref, children }) {
1898
+ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution = true, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, searchQuery = "", onSearchChange, timeFormat, editorClassName, ref, children }) {
1731
1899
  const [editor] = useState(() => {
1732
1900
  const baseExtension = defineEditorExtension({
1733
1901
  resolveFileLink,
@@ -1918,9 +2086,11 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
1918
2086
  onFilePaste,
1919
2087
  onFileSaveError
1920
2088
  }),
1921
- !readOnly && /* @__PURE__ */ jsx(LinkMenu, {
2089
+ /* @__PURE__ */ jsx(LinkMenu, {
1922
2090
  onLinkClick,
1923
- onLinkCopy
2091
+ onLinkCopy,
2092
+ resolveLinkPreview,
2093
+ readOnly
1924
2094
  }),
1925
2095
  onTagSearch && /* @__PURE__ */ jsx(TagMenu, { onTagSearch }),
1926
2096
  onWikilinkSearch && /* @__PURE__ */ jsx(WikilinkMenu, { onWikilinkSearch }),
@@ -1949,7 +2119,7 @@ const CARET_GLIDE_OFF = { "--meowdown-caret-glide": "0ms" };
1949
2119
  *
1950
2120
  * Callbacks and resolvers should be stable; pass them via `useCallback`.
1951
2121
  */
1952
- function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste = true, linkPaste = true, bulletAfterHeading = false, substitution = true, frontmatter = false, blockHandle = true, caretGlide = true, placeholder, readOnly, spellCheck, searchQuery, onSearchChange, timeFormat, editorClassName, wrapperClassName, handleRef, children }) {
2122
+ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste = true, linkPaste = true, bulletAfterHeading = false, substitution = true, frontmatter = false, blockHandle = true, caretGlide = true, placeholder, readOnly, spellCheck, searchQuery, onSearchChange, timeFormat, editorClassName, wrapperClassName, handleRef, children }) {
1953
2123
  const childRef = useRef(null);
1954
2124
  useImperativeHandle(handleRef, () => {
1955
2125
  function getMarkdown() {
@@ -2060,6 +2230,7 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashM
2060
2230
  onWikilinkClick,
2061
2231
  onLinkClick,
2062
2232
  onLinkCopy,
2233
+ resolveLinkPreview,
2063
2234
  onTagClick,
2064
2235
  onExitBoundary,
2065
2236
  resolveImageUrl,
package/dist/style.css CHANGED
@@ -294,18 +294,43 @@
294
294
 
295
295
  .meow_Popup_IjcqKG {
296
296
  box-sizing: border-box;
297
- border: 1px solid var(--meowdown-border);
297
+ width: 100%;
298
+ color: var(--meowdown-text);
299
+ border: 1px solid var(--meowdown-hover-card-border);
300
+ border-radius: var(--meowdown-hover-card-radius);
298
301
  background: var(--meowdown-popover-bg);
299
- border-radius: .75rem;
302
+ box-shadow: var(--meowdown-hover-card-shadow);
303
+ transform-origin: var(--transform-origin);
304
+ outline: none;
300
305
  flex-direction: column;
301
- width: 100%;
302
- padding: .25rem;
303
306
  font-size: .875rem;
307
+ transition: opacity .15s, transform .15s;
308
+ display: flex;
309
+ overflow: hidden;
310
+
311
+ &[data-closed] {
312
+ transition: opacity .1s, transform .1s;
313
+ }
314
+
315
+ &[data-starting-style], &[data-ending-style] {
316
+ opacity: 0;
317
+ transform: scale(.96);
318
+ }
319
+
320
+ @media (prefers-reduced-motion: reduce) {
321
+ transition: none;
322
+ }
323
+ }
324
+
325
+ .meow_Row_IjcqKG, .meow_Preview_IjcqKG {
326
+ align-items: center;
327
+ gap: .5rem;
328
+ padding: .5rem;
304
329
  display: flex;
305
- box-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
306
330
  }
307
331
 
308
- .meow_Row_IjcqKG {
332
+ .meow_Actions_IjcqKG {
333
+ flex: none;
309
334
  align-items: center;
310
335
  gap: .125rem;
311
336
  display: flex;
@@ -318,18 +343,20 @@
318
343
  color: var(--meowdown-accent);
319
344
  cursor: pointer;
320
345
  flex: 1;
321
- padding: .25rem .5rem;
346
+ padding: .25rem .375rem;
322
347
  overflow: hidden;
323
348
  }
324
349
 
325
350
  .meow_Button_IjcqKG {
351
+ width: 2rem;
352
+ height: 2rem;
326
353
  color: var(--meowdown-text);
327
354
  cursor: pointer;
328
355
  border-radius: .5rem;
329
356
  flex: none;
330
357
  justify-content: center;
331
358
  align-items: center;
332
- padding: .375rem;
359
+ padding: 0;
333
360
  display: inline-flex;
334
361
 
335
362
  &:hover {
@@ -340,26 +367,172 @@
340
367
  width: 1rem;
341
368
  height: 1rem;
342
369
  }
370
+
371
+ @media (pointer: coarse) {
372
+ width: 2.75rem;
373
+ height: 2.75rem;
374
+ }
343
375
  }
344
376
 
345
- .meow_Form_IjcqKG {
377
+ .meow_Icon_IjcqKG {
378
+ width: 2rem;
379
+ height: 2rem;
380
+ color: var(--meowdown-muted);
381
+ border-radius: .375rem;
382
+ flex: none;
383
+ justify-content: center;
384
+ align-items: center;
385
+ display: flex;
386
+ overflow: hidden;
387
+
388
+ & img, & svg {
389
+ object-fit: contain;
390
+ width: 1.25rem;
391
+ height: 1.25rem;
392
+ }
393
+ }
394
+
395
+ .meow_PreviewBody_IjcqKG {
346
396
  flex-direction: column;
347
- gap: .25rem;
348
- padding: .25rem;
397
+ flex: 1;
398
+ min-width: 0;
349
399
  display: flex;
350
400
  }
351
401
 
352
- .meow_SrOnly_IjcqKG {
353
- clip: rect(0, 0, 0, 0);
354
- border: 0;
355
- width: 1px;
356
- height: 1px;
357
- margin: -1px;
358
- padding: 0;
359
- position: absolute;
402
+ .meow_Title_IjcqKG {
403
+ white-space: nowrap;
404
+ text-overflow: ellipsis;
405
+ color: var(--meowdown-accent);
406
+ font-weight: 500;
407
+ line-height: 1.3;
360
408
  overflow: hidden;
361
409
  }
362
410
 
411
+ .meow_Host_IjcqKG {
412
+ white-space: nowrap;
413
+ text-overflow: ellipsis;
414
+ color: var(--meowdown-muted);
415
+ font-size: .8125rem;
416
+ line-height: 1.3;
417
+ overflow: hidden;
418
+ }
419
+
420
+ .meow_Description_IjcqKG {
421
+ color: var(--meowdown-muted);
422
+ -webkit-line-clamp: 2;
423
+ -webkit-box-orient: vertical;
424
+ margin: 0;
425
+ padding: 0 .75rem .75rem 3rem;
426
+ line-height: 1.4;
427
+ display: -webkit-box;
428
+ overflow: hidden;
429
+ }
430
+
431
+ .meow_ReplaceRow_IjcqKG {
432
+ min-height: 2.5rem;
433
+ color: var(--meowdown-text);
434
+ border-top: 1px solid var(--meowdown-border);
435
+ align-items: center;
436
+ gap: .5rem;
437
+ padding: .375rem .5rem .375rem .75rem;
438
+ display: flex;
439
+
440
+ & > svg {
441
+ width: .875rem;
442
+ height: .875rem;
443
+ color: var(--meowdown-accent);
444
+ }
445
+
446
+ & > span {
447
+ flex: 1;
448
+ min-width: 0;
449
+ font-size: .8125rem;
450
+ font-weight: 500;
451
+ }
452
+ }
453
+
454
+ .meow_ReplaceButton_IjcqKG, .meow_SaveButton_IjcqKG {
455
+ min-height: 2rem;
456
+ font: inherit;
457
+ color: var(--meowdown-accent-contrast);
458
+ background: var(--meowdown-accent);
459
+ cursor: pointer;
460
+ border: 0;
461
+ border-radius: .5rem;
462
+ padding: .375rem .625rem;
463
+ font-weight: 500;
464
+
465
+ @media (pointer: coarse) {
466
+ min-height: 2.75rem;
467
+ }
468
+ }
469
+
470
+ .meow_ReplaceButton_IjcqKG {
471
+ flex: none;
472
+ min-height: 1.75rem;
473
+ padding: .25rem .625rem;
474
+ transition: filter .1s, transform .1s ease-out;
475
+
476
+ &:hover {
477
+ filter: brightness(1.05);
478
+ }
479
+
480
+ &:active {
481
+ transform: scale(.97);
482
+ }
483
+ }
484
+
485
+ .meow_Skeleton_IjcqKG {
486
+ flex-direction: column;
487
+ gap: .625rem;
488
+ padding: 0 .5rem .625rem;
489
+ display: flex;
490
+ }
491
+
492
+ .meow_SkeletonLine_IjcqKG, .meow_SkeletonLineShort_IjcqKG {
493
+ background: var(--meowdown-popover-hover-bg);
494
+ border-radius: 999px;
495
+ height: .75rem;
496
+ animation: 1.4s ease-in-out infinite meow_pulse_IjcqKG;
497
+
498
+ @media (prefers-reduced-motion: reduce) {
499
+ animation: none;
500
+ }
501
+ }
502
+
503
+ .meow_SkeletonLine_IjcqKG {
504
+ width: 90%;
505
+ }
506
+
507
+ .meow_SkeletonLineShort_IjcqKG {
508
+ width: 70%;
509
+ }
510
+
511
+ @keyframes meow_pulse_IjcqKG {
512
+ 50% {
513
+ opacity: .45;
514
+ }
515
+ }
516
+
517
+ .meow_Form_IjcqKG {
518
+ flex-direction: column;
519
+ gap: .625rem;
520
+ padding: .75rem;
521
+ display: flex;
522
+ }
523
+
524
+ .meow_Field_IjcqKG {
525
+ flex-direction: column;
526
+ gap: .25rem;
527
+ display: flex;
528
+
529
+ & > span {
530
+ color: var(--meowdown-muted);
531
+ font-size: .75rem;
532
+ font-weight: 600;
533
+ }
534
+ }
535
+
363
536
  .meow_Input_IjcqKG {
364
537
  box-sizing: border-box;
365
538
  width: 100%;
@@ -368,13 +541,41 @@
368
541
  border: 1px solid var(--meowdown-border);
369
542
  background: none;
370
543
  border-radius: .5rem;
371
- padding: .375rem .5rem;
544
+ padding: .5rem .625rem;
372
545
 
373
546
  &:focus {
374
547
  outline: 2px solid var(--meowdown-accent);
375
548
  outline-offset: -1px;
376
549
  }
377
550
  }
551
+
552
+ .meow_FormActions_IjcqKG {
553
+ align-items: center;
554
+ gap: .375rem;
555
+ padding-top: .125rem;
556
+ display: flex;
557
+ }
558
+
559
+ .meow_RemoveButton_IjcqKG, .meow_UseTitleButton_IjcqKG {
560
+ min-height: 2rem;
561
+ font: inherit;
562
+ cursor: pointer;
563
+ border-radius: .5rem;
564
+ padding: .375rem .625rem;
565
+
566
+ @media (pointer: coarse) {
567
+ min-height: 2.75rem;
568
+ }
569
+ }
570
+
571
+ .meow_RemoveButton_IjcqKG {
572
+ color: var(--meowdown-danger, #c33);
573
+ }
574
+
575
+ .meow_UseTitleButton_IjcqKG {
576
+ color: var(--meowdown-accent);
577
+ margin-left: auto;
578
+ }
378
579
  .meow_Positioner_DPJ5ia {
379
580
  z-index: 50;
380
581
  width: min(30rem, 100vw - 1rem);
@@ -794,16 +995,16 @@
794
995
  .meow_Popup_HMEwgW {
795
996
  box-sizing: border-box;
796
997
  pointer-events: none;
797
- border: 1px solid var(--meowdown-border);
998
+ border: 1px solid var(--meowdown-hover-card-border);
999
+ border-radius: var(--meowdown-hover-card-radius);
798
1000
  background: var(--meowdown-popover-bg);
799
1001
  width: min(20rem, 100vw - 1rem);
800
1002
  max-height: min(12rem, 100vh - 1rem);
1003
+ box-shadow: var(--meowdown-hover-card-shadow);
801
1004
  transform-origin: var(--transform-origin);
802
- border-radius: .75rem;
803
1005
  outline: none;
804
1006
  transition: opacity .15s, transform .15s;
805
1007
  display: flex;
806
- box-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
807
1008
  }
808
1009
 
809
1010
  .meow_Popup_HMEwgW[data-closed] {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/react",
3
3
  "type": "module",
4
- "version": "0.66.0",
4
+ "version": "0.67.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -23,15 +23,15 @@
23
23
  "dependencies": {
24
24
  "@base-ui/react": "^1.7.0",
25
25
  "@ocavue/utils": "^1.7.0",
26
- "@prosekit/core": "^0.13.0",
26
+ "@prosekit/core": "^0.13.1",
27
27
  "@prosekit/pm": "^0.1.19",
28
- "@prosekit/react": "^0.8.0",
28
+ "@prosekit/react": "^0.8.1",
29
29
  "beautiful-mermaid": "^1.1.3",
30
30
  "clsx": "^2.1.1",
31
31
  "github-slugger": "^2.0.0",
32
32
  "lucide-react": "^1.28.0",
33
33
  "react-property": "^2.0.2",
34
- "@meowdown/core": "0.66.0"
34
+ "@meowdown/core": "0.67.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "react": "^19.0.0",
@@ -51,12 +51,12 @@
51
51
  "@tsdown/css": "^0.23.0-beta.2",
52
52
  "@types/react": "^19.2.18",
53
53
  "@types/react-dom": "^19.2.4",
54
- "@vitest/browser-playwright": "^4.1.10",
54
+ "@vitest/browser-playwright": "^4.1.11",
55
55
  "dedent": "^1.7.2",
56
56
  "react": "^19.2.8",
57
57
  "react-dom": "^19.2.8",
58
58
  "tsdown": "^0.23.0-beta.2",
59
- "vitest": "^4.1.10",
59
+ "vitest": "^4.1.11",
60
60
  "vitest-browser-commands": "^0.2.1",
61
61
  "vitest-browser-react": "^2.2.0",
62
62
  "@meowdown/vitest": "0.0.0"