@meowdown/react 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
@@ -3,12 +3,13 @@ import { clsx } from "clsx/lite";
3
3
  import { Fragment, ViewTransition, cloneElement, createContext, createElement, memo, startTransition, use, useDeferredValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useReducer, useRef, useState } from "react";
4
4
  import { buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineCodeBlockPreviewPlugin, defineEditorExtension, defineLinkEditKeymap, defineLinkHoverHandler, definePendingReplacementHandler, defineSearchStatusHandler, defineVirtualCaret, defineWikilinkHoverHandler, dismissLinkHover, docToMarkdown, docToParagraphMarkdown, formatFileSize, getCodeTokens, getFileKind, getLinkText, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunksWithContext, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, paragraphMarkdownToDoc, parsePostEmbedSnapshot, renderMathInto, updateEditorConfig } from "@meowdown/core";
5
5
  import { clamp } from "@ocavue/utils";
6
- import { canUseRegexLookbehind, createEditor, defineDocChangeHandler, defineUpdateHandler, isTextSelection, union } from "@prosekit/core";
6
+ import { canUseRegexLookbehind, createEditor, defineDocChangeHandler, defineKeymap, defineUpdateHandler, isApple, isTextSelection, union } from "@prosekit/core";
7
7
  import { Selection, TextSelection } from "@prosekit/pm/state";
8
8
  import { ProseKit, defineReactNodeView, useEditor, useEditor as useEditor$1, useEditorDerivedValue, useExtension, useExtension as useExtension$1, useKeymap } from "@prosekit/react";
9
9
  import GithubSlugger from "github-slugger";
10
10
  import { Combobox } from "@base-ui/react/combobox";
11
- import { CheckIcon, ChevronsUpDownIcon, CopyIcon, Globe2Icon, GripHorizontalIcon, GripVerticalIcon, PencilIcon, SparklesIcon, UnlinkIcon } from "lucide-react";
11
+ import { Menu } from "@base-ui/react/menu";
12
+ import { BracesIcon, CheckIcon, ChevronsUpDownIcon, CopyIcon, EllipsisIcon, Globe2Icon, GripHorizontalIcon, GripVerticalIcon, PencilIcon, SparklesIcon, UnlinkIcon } from "lucide-react";
12
13
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
14
  import { BlockHandleDraggable, BlockHandlePopup, BlockHandlePositioner, BlockHandleRoot } from "@prosekit/react/block-handle";
14
15
  import { DropIndicator } from "@prosekit/react/drop-indicator";
@@ -24,6 +25,33 @@ import { Mark } from "@prosekit/pm/model";
24
25
  import { BOOLEAN, OVERLOADED_BOOLEAN, getPropertyInfo, possibleStandardNames } from "react-property";
25
26
  import { PreviewCard } from "@base-ui/react/preview-card";
26
27
 
28
+ // src/extensions/block-menu.ts
29
+ const caretMenuOpeners = /* @__PURE__ */ new WeakMap();
30
+ /**
31
+ * Sets the function that opens the menu of the code block that holds the
32
+ * caret, so that `Mod-/` can open it. Returns a function that clears it.
33
+ */
34
+ function setCaretCodeBlockMenu(view, open) {
35
+ caretMenuOpeners.set(view, open);
36
+ return () => {
37
+ if (caretMenuOpeners.get(view) === open) caretMenuOpeners.delete(view);
38
+ };
39
+ }
40
+ const openCodeBlockMenu = (state, dispatch, view) => {
41
+ const { $from, $to } = state.selection;
42
+ if (!isNodeOfType($from.parent, "codeBlock") || !$from.sameParent($to) || view == null) return false;
43
+ const open = caretMenuOpeners.get(view);
44
+ if (open == null) return false;
45
+ if (dispatch) open();
46
+ return true;
47
+ };
48
+ /**
49
+ * Opens the menu of the code block that holds the selection with `Mod-/`.
50
+ */
51
+ function defineCodeBlockMenuKeymap() {
52
+ return defineKeymap({ "Mod-/": openCodeBlockMenu });
53
+ }
54
+
27
55
  // src/hooks/use-katex.ts
