@meowdown/react 0.11.0 → 0.12.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/README.md CHANGED
@@ -44,9 +44,11 @@ The Markdown editor component. Renders inside a `div.meowdown` wrapper that fill
44
44
  - `onTagSearch?: (query: string) => TagItem[] | Promise<TagItem[]>`: enables the tag menu, which opens when typing `#` followed by text in a rich mode. Returns ranked rows `{ tag, label?, detail?, onSelect? }` (the menu does not re-sort). Selecting a row inserts `#tag ` then runs its `onSelect`. Omit to disable.
45
45
  - `onWikilinkSearch?: (query: string) => WikilinkItem[] | Promise<WikilinkItem[]>`: enables the wikilink menu, which opens as soon as `[[` is typed in a rich mode. Returns ranked rows `{ target, label?, detail?, onSelect? }` (the menu does not re-sort). Selecting a row inserts `[[target]]` then runs its `onSelect`. Omit to disable.
46
46
  - `onWikilinkClick?: (payload: { target: string; event: MouseEvent }) => void`: called when a rendered wiki link is clicked. A plain click inside a link the caret already sits in just places the caret; `Mod`-click always fires. Pass a stable function (e.g. from `useCallback`). Ignored in source mode.
47
+ - `onLinkClick?: (payload: { href: string; event: MouseEvent }) => void`: called with its `href` when a rendered Markdown link (`[text](url)`) is clicked. A plain click inside a link the caret already sits in just places the caret; `Mod`-click always fires. Pass a stable function (e.g. from `useCallback`). Ignored in source mode.
47
48
  - `resolveImageUrl?: (src: string) => string | undefined`: maps an image `src` to a displayable URL (or `undefined` to skip). Enables inline image rendering: `![alt](src)` stays literal text and the image renders beneath its line. Pass a stable function. Ignored in source mode.
48
49
  - `onImagePaste?: (file: File) => string | undefined | Promise<string | undefined>`: persists a pasted or dropped image file and returns its markdown `src` (or `undefined` to decline), synchronously or as a promise. Pass a stable function. Ignored in source mode.
49
50
  - `onImageSaveError?: (error: unknown, file: File) => void`: called when `onImagePaste` throws. Defaults to `console.error`. Ignored in source mode.
51
+ - `onImageClick?: (payload: { src: string; alt: string; event: MouseEvent }) => void`: called when a rendered image is clicked, with its markdown `src`, `alt`, and the originating `MouseEvent`. Pass a stable function (e.g. from `useCallback`). Ignored in source mode.
50
52
  - `embedPaste?: boolean`: auto-embeds a pasted tweet or YouTube link as a rich embed; one undo turns the embed back into the raw link. On by default; set `false` to disable. Only takes effect when `resolveImageUrl` is set, since embeds render through the image pipeline. Ignored in source mode.
51
53
  - `bulletAfterHeading?: boolean`: pressing Enter at the end of the document's first heading (the title line) starts a fresh empty bullet on the next line instead of a plain paragraph. Off by default. Ignored in source mode.
52
54
  - `blockHandle?: boolean`: shows the per-block gutter handle in the rich modes (a drag grip for reordering blocks and a `+` add button, plus the drop indicator). On by default; set `false` to hide the gutter affordance entirely, e.g. when the host does not want block reordering. Ignored in source mode and when `readOnly` is set.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactNode, Ref } from "react";
2
- import { ImageOptions, MarkMode, PlaceholderOptions, TypedEditor, WikilinkClickHandler } from "@meowdown/core";
2
+ import { ImageClickHandler, ImageOptions, LinkClickHandler, MarkMode, PlaceholderOptions, TypedEditor, WikilinkClickHandler } 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
 
