@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 +2 -0
- package/dist/index.d.ts +20 -6
- package/dist/index.js +28 -11
- package/dist/style.css +15 -3
- package/package.json +7 -7
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: `` 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
|
-
*
|
|
123
|
-
*
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
})
|
|
460
|
+
});
|
|
458
461
|
}, [
|
|
459
462
|
resolveImageUrl,
|
|
460
463
|
onImagePaste,
|
|
461
464
|
onImageSaveError
|
|
462
465
|
]));
|
|
463
466
|
useExtension$1(useMemo(() => {
|
|
464
|
-
return
|
|
465
|
-
}, [
|
|
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", {
|
|
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", {
|
|
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
|
|
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-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
28
|
-
"@prosekit/pm": "^0.1.
|
|
29
|
-
"@prosekit/react": "^0.8.0-beta.
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|