28
56
  /**
29
57
  * The lazily loaded KaTeX render function, or `undefined` while it loads (or when
@@ -104,14 +132,74 @@ const NON_PROSE_PROPS = {
104
132
  writingsuggestions: "false"
105
133
  };
106
134
 
135
+ // src/utils/write-clipboard-text.ts
136
+ /**
137
+ * Writes `text` to the clipboard. Returns whether the write succeeded.
138
+ */
139
+ async function writeClipboardText(text) {
140
+ try {
141
+ await navigator.clipboard.writeText(text);
142
+ return true;
143
+ } catch (error) {
144
+ console.warn("[meowdown] Failed to copy:", error);
145
+ return false;
146
+ }
147
+ }
148
+
149
+ // src/components/code-block-toolbar-state.ts
150
+ const CODE_BLOCK_TOOLBAR_IDLE = {
151
+ open: void 0,
152
+ closing: [],
153
+ focusReturn: "trigger"
154
+ };
155
+ function switchPopup(state, popup, focusReturn) {
156
+ const closing = state.closing.filter((item) => item !== popup);
157
+ if (state.open != null && state.open !== popup) closing.push(state.open);
158
+ return {
159
+ open: popup,
160
+ closing,
161
+ focusReturn
162
+ };
163
+ }
164
+ function reduceCodeBlockToolbar(state, event) {
165
+ switch (event.type) {
166
+ case "open": return switchPopup(state, event.popup, event.focusReturn);
167
+ case "handOver": return state.open === "menu" ? switchPopup(state, "language", state.focusReturn) : state;
168
+ case "close": return state.open === event.popup ? {
169
+ ...state,
170
+ open: void 0,
171
+ closing: [...state.closing, event.popup]
172
+ } : state;
173
+ case "closed": return state.closing.includes(event.popup) ? {
174
+ ...state,
175
+ closing: state.closing.filter((item) => item !== event.popup)
176
+ } : state;
177
+ }
178
+ }
179
+ /**
180
+ * Whether the toolbar stays shown: a popup is open or plays its exit animation.
181
+ */
182
+ function isCodeBlockToolbarActive(state) {
183
+ return state.open != null || state.closing.length > 0;
184
+ }
185
+ /**
186
+ * Where a closing popup sends the focus. `undefined` when another popup is
187
+ * open and owns the focus.
188
+ */
189
+ function getCodeBlockToolbarFinalFocus(state) {
190
+ return state.open == null ? state.focusReturn : void 0;
191
+ }
192
+
107
193
  // src/components/code-block-view.module.css
