@powerduck/md-editor 0.2.0 → 0.5.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
@@ -1,17 +1,28 @@
1
1
  # @powerduck/md-editor
2
2
 
3
- High-performance embeddable Markdown editor built on the markdown-it ecosystem. Supports KaTeX math formulas, Markmap mindmaps, native React integration, block-level incremental rendering with debounced scheduling. Ships in two modes: **simple** (no toolbar, pure edit + preview) and **complex** (toolbar + status bar). Styled via CSS custom properties with light/dark themes.
3
+ High-performance embeddable Markdown editor built on the markdown-it ecosystem. Supports KaTeX math formulas (self-contained, no texmath), Markmap mindmaps, highlight.js code blocks with traffic-light UI and copy button, github-markdown-css preview styling, admonition/tip blocks, rich toolbar with image/video/YouTube/table popup dialogs, keyboard shortcuts with help dialog, configurable toolbar, image upload hook, standalone `renderMarkdown()` API, native React integration, block-level incremental rendering with debounced scheduling. Ships in two modes: **simple** (no toolbar, pure edit + preview) and **complex** (toolbar + status bar). Styled via CSS custom properties with light/dark themes.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **Markdown editing** powered by CodeMirror 6 with markdown syntax highlighting
8
- - **Math formulas** via KaTeX (`$E=mc^2$` inline, `$$...$$` block)
8
+ - **Math formulas** via self-contained KaTeX plugin (`$E=mc^2$` inline, `$$...$$` block) -- no texmath dependency, no double-rendering
9
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
10
20
  - **Block-level incremental rendering** -- only changed sections re-render
11
21
  - **Adaptive debounce** -- preview render delay scales with document length
12
22
  - **Simple / Complex modes** -- toggle toolbar and status bar at runtime
13
23
  - **Light / Dark themes** -- CSS custom properties, inherits host design tokens
14
24
  - **React component** -- `forwardRef` with imperative handle
25
+ - **Zero Chinese** -- all code, comments, and UI strings in American English
15
26
 
16
27
  ## Installation
17
28
 
