@meowdown/react 0.4.0 → 0.6.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 +39 -9
- package/dist/index.d.ts +85 -11
- package/dist/index.js +869 -1
- package/dist/style.css +394 -131
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ React components for Meowdown, a hybrid (live-preview) Markdown editor.
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
|
-
import {
|
|
8
|
+
import { MeowdownEditor, type EditorHandle } from '@meowdown/react'
|
|
9
9
|
import '@meowdown/react/style.css'
|
|
10
10
|
import { useRef, useCallback } from 'react'
|
|
11
11
|
|
|
@@ -15,13 +15,20 @@ export function App() {
|
|
|
15
15
|
console.log(ref.current?.getMarkdown())
|
|
16
16
|
}, [])
|
|
17
17
|
|
|
18
|
-
return
|
|
18
|
+
return (
|
|
19
|
+
<MeowdownEditor
|
|
20
|
+
handleRef={ref}
|
|
21
|
+
mode="focus"
|
|
22
|
+
initialMarkdown="# Hello"
|
|
23
|
+
onDocChange={handleDocChange}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
19
26
|
}
|
|
20
27
|
```
|
|
21
28
|
|
|
22
29
|
## API
|
|
23
30
|
|
|
24
|
-
### `<
|
|
31
|
+
### `<MeowdownEditor>`
|
|
25
32
|
|
|
26
33
|
The Markdown editor component. Renders inside a `div.meowdown` wrapper that fills a flex parent. In rich modes, typing `/` opens a slash menu for inserting blocks (headings, blockquote, lists, code block, table). Hovering a block shows a handle to its left: the plus button inserts an empty paragraph below the block, and the grip selects the block and can be dragged to move it, with a drop indicator line marking the target.
|
|
27
34
|
|
|
@@ -31,23 +38,46 @@ The Markdown editor component. Renders inside a `div.meowdown` wrapper that fill
|
|
|
31
38
|
- `'hide'`: Markdown syntax is always hidden.
|
|
32
39
|
- `'source'`: raw Markdown source with syntax highlighting.
|
|
33
40
|
- `initialMarkdown?: string`: first render only.
|
|
34
|
-
- `onDocChange?: VoidFunction`: called on every document change.
|
|
35
|
-
- `onTagSearch?: (query: string) =>
|
|
36
|
-
- `onWikilinkSearch?: (query: string) =>
|
|
37
|
-
- `
|
|
41
|
+
- `onDocChange?: VoidFunction`: called on every user-driven document change. Programmatic `setMarkdown` / `setState` on the handle do not fire it.
|
|
42
|
+
- `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.
|
|
43
|
+
- `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.
|
|
44
|
+
- `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.
|
|
45
|
+
- `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.
|
|
46
|
+
- `onImagePaste?: (file: File) => Promise<string | undefined>`: persists a pasted or dropped image file and returns its markdown `src` (or `undefined` to decline). Pass a stable function. Ignored in source mode.
|
|
47
|
+
- `onImageSaveError?: (error: Error, file: File) => void`: called when `onImagePaste` throws. Defaults to `console.error`. Ignored in source mode.
|
|
48
|
+
- `placeholder?: string | ((state) => string)`: placeholder text shown in an empty block. Pass a stable function. Ignored in source mode.
|
|
49
|
+
- `readOnly?: boolean`: makes the editor read-only, in both the rich and source modes.
|
|
50
|
+
- `spellCheck?: boolean`: toggles the browser's native spell checking in the rich modes. Defaults to the browser's behavior. Ignored in source mode.
|
|
51
|
+
- `editorClassName?: string`: class on the editable root (the contenteditable). Rich modes only.
|
|
52
|
+
- `wrapperClassName?: string`: class on the outer `div.meowdown` wrapper.
|
|
53
|
+
- `handleRef?: Ref<EditorHandle>`
|
|
54
|
+
- `children?: ReactNode`: rendered inside the editor's ProseKit context, so children can call `useEditor()`. Only rendered in the rich modes; source mode ignores them.
|
|
55
|
+
|
|
56
|
+
### `useEditor`
|
|
57
|
+
|
|
58
|
+
Re-exported from `@prosekit/react`. Call it from a component passed as `children` to read the live editor instance.
|
|
59
|
+
|
|
60
|
+
### `checkRoundTrip`
|
|
61
|
+
|
|
62
|
+
Re-exported from `@meowdown/core`. `checkRoundTrip(markdown)` returns `'exact' | 'normalizing' | 'lossy'`, for hosts that gate saving markdown files on whether the editor reproduces them faithfully.
|
|
63
|
+
|
|
64
|
+
### `EDITOR_KEY_BINDINGS`
|
|
65
|
+
|
|
66
|
+
Re-exported from `@meowdown/core`. A literal (`as const`) object mapping each editor shortcut (e.g. `Mod-b`, `Mod-1`) to its description, for host settings UIs and keybinding-collision checks.
|
|
38
67
|
|
|
39
68
|
### `EditorHandle`
|
|
40
69
|
|
|
41
|
-
Imperative handle for the editor, attached via `
|
|
70
|
+
Imperative handle for the editor, attached via `handleRef`.
|
|
42
71
|
|
|
43
72
|
- `getMarkdown(): string`: serializes the current document to Markdown. Can be expensive on large documents; call it on demand (e.g. throttled) instead of on every change.
|
|
44
|
-
- `setMarkdown(markdown: string): void`: replaces the whole document as a single undoable edit.
|
|
73
|
+
- `setMarkdown(markdown: string): void`: replaces the whole document as a single undoable edit. Does not fire `onDocChange`.
|
|
45
74
|
- `getState(): EditorStateSnapshot`: returns `[markdown, selection]`, where `selection` is a `SelectionJSON` (`{ anchor: number, head: number, type: string }`).
|
|
46
75
|
- `setState(markdown?: string, selection?: SelectionJSON | 'start' | 'end'): void`: replaces the document (if `markdown` is given) and restores `selection`: exactly when valid, otherwise clamped to the nearest text selection; out-of-range positions never throw. `'start'` and `'end'` jump to the document edges. Without a selection, the current one is mapped through the change. Restore a snapshot with `handle.setState(...handle.getState())`.
|
|
47
76
|
- `getSelection(): SelectionJSON`: returns the current selection.
|
|
48
77
|
- `setSelection(selection: SelectionJSON | 'start' | 'end'): void`: restores a selection with the same hint semantics as `setState`.
|
|
49
78
|
- `focus(): void`: focuses the editor.
|
|
50
79
|
- `scrollIntoView(): void`: scrolls the selection into view.
|
|
80
|
+
- `editor: TypedEditor | undefined`: escape hatch for the underlying ProseKit editor, `undefined` in source mode. No stability guarantees beyond what `@meowdown/core` exports.
|
|
51
81
|
|
|
52
82
|
Selection positions are in the mounted editor's coordinate space: ProseMirror document positions in the rich modes, character offsets in source mode. They round-trip within one mode but are not portable across a mode switch.
|
|
53
83
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Ref } from "react";
|
|
2
|
-
import { MarkMode, MarkMode as MarkMode$1, TypedEditor } from "@meowdown/core";
|
|
1
|
+
import { ReactNode, Ref } from "react";
|
|
2
|
+
import { EDITOR_KEY_BINDINGS, ImageOptions, MarkMode, MarkMode as MarkMode$1, PlaceholderOptions, RoundTripFidelity, TypedEditor, TypedEditor as TypedEditor$1, WikilinkClickHandler, checkRoundTrip } from "@meowdown/core";
|
|
3
3
|
import { SelectionJSON, SelectionJSON as SelectionJSON$1 } from "@prosekit/core";
|
|
4
|
+
import { useEditor } from "@prosekit/react";
|
|
4
5
|
|
|
5
6
|
//#region src/components/types.d.ts
|
|
6
7
|
/** A selection to restore: an exact JSON selection, or a document edge. */
|
|
@@ -36,19 +37,44 @@ interface EditorHandle {
|
|
|
36
37
|
focus: () => void;
|
|
37
38
|
/** Scrolls the selection into view. */
|
|
38
39
|
scrollIntoView: () => void;
|
|
40
|
+
/** Escape hatch: the underlying ProseKit editor, or `undefined` in source mode. */
|
|
41
|
+
readonly editor: TypedEditor$1 | undefined;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
/** One row in the tag menu. The host ranks the rows; the menu does not re-sort. */
|
|
45
|
+
interface TagItem {
|
|
46
|
+
/** Inserted as `#tag `. */
|
|
47
|
+
tag: string;
|
|
48
|
+
/** Display text; defaults to `#tag`. */
|
|
49
|
+
label?: string;
|
|
50
|
+
/** Secondary text shown beside the label. */
|
|
51
|
+
detail?: string;
|
|
52
|
+
/** Side effect run after the tag is inserted (e.g. create the tag). */
|
|
53
|
+
onSelect?: () => void;
|
|
39
54
|
}
|
|
40
55
|
/**
|
|
41
56
|
* Searches tags for the tag menu. Receives the query typed after `#`
|
|
42
|
-
* (lowercased, punctuation stripped) and returns the
|
|
57
|
+
* (lowercased, punctuation stripped) and returns the rows to show, either
|
|
43
58
|
* synchronously or as a promise.
|
|
44
59
|
*/
|
|
45
|
-
type TagSearchHandler = (query: string) =>
|
|
60
|
+
type TagSearchHandler = (query: string) => TagItem[] | Promise<TagItem[]>;
|
|
61
|
+
/** One row in the wikilink menu. The host ranks the rows; the menu does not re-sort. */
|
|
62
|
+
interface WikilinkItem {
|
|
63
|
+
/** Inserted as `[[target]]`. */
|
|
64
|
+
target: string;
|
|
65
|
+
/** Display text; defaults to `target`. */
|
|
66
|
+
label?: string;
|
|
67
|
+
/** Secondary text shown beside the label. */
|
|
68
|
+
detail?: string;
|
|
69
|
+
/** Side effect run after the link is inserted (e.g. create the note). */
|
|
70
|
+
onSelect?: () => void;
|
|
71
|
+
}
|
|
46
72
|
/**
|
|
47
73
|
* Searches notes for the wikilink menu. Receives the query typed after
|
|
48
74
|
* `[[` (lowercased, punctuation stripped, may be empty or contain spaces)
|
|
49
|
-
* and returns the
|
|
75
|
+
* and returns the rows to show, either synchronously or as a promise.
|
|
50
76
|
*/
|
|
51
|
-
type WikilinkSearchHandler = (query: string) =>
|
|
77
|
+
type WikilinkSearchHandler = (query: string) => WikilinkItem[] | Promise<WikilinkItem[]>;
|
|
52
78
|
//#endregion
|
|
53
79
|
//#region src/components/editor.d.ts
|
|
54
80
|
type EditorMode = MarkMode$1 | 'source';
|
|
@@ -65,7 +91,10 @@ interface EditorProps {
|
|
|
65
91
|
* first render is used; later changes are ignored.
|
|
66
92
|
*/
|
|
67
93
|
initialMarkdown?: string;
|
|
68
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Called on every user-driven document change. Programmatic `setMarkdown` and
|
|
96
|
+
* `setState` on the handle do not fire it.
|
|
97
|
+
*/
|
|
69
98
|
onDocChange?: VoidFunction;
|
|
70
99
|
/**
|
|
71
100
|
* Searches tags for the tag menu, which opens when typing `#` followed by
|
|
@@ -84,16 +113,61 @@ interface EditorProps {
|
|
|
84
113
|
* mode.
|
|
85
114
|
*/
|
|
86
115
|
onWikilinkSearch?: WikilinkSearchHandler;
|
|
116
|
+
/**
|
|
117
|
+
* Called with the link target on click of a rendered wiki link. Pass a stable
|
|
118
|
+
* function (e.g. from `useCallback`). Ignored in source mode.
|
|
119
|
+
*/
|
|
120
|
+
onWikilinkClick?: WikilinkClickHandler;
|
|
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.
|
|
124
|
+
*/
|
|
125
|
+
resolveImageUrl?: ImageOptions['resolveImageUrl'];
|
|
126
|
+
/**
|
|
127
|
+
* Persists a pasted/dropped image file and returns its markdown `src`. Pass a
|
|
128
|
+
* stable function. Ignored in source mode.
|
|
129
|
+
*/
|
|
130
|
+
onImagePaste?: ImageOptions['onImagePaste'];
|
|
131
|
+
/** Called when persisting a pasted/dropped image throws. Ignored in source mode. */
|
|
132
|
+
onImageSaveError?: ImageOptions['onImageSaveError'];
|
|
133
|
+
/**
|
|
134
|
+
* Placeholder text shown in an empty block. A function receives the editor
|
|
135
|
+
* state. Pass a stable function. Ignored in source mode.
|
|
136
|
+
*/
|
|
137
|
+
placeholder?: PlaceholderOptions['placeholder'];
|
|
138
|
+
/** Makes the editor read-only, in both the rich and source modes. */
|
|
139
|
+
readOnly?: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Enables the browser's native spell checking in the rich modes. Defaults
|
|
142
|
+
* to the browser's behavior. Ignored in source mode.
|
|
143
|
+
*/
|
|
144
|
+
spellCheck?: boolean;
|
|
145
|
+
/** Class on the editable root (the contenteditable). Rich modes only. */
|
|
146
|
+
editorClassName?: string;
|
|
147
|
+
/** Class on the outer `.meowdown` wrapper div. */
|
|
148
|
+
wrapperClassName?: string;
|
|
87
149
|
/** Imperative handle for the editor. */
|
|
88
|
-
|
|
150
|
+
handleRef?: Ref<EditorHandle>;
|
|
151
|
+
/** Nodes rendered inside the editor's ProseKit context (rich modes only). */
|
|
152
|
+
children?: ReactNode;
|
|
89
153
|
}
|
|
90
|
-
declare function
|
|
154
|
+
declare function MeowdownEditor({
|
|
91
155
|
mode,
|
|
92
156
|
initialMarkdown,
|
|
93
157
|
onDocChange,
|
|
94
158
|
onTagSearch,
|
|
95
159
|
onWikilinkSearch,
|
|
96
|
-
|
|
160
|
+
onWikilinkClick,
|
|
161
|
+
resolveImageUrl,
|
|
162
|
+
onImagePaste,
|
|
163
|
+
onImageSaveError,
|
|
164
|
+
placeholder,
|
|
165
|
+
readOnly,
|
|
166
|
+
spellCheck,
|
|
167
|
+
editorClassName,
|
|
168
|
+
wrapperClassName,
|
|
169
|
+
handleRef,
|
|
170
|
+
children
|
|
97
171
|
}: EditorProps): import("react").JSX.Element;
|
|
98
172
|
//#endregion
|
|
99
|
-
export {
|
|
173
|
+
export { EDITOR_KEY_BINDINGS, type EditorHandle, type EditorMode, type EditorProps, type EditorStateSnapshot, type MarkMode, MeowdownEditor, type RoundTripFidelity, type SelectionHint, type SelectionJSON, type TagItem, type TagSearchHandler, type TypedEditor, type WikilinkItem, type WikilinkSearchHandler, checkRoundTrip, useEditor };
|