@@ -119,8 +119,16 @@ interface EditorProps {
119
119
  */
120
120
  onWikilinkClick?: WikilinkClickHandler;
121
121
  /**
122
- * Maps an image `src` to a displayable URL (or `undefined` to skip). Enables
123
- * inline image rendering. Pass a stable function. Ignored in source mode.
122
+ * Called with the link `href` on click of a rendered Markdown link
123
+ * (`[text](url)`). A plain click inside a link the caret already sits in just
124
+ * places the caret; `Mod`-click always fires. Pass a stable function (e.g.
125
+ * from `useCallback`). Ignored in source mode.
126
+ */
127
+ onLinkClick?: LinkClickHandler;
128
+ /**
129
+ * Maps an image `src` to a displayable URL, or `undefined` to skip that image.
130
+ * Defaults to showing http(s) URLs as-is. Pass a stable function (e.g. from
131
+ * `useCallback`). Ignored in source mode.
124
132
  */
125
133
  resolveImageUrl?: ImageOptions['resolveImageUrl'];
126
134
  /**
@@ -130,11 +138,15 @@ interface EditorProps {
130
138
  onImagePaste?: ImageOptions['onImagePaste'];
131
139
  /** Called when persisting a pasted/dropped image throws. Ignored in source mode. */
132
140
  onImageSaveError?: ImageOptions['onImageSaveError'];
141
+ /**
142
+ * Called when the user clicks a rendered image, with its markdown `src`,
143
+ * `alt`, and the originating `MouseEvent`. Pass a stable function (e.g. from
144
+ * `useCallback`). Ignored in source mode.
145
+ */
146
+ onImageClick?: ImageClickHandler;
133
147
  /**
134
148
  * Auto-embeds a pasted tweet or YouTube link as a rich embed; one undo turns
135
- * the embed back into the raw link. On by default. Ignored in source mode, and
136
- * only takes effect when `resolveImageUrl` is set, since embeds render through
137
- * the same image pipeline.
149
+ * the embed back into the raw link. On by default. Ignored in source mode.
138
150
  */
139
151
  embedPaste?: boolean;
140
152
  /**
@@ -179,9 +191,11 @@ declare function MeowdownEditor({
179
191
  onTagSearch,
180
192
  onWikilinkSearch,
181
193
  onWikilinkClick,
194
+ onLinkClick,
182
195
  resolveImageUrl,
183
196
  onImagePaste,
184
197
  onImageSaveError,
198
+ onImageClick,
185
199
  embedPaste,
186
200
  bulletAfterHeading,
187
201
  blockHandle,
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { Compartment, EditorSelection, EditorState } from "@codemirror/state";
7
7
  import { EditorView, keymap } from "@codemirror/view";
8
8
  import { clamp } from "@ocavue/utils";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
- import { codeBlockLanguages, defineBulletAfterHeading, defineEditorExtension, defineEmbedPaste, defineImages, defineMarkMode, definePlaceholder, defineReadonly, defineWikilinkClickHandler, docToMarkdown, markdownToDoc } from "@meowdown/core";
10
+ import { codeBlockLanguages, defineBulletAfterHeading, defineEditorExtension, defineEmbedPaste, defineImage, defineImageClickHandler, defineLinkClickHandler, defineMarkMode, definePlaceholder, defineReadonly, defineWikilinkClickHandler, docToMarkdown, markdownToDoc } from "@meowdown/core";
11
11
  import { canUseRegexLookbehind, createEditor, defineDocChangeHandler, union } from "@prosekit/core";
12
12
  import { Selection, TextSelection } from "@prosekit/pm/state";
13
13
  import { ProseKit, defineReactNodeView, useEditor, useEditor as useEditor$1, useEditorDerivedValue, useExtension, useExtension as useExtension$1, useKeymap } from "@prosekit/react";
@@ -436,7 +436,7 @@ function DropIndicator$1() {
436
436
 
437
437
  //#endregion
438
438
  //#region src/components/editor-extensions.tsx
439
- function EditorExtensions({ markMode, onDocChange, onWikilinkClick, resolveImageUrl, onImagePaste, onImageSaveError, embedPaste, bulletAfterHeading, placeholder, readOnly }) {
439
+ function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick, resolveImageUrl, onImagePaste, onImageSaveError, onImageClick, embedPaste, bulletAfterHeading, placeholder, readOnly }) {
440
440
  useExtension$1(useMemo(() => {
441
441
  return defineMarkMode(markMode);
442
442
  }, [markMode]));
@@ -450,19 +450,25 @@ function EditorExtensions({ markMode, onDocChange, onWikilinkClick, resolveImage
450
450
  return onWikilinkClick ? defineWikilinkClickHandler(onWikilinkClick) : null;
451
451
  }, [onWikilinkClick]));
452
452
  useExtension$1(useMemo(() => {
453
- return resolveImageUrl ? defineImages({
453
+ return onLinkClick ? defineLinkClickHandler(onLinkClick) : null;
454
+ }, [onLinkClick]));
455
+ useExtension$1(useMemo(() => {
456
+ return defineImage({
454
457
  resolveImageUrl,
455
458
  onImagePaste,
456
459
  onImageSaveError
457
- }) : null;
460
+ });
458
461
  }, [
459
462
  resolveImageUrl,
460
463
  onImagePaste,
461
464
  onImageSaveError
462
465
  ]));
463
466
  useExtension$1(useMemo(() => {
464
- return embedPaste && resolveImageUrl ? defineEmbedPaste() : null;
465
- }, [embedPaste, resolveImageUrl]));
467
+ return onImageClick ? defineImageClickHandler(onImageClick) : null;
468
+ }, [onImageClick]));
469
+ useExtension$1(useMemo(() => {
470
+ return embedPaste ? defineEmbedPaste() : null;
471
+ }, [embedPaste]));
466
472
  useExtension$1(useMemo(() => {
467
473
  return bulletAfterHeading ? defineBulletAfterHeading() : null;
468
474
  }, [bulletAfterHeading]));
@@ -480,6 +486,7 @@ function EditorExtensions({ markMode, onDocChange, onWikilinkClick, resolveImage
480
486
  var autocomplete_menu_module_default = {
481
487
  "Detail": "meow_Detail_Dqll0G",
482
488
  "Item": "meow_Item_Dqll0G",
489
+ "Label": "meow_Label_Dqll0G",
483
490
  "Popup": "meow_Popup_Dqll0G",
484
491
  "Positioner": "meow_Positioner_Dqll0G"
485
492
  };
@@ -826,7 +833,10 @@ function TagMenu({ onTagSearch }) {
826
833
  editor.commands.insertText({ text: `#${item.tag} ` });
827
834
  item.onSelect?.();
828
835
  },
829
- children: [/* @__PURE__ */ jsx("span", { children: item.label ?? `#${item.tag}` }), item.detail ? /* @__PURE__ */ jsx("span", {
836
+ children: [/* @__PURE__ */ jsx("span", {
837
+ className: autocomplete_menu_module_default.Label,
838
+ children: item.label ?? `#${item.tag}`
839
+ }), item.detail ? /* @__PURE__ */ jsx("span", {
830
840
  className: autocomplete_menu_module_default.Detail,
831
841
  children: item.detail
832
842
  }) : null]
@@ -886,7 +896,10 @@ function WikilinkMenu({ onWikilinkSearch }) {
886
896
  editor.commands.insertText({ text: `[[${item.target}]]` });
887
897
  item.onSelect?.();
888
898
  },
889
- children: [/* @__PURE__ */ jsx("span", { children: item.label ?? item.target }), item.detail ? /* @__PURE__ */ jsx("span", {
899
+ children: [/* @__PURE__ */ jsx("span", {
900
+ className: autocomplete_menu_module_default.Label,
901
+ children: item.label ?? item.target
902
+ }), item.detail ? /* @__PURE__ */ jsx("span", {
890
903
  className: autocomplete_menu_module_default.Detail,
891
904
  children: item.detail
892
905
  }) : null]
@@ -913,7 +926,7 @@ function resolveSelection(doc, selection) {
913
926
  return TextSelection.between(doc.resolve(anchor), doc.resolve(head));
914
927
  }
915
928
  }
916
- function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onTagSearch, onWikilinkSearch, onWikilinkClick, resolveImageUrl, onImagePaste, onImageSaveError, embedPaste, bulletAfterHeading, blockHandle = true, placeholder, readOnly, spellCheck, editorClassName, ref, children }) {
929
+ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onTagSearch, onWikilinkSearch, onWikilinkClick, onLinkClick, resolveImageUrl, onImagePaste, onImageSaveError, onImageClick, embedPaste, bulletAfterHeading, blockHandle = true, placeholder, readOnly, spellCheck, editorClassName, ref, children }) {
917
930
  const [editor] = useState(() => {
918
931
  const editor = createEditor({ extension: union(defineEditorExtension(), defineCodeBlockView()) });
919
932
  if (initialMarkdown) editor.setContent(markdownToDoc(initialMarkdown, editor.nodes));
@@ -970,7 +983,7 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onTa
970
983
  };
971
984
  }, [editor]);
972
985
  const handleDocChange = useMemo(() => {
973
- if (!onDocChange) return void 0;
986
+ if (!onDocChange) return;
974
987
  return () => {
975
988
  if (suppressDocChangeRef.current) return;
976
989
  onDocChange();
@@ -988,9 +1001,11 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onTa
988
1001
  markMode,
989
1002
  onDocChange: handleDocChange,
990
1003
  onWikilinkClick,
1004
+ onLinkClick,
991
1005
  resolveImageUrl,
992
1006
  onImagePaste,
993
1007
  onImageSaveError,
1008
+ onImageClick,
994
1009
  embedPaste,
995
1010
  bulletAfterHeading,
996
1011
  placeholder,
@@ -1009,7 +1024,7 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onTa
1009
1024
 
1010
1025
  //#endregion
1011
1026
  //#region src/components/editor.tsx
1012
- function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onTagSearch, onWikilinkSearch, onWikilinkClick, resolveImageUrl, onImagePaste, onImageSaveError, embedPaste = true, bulletAfterHeading = false, blockHandle = true, placeholder, readOnly, spellCheck, editorClassName, wrapperClassName, handleRef, children }) {
1027
+ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onTagSearch, onWikilinkSearch, onWikilinkClick, onLinkClick, resolveImageUrl, onImagePaste, onImageSaveError, onImageClick, embedPaste = true, bulletAfterHeading = false, blockHandle = true, placeholder, readOnly, spellCheck, editorClassName, wrapperClassName, handleRef, children }) {
1013
1028
  const childRef = useRef(null);
1014
1029
  useImperativeHandle(handleRef, () => {
1015
1030
  function getMarkdown() {
@@ -1074,9 +1089,11 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onTagSea
1074
1089
  onTagSearch,
1075
1090
  onWikilinkSearch,
1076
1091
  onWikilinkClick,
1092
+ onLinkClick,
1077
1093
  resolveImageUrl,
1078
1094
  onImagePaste,
1079
1095
  onImageSaveError,
1096
+ onImageClick,
1080
1097
  embedPaste,
1081
1098
  bulletAfterHeading,
1082
1099
  blockHandle,
package/dist/style.css CHANGED
@@ -264,7 +264,7 @@
264
264
  }
265
265
  .meow_Positioner_Dqll0G {
266
266
  z-index: 50;
267
- width: min-content;
267
+ width: min(24rem, 100vw - 1rem);
268
268
  height: min-content;
269
269
  display: block;
270
270
  overflow: visible;
@@ -279,11 +279,13 @@
279
279
  background: light-dark(#fff, #18181b);
280
280
  border-radius: .75rem;
281
281
  flex-direction: column;
282
- min-width: 15rem;
282
+ width: 100%;
283
+ min-width: min(15rem, 100%);
284
+ max-width: 100%;
283
285
  max-height: 25rem;
284
286
  padding: .25rem;
285
287
  display: flex;
286
- overflow-y: auto;
288
+ overflow: hidden auto;
287
289
  box-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
288
290
  }
289
291
 
@@ -297,6 +299,7 @@
297
299
  justify-content: space-between;
298
300
  align-items: center;
299
301
  gap: 1.5rem;
302
+ min-width: 0;
300
303
  padding: .375rem .75rem;
301
304
  scroll-margin: .25rem;
302
305
  font-size: .875rem;
@@ -318,10 +321,19 @@
318
321
  }
319
322
  }
320
323
 
324
+ .meow_Label_Dqll0G {
325
+ text-overflow: ellipsis;
326
+ min-width: 0;
327
+ overflow: hidden;
328
+ }
329
+
321
330
  .meow_Detail_Dqll0G {
331
+ text-overflow: ellipsis;
332
+ max-width: 40%;
322
333
  color: var(--meowdown-muted);
323
334
  flex-shrink: 0;
324
335
  font-size: .8125rem;
336
+ overflow: hidden;
325
337
  }
326
338
  .meow_Positioner_hpZLgq, .meow_MenuPositioner_hpZLgq {
327
339
  z-index: 50;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/react",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.12.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,11 +24,11 @@
24
24
  "@codemirror/state": "^6.6.0",
25
25
  "@codemirror/view": "^6.43.1",
26
26
  "@ocavue/utils": "^1.7.0",
27
- "@prosekit/core": "^0.13.0-beta.1",
28
- "@prosekit/pm": "^0.1.18",
29
- "@prosekit/react": "^0.8.0-beta.2",
27
+ "@prosekit/core": "^0.13.0-beta.3",
28
+ "@prosekit/pm": "^0.1.19-beta.0",
29
+ "@prosekit/react": "^0.8.0-beta.4",
30
30
  "clsx": "^2.1.1",
31
- "@meowdown/core": "0.11.0"
31
+ "@meowdown/core": "0.12.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "react": "^19.0.0",
@@ -45,14 +45,14 @@
45
45
  "devDependencies": {
46
46
  "@css-modules-kit/codegen": "^1.3.0",
47
47
  "@ocavue/tsconfig": "^0.7.1",
48
- "@tsdown/css": "^0.22.2",
48
+ "@tsdown/css": "^0.22.3",
49
49
  "@types/react": "^19.2.17",
50
50
  "@types/react-dom": "^19.2.3",
51
51
  "@vitest/browser-playwright": "^4.1.9",
52
52
  "dedent": "^1.7.2",
53
53
  "react": "^19.2.7",
54
54
  "react-dom": "^19.2.7",
55
- "tsdown": "^0.22.2",
55
+ "tsdown": "^0.22.3",
56
56
  "vitest": "^4.1.9",
57
57
  "vitest-browser-commands": "^0.2.1",
58
58
  "vitest-browser-react": "^2.2.0"