@@ -65,7 +76,11 @@ function App() {
65
76
  | `theme` | `'light' \| 'dark'` | `'light'` | Color theme |
66
77
  | `math` | `boolean` | `true` | Enable KaTeX math |
67
78
  | `mindmap` | `boolean` | `true` | Enable Markmap mindmaps |
79
+ | `codeHighlight` | `boolean` | `true` | Enable highlight.js code blocks with copy button |
80
+ | `tips` | `boolean` | `true` | Enable admonition/tip blocks (:::notice, :::warning, etc.) |
68
81
  | `preview` | `boolean` | `true` | Show preview pane |
82
+ | `toolbar` | `ToolbarConfig` | all items | Configure which toolbar items show, their labels and icons |
83
+ | `onImageUpload` | `(file: File) => Promise<string>` | - | Image upload hook for paste/drop |
69
84
  | `autoPreview` | `boolean` | `true` | Auto-render preview on edit |
70
85
  | `renderDebounce` | `number \| false` | adaptive | Preview debounce in ms |
71
86
  | `onChange` | `(value: string) => void` | - | Change callback |
@@ -95,6 +110,123 @@ function App() {
95
110
  ```
96
111
  ````
97
112
 
113
+ ## Code Blocks
114
+
115
+ 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.
116
+
117
+ ````markdown
118
+ ```typescript
119
+ interface User {
120
+ name: string;
121
+ age: number;
122
+ }
123
+ ```
124
+ ````
125
+
126
+ Unknown languages are auto-detected; if detection fails, the content is HTML-escaped and displayed as plain text. Disable code highlighting with `codeHighlight: false`.
127
+
128
+ ## Media Insertion
129
+
130
+ The complex-mode toolbar includes popup dialogs for inserting media:
131
+
132
+ - **Image** (`image` button): URL + alt text -> `![alt](url)`
133
+ - **Video** (`video` button): URL (.mp4, .webm, .ogg) -> renders as `<video controls>` in preview
134
+ - **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
135
+
136
+ YouTube insertion extracts the video ID automatically and produces:
137
+ ```markdown
138
+ [![YouTube video](https://img.youtube.com/vi/VIDEO_ID/hqdefault.jpg)](https://www.youtube.com/watch?v=VIDEO_ID)
139
+ ```
140
+
141
+ ## Standalone Renderer
142
+
143
+ Use `renderMarkdown()` to render markdown with the same styling and plugins as the editor preview, without mounting an editor:
144
+
145
+ ```ts
146
+ import { renderMarkdown } from '@powerduck/md-editor';
147
+ import '@powerduck/md-editor/dist/style.css';
148
+
149
+ const html = renderMarkdown('# Hello\n\n$E=mc^2$\n\n:::tip\nWorks!\n:::');
150
+ container.innerHTML = html;
151
+ ```
152
+
153
+ Options: `math`, `mindmap`, `codeHighlight`, `tips`, `html`, `breaks` -- all default to `true` except `html` and `breaks`.
154
+
155
+ ## Keyboard Shortcuts
156
+
157
+ | Shortcut | Action |
158
+ |----------|--------|
159
+ | `Ctrl+B` / `Cmd+B` | Bold |
160
+ | `Ctrl+I` / `Cmd+I` | Italic |
161
+ | `Ctrl+Alt+1` | Heading |
162
+ | `Ctrl+K` / `Cmd+K` | Insert link |
163
+ | `Ctrl+Alt+C` | Code block |
164
+ | `Ctrl+Shift+.` | Blockquote |
165
+ | `Ctrl+Shift+8` | Unordered list |
166
+ | `Ctrl+Shift+7` | Ordered list |
167
+ | `Ctrl+Alt+M` | Math formula |
168
+ | `Ctrl+Alt+P` | Toggle preview |
169
+ | `Ctrl+Shift+L` | Toggle theme |
170
+
171
+ Click the **Help** (?) button in the toolbar to see all shortcuts at runtime.
172
+
173
+ ## Configurable Toolbar
174
+
175
+ Control which buttons appear and customize their labels/icons:
176
+
177
+ ```ts
178
+ // Only show specific buttons
179
+ new MarkdownEditor('#editor', {
180
+ mode: 'complex',
181
+ toolbar: ['bold', 'italic', 'link', 'code']
182
+ });
183
+
184
+ // Fine-grained control
185
+ new MarkdownEditor('#editor', {
186
+ mode: 'complex',
187
+ toolbar: {
188
+ bold: { label: 'Make bold', icon: '<b>B</b>' },
189
+ image: { show: false },
190
+ youtube: { show: false }
191
+ }
192
+ });
193
+ ```
194
+
195
+ ## Image Upload
196
+
197
+ Provide an upload hook to handle pasted/dropped images:
198
+
199
+ ```ts
200
+ new MarkdownEditor('#editor', {
201
+ mode: 'complex',
202
+ onImageUpload: async (file) => {
203
+ const formData = new FormData();
204
+ formData.append('file', file);
205
+ const res = await fetch('/api/upload', { method: 'POST', body: formData });
206
+ const { url } = await res.json();
207
+ return url; // inserted as ![filename](url)
208
+ }
209
+ });
210
+ ```
211
+
212
+ ## Admonition Blocks
213
+
214
+ ```markdown
215
+ :::tip Pro tip
216
+ Use admonition blocks for callouts.
217
+ :::
218
+
219
+ :::warning
220
+ This action cannot be undone.
221
+ :::
222
+
223
+ :::danger
224
+ Destructive operation.
225
+ :::
226
+ ```
227
+
228
+ Supported types: `notice`, `info`, `tip`, `success`, `warning`, `danger`.
229
+
98
230
  ## Performance
99
231
 
100
232
  - **Block splitting**: documents are split at ATX headings (`#` through `######`). Content inside code fences is never split.
package/dist/Editor.d.ts CHANGED
@@ -5,15 +5,35 @@ export interface CodeEditorOptions {
5
5
  /** Show line numbers. Off in simple mode, on in complex mode by default. */
6
6
  lineNumbers?: boolean;
7
7
  placeholder?: string;
8
+ /** Called when an image file is pasted or dropped into the editor. */
9
+ onImageFile?: (file: File) => void;
8
10
  }
9
11
  export declare class CodeEditor {
10
12
  readonly view: EditorView;
11
13
  private lineNumbersCompartment;
14
+ private customKeymapCompartment;
15
+ private onImageFile?;
12
16
  constructor(container: HTMLElement, options: CodeEditorOptions);
17
+ private handlePaste;
18
+ private handleDrop;
19
+ /**
20
+ * Register custom keyboard shortcuts. Each entry is a CodeMirror keymap
21
+ * spec: { key: 'Mod-b', run: (view) => boolean }.
22
+ */
23
+ setKeymap(bindings: Array<{
24
+ key: string;
25
+ run: (view: EditorView) => boolean;
26
+ }>): void;
13
27
  getValue(): string;
14
28
  setValue(value: string): void;
15
29
  setLineNumbers(enabled: boolean): void;
16
30
  insertAtCursor(text: string): void;
31
+ /**
32
+ * Wrap the current selection with prefix and suffix. If nothing is
33
+ * selected, insert prefix + placeholder + suffix and select the placeholder.
34
+ */
35
+ wrapSelection(prefix: string, suffix: string, placeholder: string): void;
36
+ getSelection(): string;
17
37
  focus(): void;
18
38
  destroy(): void;
19
39
  }
@@ -1,9 +1,14 @@
1
1
  import MarkdownIt from 'markdown-it';
2
+ import 'github-markdown-css/github-markdown.css';
2
3
  export interface RendererOptions {
3
4
  /** Enable KaTeX math rendering. Default: true. */
4
5
  math?: boolean;
5
6
  /** Enable Markmap mindmap rendering. Default: true. */
6
7
  mindmap?: boolean;
8
+ /** Enable syntax highlighting for code blocks. Default: true. */
9
+ codeHighlight?: boolean;
10
+ /** Enable admonition/tip blocks (:::notice, :::warning, etc.). Default: true. */
11
+ tips?: boolean;
7
12
  /** Allow raw HTML in markdown source. Default: false (safer). */
8
13
  html?: boolean;
9
14
  /** Convert soft line breaks to <br>. Default: false. */
@@ -20,3 +25,18 @@ export declare class Renderer {
20
25
  */
21
26
  hydrate(container: HTMLElement): Promise<void>;
22
27
  }
28
+ /**
29
+ * Standalone markdown rendering function with the same styling and plugins
30
+ * as the editor preview. Use this in other parts of your app to render
31
+ * markdown consistently without mounting an editor.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * import { renderMarkdown } from '@powerduck/md-editor';
36
+ * import '@powerduck/md-editor/dist/style.css';
37
+ *
38
+ * const html = renderMarkdown('# Hello\n\n$E=mc^2$');
39
+ * container.innerHTML = html;
40
+ * ```
41
+ */
42
+ export declare function renderMarkdown(source: string, options?: RendererOptions): string;
package/dist/Toolbar.d.ts CHANGED
@@ -3,7 +3,7 @@ export interface ToolbarAction {
3
3
  title: string;
4
4
  /** SVG/HTML icon markup */
5
5
  icon: string;
6
- handler: () => void;
6
+ handler: (button: HTMLButtonElement) => void;
7
7
  /** Append a group separator after this button */
8
8
  groupEnd?: boolean;
9
9
  /** Whether this is a toggle button (e.g. preview visibility, theme) */
package/dist/icons.d.ts CHANGED
@@ -11,5 +11,13 @@ export declare const icons: {
11
11
  readonly refresh: string;
12
12
  readonly moon: string;
13
13
  readonly sun: string;
14
+ readonly image: string;
15
+ readonly video: string;
16
+ readonly youtube: string;
17
+ readonly quote: string;
18
+ readonly list: string;
19
+ readonly listOrdered: string;
20
+ readonly hr: string;
21
+ readonly help: string;
14
22
  };
15
23
  export type IconName = keyof typeof icons;