108
194
  var code_block_view_module_default = {
109
- "CopyButton": "meow_CopyButton_D3j_9q",
110
195
  "Empty": "meow_Empty_D3j_9q",
196
+ "IconButton": "meow_IconButton_D3j_9q",
111
197
  "Item": "meow_Item_D3j_9q",
112
198
  "ItemIndicator": "meow_ItemIndicator_D3j_9q",
113
199
  "ItemText": "meow_ItemText_D3j_9q",
114
200
  "List": "meow_List_D3j_9q",
201
+ "Menu": "meow_Menu_D3j_9q",
202
+ "MenuItem": "meow_MenuItem_D3j_9q",
115
203
  "MermaidPreview": "meow_MermaidPreview_D3j_9q",
116
204
  "Popup": "meow_Popup_D3j_9q",
117
205
  "Positioner": "meow_Positioner_D3j_9q",
@@ -170,12 +258,7 @@ function CopyButton(t0) {
170
258
  let t1;
171
259
  if ($[6] !== getText || $[7] !== onCopy) {
172
260
  t1 = async () => {
173
- try {
174
- await navigator.clipboard.writeText(getText());
175
- } catch (t2) {
176
- console.warn("[meowdown] Failed to copy:", t2);
177
- return;
178
- }
261
+ if (!await writeClipboardText(getText())) return;
179
262
  setCopied(true);
180
263
  clearTimeout(resetTimerRef.current);
181
264
  resetTimerRef.current = setTimeout(() => setCopied(false), COPIED_RESET_MS);
@@ -383,7 +466,7 @@ function MermaidRender(props) {
383
466
 
384
467
  // src/components/code-block-view.tsx
385
468
  function CodeBlockView(props) {
386
- const $ = c(36);
469
+ const $ = c(38);
387
470
  const { node, view, getPos, decorations, selected, setAttrs, contentRef } = props;
388
471
  const language = node.attrs.language || "";
389
472
  const isMath = language === "math";
@@ -450,20 +533,24 @@ function CodeBlockView(props) {
450
533
  $[13] = t6;
451
534
  } else t6 = $[13];
452
535
  let t7;
453
- if ($[14] !== code || $[15] !== language || $[16] !== selected || $[17] !== setLanguage) {
536
+ if ($[14] !== caretInside || $[15] !== code || $[16] !== language || $[17] !== selected || $[18] !== setLanguage || $[19] !== view) {
454
537
  t7 = selected ? null : /* @__PURE__ */ jsx(CodeBlockToolbar, {
455
538
  code,
456
539
  language,
457
- setLanguage
540
+ setLanguage,
541
+ view,
542
+ caretInside
458
543
  });
459
- $[14] = code;
460
- $[15] = language;
461
- $[16] = selected;
462
- $[17] = setLanguage;
463
- $[18] = t7;
464
- } else t7 = $[18];
544
+ $[14] = caretInside;
545
+ $[15] = code;
546
+ $[16] = language;
547
+ $[17] = selected;
548
+ $[18] = setLanguage;
549
+ $[19] = view;
550
+ $[20] = t7;
551
+ } else t7 = $[20];
465
552
  let t8;
466
- if ($[19] !== focusSource || $[20] !== katex || $[21] !== previewCode || $[22] !== showMathPreview) {
553
+ if ($[21] !== focusSource || $[22] !== katex || $[23] !== previewCode || $[24] !== showMathPreview) {
467
554
  t8 = showMathPreview && /* @__PURE__ */ jsx(MathRender, {
468
555
  katex,
469
556
  formula: previewCode,
@@ -473,30 +560,30 @@ function CodeBlockView(props) {
473
560
  "data-scroll-x": true,
474
561
  onMouseDown: focusSource
475
562
  });
476
- $[19] = focusSource;
477
- $[20] = katex;
478
- $[21] = previewCode;
479
- $[22] = showMathPreview;
480
- $[23] = t8;
481
- } else t8 = $[23];
563
+ $[21] = focusSource;
564
+ $[22] = katex;
565
+ $[23] = previewCode;
566
+ $[24] = showMathPreview;
567
+ $[25] = t8;
568
+ } else t8 = $[25];
482
569
  let t9;
483
- if ($[24] !== focusSource || $[25] !== mermaid || $[26] !== previewCode || $[27] !== showMermaidPreview) {
570
+ if ($[26] !== focusSource || $[27] !== mermaid || $[28] !== previewCode || $[29] !== showMermaidPreview) {
484
571
  t9 = showMermaidPreview && /* @__PURE__ */ jsx(MermaidRender, {
485
572
  renderer: mermaid,
486
573
  source: previewCode,
487
- className: `${code_block_view_module_default.Preview} ${code_block_view_module_default.MermaidPreview}`,
574
+ className: clsx(code_block_view_module_default.Preview, code_block_view_module_default.MermaidPreview),
488
575
  "data-testid": "code-block-mermaid-preview",
489
576
  "data-scroll-x": true,
490
577
  onMouseDown: focusSource
491
578
  });
492
- $[24] = focusSource;
493
- $[25] = mermaid;
494
- $[26] = previewCode;
495
- $[27] = showMermaidPreview;
496
- $[28] = t9;
497
- } else t9 = $[28];
579
+ $[26] = focusSource;
580
+ $[27] = mermaid;
581
+ $[28] = previewCode;
582
+ $[29] = showMermaidPreview;
583
+ $[30] = t9;
584
+ } else t9 = $[30];
498
585
  let t10;
499
- if ($[29] !== t4 || $[30] !== t5 || $[31] !== t6 || $[32] !== t7 || $[33] !== t8 || $[34] !== t9) {
586
+ if ($[31] !== t4 || $[32] !== t5 || $[33] !== t6 || $[34] !== t7 || $[35] !== t8 || $[36] !== t9) {
500
587
  t10 = /* @__PURE__ */ jsxs("div", {
501
588
  className: code_block_view_module_default.Root,
502
589
  "data-preview": t4,
@@ -509,61 +596,327 @@ function CodeBlockView(props) {
509
596
  t9
510
597
  ]
511
598
  });
512
- $[29] = t4;
513
- $[30] = t5;
514
- $[31] = t6;
515
- $[32] = t7;
516
- $[33] = t8;
517
- $[34] = t9;
518
- $[35] = t10;
519
- } else t10 = $[35];
599
+ $[31] = t4;
600
+ $[32] = t5;
601
+ $[33] = t6;
602
+ $[34] = t7;
603
+ $[35] = t8;
604
+ $[36] = t9;
605
+ $[37] = t10;
606
+ } else t10 = $[37];
520
607
  return t10;
521
608
  }
609
+ const MENU_SHORTCUT = isApple ? "⌘/" : "Ctrl+/";
522
610
  function CodeBlockToolbar(t0) {
523
- const $ = c(9);
524
- const { code, language, setLanguage } = t0;
525
- const [comboboxOpen, setComboboxOpen] = useState(false);
526
- const t1 = comboboxOpen || void 0;
611
+ const $ = c(34);
612
+ const { code, language, setLanguage, view, caretInside } = t0;
613
+ const [state, dispatch] = useReducer(reduceCodeBlockToolbar, CODE_BLOCK_TOOLBAR_IDLE);
614
+ let t1;
615
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
616
+ t1 = (open) => {
617
+ dispatch(open ? {
618
+ type: "open",
619
+ popup: "menu",
620
+ focusReturn: "trigger"
621
+ } : {
622
+ type: "close",
623
+ popup: "menu"
624
+ });
625
+ };
626
+ $[0] = t1;
627
+ } else t1 = $[0];
628
+ const changeMenuOpen = t1;
527
629
  let t2;
528
- if ($[0] !== language || $[1] !== setLanguage) {
529
- t2 = /* @__PURE__ */ jsx(CodeBlockLanguagePickerMemo, {
630
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
631
+ t2 = (open_0) => {
632
+ if (!open_0) dispatch({
633
+ type: "closed",
634
+ popup: "menu"
635
+ });
636
+ };
637
+ $[1] = t2;
638
+ } else t2 = $[1];
639
+ const completeMenuOpenChange = t2;
640
+ let t3;
641
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
642
+ t3 = (open_1) => {
643
+ dispatch(open_1 ? {
644
+ type: "open",
645
+ popup: "language",
646
+ focusReturn: "trigger"
647
+ } : {
648
+ type: "close",
649
+ popup: "language"
650
+ });
651
+ };
652
+ $[2] = t3;
653
+ } else t3 = $[2];
654
+ const changeLanguageOpen = t3;
655
+ let t4;
656
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
657
+ t4 = (open_2) => {
658
+ if (!open_2) dispatch({
659
+ type: "closed",
660
+ popup: "language"
661
+ });
662
+ };
663
+ $[3] = t4;
664
+ } else t4 = $[3];
665
+ const completeLanguageOpenChange = t4;
666
+ let t5;
667
+ let t6;
668
+ if ($[4] !== caretInside || $[5] !== view) {
669
+ t5 = () => {
670
+ if (!caretInside) return;
671
+ return setCaretCodeBlockMenu(view, () => {
672
+ dispatch({
673
+ type: "open",
674
+ popup: "menu",
675
+ focusReturn: "editor"
676
+ });
677
+ });
678
+ };
679
+ t6 = [caretInside, view];
680
+ $[4] = caretInside;
681
+ $[5] = view;
682
+ $[6] = t5;
683
+ $[7] = t6;
684
+ } else {
685
+ t5 = $[6];
686
+ t6 = $[7];
687
+ }
688
+ useEffect(t5, t6);
689
+ let t7;
690
+ if ($[8] !== state) {
691
+ t7 = getCodeBlockToolbarFinalFocus(state);
692
+ $[8] = state;
693
+ $[9] = t7;
694
+ } else t7 = $[9];
695
+ const finalFocus = t7;
696
+ let t8;
697
+ if ($[10] !== finalFocus || $[11] !== view) {
698
+ t8 = () => {
699
+ if (finalFocus !== "editor") return finalFocus === "trigger";
700
+ view.focus();
701
+ return false;
702
+ };
703
+ $[10] = finalFocus;
704
+ $[11] = view;
705
+ $[12] = t8;
706
+ } else t8 = $[12];
707
+ const getFinalFocus = t8;
708
+ let t9;
709
+ if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
710
+ t9 = () => {
711
+ dispatch({ type: "handOver" });
712
+ };
713
+ $[13] = t9;
714
+ } else t9 = $[13];
715
+ const handOverToLanguagePicker = t9;
716
+ let t10;
717
+ if ($[14] !== code) {
718
+ t10 = () => {
719
+ writeClipboardText(code);
720
+ };
721
+ $[14] = code;
722
+ $[15] = t10;
723
+ } else t10 = $[15];
724
+ const copyCode = t10;
725
+ let t11;
726
+ if ($[16] !== state) {
727
+ t11 = isCodeBlockToolbarActive(state) || void 0;
728
+ $[16] = state;
729
+ $[17] = t11;
730
+ } else t11 = $[17];
731
+ const t12 = state.open === "language";
732
+ let t13;
733
+ if ($[18] !== getFinalFocus || $[19] !== language || $[20] !== setLanguage || $[21] !== t12) {
734
+ t13 = /* @__PURE__ */ jsx(CodeBlockLanguagePickerMemo, {
530
735
  language,
531
736
  setLanguage,
532
- onShownChange: setComboboxOpen
737
+ open: t12,
738
+ onOpenChange: changeLanguageOpen,
739
+ onOpenChangeComplete: completeLanguageOpenChange,
740
+ getFinalFocus
533
741
  });
534
- $[0] = language;
535
- $[1] = setLanguage;
536
- $[2] = t2;
537
- } else t2 = $[2];
538
- let t3;
539
- if ($[3] !== code) {
540
- t3 = /* @__PURE__ */ jsx(CopyButton, {
742
+ $[18] = getFinalFocus;
743
+ $[19] = language;
744
+ $[20] = setLanguage;
745
+ $[21] = t12;
746
+ $[22] = t13;
747
+ } else t13 = $[22];
748
+ let t14;
749
+ if ($[23] !== code) {
750
+ t14 = /* @__PURE__ */ jsx(CopyButton, {
541
751
  getText: () => code,
542
752
  label: "Copy code",
543
- className: code_block_view_module_default.CopyButton,
753
+ className: code_block_view_module_default.IconButton,
544
754
  "data-testid": "code-block-copy"
545
755
  });
546
- $[3] = code;
547
- $[4] = t3;
548
- } else t3 = $[4];
549
- let t4;
550
- if ($[5] !== t1 || $[6] !== t2 || $[7] !== t3) {
551
- t4 = /* @__PURE__ */ jsxs("div", {
756
+ $[23] = code;
757
+ $[24] = t14;
758
+ } else t14 = $[24];
759
+ const t15 = state.open === "menu";
760
+ let t16;
761
+ if ($[25] !== copyCode || $[26] !== getFinalFocus || $[27] !== t15) {
762
+ t16 = /* @__PURE__ */ jsx(CodeBlockMenu, {
763
+ open: t15,
764
+ onOpenChange: changeMenuOpen,
765
+ onOpenChangeComplete: completeMenuOpenChange,
766
+ getFinalFocus,
767
+ onChangeLanguage: handOverToLanguagePicker,
768
+ onCopyCode: copyCode
769
+ });
770
+ $[25] = copyCode;
771
+ $[26] = getFinalFocus;
772
+ $[27] = t15;
773
+ $[28] = t16;
774
+ } else t16 = $[28];
775
+ let t17;
776
+ if ($[29] !== t11 || $[30] !== t13 || $[31] !== t14 || $[32] !== t16) {
777
+ t17 = /* @__PURE__ */ jsxs("div", {
552
778
  className: code_block_view_module_default.Toolbar,
553
779
  contentEditable: false,
554
- "data-open": t1,
780
+ "data-open": t11,
781
+ children: [
782
+ t13,
783
+ t14,
784
+ t16
785
+ ]
786
+ });
787
+ $[29] = t11;
788
+ $[30] = t13;
789
+ $[31] = t14;
790
+ $[32] = t16;
791
+ $[33] = t17;
792
+ } else t17 = $[33];
793
+ return t17;
794
+ }
795
+ function CodeBlockMenu(t0) {
796
+ const $ = c(10);
797
+ const { open, onOpenChange, onOpenChangeComplete, getFinalFocus, onChangeLanguage, onCopyCode } = t0;
798
+ let t1;
799
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
800
+ t1 = /* @__PURE__ */ jsx(Menu.Trigger, {
801
+ className: code_block_view_module_default.IconButton,
802
+ "aria-label": "More",
803
+ title: `More (${MENU_SHORTCUT})`,
804
+ "data-testid": "code-block-menu",
805
+ children: /* @__PURE__ */ jsx(EllipsisIcon, {})
806
+ });
807
+ $[0] = t1;
808
+ } else t1 = $[0];
809
+ let t2;
810
+ if ($[1] !== getFinalFocus || $[2] !== onChangeLanguage || $[3] !== onCopyCode) {
811
+ t2 = /* @__PURE__ */ jsx(CodeBlockMenuPopup, {
812
+ getFinalFocus,
813
+ onChangeLanguage,
814
+ onCopyCode
815
+ });
816
+ $[1] = getFinalFocus;
817
+ $[2] = onChangeLanguage;
818
+ $[3] = onCopyCode;
819
+ $[4] = t2;
820
+ } else t2 = $[4];
821
+ let t3;
822
+ if ($[5] !== onOpenChange || $[6] !== onOpenChangeComplete || $[7] !== open || $[8] !== t2) {
823
+ t3 = /* @__PURE__ */ jsxs(Menu.Root, {
824
+ open,
825
+ onOpenChange,
826
+ onOpenChangeComplete,
827
+ children: [t1, t2]
828
+ });
829
+ $[5] = onOpenChange;
830
+ $[6] = onOpenChangeComplete;
831
+ $[7] = open;
832
+ $[8] = t2;
833
+ $[9] = t3;
834
+ } else t3 = $[9];
835
+ return t3;
836
+ }
837
+ function CodeBlockMenuPopup(t0) {
838
+ const $ = c(16);
839
+ const { getFinalFocus, onChangeLanguage, onCopyCode } = t0;
840
+ const collisionBoundary = use(CollisionBoundaryContext);
841
+ let t1;
842
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
843
+ t1 = clsx(code_block_view_module_default.Popup, code_block_view_module_default.Menu);
844
+ $[0] = t1;
845
+ } else t1 = $[0];
846
+ let t2;
847
+ let t3;
848
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
849
+ t2 = /* @__PURE__ */ jsx(BracesIcon, {});
850
+ t3 = /* @__PURE__ */ jsx("span", { children: "Change language" });
851
+ $[1] = t2;
852
+ $[2] = t3;
853
+ } else {
854
+ t2 = $[1];
855
+ t3 = $[2];
856
+ }
857
+ let t4;
858
+ if ($[3] !== onChangeLanguage) {
859
+ t4 = /* @__PURE__ */ jsxs(Menu.Item, {
860
+ className: code_block_view_module_default.MenuItem,
861
+ onClick: onChangeLanguage,
862
+ "data-testid": "code-block-menu-language",
555
863
  children: [t2, t3]
556
864
  });
557
- $[5] = t1;
558
- $[6] = t2;
559
- $[7] = t3;
560
- $[8] = t4;
561
- } else t4 = $[8];
562
- return t4;
865
+ $[3] = onChangeLanguage;
866
+ $[4] = t4;
867
+ } else t4 = $[4];
868
+ let t5;
869
+ let t6;
870
+ if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
871
+ t5 = /* @__PURE__ */ jsx(CopyIcon, {});
872
+ t6 = /* @__PURE__ */ jsx("span", { children: "Copy code" });
873
+ $[5] = t5;
874
+ $[6] = t6;
875
+ } else {
876
+ t5 = $[5];
877
+ t6 = $[6];
878
+ }
879
+ let t7;
880
+ if ($[7] !== onCopyCode) {
881
+ t7 = /* @__PURE__ */ jsxs(Menu.Item, {
882
+ className: code_block_view_module_default.MenuItem,
883
+ onClick: onCopyCode,
884
+ "data-testid": "code-block-menu-copy",
885
+ children: [t5, t6]
886
+ });
887
+ $[7] = onCopyCode;
888
+ $[8] = t7;
889
+ } else t7 = $[8];
890
+ let t8;
891
+ if ($[9] !== getFinalFocus || $[10] !== t4 || $[11] !== t7) {
892
+ t8 = /* @__PURE__ */ jsxs(Menu.Popup, {
893
+ className: t1,
894
+ finalFocus: getFinalFocus,
895
+ children: [t4, t7]
896
+ });
897
+ $[9] = getFinalFocus;
898
+ $[10] = t4;
899
+ $[11] = t7;
900
+ $[12] = t8;
901
+ } else t8 = $[12];
902
+ let t9;
903
+ if ($[13] !== collisionBoundary || $[14] !== t8) {
904
+ t9 = /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, {
905
+ className: code_block_view_module_default.Positioner,
906
+ sideOffset: 4,
907
+ align: "end",
908
+ collisionBoundary,
909
+ children: t8
910
+ }) });
911
+ $[13] = collisionBoundary;
912
+ $[14] = t8;
913
+ $[15] = t9;
914
+ } else t9 = $[15];
915
+ return t9;
563
916
  }
