@meowdown/react 0.70.0 → 0.71.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
@@ -381,14 +381,13 @@ interface EditorProps {
381
381
  * inline pill (file icon, name, size) instead of a link, behaves as one
382
382
  * caret unit, and reports clicks through `onFileClick` instead of
383
383
  * `onLinkClick`. The markdown text is untouched. Must be pure (same link,
384
- * same answer). Read once when the editor is created: later identity
385
- * changes are ignored, like `initialMarkdown`.
384
+ * same answer). Changing the resolver reparses existing content.
386
385
  */
387
386
  resolveFileLink?: FileLinkResolver;
388
387
  /**
389
388
  * Classifies `![[target]]` as an image, file, or note atom. Return
390
389
  * `undefined` for missing or ambiguous targets to leave the source literal
391
- * and editable. Must be pure and is read once when the editor is created.
390
+ * and editable. Must be pure; changing it reparses existing content.
392
391
  */
393
392
  resolveWikiEmbed?: WikiEmbedResolver;
394
393
  /**
@@ -396,8 +395,8 @@ interface EditorProps {
396
395
  * handlers receive and the label its chip shows. Meowdown reads no syntax
397
396
  * inside the brackets, so an alias form such as `[[target|alias]]` is
398
397
  * split here by the host. Return `undefined` to use the bracketed text as
399
- * both; the markdown text is untouched either way. Must be pure and is
400
- * read once when the editor is created.
398
+ * both; the markdown text is untouched either way. Must be pure; changing
399
+ * it reparses existing content.
401
400
  */
402
401
  resolveWikilink?: WikilinkResolver;
403
402
  /**
@@ -409,12 +408,15 @@ interface EditorProps {
409
408
  */
410
409
  resolveFileInfo?: FileViewOptions['resolveFileInfo'];
411
410
  /**
412
- * Resolves the data behind an X post URL, directly or as a promise; the post
413
- * renders as a `post-embed-x-post` card. Defaults to `defaultResolveXPost`,
414
- * which fetches through react-tweet's hosted proxy. Pass a stable function
415
- * (e.g. from `useCallback`).
411
+ * Resolve the data behind an X post URL.
412
+ * Keep the function stable (for example, with `useCallback`). When omitted, public
413
+ * posts use `defaultResolveXPost` through react-tweet's hosted proxy.
416
414
  */
417
415
  resolveXPost?: XPostResolver$1;
416
+ /**
417
+ * Additional trusted protocols for X media URLs, such as `reflect-asset:`.
418
+ */
419
+ mediaUrlProtocols?: string[];
418
420
  /**
419
421
  * Resolves the data behind a YouTube video URL, directly or as a promise;
420
422
  * the video renders as a `post-embed-youtube-video` card. Defaults to
@@ -543,7 +545,7 @@ interface EditorProps {
543
545
  *
544
546
  * Callbacks and resolvers should be stable; pass them via `useCallback`.
545
547
  */
546
- export declare function MeowdownEditor({ mode, initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, resolveYouTubeVideo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution, frontmatter, blockHandle, caretGlide, placeholder, readOnly, spellCheck, searchQuery, onSearchChange, timeFormat, editorClassName, wrapperClassName, CodeBlockView, handleRef, children }: EditorProps): ReactElement;
548
+ export declare function MeowdownEditor({ mode, initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution, frontmatter, blockHandle, caretGlide, placeholder, readOnly, spellCheck, searchQuery, onSearchChange, timeFormat, editorClassName, wrapperClassName, CodeBlockView, handleRef, children }: EditorProps): ReactElement;
547
549
  //#endregion
548
550
  //#region src/components/markdown-view.d.ts
549
551
  /**
@@ -637,6 +639,10 @@ interface MarkdownViewProps {
637
639
  * card. Defaults to `defaultResolveXPost`.
638
640
  */
639
641
  resolveXPost?: XPostResolver$1;
