@powerduck/md-editor 0.10.10 → 0.11.2
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 +185 -397
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,467 +1,255 @@
|
|
|
1
1
|
# @powerduck/md-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@powerduck/md-editor)
|
|
4
|
+
[](https://github.com/PowerDuckie/md-editor/blob/main/LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/@powerduck/md-editor)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
High-performance embeddable Markdown editor with syntax highlighting, math formulas, mindmaps, @mentions, document link cards, and incremental rendering. Built on CodeMirror 6 with a React wrapper.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
- **Math formulas** via self-contained KaTeX plugin (`$E=mc^2$` inline, `$$...$$` block) -- no texmath dependency, no double-rendering
|
|
9
|
-
- **Mindmaps** via Markmap (```` ```mindmap ```` code fences)
|
|
10
|
-
- **Code highlighting** via highlight.js -- 20+ languages, macOS-style traffic-light header, one-click copy button
|
|
11
|
-
- **Admonition blocks** -- `:::notice`, `:::warning`, `:::tip`, `:::danger`, `:::info`, `:::success` with icons and colored borders
|
|
12
|
-
- **github-markdown-css** preview -- proper heading, list, table, and typography styling
|
|
13
|
-
- **Rich toolbar** -- heading, bold, italic, link, image, video, YouTube, quote, lists, code, table, math, mindmap, horizontal rule, tip block, preview toggle, help, theme toggle
|
|
14
|
-
- **Popup dialogs** -- image URL, video URL, YouTube URL/ID, table rows/columns
|
|
15
|
-
- **Keyboard shortcuts** -- Ctrl+B bold, Ctrl+I italic, Ctrl+K link, etc. with a Help dialog listing all shortcuts
|
|
16
|
-
- **Configurable toolbar** -- show/hide items, custom labels and icons via `toolbar` option
|
|
17
|
-
- **Image upload hook** -- paste/drop images, call `onImageUpload(file)` to get a URL
|
|
18
|
-
- **Video rendering** -- `.mp4`, `.webm`, `.ogg` links render as `<video>` elements
|
|
19
|
-
- **Standalone renderer** -- `renderMarkdown(source, options)` for consistent styling outside the editor
|
|
20
|
-
- **Block-level incremental rendering** -- only changed sections re-render
|
|
21
|
-
- **Adaptive debounce** -- preview render delay scales with document length
|
|
22
|
-
- **Simple / Complex modes** -- toggle toolbar and status bar at runtime
|
|
23
|
-
- **Light / Dark themes** -- CSS custom properties, inherits host design tokens
|
|
24
|
-
- **React component** -- `forwardRef` with imperative handle
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
9
|
+
---
|
|
27
10
|
|
|
28
|
-
|
|
29
|
-
npm install @powerduck/md-editor
|
|
30
|
-
```
|
|
11
|
+
Powerduck is an open-source developer tooling platform for teams building modern API workflows.
|
|
31
12
|
|
|
32
|
-
|
|
13
|
+
- **CodeMirror 6 Core** — Incremental rendering, virtual scrolling, and 60fps editing for large documents
|
|
14
|
+
- **KaTeX Math** — Render LaTeX math formulas inline and in display blocks
|
|
15
|
+
- **Markmap Mindmaps** — Visualize markdown outlines as interactive mindmaps
|
|
16
|
+
- **highlight.js Code Blocks** — Syntax highlighting for 190+ programming languages
|
|
17
|
+
- **@mentions** — Type `@` to search and mention users with avatars and descriptions
|
|
18
|
+
- **Document Link Cards** — Type `/` to insert rich doc-link preview cards with thumbnails
|
|
19
|
+
- **Admonition Blocks** — `:::tip`, `:::warning`, `:::note`, and more
|
|
20
|
+
- **Simple & Complex Modes** — Lightweight mode for comments, full-featured mode for docs
|
|
21
|
+
- **Light & Dark Themes** — Built-in themes with CSS variable customization
|
|
33
22
|
|
|
34
|
-
|
|
23
|
+
---
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
mode: 'complex', // 'simple' | 'complex'
|
|
43
|
-
theme: 'light', // 'light' | 'dark'
|
|
44
|
-
math: true,
|
|
45
|
-
mindmap: true,
|
|
46
|
-
onChange: (value) => console.log(value)
|
|
47
|
-
});
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @powerduck/md-editor
|
|
48
31
|
```
|
|
49
32
|
|
|
50
|
-
### React
|
|
33
|
+
### React Component
|
|
51
34
|
|
|
52
35
|
```tsx
|
|
53
|
-
import {
|
|
54
|
-
import
|
|
36
|
+
import { useRef, useState } from "react";
|
|
37
|
+
import {
|
|
38
|
+
MarkdownEditorReact,
|
|
39
|
+
type MarkdownEditorHandle,
|
|
40
|
+
} from "@powerduck/md-editor/react";
|
|
41
|
+
import "@powerduck/md-editor/dist/style.css";
|
|
55
42
|
|
|
56
43
|
function App() {
|
|
44
|
+
const [value, setValue] = useState("# Hello\n\nStart typing...");
|
|
45
|
+
const editorRef = useRef<MarkdownEditorHandle>(null);
|
|
46
|
+
|
|
57
47
|
return (
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
48
|
+
<div style={{ height: "600px" }}>
|
|
49
|
+
<MarkdownEditorReact
|
|
50
|
+
ref={editorRef}
|
|
51
|
+
value={value}
|
|
52
|
+
onChange={setValue}
|
|
53
|
+
mode="complex"
|
|
54
|
+
theme="light"
|
|
55
|
+
/>
|
|
56
|
+
<button onClick={() => console.log(editorRef.current?.getHtml())}>
|
|
57
|
+
Get HTML
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
63
60
|
);
|
|
64
61
|
}
|
|
65
62
|
```
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
### MarkdownEditorOptions
|
|
70
|
-
|
|
71
|
-
| Option | Type | Default | Description |
|
|
72
|
-
|--------|------|---------|-------------|
|
|
73
|
-
| `value` | `string` | `''` | Initial content |
|
|
74
|
-
| `mode` | `'simple' \| 'complex'` | `'simple'` | Toolbar + status bar in complex mode |
|
|
75
|
-
| `theme` | `'light' \| 'dark'` | `'light'` | Color theme |
|
|
76
|
-
| `math` | `boolean` | `true` | Enable KaTeX math |
|
|
77
|
-
| `mindmap` | `boolean` | `true` | Enable Markmap mindmaps |
|
|
78
|
-
| `codeHighlight` | `boolean` | `true` | Enable highlight.js code blocks with copy button |
|
|
79
|
-
| `tips` | `boolean` | `true` | Enable admonition/tip blocks (:::notice, :::warning, etc.) |
|
|
80
|
-
| `preview` | `boolean` | `true` | Show preview pane |
|
|
81
|
-
| `toolbar` | `ToolbarConfig` | all items | Configure which toolbar items show, their labels and icons |
|
|
82
|
-
| `onImageUpload` | `(file: File) => Promise<string>` | - | Image upload hook for paste/drop |
|
|
83
|
-
| `autoPreview` | `boolean` | `true` | Auto-render preview on edit |
|
|
84
|
-
| `renderDebounce` | `number \| false` | adaptive | Preview debounce in ms |
|
|
85
|
-
| `onChange` | `(value: string) => void` | - | Change callback |
|
|
86
|
-
|
|
87
|
-
### Methods
|
|
88
|
-
|
|
89
|
-
- `getValue(): string`
|
|
90
|
-
- `setValue(value: string): void`
|
|
91
|
-
- `getHtml(): string`
|
|
92
|
-
- `renderNow(): void` -- manual preview render (use with `autoPreview: false`)
|
|
93
|
-
- `setMode(mode: EditorMode): void`
|
|
94
|
-
- `setTheme(theme: EditorTheme): void`
|
|
95
|
-
- `setAutoPreview(auto: boolean): void`
|
|
96
|
-
- `focus(): void`
|
|
97
|
-
- `destroy(): void`
|
|
98
|
-
|
|
99
|
-
## Mindmap Syntax
|
|
100
|
-
|
|
101
|
-
````markdown
|
|
102
|
-
```mindmap
|
|
103
|
-
# Root topic
|
|
104
|
-
## Branch one
|
|
105
|
-
- Child A
|
|
106
|
-
- Child B
|
|
107
|
-
## Branch two
|
|
108
|
-
- Child C
|
|
109
|
-
```
|
|
110
|
-
````
|
|
111
|
-
|
|
112
|
-
## Code Blocks
|
|
113
|
-
|
|
114
|
-
Code fences are automatically highlighted with highlight.js (20+ languages including JavaScript, TypeScript, Python, JSON, Bash, CSS, HTML, SQL, YAML, Go, Rust, Java, C/C++). Each code block gets a macOS-style header with traffic-light dots, a language label, and a copy button.
|
|
64
|
+
### Vanilla JS
|
|
115
65
|
|
|
116
|
-
````markdown
|
|
117
66
|
```typescript
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
age: number;
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
````
|
|
124
|
-
|
|
125
|
-
Unknown languages are auto-detected; if detection fails, the content is HTML-escaped and displayed as plain text. Disable code highlighting with `codeHighlight: false`.
|
|
126
|
-
|
|
127
|
-
## Media Insertion
|
|
128
|
-
|
|
129
|
-
The complex-mode toolbar includes popup dialogs for inserting media:
|
|
130
|
-
|
|
131
|
-
- **Image** (`image` button): URL + alt text -> ``
|
|
132
|
-
- **Video** (`video` button): URL (.mp4, .webm, .ogg) -> renders as `<video controls>` in preview
|
|
133
|
-
- **YouTube** (`youtube` button): accepts any YouTube URL format (watch?v=, youtu.be, embed, shorts) or raw 11-char ID -> inserts a linked thumbnail image that opens the video on click
|
|
134
|
-
|
|
135
|
-
YouTube insertion extracts the video ID automatically and produces:
|
|
136
|
-
```markdown
|
|
137
|
-
[](https://www.youtube.com/watch?v=VIDEO_ID)
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## Standalone Renderer
|
|
141
|
-
|
|
142
|
-
Use `renderMarkdown()` to render markdown with the same styling and plugins as the editor preview, without mounting an editor:
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
import { renderMarkdown } from '@powerduck/md-editor';
|
|
146
|
-
import '@powerduck/md-editor/dist/style.css';
|
|
147
|
-
|
|
148
|
-
const html = renderMarkdown('# Hello\n\n$E=mc^2$\n\n:::tip\nWorks!\n:::');
|
|
149
|
-
container.innerHTML = html;
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Options: `math`, `mindmap`, `codeHighlight`, `tips`, `html`, `breaks` -- all default to `true` except `html` and `breaks`.
|
|
153
|
-
|
|
154
|
-
## Keyboard Shortcuts
|
|
155
|
-
|
|
156
|
-
| Shortcut | Action |
|
|
157
|
-
|----------|--------|
|
|
158
|
-
| `Ctrl+B` / `Cmd+B` | Bold |
|
|
159
|
-
| `Ctrl+I` / `Cmd+I` | Italic |
|
|
160
|
-
| `Ctrl+Alt+1` | Heading |
|
|
161
|
-
| `Ctrl+K` / `Cmd+K` | Insert link |
|
|
162
|
-
| `Ctrl+Alt+C` | Code block |
|
|
163
|
-
| `Ctrl+Shift+.` | Blockquote |
|
|
164
|
-
| `Ctrl+Shift+8` | Unordered list |
|
|
165
|
-
| `Ctrl+Shift+7` | Ordered list |
|
|
166
|
-
| `Ctrl+Alt+M` | Math formula |
|
|
167
|
-
| `Ctrl+Alt+P` | Toggle preview |
|
|
168
|
-
| `Ctrl+Shift+L` | Toggle theme |
|
|
169
|
-
|
|
170
|
-
Click the **Help** (?) button in the toolbar to see all shortcuts at runtime.
|
|
171
|
-
|
|
172
|
-
## Configurable Toolbar
|
|
173
|
-
|
|
174
|
-
Control which buttons appear and customize their labels/icons:
|
|
67
|
+
import { MarkdownEditor } from "@powerduck/md-editor";
|
|
68
|
+
import "@powerduck/md-editor/dist/style.css";
|
|
175
69
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
70
|
+
const editor = new MarkdownEditor("#editor", {
|
|
71
|
+
value: "# Hello\n\nStart typing...",
|
|
72
|
+
mode: "complex",
|
|
73
|
+
theme: "light",
|
|
74
|
+
onChange: (value) => console.log(value.length, "chars"),
|
|
181
75
|
});
|
|
182
76
|
|
|
183
|
-
|
|
184
|
-
new MarkdownEditor('#editor', {
|
|
185
|
-
mode: 'complex',
|
|
186
|
-
toolbar: {
|
|
187
|
-
bold: { label: 'Make bold', icon: '<b>B</b>' },
|
|
188
|
-
image: { show: false },
|
|
189
|
-
youtube: { show: false }
|
|
190
|
-
}
|
|
191
|
-
});
|
|
77
|
+
const html = editor.getHtml();
|
|
192
78
|
```
|
|
193
79
|
|
|
194
|
-
|
|
80
|
+
---
|
|
195
81
|
|
|
196
|
-
|
|
82
|
+
## Links
|
|
197
83
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
formData.append('file', file);
|
|
204
|
-
const res = await fetch('/api/upload', { method: 'POST', body: formData });
|
|
205
|
-
const { url } = await res.json();
|
|
206
|
-
return url; // inserted as 
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Admonition Blocks
|
|
212
|
-
|
|
213
|
-
```markdown
|
|
214
|
-
:::tip Pro tip
|
|
215
|
-
Use admonition blocks for callouts.
|
|
216
|
-
:::
|
|
217
|
-
|
|
218
|
-
:::warning
|
|
219
|
-
This action cannot be undone.
|
|
220
|
-
:::
|
|
221
|
-
|
|
222
|
-
:::danger
|
|
223
|
-
Destructive operation.
|
|
224
|
-
:::
|
|
225
|
-
```
|
|
84
|
+
- [Official Website](https://www.powerduck.com/opensource/md-editor.html)
|
|
85
|
+
- [Documentation](https://www.powerduck.com/docs/md-editor/introduction)
|
|
86
|
+
- [Live Demo](https://www.powerduck.com/demo/md-editor)
|
|
87
|
+
- [GitHub](https://github.com/PowerDuckie/md-editor)
|
|
88
|
+
- [npm](https://www.npmjs.com/package/@powerduck/md-editor)
|
|
226
89
|
|
|
227
|
-
|
|
90
|
+
---
|
|
228
91
|
|
|
229
|
-
##
|
|
92
|
+
## Features
|
|
230
93
|
|
|
231
|
-
- **
|
|
232
|
-
- **
|
|
233
|
-
- **
|
|
234
|
-
- **
|
|
235
|
-
-
|
|
94
|
+
- **CodeMirror 6 core** — incremental rendering, virtual scrolling, and 60fps editing for large documents
|
|
95
|
+
- **KaTeX math** — render LaTeX math formulas inline and in display blocks
|
|
96
|
+
- **Markmap mindmaps** — visualize markdown outlines as interactive mindmaps
|
|
97
|
+
- **highlight.js code blocks** — syntax highlighting for 190+ programming languages
|
|
98
|
+
- **@mentions** — type `@` to search and mention users with avatars and descriptions
|
|
99
|
+
- **Document link cards** — type `/` to insert rich doc-link preview cards with thumbnails
|
|
100
|
+
- **Admonition blocks** — `:::tip`, `:::warning`, `:::note`, and more
|
|
101
|
+
- **Simple & complex modes** — lightweight mode for comments, full-featured mode for docs
|
|
102
|
+
- **Light & dark themes** — built-in themes with CSS variable customization
|
|
103
|
+
- **Image upload hooks** — custom upload handlers with paste and drag-and-drop support
|
|
104
|
+
- **Toolbar & slash commands** — rich formatting toolbar and `/` command palette
|
|
105
|
+
- **Dual ESM/CJS builds** — works with `import` and `require`, with bundled TypeScript declarations
|
|
236
106
|
|
|
237
|
-
|
|
107
|
+
---
|
|
238
108
|
|
|
239
|
-
|
|
240
|
-
npm install
|
|
241
|
-
npm run typecheck # src only
|
|
242
|
-
npm run typecheck:test # src + tests
|
|
243
|
-
npm test # run all tests (vitest + jsdom)
|
|
244
|
-
npm run build # vite build + d.ts generation
|
|
245
|
-
```
|
|
109
|
+
## @mention Integration
|
|
246
110
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
import { useRef, useCallback } from "react";
|
|
250
|
-
import { type MentionItem, type DocItem } from "@powerduck/md-editor";
|
|
251
|
-
import {
|
|
252
|
-
MarkdownEditorReact,
|
|
253
|
-
type MarkdownEditorHandle,
|
|
254
|
-
} from "@powerduck/md-editor/react";
|
|
255
|
-
import "@powerduck/md-editor/dist/style.css";
|
|
111
|
+
```typescript
|
|
112
|
+
import { MarkdownEditor, type MentionItem } from "@powerduck/md-editor";
|
|
256
113
|
|
|
257
114
|
const USERS: MentionItem[] = [
|
|
258
115
|
{
|
|
259
116
|
id: "1",
|
|
260
117
|
label: "Alice",
|
|
261
|
-
avatar: "https://
|
|
118
|
+
avatar: "https://example.com/a.png",
|
|
262
119
|
description: "alice@example.com",
|
|
263
120
|
},
|
|
264
|
-
{
|
|
265
|
-
id: "2",
|
|
266
|
-
label: "Bob",
|
|
267
|
-
avatar: "https://i.pravatar.cc/40?u=bob",
|
|
268
|
-
description: "bob@example.com",
|
|
269
|
-
},
|
|
270
|
-
{ id: "3", label: "Charlie", description: "charlie@example.com" },
|
|
121
|
+
{ id: "2", label: "Bob", description: "bob@example.com" },
|
|
271
122
|
];
|
|
272
123
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
124
|
+
new MarkdownEditor("#editor", {
|
|
125
|
+
mode: "complex",
|
|
126
|
+
mention: {
|
|
127
|
+
onMentionSearch: async (query) =>
|
|
128
|
+
USERS.filter((u) => u.label.toLowerCase().includes(query.toLowerCase())),
|
|
129
|
+
onMentionSelect: (item) => `[@${item.label}](mention:${item.id})`,
|
|
130
|
+
minChars: 0,
|
|
131
|
+
maxItems: 8,
|
|
280
132
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
title: "API Reference",
|
|
284
|
-
url: "https://docs.example.com/api",
|
|
285
|
-
description: "Full API documentation",
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
id: "d3",
|
|
289
|
-
title: "Changelog",
|
|
290
|
-
url: "https://docs.example.com/changelog",
|
|
291
|
-
description: "Release notes",
|
|
292
|
-
},
|
|
293
|
-
];
|
|
133
|
+
});
|
|
134
|
+
```
|
|
294
135
|
|
|
295
|
-
|
|
296
|
-
const editorRef = useRef<MarkdownEditorHandle>(null);
|
|
136
|
+
---
|
|
297
137
|
|
|
298
|
-
|
|
299
|
-
const markdown = editorRef.current?.getValue() ?? "";
|
|
300
|
-
console.log("Saving:", markdown);
|
|
301
|
-
}, []);
|
|
302
|
-
|
|
303
|
-
// Stable callbacks so the editor does not recreate on every render
|
|
304
|
-
const searchUsers = useCallback(async (query: string) => {
|
|
305
|
-
return USERS.filter((u) =>
|
|
306
|
-
u.label.toLowerCase().includes(query.toLowerCase()),
|
|
307
|
-
);
|
|
308
|
-
}, []);
|
|
309
|
-
|
|
310
|
-
const searchDocs = useCallback(async (query: string) => {
|
|
311
|
-
return DOCS.filter((d) =>
|
|
312
|
-
d.title.toLowerCase().includes(query.toLowerCase()),
|
|
313
|
-
);
|
|
314
|
-
}, []);
|
|
315
|
-
|
|
316
|
-
const fetchDocMeta = useCallback(async (url: string) => {
|
|
317
|
-
// In production, route through your backend to avoid CORS:
|
|
318
|
-
// const res = await fetch(`/api/og?url=${encodeURIComponent(url)}`);
|
|
319
|
-
// return res.json();
|
|
320
|
-
// For demo: return whatever metadata the item already has
|
|
321
|
-
const doc = DOCS.find((d) => d.url === url);
|
|
322
|
-
return {
|
|
323
|
-
title: doc?.title ?? new URL(url).hostname,
|
|
324
|
-
description: doc?.description,
|
|
325
|
-
thumbnail: doc?.thumbnail,
|
|
326
|
-
url,
|
|
327
|
-
};
|
|
328
|
-
}, []);
|
|
138
|
+
## Document Link Card
|
|
329
139
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
<div style={{ marginBottom: 12, display: "flex", gap: 8 }}>
|
|
333
|
-
<button onClick={handleSave}>Save</button>
|
|
334
|
-
<button onClick={() => editorRef.current?.setTheme("dark")}>
|
|
335
|
-
Dark
|
|
336
|
-
</button>
|
|
337
|
-
<button onClick={() => editorRef.current?.setTheme("light")}>
|
|
338
|
-
Light
|
|
339
|
-
</button>
|
|
340
|
-
</div>
|
|
140
|
+
```typescript
|
|
141
|
+
import { MarkdownEditor, type DocItem } from "@powerduck/md-editor";
|
|
341
142
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
"- [x] This is unchecked",
|
|
352
|
-
"- [ x] Spaces inside brackets also work",
|
|
353
|
-
"",
|
|
354
|
-
"## Table",
|
|
355
|
-
"| Name | Role |",
|
|
356
|
-
"|------|------|",
|
|
357
|
-
"| Alice | Admin |",
|
|
358
|
-
"| Bob | Editor |",
|
|
359
|
-
].join("\n")}
|
|
360
|
-
mode="complex"
|
|
361
|
-
theme="light"
|
|
362
|
-
onChange={(v) => console.log("auto-save:", v.length)}
|
|
363
|
-
mention={{
|
|
364
|
-
onMentionSearch: searchUsers,
|
|
365
|
-
minChars: 0,
|
|
366
|
-
maxItems: 8,
|
|
367
|
-
}}
|
|
368
|
-
docLink={{
|
|
369
|
-
onDocSearch: searchDocs,
|
|
370
|
-
onFetchDocMeta: fetchDocMeta,
|
|
371
|
-
insertStyle: "auto",
|
|
372
|
-
minChars: 0,
|
|
373
|
-
maxItems: 8,
|
|
374
|
-
}}
|
|
375
|
-
/>
|
|
376
|
-
</div>
|
|
377
|
-
);
|
|
378
|
-
}
|
|
143
|
+
const DOCS: DocItem[] = [
|
|
144
|
+
{
|
|
145
|
+
id: "1",
|
|
146
|
+
title: "Getting Started",
|
|
147
|
+
url: "https://docs.example.com/getting-started",
|
|
148
|
+
thumbnail: "https://example.com/thumb.jpg",
|
|
149
|
+
description: "Quick start tutorial",
|
|
150
|
+
},
|
|
151
|
+
];
|
|
379
152
|
|
|
380
|
-
|
|
153
|
+
new MarkdownEditor("#editor", {
|
|
154
|
+
mode: "complex",
|
|
155
|
+
docLink: {
|
|
156
|
+
onDocSearch: async (query) =>
|
|
157
|
+
DOCS.filter((d) => d.title.toLowerCase().includes(query.toLowerCase())),
|
|
158
|
+
insertStyle: "card",
|
|
159
|
+
minChars: 0,
|
|
160
|
+
maxItems: 6,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
381
163
|
```
|
|
382
164
|
|
|
383
|
-
|
|
165
|
+
---
|
|
384
166
|
|
|
385
|
-
|
|
167
|
+
## API Reference
|
|
386
168
|
|
|
387
|
-
|
|
388
|
-
- **Test**: 311 tests, all passing, zero unhandled errors.
|
|
169
|
+
### React Props
|
|
389
170
|
|
|
390
|
-
|
|
171
|
+
| Prop | Type | Default | Description |
|
|
172
|
+
| ---------------- | ------------------------- | ----------- | --------------------------------------- |
|
|
173
|
+
| `value` | `string` | - | Markdown content (controlled) |
|
|
174
|
+
| `defaultValue` | `string` | - | Initial markdown content (uncontrolled) |
|
|
175
|
+
| `onChange` | `(value: string) => void` | - | Content change callback |
|
|
176
|
+
| `mode` | `"simple" \| "complex"` | `"complex"` | Editor mode |
|
|
177
|
+
| `theme` | `"light" \| "dark"` | `"light"` | Color theme |
|
|
178
|
+
| `math` | `boolean` | `true` | Enable KaTeX math rendering |
|
|
179
|
+
| `mindmap` | `boolean` | `true` | Enable Markmap mindmaps |
|
|
180
|
+
| `codeHighlight` | `boolean` | `true` | Enable highlight.js code blocks |
|
|
181
|
+
| `tips` | `boolean` | `true` | Enable admonition blocks |
|
|
182
|
+
| `preview` | `boolean` | `true` | Show preview pane |
|
|
183
|
+
| `autoPreview` | `boolean` | `true` | Auto-update preview |
|
|
184
|
+
| `renderDebounce` | `number` | `200` | Preview render debounce in ms |
|
|
185
|
+
| `mention` | `MentionConfig` | - | @mention configuration |
|
|
186
|
+
| `docLink` | `DocLinkConfig` | - | Document link card configuration |
|
|
391
187
|
|
|
392
|
-
|
|
393
|
-
- **Improved**: Clicking the @mention or document-link toolbar button without configuring the search hook now logs a clear `console.warn` explaining that `mention.onMentionSearch` / `docLink.onDocSearch` must be provided.
|
|
394
|
-
- **Test**: 310 tests, all passing, zero unhandled errors.
|
|
188
|
+
### Imperative Handle (React)
|
|
395
189
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
190
|
+
```typescript
|
|
191
|
+
interface MarkdownEditorHandle {
|
|
192
|
+
getValue(): string;
|
|
193
|
+
setValue(value: string): void;
|
|
194
|
+
getHtml(): string;
|
|
195
|
+
focus(): void;
|
|
196
|
+
blur(): void;
|
|
197
|
+
clear(): void;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
406
200
|
|
|
407
|
-
|
|
408
|
-
- **Fixed**: `toggleTaskList()` mixed-state logic. Previously, a mix of checked and unchecked items (or all-unchecked) removed all task markers. Now: mixed or all-unchecked → check all; all-checked → uncheck all. Also supports uppercase `[X]` markers.
|
|
409
|
-
- **Fixed**: Code block copy button now falls back to `document.execCommand('copy')` when `navigator.clipboard` is unavailable (non-HTTPS contexts), instead of silently failing.
|
|
410
|
-
- **Fixed**: @mention and doc-link dropdowns now close when the editor loses focus (blur), with a 150ms delay so clicks on the dropdown itself register first.
|
|
411
|
-
- **Fixed**: @mention and doc-link dropdowns had transparent backgrounds because they used editor-scoped CSS variables (`--_surface`) while being appended to `document.body`. Now appended inside `.md-editor-root` with `position: fixed` (not clipped by `overflow: hidden`), so theme variables resolve correctly.
|
|
412
|
-
- **Fixed**: Popup dialogs (image, video, YouTube, table, help, callout) now respect the editor theme. Previously always light-themed; now dark-themed when the editor is in dark mode.
|
|
413
|
-
- **Test**: 310 tests, all passing, zero unhandled errors.
|
|
201
|
+
### MentionConfig
|
|
414
202
|
|
|
415
|
-
|
|
203
|
+
```typescript
|
|
204
|
+
interface MentionConfig {
|
|
205
|
+
onMentionSearch: (query: string) => MentionItem[] | Promise<MentionItem[]>;
|
|
206
|
+
onMentionSelect?: (item: MentionItem) => string;
|
|
207
|
+
minChars?: number;
|
|
208
|
+
maxItems?: number;
|
|
209
|
+
triggerChar?: string;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
416
212
|
|
|
417
|
-
|
|
418
|
-
- **Added**: `:::doc-link` preview card rendering — displays title, thumbnail, description, and domain as a clickable card. Auto-fetches Open Graph / meta tags via `onFetchDocMeta(url)` hook; falls back to plain link when metadata is unavailable or fetch fails.
|
|
419
|
-
- **Added**: `insertStyle` option: `'auto'` (default, card when meta available), `'card'` (always card, fallback to link), `'link'` (always plain link).
|
|
420
|
-
- **Added**: `docLink` option to `MarkdownEditorOptions` and `CodeEditorOptions`, with `triggerChar`, `minChars`, `maxItems` configuration.
|
|
421
|
-
- **Added**: Toolbar button for document link insertion.
|
|
422
|
-
- **Test**: 307 tests, all passing, zero unhandled errors.
|
|
213
|
+
### DocLinkConfig
|
|
423
214
|
|
|
424
|
-
|
|
215
|
+
```typescript
|
|
216
|
+
interface DocLinkConfig {
|
|
217
|
+
onDocSearch: (query: string) => DocItem[] | Promise<DocItem[]>;
|
|
218
|
+
onFetchDocMeta?: (url: string) => Promise<DocMeta>;
|
|
219
|
+
insertStyle?: "card" | "link" | "auto";
|
|
220
|
+
minChars?: number;
|
|
221
|
+
maxItems?: number;
|
|
222
|
+
triggerChar?: string;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
425
225
|
|
|
426
|
-
|
|
427
|
-
- **Added**: Task list (checkbox) toolbar button and `toggleTaskList()` API. Toggles `- [ ]` / `- [x]` on current line or selected lines. Renders as checkboxes in preview. Keyboard shortcut: `Ctrl+Shift+9`.
|
|
428
|
-
- **Added**: @mention dropdown with configurable hooks. `onMentionSearch(query)` returns matching items, `onMentionSelect(item)` returns replacement text. Supports keyboard navigation (ArrowUp/Down, Enter, Escape), avatars, descriptions, `minChars`, and `maxItems` options.
|
|
429
|
-
- **Added**: `mention` option to `MarkdownEditorOptions` and `CodeEditorOptions`.
|
|
430
|
-
- **Test**: 281 tests, all passing, zero unhandled errors.
|
|
226
|
+
---
|
|
431
227
|
|
|
432
|
-
|
|
228
|
+
## TypeScript Types
|
|
433
229
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
230
|
+
```typescript
|
|
231
|
+
import type {
|
|
232
|
+
MentionItem,
|
|
233
|
+
DocItem,
|
|
234
|
+
DocMeta,
|
|
235
|
+
MentionConfig,
|
|
236
|
+
DocLinkConfig,
|
|
237
|
+
MarkdownEditorHandle,
|
|
238
|
+
MarkdownEditorProps,
|
|
239
|
+
} from "@powerduck/md-editor";
|
|
240
|
+
```
|
|
441
241
|
|
|
442
|
-
|
|
242
|
+
---
|
|
443
243
|
|
|
444
|
-
|
|
445
|
-
- **Fixed**: Quote, unordered list, and ordered list toolbar actions now wrap selected text (or the current line) instead of replacing it.
|
|
446
|
-
- **Improved**: Tip/admonition toolbar button now opens a type-picker popup with all six styles (notice, info, tip, success, warning, danger).
|
|
447
|
-
- **Improved**: Help dialog now displays keyboard shortcuts with styled key caps and modifier-key symbols (⌘, ⌥, ⇧).
|
|
448
|
-
- **Improved**: All toolbar icons redesigned in a clean Lucide/Feather line style for a modern Western aesthetic.
|
|
449
|
-
- **Fixed**: Mindmap `transform: translate(NaN,NaN)` error guarded when SVG has zero layout size.
|
|
450
|
-
- **Added**: `formatShortcutHtml()` export for rendering styled key caps.
|
|
451
|
-
- **Test**: 251 tests, all passing.
|
|
244
|
+
## Browser Support
|
|
452
245
|
|
|
453
|
-
|
|
246
|
+
- Chrome 90+
|
|
247
|
+
- Firefox 88+
|
|
248
|
+
- Safari 14+
|
|
249
|
+
- Edge 90+
|
|
454
250
|
|
|
455
|
-
|
|
456
|
-
- Image upload hook `onImageUpload(file)` with paste/drag support.
|
|
457
|
-
- 11 keyboard shortcuts with macOS Cmd auto-mapping.
|
|
458
|
-
- Help dialog listing all shortcuts.
|
|
459
|
-
- Table rows/columns popup dialog.
|
|
460
|
-
- Theme toggle fixed for dark mode with github-markdown-css.
|
|
461
|
-
- Admonition/tip blocks (`:::notice`, `:::warning`, etc.).
|
|
462
|
-
- Configurable toolbar (include-list or per-item show/label/icon).
|
|
463
|
-
- Standalone `renderMarkdown(source, options)` API export.
|
|
251
|
+
---
|
|
464
252
|
|
|
465
253
|
## License
|
|
466
254
|
|
|
467
|
-
MIT
|
|
255
|
+
MIT © [POWERDUCK LIMITED](https://www.powerduck.com)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerduck/md-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "High-performance embeddable Markdown editor: KaTeX math, Markmap mindmaps, highlight.js code blocks with copy button, github-markdown-css preview, admonition blocks, rich toolbar with image/video/YouTube/table popups, keyboard shortcuts, configurable toolbar, image upload hook, standalone renderMarkdown API, React component, block-level incremental rendering. Simple and complex modes, light/dark themes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|