564
917
  function CodeBlockLanguagePicker(t0) {
565
- const $ = c(22);
566
- const { language, setLanguage, onShownChange } = t0;
918
+ const $ = c(27);
919
+ const { language, setLanguage, open, onOpenChange, onOpenChangeComplete, getFinalFocus } = t0;
567
920
  const collisionBoundary = use(CollisionBoundaryContext);
568
921
  let t1;
569
922
  if ($[0] !== language) {
@@ -601,97 +954,110 @@ function CodeBlockLanguagePicker(t0) {
601
954
  $[5] = t3;
602
955
  } else t3 = $[5];
603
956
  let t4;
604
- let t5;
605
- if ($[6] !== onShownChange) {
606
- t4 = (open) => {
607
- if (open) onShownChange(true);
608
- else setQuery("");
609
- };
610
- t5 = (open_0) => {
611
- if (!open_0) onShownChange(false);
957
+ if ($[6] !== onOpenChangeComplete) {
958
+ t4 = (open_0) => {
959
+ onOpenChangeComplete(open_0);
960
+ if (!open_0) setQuery("");
612
961
  };
613
- $[6] = onShownChange;
962
+ $[6] = onOpenChangeComplete;
614
963
  $[7] = t4;
964
+ } else t4 = $[7];
965
+ let t5;
966
+ if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
967
+ t5 = /* @__PURE__ */ jsx(Combobox.Value, { placeholder: "Plain Text" });
615
968
  $[8] = t5;
616
- } else {
617
- t4 = $[7];
618
- t5 = $[8];
619
- }
969
+ } else t5 = $[8];
620
970
  let t6;
621
971
  if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
622
- t6 = /* @__PURE__ */ jsx(Combobox.Value, { placeholder: "Plain Text" });
623
- $[9] = t6;
624
- } else t6 = $[9];
625
- let t7;
626
- if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
627
- t7 = /* @__PURE__ */ jsxs(Combobox.Trigger, {
972
+ t6 = /* @__PURE__ */ jsxs(Combobox.Trigger, {
628
973
  className: code_block_view_module_default.Trigger,
629
974
  "data-testid": "code-block-language",
630
- children: [t6, /* @__PURE__ */ jsx(Combobox.Icon, {
975
+ children: [t5, /* @__PURE__ */ jsx(Combobox.Icon, {
631
976
  className: code_block_view_module_default.TriggerIcon,
632
977
  children: /* @__PURE__ */ jsx(ChevronsUpDownIcon, {})
633
978
  })]
634
979
  });
635
- $[10] = t7;
636
- } else t7 = $[10];
980
+ $[9] = t6;
981
+ } else t6 = $[9];
982
+ let t7;
637
983
  let t8;
638
- if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
639
- t8 = /* @__PURE__ */ jsxs(Combobox.Popup, {
984
+ let t9;
985
+ if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
986
+ t7 = /* @__PURE__ */ jsx("div", {
987
+ className: code_block_view_module_default.SearchRow,
988
+ children: /* @__PURE__ */ jsx(Combobox.Input, {
989
+ className: code_block_view_module_default.Search,
990
+ placeholder: "Search or type a language",
991
+ "data-testid": "code-block-language-search"
992
+ })
993
+ });
994
+ t8 = /* @__PURE__ */ jsx(Combobox.Empty, {
995
+ className: code_block_view_module_default.Empty,
996
+ children: "No languages found."
997
+ });
998
+ t9 = /* @__PURE__ */ jsx(Combobox.List, {
999
+ className: code_block_view_module_default.List,
1000
+ children: _temp$3
1001
+ });
1002
+ $[10] = t7;
1003
+ $[11] = t8;
1004
+ $[12] = t9;
1005
+ } else {
1006
+ t7 = $[10];
1007
+ t8 = $[11];
1008
+ t9 = $[12];
1009
+ }
1010
+ let t10;
1011
+ if ($[13] !== getFinalFocus) {
1012
+ t10 = /* @__PURE__ */ jsxs(Combobox.Popup, {
640
1013
  className: code_block_view_module_default.Popup,
1014
+ finalFocus: getFinalFocus,
641
1015
  children: [
642
- /* @__PURE__ */ jsx("div", {
643
- className: code_block_view_module_default.SearchRow,
644
- children: /* @__PURE__ */ jsx(Combobox.Input, {
645
- className: code_block_view_module_default.Search,
646
- placeholder: "Search or type a language",
647
- "data-testid": "code-block-language-search"
648
- })
649
- }),
650
- /* @__PURE__ */ jsx(Combobox.Empty, {
651
- className: code_block_view_module_default.Empty,
652
- children: "No languages found."
653
- }),
654
- /* @__PURE__ */ jsx(Combobox.List, {
655
- className: code_block_view_module_default.List,
656
- children: _temp$3
657
- })
1016
+ t7,
1017
+ t8,
1018
+ t9
658
1019
  ]
659
1020
  });
660
- $[11] = t8;
661
- } else t8 = $[11];
662
- let t9;
663
- if ($[12] !== collisionBoundary) {
664
- t9 = /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, {
1021
+ $[13] = getFinalFocus;
1022
+ $[14] = t10;
1023
+ } else t10 = $[14];
1024
+ let t11;
1025
+ if ($[15] !== collisionBoundary || $[16] !== t10) {
1026
+ t11 = /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, {
665
1027
  className: code_block_view_module_default.Positioner,
666
1028
  sideOffset: 4,
667
1029
  collisionBoundary,
668
- children: t8
1030
+ children: t10
669
1031
  }) });
670
- $[12] = collisionBoundary;
671
- $[13] = t9;
672
- } else t9 = $[13];
673
- let t10;
674
- if ($[14] !== itemsForView || $[15] !== query || $[16] !== selected || $[17] !== t3 || $[18] !== t4 || $[19] !== t5 || $[20] !== t9) {
675
- t10 = /* @__PURE__ */ jsxs(Combobox.Root, {
1032
+ $[15] = collisionBoundary;
1033
+ $[16] = t10;
1034
+ $[17] = t11;
1035
+ } else t11 = $[17];
1036
+ let t12;
1037
+ if ($[18] !== itemsForView || $[19] !== onOpenChange || $[20] !== open || $[21] !== query || $[22] !== selected || $[23] !== t11 || $[24] !== t3 || $[25] !== t4) {
1038
+ t12 = /* @__PURE__ */ jsxs(Combobox.Root, {
676
1039
  items: itemsForView,
1040
+ autoHighlight: true,
677
1041
  value: selected,
678
1042
  onValueChange: t3,
679
1043
  inputValue: query,
680
1044
  onInputValueChange: setQuery,
681
- onOpenChange: t4,
682
- onOpenChangeComplete: t5,
683
- children: [t7, t9]
1045
+ open,
1046
+ onOpenChange,
1047
+ onOpenChangeComplete: t4,
1048
+ children: [t6, t11]
684
1049
  });
685
- $[14] = itemsForView;
686
- $[15] = query;
687
- $[16] = selected;
688
- $[17] = t3;
689
- $[18] = t4;
690
- $[19] = t5;
691
- $[20] = t9;
692
- $[21] = t10;
693
- } else t10 = $[21];
694
- return t10;
1050
+ $[18] = itemsForView;
1051
+ $[19] = onOpenChange;
1052
+ $[20] = open;
1053
+ $[21] = query;
1054
+ $[22] = selected;
1055
+ $[23] = t11;
1056
+ $[24] = t3;
1057
+ $[25] = t4;
1058
+ $[26] = t12;
1059
+ } else t12 = $[26];
1060
+ return t12;
695
1061
  }
696
1062
  function _temp$3(item_2) {
697
1063
  return /* @__PURE__ */ jsxs(Combobox.Item, {
@@ -715,7 +1081,7 @@ function defineCodeBlockView(component = CodeBlockView) {
715
1081
  as: createCodeBlockRoot,
716
1082
  contentAs: "code",
717
1083
  component
718
- }), defineCodeBlockPreviewPlugin());
1084
+ }), defineCodeBlockPreviewPlugin(), defineCodeBlockMenuKeymap());
719
1085
  }