642
+ /**
643
+ * Additional trusted protocols for X media URLs, such as `reflect-asset:`.
644
+ */
645
+ mediaUrlProtocols?: string[];
640
646
  /**
641
647
  * Resolve the data behind a YouTube video URL, rendered as a
642
648
  * `post-embed-youtube-video` card. Defaults to `defaultResolveYouTubeVideo`.
@@ -678,7 +684,7 @@ interface MarkdownViewProps {
678
684
  * Callbacks (`onWikilinkClick`, etc.) and resolvers should be stable; pass them via
679
685
  * `useCallback` to avoid re-rendering the whole tree.
680
686
  */
681
- export declare function MarkdownView({ markdown, markMode, frontmatter, interactive, expandCollapsed, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, resolveYouTubeVideo, onWikilinkClick, onLinkClick, onImageClick, onFileClick, onTaskClick, className }: MarkdownViewProps): ReactElement;
687
+ export declare function MarkdownView({ markdown, markMode, frontmatter, interactive, expandCollapsed, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, onWikilinkClick, onLinkClick, onImageClick, onFileClick, onTaskClick, className }: MarkdownViewProps): ReactElement;
682
688
  //#endregion
683
689
  //#region src/components/wikilink-hover-card.d.ts
684
690
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { clsx } from "clsx/lite";
2
2
  import { Fragment, cloneElement, createElement, memo, useCallback, useDeferredValue, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
3
- import { buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, 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, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, matchPostEmbed, normalizeHref, parsePostEmbedSnapshot, renderMathInto } from "@meowdown/core";
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, matchPostEmbed, 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";
@@ -403,21 +403,12 @@ function DropIndicator$1() {
403
403
 
404
404
  //#endregion
405
405
  //#region src/components/editor-extensions.tsx
406
- function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick, onTagClick, onExitBoundary, resolveImageUrl, resolveFileInfo, resolveXPost, resolveYouTubeVideo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution, placeholder, readOnly, wikilinkEnabled, spellCheck, searchQuery, onSearchChange, editorClassName }) {
406
+ function EditorExtensions({ config, searchQuery, onDocChange, onSearchChange }) {
407
407
  const editor = useEditor$1();
408
- useLayoutEffect(() => {
409
- editor.commands.setMarkMode(markMode);
410
- }, [editor, markMode]);
411
- useLayoutEffect(() => {
412
- if (!editorClassName) return;
413
- const extension = defineViewAttributes({ class: editorClassName });
414
- return editor.use(extension);
415
- }, [editor, editorClassName]);
416
- useLayoutEffect(() => {
417
- if (spellCheck == null) return;
418
- const extension = defineViewAttributes({ spellcheck: spellCheck ? "true" : "false" });
419
- return editor.use(extension);
420
- }, [editor, spellCheck]);
408
+ useEffect(() => {
409
+ const timer = setTimeout(() => updateEditorConfig(editor, config));
410
+ return () => clearTimeout(timer);
411
+ }, [editor, config]);
421
412
  const deferredSearchQuery = useDeferredValue(searchQuery);
422
413
  useEffect(() => {
423
414
  editor.commands.setSearchQuery({
@@ -425,87 +416,8 @@ function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick,
425
416
  literal: true
426
417
  });
427
418
  }, [editor, deferredSearchQuery]);
419
+ useExtension$1(useMemo(() => onDocChange ? defineDocChangeHandler(onDocChange) : null, [onDocChange]));
428
420
  useExtension$1(useMemo(() => onSearchChange ? defineSearchStatusHandler(onSearchChange) : null, [onSearchChange]));
429
- useExtension$1(useMemo(() => {
430
- return readOnly ? defineReadonly() : null;
431
- }, [readOnly]));
432
- useExtension$1(useMemo(() => {
433
- return onDocChange ? defineDocChangeHandler(onDocChange) : null;
434
- }, [onDocChange]));
435
- useExtension$1(useMemo(() => {
436
- return onWikilinkClick ? defineWikilinkClickHandler(onWikilinkClick) : null;
437
- }, [onWikilinkClick]));
438
- useExtension$1(useMemo(() => {
439
- return onLinkClick ? defineLinkClickHandler(onLinkClick) : null;
440
- }, [onLinkClick]));
441
- useExtension$1(useMemo(() => {
442
- return onTagClick ? defineTagClickHandler(onTagClick) : null;
443
- }, [onTagClick]));
444
- useExtension$1(useMemo(() => {
445
- return onWikilinkClick || onTagClick || onFileClick || onLinkClick || onImageClick ? defineFollowLinkHandler({
446
- onWikilinkClick,
447
- onTagClick,
448
- onFileClick,
449
- onLinkClick,
450
- onImageClick
451
- }) : null;
452
- }, [
453
- onWikilinkClick,
454
- onTagClick,
455
- onFileClick,
456
- onLinkClick,
457
- onImageClick
458
- ]));
459
- useExtension$1(useMemo(() => {
460
- return onExitBoundary ? defineExitBoundaryHandler(onExitBoundary) : null;
461
- }, [onExitBoundary]));
462
- useExtension$1(useMemo(() => {
463
- return defineImage({
464
- resolveImageUrl,
465
- resolveXPost,
466
- resolveYouTubeVideo
467
- });
468
- }, [
469
- resolveImageUrl,
470
- resolveXPost,
471
- resolveYouTubeVideo
472
- ]));
473
- useExtension$1(useMemo(() => {
474
- return defineFileView({ resolveFileInfo });
475
- }, [resolveFileInfo]));
476
- useExtension$1(useMemo(() => {
477
- return onFileClick ? defineFileClickHandler(onFileClick) : null;
478
- }, [onFileClick]));
479
- useExtension$1(useMemo(() => {
480
- return onFilePaste ? defineFilePaste({
481
- onFilePaste,
482
- onFileSaveError
483
- }) : null;
484
- }, [onFilePaste, onFileSaveError]));
485
- useExtension$1(useMemo(() => {
486
- return onImageClick ? defineImageClickHandler(onImageClick) : null;
487
- }, [onImageClick]));
488
- useExtension$1(useMemo(() => {
489
- return embedPaste ? defineEmbedPaste() : null;
490
- }, [embedPaste]));
491
- useExtension$1(useMemo(() => {
492
- return linkPaste ? defineLinkPaste() : null;
493
- }, [linkPaste]));
494
- useExtension$1(useMemo(() => {
495
- return bulletAfterHeading ? defineBulletAfterHeading() : null;
496
- }, [bulletAfterHeading]));
497
- useExtension$1(useMemo(() => {
498
- return substitution ? defineSubstitution() : null;
499
- }, [substitution]));
500
- useExtension$1(useMemo(() => {
501
- return placeholder ? definePlaceholder({
502
- placeholder,
503
- strategy: "doc"
504
- }) : null;
505
- }, [placeholder]));
506
- useExtension$1(useMemo(() => {
507
- return wikilinkEnabled ? defineWikilinkTrigger() : null;
508
- }, [wikilinkEnabled]));
509
421
  return null;
510
422
  }
511
423
 
@@ -1935,14 +1847,73 @@ function findHeadingPosition(doc, fragment) {
1935
1847
  });
