@meowdown/react 0.73.1 → 0.75.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 +10 -6
- package/dist/index.js +163 -86
- package/dist/style.css +3 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComponentProps, ReactElement, ReactNode, Ref } from "react";
|
|
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, WikilinkResolver, XPostMediaClickHandler, XPostResolver, XPostResolver as XPostResolver$1, YouTubeVideoClickHandler, YouTubeVideoResolver, YouTubeVideoResolver as YouTubeVideoResolver$1 } from "@meowdown/core";
|
|
2
|
+
import { AcceptPendingReplacementOptions, ExitBoundaryHandler, FileClickHandler, FileInfoResolver, FileLinkResolver, FilePasteOptions, FileViewOptions, ImageClickHandler, ImageOptions, ImageUrlResolver, LinkClickHandler, LinkCopyHandler, LinkPreview, LinkPreviewResolver, LinkPreviewResolver as LinkPreviewResolver$1, ListMarker, MarkMode, PendingReplacement, PendingReplacementOutcome, PlaceholderOptions, SearchStatusHandler, StartPendingReplacementOptions, TagClickHandler, TypedEditor, WikiEmbedResolver, WikilinkClickHandler, WikilinkHoverHit, WikilinkResolver, XPostMediaClickHandler, XPostResolver, XPostResolver as XPostResolver$1, YouTubeVideoClickHandler, YouTubeVideoResolver, YouTubeVideoResolver as YouTubeVideoResolver$1 } from "@meowdown/core";
|
|
3
3
|
import { SelectionJSON, SelectionJSON as SelectionJSON$1 } from "@prosekit/core";
|
|
4
4
|
import { ReactNodeViewComponent, ReactNodeViewComponent as ReactNodeViewComponent$1, useEditor, useExtension, useKeymap } from "@prosekit/react";
|
|
5
5
|
//#region src/utils/date-format.d.ts
|
|
@@ -122,7 +122,7 @@ interface EditorHandle {
|
|
|
122
122
|
* Escape hatch: the underlying ProseKit editor, or `undefined` when the
|
|
123
123
|
* handle does not wrap one.
|
|
124
124
|
*/
|
|
125
|
-
|
|
125
|
+
getEditor: () => TypedEditor | undefined;
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
128
|
* One row of host items in the slash menu. The host ranks the rows; the menu does not re-sort.
|
|
@@ -372,8 +372,10 @@ interface EditorProps {
|
|
|
372
372
|
onExitBoundary?: ExitBoundaryHandler;
|
|
373
373
|
/**
|
|
374
374
|
* Maps an image `src` to a displayable URL, or `undefined` to skip that image.
|
|
375
|
-
*
|
|
376
|
-
*
|
|
375
|
+
* May return a `Promise` when the answer depends on data that has not loaded
|
|
376
|
+
* yet; the image appears once it resolves, in a box of its persisted size
|
|
377
|
+
* when one exists. Defaults to showing http(s) URLs as-is. Pass a stable
|
|
378
|
+
* function (e.g. from `useCallback`).
|
|
377
379
|
*/
|
|
378
380
|
resolveImageUrl?: ImageOptions['resolveImageUrl'];
|
|
379
381
|
/**
|
|
@@ -631,9 +633,11 @@ interface MarkdownViewProps {
|
|
|
631
633
|
*/
|
|
632
634
|
expandCollapsed?: boolean;
|
|
633
635
|
/**
|
|
634
|
-
* Map an image `src` to a displayable URL, or `undefined` to skip it.
|
|
636
|
+
* Map an image `src` to a displayable URL, or `undefined` to skip it. May
|
|
637
|
+
* return a `Promise`; the image appears once it resolves, in a box of its
|
|
638
|
+
* persisted size when both dimensions are known.
|
|
635
639
|
*/
|
|
636
|
-
resolveImageUrl?:
|
|
640
|
+
resolveImageUrl?: ImageUrlResolver;
|
|
637
641
|
/**
|
|
638
642
|
* Claim a `[label](url)` link as a file pill instead of a regular link.
|
|
639
643
|
* Must be pure; return `false` for links that should render normally.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { clsx } from "clsx/lite";
|
|
2
|
-
import { Fragment, ViewTransition, cloneElement, createElement, memo, startTransition, useCallback, useDeferredValue, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineCodeBlockPreviewPlugin, defineEditorExtension, defineLinkEditKeymap, defineLinkHoverHandler, definePendingReplacementHandler, defineSearchStatusHandler, defineVirtualCaret, defineWikilinkHoverHandler, docToMarkdown, formatFileSize, getCodeTokens, getFileKind, getLinkText, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunksWithContext, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, parsePostEmbedSnapshot, renderMathInto, updateEditorConfig } from "@meowdown/core";
|
|
2
|
+
import { Fragment, ViewTransition, cloneElement, createElement, memo, startTransition, useCallback, useDeferredValue, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
3
|
+
import { buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineCodeBlockPreviewPlugin, defineEditorExtension, defineLinkEditKeymap, defineLinkHoverHandler, definePendingReplacementHandler, defineSearchStatusHandler, defineVirtualCaret, defineWikilinkHoverHandler, dismissLinkHover, docToMarkdown, formatFileSize, getCodeTokens, getFileKind, getLinkText, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunksWithContext, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, parsePostEmbedSnapshot, renderMathInto, updateEditorConfig } 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";
|
|
@@ -12,6 +12,7 @@ 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";
|
|
14
14
|
import { Popover } from "@base-ui/react/popover";
|
|
15
|
+
import { queueExtension } from "@prosekit/web";
|
|
15
16
|
import { AutocompleteEmpty, AutocompleteItem, AutocompletePopup, AutocompletePositioner, AutocompleteRoot } from "@prosekit/react/autocomplete";
|
|
16
17
|
import { MenuItem, MenuPopup, MenuPositioner } from "@prosekit/react/menu";
|
|
17
18
|
import { TableHandleColumnMenuRoot, TableHandleColumnMenuTrigger, TableHandleColumnPopup, TableHandleColumnPositioner, TableHandleDragPreview, TableHandleDropIndicator, TableHandleRoot, TableHandleRowMenuRoot, TableHandleRowMenuTrigger, TableHandleRowPopup, TableHandleRowPositioner } from "@prosekit/react/table-handle";
|
|
@@ -422,6 +423,44 @@ function EditorExtensions({ config, searchQuery, onDocChange, onSearchChange })
|
|
|
422
423
|
return null;
|
|
423
424
|
}
|
|
424
425
|
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/components/link-menu-state.ts
|
|
428
|
+
const LINK_MENU_IDLE = { kind: "idle" };
|
|
429
|
+
/**
|
|
430
|
+
* What the popup renders: the open view, or the closing one during the exit
|
|
431
|
+
* animation.
|
|
432
|
+
*/
|
|
433
|
+
function getLinkMenuView(state) {
|
|
434
|
+
switch (state.kind) {
|
|
435
|
+
case "idle": return;
|
|
436
|
+
case "closing": return state.view;
|
|
437
|
+
default: return state;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function reduceLinkMenu(state, event) {
|
|
441
|
+
switch (event.type) {
|
|
442
|
+
case "hover":
|
|
443
|
+
if (state.kind === "edit") return state;
|
|
444
|
+
if (event.link) return {
|
|
445
|
+
kind: "preview",
|
|
446
|
+
link: event.link
|
|
447
|
+
};
|
|
448
|
+
return state.kind === "preview" ? {
|
|
449
|
+
kind: "closing",
|
|
450
|
+
view: state
|
|
451
|
+
} : state;
|
|
452
|
+
case "edit": return {
|
|
453
|
+
kind: "edit",
|
|
454
|
+
edit: event.edit
|
|
455
|
+
};
|
|
456
|
+
case "close": return state.kind === "preview" || state.kind === "edit" ? {
|
|
457
|
+
kind: "closing",
|
|
458
|
+
view: state
|
|
459
|
+
} : state;
|
|
460
|
+
case "closed": return state.kind === "closing" ? LINK_MENU_IDLE : state;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
425
464
|
//#endregion
|
|
426
465
|
//#region src/components/link-menu.module.css
|
|
427
466
|
var link_menu_module_default = {
|
|
@@ -735,41 +774,39 @@ function LinkEditContent({ edit, resolveLinkPreview, onSubmit, onRemove }) {
|
|
|
735
774
|
}
|
|
736
775
|
function LinkMenu({ onLinkClick, onLinkCopy, resolveLinkPreview, readOnly = false }) {
|
|
737
776
|
const editor = useEditor$1();
|
|
738
|
-
const [
|
|
739
|
-
const [editOpen, setEditOpen] = useState(false);
|
|
740
|
-
const [link, setLink] = useState();
|
|
741
|
-
const [edit, setEdit] = useState();
|
|
777
|
+
const [state, dispatch] = useReducer(reduceLinkMenu, LINK_MENU_IDLE);
|
|
742
778
|
const isPointerOverPopupRef = useRef(false);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
779
|
+
useEffect(() => {
|
|
780
|
+
const canLeave = () => !isPointerOverPopupRef.current;
|
|
781
|
+
const linkHoverExtension = defineLinkHoverHandler((hit) => dispatch({
|
|
782
|
+
type: "hover",
|
|
783
|
+
link: hit?.payload
|
|
784
|
+
}), { canLeave });
|
|
785
|
+
return queueExtension(editor, linkHoverExtension);
|
|
786
|
+
}, [editor]);
|
|
787
|
+
useEffect(() => {
|
|
788
|
+
if (readOnly) return;
|
|
789
|
+
const linkEditExtension = defineLinkEditKeymap((edit) => dispatch({
|
|
790
|
+
type: "edit",
|
|
791
|
+
edit
|
|
792
|
+
}));
|
|
793
|
+
return queueExtension(editor, linkEditExtension);
|
|
794
|
+
}, [editor, readOnly]);
|
|
758
795
|
const handlePointerHover = useCallback((over) => {
|
|
759
796
|
isPointerOverPopupRef.current = over;
|
|
760
797
|
}, []);
|
|
761
|
-
const
|
|
798
|
+
const close = useCallback(() => {
|
|
762
799
|
isPointerOverPopupRef.current = false;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
800
|
+
dismissLinkHover(editor.state);
|
|
801
|
+
dispatch({ type: "close" });
|
|
802
|
+
}, [editor]);
|
|
803
|
+
const view = getLinkMenuView(state);
|
|
804
|
+
const edit = view?.kind === "edit" ? view.edit : void 0;
|
|
805
|
+
const link = view?.kind === "preview" ? view.link : void 0;
|
|
769
806
|
const handleEditRemove = useCallback(() => {
|
|
770
807
|
editor.commands.removeLink();
|
|
771
|
-
|
|
772
|
-
}, [editor,
|
|
808
|
+
close();
|
|
809
|
+
}, [editor, close]);
|
|
773
810
|
const handleEditSubmit = useCallback((text, href) => {
|
|
774
811
|
if (!edit) return;
|
|
775
812
|
if (edit.link) editor.commands.updateLink({
|
|
@@ -781,52 +818,52 @@ function LinkMenu({ onLinkClick, onLinkCopy, resolveLinkPreview, readOnly = fals
|
|
|
781
818
|
text,
|
|
782
819
|
href
|
|
783
820
|
});
|
|
784
|
-
|
|
821
|
+
close();
|
|
785
822
|
}, [
|
|
786
823
|
edit,
|
|
787
824
|
editor,
|
|
788
|
-
|
|
825
|
+
close
|
|
789
826
|
]);
|
|
790
827
|
const mutable = link != null && !readOnly && link.form !== "reference";
|
|
791
828
|
const linkText = useMemo(() => link ? getLinkText(link) : "", [link]);
|
|
792
829
|
const editLink = useCallback(() => {
|
|
793
830
|
if (!link) return;
|
|
794
831
|
selectLinkUnit(editor, link);
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
832
|
+
dispatch({
|
|
833
|
+
type: "edit",
|
|
834
|
+
edit: {
|
|
835
|
+
from: link.unit.from,
|
|
836
|
+
to: link.unit.to,
|
|
837
|
+
link,
|
|
838
|
+
text: linkText
|
|
839
|
+
}
|
|
800
840
|
});
|
|
801
|
-
setEditOpen(true);
|
|
802
|
-
closeInfo();
|
|
803
841
|
}, [
|
|
804
842
|
link,
|
|
805
843
|
editor,
|
|
806
|
-
linkText
|
|
807
|
-
closeInfo
|
|
844
|
+
linkText
|
|
808
845
|
]);
|
|
809
846
|
const removeLink = useCallback(() => {
|
|
810
847
|
if (!link) return;
|
|
811
848
|
selectLinkUnit(editor, link);
|
|
812
849
|
editor.commands.removeLink();
|
|
813
|
-
|
|
850
|
+
close();
|
|
814
851
|
}, [
|
|
815
852
|
link,
|
|
816
853
|
editor,
|
|
817
|
-
|
|
854
|
+
close
|
|
818
855
|
]);
|
|
819
856
|
const handleUseTitle = useMemo(() => {
|
|
820
857
|
if (!link || !mutable || !isLinkTextForHref(linkText, link.href)) return;
|
|
821
858
|
return (title) => {
|
|
822
859
|
selectLinkUnit(editor, link);
|
|
823
860
|
editor.commands.updateLink({ text: title });
|
|
824
|
-
|
|
861
|
+
close();
|
|
825
862
|
};
|
|
826
863
|
}, [
|
|
827
864
|
link,
|
|
828
865
|
editor,
|
|
829
|
-
|
|
866
|
+
close,
|
|
830
867
|
linkText,
|
|
831
868
|
mutable
|
|
832
869
|
]);
|
|
@@ -836,24 +873,21 @@ function LinkMenu({ onLinkClick, onLinkCopy, resolveLinkPreview, readOnly = fals
|
|
|
836
873
|
if (!range) return;
|
|
837
874
|
return getVirtualElementFromRange(editor.view, range);
|
|
838
875
|
}, [range, editor]);
|
|
839
|
-
const editing = edit != null && !readOnly;
|
|
840
876
|
return /* @__PURE__ */ jsx(LinkPopover, {
|
|
841
877
|
anchor,
|
|
842
|
-
open:
|
|
843
|
-
onClose:
|
|
878
|
+
open: state.kind === "preview" || state.kind === "edit",
|
|
879
|
+
onClose: close,
|
|
844
880
|
onCloseComplete: () => {
|
|
845
|
-
if (
|
|
846
|
-
|
|
847
|
-
editor.focus();
|
|
848
|
-
} else if (!infoOpen) setLink(void 0);
|
|
881
|
+
if (edit) editor.focus();
|
|
882
|
+
dispatch({ type: "closed" });
|
|
849
883
|
},
|
|
850
884
|
onPopupHover: handlePointerHover,
|
|
851
|
-
children:
|
|
885
|
+
children: edit ? /* @__PURE__ */ jsx(LinkEditContent, {
|
|
852
886
|
edit,
|
|
853
887
|
resolveLinkPreview,
|
|
854
888
|
onRemove: edit.link ? handleEditRemove : void 0,
|
|
855
889
|
onSubmit: handleEditSubmit
|
|
856
|
-
}) : link ? /* @__PURE__ */ jsx(LinkInfoContent, {
|
|
890
|
+
}, `${edit.from}:${edit.to}`) : link ? /* @__PURE__ */ jsx(LinkInfoContent, {
|
|
857
891
|
href: link.href,
|
|
858
892
|
previewState,
|
|
859
893
|
onLinkClick,
|
|
@@ -1706,6 +1740,7 @@ function TagMenu({ onTagSearch }) {
|
|
|
1706
1740
|
className: autocomplete_menu_module_default.Popup,
|
|
1707
1741
|
"data-testid": "tag-menu",
|
|
1708
1742
|
children: [items.map((item) => /* @__PURE__ */ jsxs(AutocompleteItem, {
|
|
1743
|
+
value: item.tag,
|
|
1709
1744
|
className: autocomplete_menu_module_default.Item,
|
|
1710
1745
|
onSelect: () => {
|
|
1711
1746
|
editor.commands.insertText({ text: `#${item.tag} ` });
|
|
@@ -1782,6 +1817,7 @@ function WikilinkMenu({ onWikilinkSearch }) {
|
|
|
1782
1817
|
className: autocomplete_menu_module_default.Popup,
|
|
1783
1818
|
"data-testid": "wikilink-menu",
|
|
1784
1819
|
children: [items.map((item) => /* @__PURE__ */ jsxs(AutocompleteItem, {
|
|
1820
|
+
value: item.target,
|
|
1785
1821
|
className: autocomplete_menu_module_default.Item,
|
|
1786
1822
|
onSelect: () => {
|
|
1787
1823
|
editor.commands.insertText({ text: `[[${item.target}]]` });
|
|
@@ -1970,8 +2006,10 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
|
|
|
1970
2006
|
suppressDocChangeRef.current = true;
|
|
1971
2007
|
try {
|
|
1972
2008
|
editor.view.dispatch(transaction);
|
|
1973
|
-
} finally {
|
|
1974
2009
|
suppressDocChangeRef.current = false;
|
|
2010
|
+
} catch (error) {
|
|
2011
|
+
suppressDocChangeRef.current = false;
|
|
2012
|
+
throw error;
|
|
1975
2013
|
}
|
|
1976
2014
|
}
|
|
1977
2015
|
function setState(markdown, selection) {
|
|
@@ -2048,7 +2086,7 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
|
|
|
2048
2086
|
discardPendingReplacement,
|
|
2049
2087
|
findNext,
|
|
2050
2088
|
findPrevious,
|
|
2051
|
-
editor
|
|
2089
|
+
getEditor: () => editor
|
|
2052
2090
|
};
|
|
2053
2091
|
}, [
|
|
2054
2092
|
editor,
|
|
@@ -2056,11 +2094,14 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
|
|
|
2056
2094
|
hasSelectionMenu,
|
|
2057
2095
|
openSelectionMenu
|
|
2058
2096
|
]);
|
|
2097
|
+
const mount = useCallback((element) => {
|
|
2098
|
+
return editor.mount(element);
|
|
2099
|
+
}, [editor]);
|
|
2059
2100
|
return /* @__PURE__ */ jsxs(ProseKit, {
|
|
2060
2101
|
editor,
|
|
2061
2102
|
children: [
|
|
2062
2103
|
/* @__PURE__ */ jsx(VirtualCaret, {}),
|
|
2063
|
-
/* @__PURE__ */ jsx("div", { ref:
|
|
2104
|
+
/* @__PURE__ */ jsx("div", { ref: mount }),
|
|
2064
2105
|
/* @__PURE__ */ jsx(EditorExtensions, {
|
|
2065
2106
|
config,
|
|
2066
2107
|
searchQuery,
|
|
@@ -2081,7 +2122,7 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
|
|
|
2081
2122
|
onLinkCopy,
|
|
2082
2123
|
resolveLinkPreview,
|
|
2083
2124
|
readOnly
|
|
2084
|
-
}),
|
|
2125
|
+
}, String(readOnly)),
|
|
2085
2126
|
onTagSearch && /* @__PURE__ */ jsx(TagMenu, { onTagSearch }),
|
|
2086
2127
|
onWikilinkSearch && /* @__PURE__ */ jsx(WikilinkMenu, { onWikilinkSearch }),
|
|
2087
2128
|
onSelectionMenuSearch && !readOnly && /* @__PURE__ */ jsx(SelectionMenu, {
|
|
@@ -2177,6 +2218,9 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashM
|
|
|
2177
2218
|
function findPrevious() {
|
|
2178
2219
|
childRef.current?.findPrevious();
|
|
2179
2220
|
}
|
|
2221
|
+
function getEditor() {
|
|
2222
|
+
return childRef.current?.getEditor();
|
|
2223
|
+
}
|
|
2180
2224
|
return {
|
|
2181
2225
|
getMarkdown,
|
|
2182
2226
|
setMarkdown,
|
|
@@ -2197,9 +2241,7 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashM
|
|
|
2197
2241
|
discardPendingReplacement,
|
|
2198
2242
|
findNext,
|
|
2199
2243
|
findPrevious,
|
|
2200
|
-
|
|
2201
|
-
return childRef.current?.editor;
|
|
2202
|
-
}
|
|
2244
|
+
getEditor
|
|
2203
2245
|
};
|
|
2204
2246
|
}, []);
|
|
2205
2247
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -2256,6 +2298,37 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashM
|
|
|
2256
2298
|
});
|
|
2257
2299
|
}
|
|
2258
2300
|
|
|
2301
|
+
//#endregion
|
|
2302
|
+
//#region src/hooks/use-maybe-promise.ts
|
|
2303
|
+
/**
|
|
2304
|
+
* The settled value of `input`: `input` itself when it is not a `Promise`,
|
|
2305
|
+
* otherwise `undefined` until it resolves. A rejection stays `undefined` and is
|
|
2306
|
+
* reported with `console.warn`, since the caller has no other way to see it. A
|
|
2307
|
+
* new `input` discards the previous `Promise`'s result at once, so a stale
|
|
2308
|
+
* value never shows for a newer input. Callers memoize `input`, or a fresh
|
|
2309
|
+
* `Promise` on every render never settles.
|
|
2310
|
+
*/
|
|
2311
|
+
function useMaybePromise(input) {
|
|
2312
|
+
const [settled, setSettled] = useState(null);
|
|
2313
|
+
useEffect(() => {
|
|
2314
|
+
if (!(input instanceof Promise)) return;
|
|
2315
|
+
let active = true;
|
|
2316
|
+
input.then((value) => {
|
|
2317
|
+
if (active) setSettled({
|
|
2318
|
+
input,
|
|
2319
|
+
value
|
|
2320
|
+
});
|
|
2321
|
+
}, (error) => {
|
|
2322
|
+
if (active) console.warn("[meowdown] useMaybePromise input rejected:", error);
|
|
2323
|
+
});
|
|
2324
|
+
return () => {
|
|
2325
|
+
active = false;
|
|
2326
|
+
};
|
|
2327
|
+
}, [input]);
|
|
2328
|
+
if (!(input instanceof Promise)) return input;
|
|
2329
|
+
return settled?.input === input ? settled.value : void 0;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2259
2332
|
//#endregion
|
|
2260
2333
|
//#region src/components/attributes-to-props.ts
|
|
2261
2334
|
const UNCONTROLLED_COMPONENT_ATTRIBUTES = ["checked", "value"];
|
|
@@ -2412,8 +2485,14 @@ function PostEmbed(props) {
|
|
|
2412
2485
|
});
|
|
2413
2486
|
}
|
|
2414
2487
|
function ImagePreview(props) {
|
|
2415
|
-
const { src, alt, width, snapshot, resolveImageUrl, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, onImageClick, interactive } = props;
|
|
2488
|
+
const { src, alt, width, height, snapshot, resolveImageUrl, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, onImageClick, interactive } = props;
|
|
2416
2489
|
const kind = matchEmbed(src);
|
|
2490
|
+
const resolved = useMemo(() => kind ? void 0 : (resolveImageUrl ?? defaultResolveImageUrl)(src), [
|
|
2491
|
+
kind,
|
|
2492
|
+
resolveImageUrl,
|
|
2493
|
+
src
|
|
2494
|
+
]);
|
|
2495
|
+
const url = useMaybePromise(resolved);
|
|
2417
2496
|
if (kind) {
|
|
2418
2497
|
if (!interactive) return null;
|
|
2419
2498
|
return /* @__PURE__ */ jsx(PostEmbed, {
|
|
@@ -2426,8 +2505,23 @@ function ImagePreview(props) {
|
|
|
2426
2505
|
resolveYouTubeVideo: resolveYouTubeVideo ?? defaultResolveYouTubeVideo
|
|
2427
2506
|
}, src);
|
|
2428
2507
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2508
|
+
if (!url) {
|
|
2509
|
+
if (resolved instanceof Promise && width != null && height != null) return /* @__PURE__ */ jsx("span", {
|
|
2510
|
+
className: "md-image-view-preview md-atom-view-preview",
|
|
2511
|
+
"data-testid": "image-preview",
|
|
2512
|
+
contentEditable: false,
|
|
2513
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
2514
|
+
className: "md-image-resizable",
|
|
2515
|
+
"data-testid": "image-resizable",
|
|
2516
|
+
"data-loading": "",
|
|
2517
|
+
style: {
|
|
2518
|
+
width: `${width}px`,
|
|
2519
|
+
height: `${height}px`
|
|
2520
|
+
}
|
|
2521
|
+
})
|
|
2522
|
+
});
|
|
2523
|
+
return null;
|
|
2524
|
+
}
|
|
2431
2525
|
return /* @__PURE__ */ jsx("span", {
|
|
2432
2526
|
className: "md-image-view-preview md-atom-view-preview",
|
|
2433
2527
|
"data-testid": "image-preview",
|
|
@@ -2450,13 +2544,14 @@ function ImagePreview(props) {
|
|
|
2450
2544
|
});
|
|
2451
2545
|
}
|
|
2452
2546
|
function ImageView(props) {
|
|
2453
|
-
const { src, alt, width, snapshot, context, children } = props;
|
|
2547
|
+
const { src, alt, width, height, snapshot, context, children } = props;
|
|
2454
2548
|
return /* @__PURE__ */ jsxs("span", {
|
|
2455
2549
|
className: "md-image-view md-atom-view",
|
|
2456
2550
|
children: [/* @__PURE__ */ jsx(ImagePreview, {
|
|
2457
2551
|
src,
|
|
2458
2552
|
alt,
|
|
2459
2553
|
width,
|
|
2554
|
+
height,
|
|
2460
2555
|
snapshot,
|
|
2461
2556
|
resolveImageUrl: context.resolveImageUrl,
|
|
2462
2557
|
resolveXPost: context.resolveXPost,
|
|
@@ -2558,27 +2653,8 @@ function renderTokens(code, tokens) {
|
|
|
2558
2653
|
return out;
|
|
2559
2654
|
}
|
|
2560
2655
|
function CodeBlock({ code, language }) {
|
|
2561
|
-
const
|
|
2562
|
-
|
|
2563
|
-
return Array.isArray(result) ? result : null;
|
|
2564
|
-
}, [code, language]);
|
|
2565
|
-
const [asyncTokens, setAsyncTokens] = useState(null);
|
|
2566
|
-
useEffect(() => {
|
|
2567
|
-
if (syncTokens) return;
|
|
2568
|
-
let active = true;
|
|
2569
|
-
const result = getCodeTokens(code, language);
|
|
2570
|
-
if (!Array.isArray(result)) result.then((loaded) => {
|
|
2571
|
-
if (active) setAsyncTokens(loaded);
|
|
2572
|
-
});
|
|
2573
|
-
return () => {
|
|
2574
|
-
active = false;
|
|
2575
|
-
};
|
|
2576
|
-
}, [
|
|
2577
|
-
code,
|
|
2578
|
-
language,
|
|
2579
|
-
syncTokens
|
|
2580
|
-
]);
|
|
2581
|
-
const tokens = syncTokens ?? asyncTokens ?? [];
|
|
2656
|
+
const result = useMemo(() => getCodeTokens(code, language), [code, language]);
|
|
2657
|
+
const tokens = useMaybePromise(result) ?? [];
|
|
2582
2658
|
return /* @__PURE__ */ jsx("pre", {
|
|
2583
2659
|
"data-language": language || void 0,
|
|
2584
2660
|
children: /* @__PURE__ */ jsx("code", { children: tokens.length > 0 ? renderTokens(code, tokens) : code })
|
|
@@ -2657,6 +2733,7 @@ function wrapMark(mark, children, context) {
|
|
|
2657
2733
|
src: attrs.src,
|
|
2658
2734
|
alt: attrs.alt,
|
|
2659
2735
|
width: attrs.width,
|
|
2736
|
+
height: attrs.height,
|
|
2660
2737
|
snapshot: attrs.snapshot,
|
|
2661
2738
|
context,
|
|
2662
2739
|
children
|
package/dist/style.css
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meowdown/react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.75.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@base-ui/react": "^1.8.0",
|
|
25
|
-
"@meowdown/core": "0.
|
|
25
|
+
"@meowdown/core": "0.75.0",
|
|
26
26
|
"@meowdown/embed": "0.2.0",
|
|
27
27
|
"@meowdown/markdown": "0.72.0",
|
|
28
28
|
"@ocavue/utils": "^1.8.0",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@prosekit/core": "^0.13.3",
|
|
31
31
|
"@prosekit/pm": "^0.1.20",
|
|
32
32
|
"@prosekit/react": "^0.8.3",
|
|
33
|
+
"@prosekit/web": "^0.9.3",
|
|
33
34
|
"beautiful-mermaid": "^1.1.3",
|
|
34
35
|
"clsx": "^2.1.1",
|
|
35
36
|
"github-slugger": "^2.0.0",
|