720
1086
  function createCodeBlockRoot() {
721
1087
  const element = document.createElement("div");
@@ -5359,7 +5725,7 @@ function MathCodeBlock(t0) {
5359
5725
  return t1;
5360
5726
  }
5361
5727
  function MermaidCodeBlock(t0) {
5362
- const $ = c(5);
5728
+ const $ = c(6);
5363
5729
  const { code } = t0;
5364
5730
  const renderer = usePele(true);
5365
5731
  if (!renderer || code.trim() === "") {
@@ -5375,19 +5741,24 @@ function MermaidCodeBlock(t0) {
5375
5741
  return t1;
5376
5742
  }
5377
5743
  let t1;
5378
- if ($[2] !== code || $[3] !== renderer) {
5379
- t1 = /* @__PURE__ */ jsx(MermaidRender, {
5744
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
5745
+ t1 = clsx(code_block_view_module_default.Preview, code_block_view_module_default.MermaidPreview);
5746
+ $[2] = t1;
5747
+ } else t1 = $[2];
5748
+ let t2;
5749
+ if ($[3] !== code || $[4] !== renderer) {
5750
+ t2 = /* @__PURE__ */ jsx(MermaidRender, {
5380
5751
  renderer,
5381
5752
  source: code,
5382
- className: `${code_block_view_module_default.Preview} ${code_block_view_module_default.MermaidPreview}`,
5753
+ className: t1,
5383
5754
  "data-testid": "code-block-mermaid-preview",
5384
5755
  "data-scroll-x": true
5385
5756
  });
5386
- $[2] = code;
5387
- $[3] = renderer;
5388
- $[4] = t1;
5389
- } else t1 = $[4];
5390
- return t1;
5757
+ $[3] = code;
5758
+ $[4] = renderer;
5759
+ $[5] = t2;
5760
+ } else t2 = $[5];
5761
+ return t2;
5391
5762
  }
5392
5763
  /**
5393
5764
  * Wrap inline `children` in one mark, special-casing the view/link marks.
package/dist/style.css CHANGED
@@ -103,7 +103,7 @@
103
103
  }
104
104
  }
105
105
 
106
- .meow_CopyButton_D3j_9q {
106
+ .meow_IconButton_D3j_9q {
107
107
  box-sizing: border-box;
108
108
  border: 1px solid var(--meowdown-border);
109
109
  background: var(--meowdown-popover-bg);
@@ -117,7 +117,7 @@
117
117
  padding: 0;
118
118
  display: flex;
119
119
 
120
- &:hover {
120
+ &:hover, &[data-popup-open] {
121
121
  color: var(--meowdown-heading);
122
122
  }
123
123
 
@@ -163,6 +163,35 @@
163
163
  }
164
164
  }
165
165
 
166
+ .meow_Menu_D3j_9q {
167
+ outline: none;
168
+ width: auto;
169
+ min-width: 10rem;
170
+ padding: .25rem;
171
+ }
172
+
173
+ .meow_MenuItem_D3j_9q {
174
+ cursor: pointer;
175
+ border-radius: .35rem;
176
+ outline: none;
177
+ align-items: center;
178
+ gap: .5rem;
179
+ padding: .3rem .5rem;
180
+ display: flex;
181
+
182
+ &[data-highlighted] {
183
+ background: color-mix(in oklab, var(--meowdown-accent) 14%, transparent);
184
+ color: var(--meowdown-heading);
185
+ }
186
+
187
+ & svg {
188
+ width: .9rem;
189
+ height: .9rem;
190
+ color: var(--meowdown-muted);
191
+ display: block;
192
+ }
193
+ }
194
+
166
195
  .meow_SearchRow_D3j_9q {
167
196
  border-bottom: 1px solid var(--meowdown-border);
168
197
  padding: .35rem;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/react",
3
3
  "type": "module",
4
- "version": "0.83.2",
4
+ "version": "0.84.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -22,8 +22,8 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "@base-ui/react": "^1.9.0",
25
- "@meowdown/core": "0.83.2",
26
- "@meowdown/embed": "0.3.1",
25
+ "@meowdown/core": "0.83.3",
26
+ "@meowdown/embed": "0.3.2",
27
27
  "@meowdown/markdown": "0.78.0",
28
28
  "@ocavue/utils": "^1.11.0",
29
29
  "@post-embed/types": "^0.5.0",