1936
1848
  return match;
1937
1849
  }
1938
- function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, resolveYouTubeVideo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution = true, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, searchQuery = "", onSearchChange, timeFormat, editorClassName, CodeBlockView, ref, children }) {
1850
+ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, substitution = true, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, searchQuery = "", onSearchChange, timeFormat, editorClassName, CodeBlockView, ref, children }) {
1851
+ const suppressDocChangeRef = useRef(false);
1852
+ const handleDocChange = useMemo(() => {
1853
+ if (!onDocChange) return;
1854
+ return () => {
1855
+ if (suppressDocChangeRef.current) return;
1856
+ onDocChange();
1857
+ };
1858
+ }, [onDocChange]);
1859
+ const wikilinkEnabled = !!onWikilinkSearch;
1860
+ const config = useMemo(() => ({
1861
+ markMode,
1862
+ resolveFileLink,
1863
+ resolveWikiEmbed,
1864
+ resolveWikilink,
1865
+ onWikilinkClick,
1866
+ onLinkClick,
1867
+ onTagClick,
1868
+ onExitBoundary,
1869
+ resolveImageUrl,
1870
+ resolveFileInfo,
1871
+ resolveXPost,
1872
+ mediaUrlProtocols,
1873
+ resolveYouTubeVideo,
1874
+ onFileClick,
1875
+ onFilePaste,
1876
+ onFileSaveError,
1877
+ onImageClick,
1878
+ embedPaste,
1879
+ linkPaste,
1880
+ bulletAfterHeading,
1881
+ substitution,
1882
+ placeholder,
1883
+ readOnly,
1884
+ spellCheck,
1885
+ editorClassName,
1886
+ wikilinkEnabled
1887
+ }), [
1888
+ markMode,
1889
+ resolveFileLink,
1890
+ resolveWikiEmbed,
1891
+ resolveWikilink,
1892
+ onWikilinkClick,
1893
+ onLinkClick,
1894
+ onTagClick,
1895
+ onExitBoundary,
1896
+ resolveImageUrl,
1897
+ resolveFileInfo,
1898
+ resolveXPost,
1899
+ mediaUrlProtocols,
1900
+ resolveYouTubeVideo,
1901
+ onFileClick,
1902
+ onFilePaste,
1903
+ onFileSaveError,
1904
+ onImageClick,
1905
+ embedPaste,
1906
+ linkPaste,
1907
+ bulletAfterHeading,
1908
+ substitution,
1909
+ placeholder,
1910
+ readOnly,
1911
+ spellCheck,
1912
+ editorClassName,
1913
+ wikilinkEnabled
1914
+ ]);
1939
1915
  const [editor] = useState(() => {
1940
- const baseExtension = defineEditorExtension({
1941
- resolveFileLink,
1942
- resolveWikiEmbed,
1943
- resolveWikilink,
1944
- markMode
1945
- });
1916
+ const baseExtension = defineEditorExtension(config);
1946
1917
  const extension = CodeBlockView === false ? baseExtension : union(baseExtension, defineCodeBlockView(CodeBlockView));
1947
1918
  const editor = createEditor({ extension });
1948
1919
  if (initialMarkdown) editor.setContent(markdownToDoc(initialMarkdown, {
@@ -1951,7 +1922,6 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
1951
1922
  }));
1952
1923
  return editor;
1953
1924
  });
1954
- const suppressDocChangeRef = useRef(false);
1955
1925
  const [selectionMenuContext, setSelectionMenuContext] = useState();
1956
1926
  const hasSelectionMenu = !!onSelectionMenuSearch;
1957
1927
  const openSelectionMenu = useCallback(() => {
@@ -2081,44 +2051,16 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
2081
2051
  hasSelectionMenu,
2082
2052
  openSelectionMenu
2083
2053
  ]);
2084
- const handleDocChange = useMemo(() => {
2085
- if (!onDocChange) return;
2086
- return () => {
2087
- if (suppressDocChangeRef.current) return;
2088
- onDocChange();
2089
- };
2090
- }, [onDocChange]);
2091
2054
  return /* @__PURE__ */ jsxs(ProseKit, {
2092
2055
  editor,
2093
2056
  children: [
2094
2057
  /* @__PURE__ */ jsx(VirtualCaret, {}),
2095
2058
  /* @__PURE__ */ jsx("div", { ref: editor.mount }),
2096
2059
  /* @__PURE__ */ jsx(EditorExtensions, {
2097
- markMode,
2098
- onDocChange: handleDocChange,
2099
- onWikilinkClick,
2100
- onLinkClick,
2101
- onTagClick,
2102
- onExitBoundary,
2103
- resolveImageUrl,
2104
- resolveFileInfo,
2105
- resolveXPost,
2106
- resolveYouTubeVideo,
2107
- onFileClick,
2108
- onFilePaste,
2109
- onFileSaveError,
2110
- onImageClick,
2111
- embedPaste,
2112
- linkPaste,
2113
- bulletAfterHeading,
2114
- substitution,
2115
- placeholder,
2116
- readOnly,
2117
- wikilinkEnabled: !!onWikilinkSearch,
2118
- spellCheck,
2060
+ config,
2119
2061
  searchQuery,
2120
- onSearchChange,
2121
- editorClassName
2062
+ onDocChange: handleDocChange,
2063
+ onSearchChange
2122
2064
  }),
2123
2065
  blockHandle && !readOnly && /* @__PURE__ */ jsx(BlockHandle, {}),
2124
2066
  !readOnly && /* @__PURE__ */ jsx(TableHandle, {}),
@@ -2162,7 +2104,7 @@ const CARET_GLIDE_OFF = { "--meowdown-caret-glide": "0ms" };
2162
2104
  *
2163
2105
  * Callbacks and resolvers should be stable; pass them via `useCallback`.
2164
2106
  */
2165
- function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, resolveYouTubeVideo, 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, CodeBlockView, handleRef, children }) {
2107
+ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, resolveLinkPreview, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, 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, CodeBlockView, handleRef, children }) {
2166
2108
  const childRef = useRef(null);
2167
2109
  useImperativeHandle(handleRef, () => {
2168
2110
  function getMarkdown() {
@@ -2282,6 +2224,7 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashM
2282
2224
  resolveWikilink,
2283
2225
  resolveFileInfo,
2284
2226
  resolveXPost,
2227
+ mediaUrlProtocols,
2285
2228
  resolveYouTubeVideo,
2286
2229
  onFileClick,
2287
2230
  onFilePaste,
@@ -2435,7 +2378,7 @@ function WikilinkChip(props) {
2435
2378
  });
2436
2379
  }
2437
2380
  function PostEmbed(props) {
2438
- const { kind, src, width, snapshot, resolveXPost, resolveYouTubeVideo } = props;
2381
+ const { kind, src, width, snapshot, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo } = props;
2439
2382
  registerXPost();
2440
2383
  registerYouTubeVideo();
2441
2384
  const saved = snapshot == null ? void 0 : parsePostEmbedSnapshot(snapshot);
@@ -2443,10 +2386,12 @@ function PostEmbed(props) {
2443
2386
  className: "md-image-view-preview md-atom-view-preview",
2444
2387
  contentEditable: false,
2445
2388
  "data-testid": `${kind}-embed`,
2389
+ "data-post-embed": kind,
2446
2390
  children: kind === "x-post" ? createElement("post-embed-x-post", {
2447
- data: saved?.kind === "x-post" ? saved.data : null,
2391
+ data: null,
2448
2392
  url: src,
2449
- resolver: resolveXPost
2393
+ resolver: resolveXPost ?? defaultResolveXPost,
2394
+ mediaUrlProtocols: mediaUrlProtocols ?? null
2450
2395
  }) : createElement("post-embed-youtube-video", {
2451
2396
  data: saved?.kind === "youtube-video" ? saved.data : null,
2452
2397
  url: src,
@@ -2460,7 +2405,7 @@ function PostEmbed(props) {
2460
2405
  });
2461
2406
  }
2462
2407
  function ImagePreview(props) {
2463
- const { src, alt, width, snapshot, resolveImageUrl, resolveXPost, resolveYouTubeVideo, onImageClick, interactive } = props;
2408
+ const { src, alt, width, snapshot, resolveImageUrl, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, onImageClick, interactive } = props;
2464
2409
  const kind = matchPostEmbed(src);
2465
2410
  if (kind) {
2466
2411
  if (!interactive) return null;
@@ -2469,7 +2414,8 @@ function ImagePreview(props) {
2469
2414
  src,
2470
2415
  width,
2471
2416
  snapshot,
2472
- resolveXPost: resolveXPost ?? defaultResolveXPost,
2417
+ resolveXPost,
2418
+ mediaUrlProtocols,
2473
2419
  resolveYouTubeVideo: resolveYouTubeVideo ?? defaultResolveYouTubeVideo
2474
2420
  }, src);
2475
2421
  }
@@ -2506,6 +2452,7 @@ function ImageView(props) {
2506
2452
  snapshot,
2507
2453
  resolveImageUrl: context.resolveImageUrl,
2508
2454
  resolveXPost: context.resolveXPost,
2455
+ mediaUrlProtocols: context.mediaUrlProtocols,
2509
2456
  resolveYouTubeVideo: context.resolveYouTubeVideo,
2510
2457
  onImageClick: context.onImageClick,
2511
2458
  interactive: context.interactive
@@ -2913,7 +2860,7 @@ const MarkdownBlock = memo(function MarkdownBlock({ node, taskBase, context, ref
2913
2860
  * Callbacks (`onWikilinkClick`, etc.) and resolvers should be stable; pass them via
2914
2861
  * `useCallback` to avoid re-rendering the whole tree.
2915
2862
  */
2916
- function MarkdownView({ markdown, markMode = "hide", frontmatter = false, interactive = true, expandCollapsed = false, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, resolveYouTubeVideo, onWikilinkClick, onLinkClick, onImageClick, onFileClick, onTaskClick, className }) {
2863
+ function MarkdownView({ markdown, markMode = "hide", frontmatter = false, interactive = true, expandCollapsed = false, resolveImageUrl, resolveFileLink, resolveWikiEmbed, resolveWikilink, resolveFileInfo, resolveXPost, mediaUrlProtocols, resolveYouTubeVideo, onWikilinkClick, onLinkClick, onImageClick, onFileClick, onTaskClick, className }) {
2917
2864
  const context = useMemo(() => ({
2918
2865
  interactive,
2919
2866
  expandCollapsed,
@@ -2923,6 +2870,7 @@ function MarkdownView({ markdown, markMode = "hide", frontmatter = false, intera
2923
2870
  resolveWikilink,
2924
2871
  resolveFileInfo,
2925
2872
  resolveXPost,
2873
+ mediaUrlProtocols,
2926
2874
  resolveYouTubeVideo,
2927
2875
  onWikilinkClick: interactive ? onWikilinkClick : void 0,
2928
2876
  onLinkClick: interactive ? onLinkClick : void 0,
@@ -2938,6 +2886,7 @@ function MarkdownView({ markdown, markMode = "hide", frontmatter = false, intera
2938
2886
  resolveWikilink,
2939
2887
  resolveFileInfo,
2940
2888
  resolveXPost,
2889
+ mediaUrlProtocols,
2941
2890
  resolveYouTubeVideo,
2942
2891
  onWikilinkClick,
2943
2892
  onLinkClick,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/react",
3
3
  "type": "module",
4
- "version": "0.70.0",
4
+ "version": "0.71.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -23,8 +23,8 @@
23
23
  "dependencies": {
24
24
  "@base-ui/react": "^1.7.0",
25
25
  "@ocavue/utils": "^1.8.0",
26
- "@post-embed/elements": "^0.2.0",
27
- "@post-embed/types": "^0.2.0",
26
+ "@post-embed/elements": "^0.4.0",
27
+ "@post-embed/types": "^0.4.0",
28
28
  "@prosekit/core": "^0.13.2",
29
29
  "@prosekit/pm": "^0.1.19",
30
30
  "@prosekit/react": "^0.8.2",
@@ -33,7 +33,7 @@
33
33
  "github-slugger": "^2.0.0",
34
34
  "lucide-react": "^1.38.0",
35
35
  "react-property": "^2.0.2",
36
- "@meowdown/core": "0.70.0"
36
+ "@meowdown/core": "0.71.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "react": "^19.0.0",