@pubwave/editor 0.1.1-alpha.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/LICENSE +22 -0
- package/README.md +661 -0
- package/dist/index.cjs +7499 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +1339 -0
- package/dist/index.js +7499 -0
- package/dist/index.js.map +1 -0
- package/package.json +132 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Pubwave
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
# @pubwave/editor
|
|
2
|
+
|
|
3
|
+
A Notion-level block editor built with React and Tiptap.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@pubwave/editor)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## ✨ Features
|
|
9
|
+
|
|
10
|
+
- 📝 **Premium Writing Experience** - Fluid, responsive editing with natural Enter-driven writing flow
|
|
11
|
+
- ✨ **Selection-Only Toolbar** - Contextual formatting that appears only when you select text
|
|
12
|
+
- 🎯 **Block Drag & Drop** - Intuitive reordering with clear visual feedback
|
|
13
|
+
- 🎨 **Theme Tokens** - Full styling control via CSS custom properties
|
|
14
|
+
- ⚡ **SSR Safe** - Works with Next.js and other SSR frameworks
|
|
15
|
+
- 📦 **Lightweight** - Only ships what you need (React + Tiptap peer dependencies)
|
|
16
|
+
- ⌨️ **Slash Commands** - Type `/` to quickly insert blocks and formatting
|
|
17
|
+
- 🎨 **Text & Background Colors** - Rich color picker with recently used colors
|
|
18
|
+
- 🔄 **Turn Into** - Convert blocks between different types (paragraph, headings, lists, etc.)
|
|
19
|
+
- 📋 **Rich Formatting** - Bold, italic, underline, strikethrough, code, and links
|
|
20
|
+
- 📝 **Multiple Block Types** - Paragraphs, headings, lists, quotes, code blocks, and more
|
|
21
|
+
- 🖼️ **Image Support** - Upload images via file picker or paste from clipboard, with base64 or custom upload service
|
|
22
|
+
- 🌐 **Internationalization** - Multi-language support with English as default
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 📦 Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @pubwave/editor
|
|
30
|
+
# or
|
|
31
|
+
yarn add @pubwave/editor
|
|
32
|
+
# or
|
|
33
|
+
pnpm add @pubwave/editor
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🚀 Quick Start
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { PubwaveEditor } from '@pubwave/editor';
|
|
42
|
+
import '@pubwave/editor/index.css';
|
|
43
|
+
|
|
44
|
+
function MyEditor() {
|
|
45
|
+
const [content, setContent] = useState({
|
|
46
|
+
type: 'doc',
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: 'paragraph',
|
|
50
|
+
content: [{ type: 'text', text: 'Start writing...' }],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<PubwaveEditor
|
|
57
|
+
content={content}
|
|
58
|
+
onChange={(newContent) => setContent(newContent)}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 📋 Requirements
|
|
67
|
+
|
|
68
|
+
### React Versions
|
|
69
|
+
|
|
70
|
+
| React Version | Status |
|
|
71
|
+
|---------------|--------|
|
|
72
|
+
| 18.x | ✅ Tested, Recommended |
|
|
73
|
+
| 19.x | ✅ Supported |
|
|
74
|
+
|
|
75
|
+
### Browser Support
|
|
76
|
+
|
|
77
|
+
- Chrome (latest)
|
|
78
|
+
- Firefox (latest)
|
|
79
|
+
- Safari (latest)
|
|
80
|
+
- Edge (latest)
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 📖 API Reference
|
|
85
|
+
|
|
86
|
+
### `PubwaveEditor` Component
|
|
87
|
+
|
|
88
|
+
The main React component for rendering the editor.
|
|
89
|
+
|
|
90
|
+
#### Component Props
|
|
91
|
+
|
|
92
|
+
| Prop | Type | Default | Description |
|
|
93
|
+
|------|------|---------|-------------|
|
|
94
|
+
| `content` | `JSONContent` | `undefined` | Initial content in Tiptap JSON format |
|
|
95
|
+
| `editable` | `boolean` | `true` | Whether the editor is in read-only mode |
|
|
96
|
+
| `placeholder` | `string` | `'Start writing...'` | Placeholder text shown when the editor is empty |
|
|
97
|
+
| `theme` | `EditorTheme` | `undefined` | Theme configuration object (see [Theming](#-theming)) |
|
|
98
|
+
| `autofocus` | `boolean \| 'start' \| 'end'` | `false` | Enable autofocus on mount. `true` or `'end'` focuses at the end, `'start'` focuses at the beginning |
|
|
99
|
+
| `imageUpload` | `ImageUploadConfig` | `undefined` | Image upload configuration (see [Image Upload](#-image-upload)) |
|
|
100
|
+
| `width` | `string` | `'100%'` | Editor container width. Can be a CSS value like `'100%'`, `'1200px'`, `'90vw'`, etc. Defaults to `'100%'` (full width of parent container) |
|
|
101
|
+
| `onChange` | `(content: JSONContent) => void` | `undefined` | Callback fired when the editor content changes |
|
|
102
|
+
| `onSelectionChange` | `() => void` | `undefined` | Callback fired when the selection changes |
|
|
103
|
+
| `onFocus` | `() => void` | `undefined` | Callback fired when the editor gains focus |
|
|
104
|
+
| `onBlur` | `() => void` | `undefined` | Callback fired when the editor loses focus |
|
|
105
|
+
| `onReady` | `(api: EditorAPI) => void` | `undefined` | Callback fired when the editor is ready with API access |
|
|
106
|
+
| `className` | `string` | `undefined` | Additional CSS class for the container |
|
|
107
|
+
| `data-testid` | `string` | `'pubwave-editor'` | Test ID for testing purposes |
|
|
108
|
+
|
|
109
|
+
For complete TypeScript type definitions, see the exported types from `@pubwave/editor`:
|
|
110
|
+
- `EditorTheme` - Theme configuration
|
|
111
|
+
- `EditorAPI` - Editor API interface
|
|
112
|
+
- `EditorLocale` - Supported locale codes: `'en' | 'zh' | 'zh-CN' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'pt'`
|
|
113
|
+
- `ImageUploadConfig` - Image upload configuration
|
|
114
|
+
|
|
115
|
+
### `EditorAPI` Methods
|
|
116
|
+
|
|
117
|
+
The editor exposes a public API through the `ref` prop or `onReady` callback:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { useRef } from 'react';
|
|
121
|
+
import type { EditorAPI } from '@pubwave/editor';
|
|
122
|
+
|
|
123
|
+
const editorRef = useRef<EditorAPI | null>(null);
|
|
124
|
+
|
|
125
|
+
<PubwaveEditor
|
|
126
|
+
ref={editorRef}
|
|
127
|
+
onReady={(api) => {
|
|
128
|
+
editorRef.current = api;
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
131
|
+
|
|
132
|
+
// Later, use the API
|
|
133
|
+
editorRef.current?.setContent(newContent);
|
|
134
|
+
editorRef.current?.getJSON();
|
|
135
|
+
editorRef.current?.toggleBold();
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Available Methods
|
|
139
|
+
|
|
140
|
+
| Method | Description | Parameters | Returns |
|
|
141
|
+
|--------|-------------|------------|---------|
|
|
142
|
+
| `getState()` | Get current editor state | - | `EditorState` |
|
|
143
|
+
| `getJSON()` | Get content as JSON | - | `JSONContent` |
|
|
144
|
+
| `getHTML()` | Get content as HTML | - | `string` |
|
|
145
|
+
| `getText()` | Get content as plain text | - | `string` |
|
|
146
|
+
| `setContent(content)` | Set new content | `content: JSONContent` | `void` |
|
|
147
|
+
| `clearContent()` | Clear all content | - | `void` |
|
|
148
|
+
| `setEditable(editable)` | Toggle read-only mode | `editable: boolean` | `void` |
|
|
149
|
+
| `focus(position?)` | Focus the editor | `position?: 'start' \| 'end'` | `void` |
|
|
150
|
+
| `blur()` | Blur the editor | - | `void` |
|
|
151
|
+
| `toggleBold()` | Toggle bold formatting | - | `void` |
|
|
152
|
+
| `toggleItalic()` | Toggle italic formatting | - | `void` |
|
|
153
|
+
| `setLink(href)` | Set or remove a link | `href: string \| null` | `void` |
|
|
154
|
+
| `destroy()` | Destroy the editor instance | - | `void` |
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 🖼️ Image Upload
|
|
160
|
+
|
|
161
|
+
The editor supports two image upload modes:
|
|
162
|
+
|
|
163
|
+
1. **Base64 (Default)** - Images are converted to base64 data URLs and embedded directly
|
|
164
|
+
2. **Custom Upload Service** - Images are uploaded to your server and URLs are stored
|
|
165
|
+
|
|
166
|
+
### Base64 Mode (Default)
|
|
167
|
+
|
|
168
|
+
By default, images are converted to base64. No configuration needed:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
<PubwaveEditor />
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Custom Upload Service
|
|
175
|
+
|
|
176
|
+
Configure a custom upload handler to upload images to your server:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
<PubwaveEditor
|
|
180
|
+
imageUpload={{
|
|
181
|
+
handler: async (file: File) => {
|
|
182
|
+
const formData = new FormData();
|
|
183
|
+
formData.append('image', file);
|
|
184
|
+
|
|
185
|
+
const response = await fetch('/api/upload', {
|
|
186
|
+
method: 'POST',
|
|
187
|
+
body: formData,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const data = await response.json();
|
|
191
|
+
return data.url; // Return the image URL
|
|
192
|
+
},
|
|
193
|
+
maxSize: 5 * 1024 * 1024, // 5MB (optional, default: 10MB)
|
|
194
|
+
accept: ['image/jpeg', 'image/png', 'image/webp'], // Optional, default: ['image/*']
|
|
195
|
+
}}
|
|
196
|
+
/>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
### Image Upload Features
|
|
201
|
+
|
|
202
|
+
- **File Picker**: Use `/image` command in slash menu to open file picker
|
|
203
|
+
- **Paste Support**: Paste images from clipboard (Ctrl/Cmd+V)
|
|
204
|
+
- **Automatic Fallback**: If custom upload fails, automatically falls back to base64
|
|
205
|
+
- **File Validation**: Validates file type and size before upload
|
|
206
|
+
- **Error Handling**: Logs errors to console if upload fails
|
|
207
|
+
|
|
208
|
+
### Custom Upload Service
|
|
209
|
+
|
|
210
|
+
The `handler` function receives a `File` object and should return a Promise that resolves to the image URL. The editor will automatically fall back to base64 if the upload fails.
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
<PubwaveEditor
|
|
214
|
+
imageUpload={{
|
|
215
|
+
handler: async (file: File) => {
|
|
216
|
+
// Upload to your server (Cloudinary, AWS S3, etc.)
|
|
217
|
+
const formData = new FormData();
|
|
218
|
+
formData.append('image', file);
|
|
219
|
+
|
|
220
|
+
const response = await fetch('/api/upload', {
|
|
221
|
+
method: 'POST',
|
|
222
|
+
body: formData,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const data = await response.json();
|
|
226
|
+
return data.url; // Return the image URL
|
|
227
|
+
},
|
|
228
|
+
maxSize: 10 * 1024 * 1024, // 10MB (optional)
|
|
229
|
+
accept: ['image/jpeg', 'image/png', 'image/webp'], // Optional
|
|
230
|
+
}}
|
|
231
|
+
/>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 🎨 Theming
|
|
237
|
+
|
|
238
|
+
Pubwave Editor supports full theme customization through the `theme` prop. You can customize colors, backgrounds, and more.
|
|
239
|
+
|
|
240
|
+
### Basic Theme Configuration
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { PubwaveEditor } from '@pubwave/editor';
|
|
244
|
+
import type { EditorTheme } from '@pubwave/editor';
|
|
245
|
+
|
|
246
|
+
const theme: EditorTheme = {
|
|
247
|
+
colors: {
|
|
248
|
+
background: '#ffffff',
|
|
249
|
+
text: '#1f2937',
|
|
250
|
+
textMuted: '#6b7280',
|
|
251
|
+
border: '#e5e7eb',
|
|
252
|
+
primary: '#3b82f6',
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
<PubwaveEditor theme={theme} />
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
For complete `EditorTheme` interface definition, see TypeScript definitions. Key properties:
|
|
260
|
+
- `colors` - Color configuration (background, text, border, primary, linkColor)
|
|
261
|
+
- `locale` - Locale code for internationalization
|
|
262
|
+
- `backgroundImage` - Optional background image URL
|
|
263
|
+
- `backgroundImageOptions` - Background image display options (size, position, repeat, attachment)
|
|
264
|
+
|
|
265
|
+
### Predefined Themes
|
|
266
|
+
|
|
267
|
+
The editor includes several predefined themes:
|
|
268
|
+
|
|
269
|
+
#### Light Theme
|
|
270
|
+
```tsx
|
|
271
|
+
const lightTheme: EditorTheme = {
|
|
272
|
+
colors: {
|
|
273
|
+
background: 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)',
|
|
274
|
+
text: '#1f2937',
|
|
275
|
+
textMuted: '#6b7280',
|
|
276
|
+
border: '#e5e7eb',
|
|
277
|
+
primary: '#3b82f6',
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Dark Theme
|
|
283
|
+
```tsx
|
|
284
|
+
const darkTheme: EditorTheme = {
|
|
285
|
+
colors: {
|
|
286
|
+
background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)',
|
|
287
|
+
text: '#f1f5f9',
|
|
288
|
+
textMuted: '#94a3b8',
|
|
289
|
+
border: '#334155',
|
|
290
|
+
primary: '#60a5fa',
|
|
291
|
+
linkColor: '#3b82f6',
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Other themes:** Violet, Rose, Sky (gradient themes).
|
|
297
|
+
|
|
298
|
+
### Background Image
|
|
299
|
+
|
|
300
|
+
You can add a background image to the editor container:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
<PubwaveEditor
|
|
304
|
+
theme={{
|
|
305
|
+
colors: {
|
|
306
|
+
background: '#ffffff',
|
|
307
|
+
text: '#1f2937',
|
|
308
|
+
},
|
|
309
|
+
backgroundImage: 'https://example.com/background.jpg',
|
|
310
|
+
backgroundImageOptions: {
|
|
311
|
+
size: 'cover',
|
|
312
|
+
position: 'center',
|
|
313
|
+
repeat: 'no-repeat',
|
|
314
|
+
attachment: 'fixed',
|
|
315
|
+
},
|
|
316
|
+
}}
|
|
317
|
+
/>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Background Image Options:**
|
|
321
|
+
- `size`: `'cover'` (default) | `'contain'` | custom CSS value (e.g., `'100% 100%'`)
|
|
322
|
+
- `position`: CSS position value (default: `'center'`)
|
|
323
|
+
- `repeat`: `'no-repeat'` (default) | `'repeat'` | `'repeat-x'` | `'repeat-y'`
|
|
324
|
+
- `attachment`: `'scroll'` (default) | `'fixed'` | `'local'`
|
|
325
|
+
|
|
326
|
+
**Note:** The background image will be layered on top of the background color. If you want the image to be the only background, set `background: 'transparent'` in the colors.
|
|
327
|
+
|
|
328
|
+
### Internationalization (i18n)
|
|
329
|
+
|
|
330
|
+
Pubwave Editor supports multiple languages through the `locale` option in the theme configuration. The editor defaults to English (`'en'`).
|
|
331
|
+
|
|
332
|
+
#### Supported Locales
|
|
333
|
+
|
|
334
|
+
The following locale codes are supported:
|
|
335
|
+
- `'en'` - English (default)
|
|
336
|
+
- `'zh'` - Chinese
|
|
337
|
+
- `'zh-CN'` - Simplified Chinese
|
|
338
|
+
- `'ja'` - Japanese
|
|
339
|
+
- `'ko'` - Korean
|
|
340
|
+
- `'fr'` - French
|
|
341
|
+
- `'de'` - German
|
|
342
|
+
- `'es'` - Spanish
|
|
343
|
+
- `'pt'` - Portuguese
|
|
344
|
+
|
|
345
|
+
**Note:** Currently, only English translations are fully implemented. Other languages will fall back to English. The framework is ready for future language additions.
|
|
346
|
+
|
|
347
|
+
#### Setting the Locale
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
import { PubwaveEditor } from '@pubwave/editor';
|
|
351
|
+
import type { EditorTheme } from '@pubwave/editor';
|
|
352
|
+
|
|
353
|
+
const theme: EditorTheme = {
|
|
354
|
+
locale: 'en', // Default, can be set to any supported locale
|
|
355
|
+
colors: {
|
|
356
|
+
background: '#ffffff',
|
|
357
|
+
text: '#1f2937',
|
|
358
|
+
primary: '#3b82f6',
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
<PubwaveEditor theme={theme} />
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
The locale affects all user-facing text in the editor, including:
|
|
366
|
+
- Slash command menu items and descriptions
|
|
367
|
+
- Toolbar button labels and tooltips
|
|
368
|
+
- Accessibility labels (ARIA)
|
|
369
|
+
- Block type labels
|
|
370
|
+
- Placeholder text
|
|
371
|
+
|
|
372
|
+
### CSS Custom Properties
|
|
373
|
+
|
|
374
|
+
The editor also uses CSS custom properties for styling. Define these in your CSS:
|
|
375
|
+
|
|
376
|
+
```css
|
|
377
|
+
:root {
|
|
378
|
+
--pubwave-bg: #ffffff;
|
|
379
|
+
--pubwave-text: #1a1a1a;
|
|
380
|
+
--pubwave-text-muted: #6b7280;
|
|
381
|
+
--pubwave-border: #e5e7eb;
|
|
382
|
+
--pubwave-primary: #3b82f6;
|
|
383
|
+
--pubwave-hover: rgba(0, 0, 0, 0.05);
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## 🎯 Core Features
|
|
390
|
+
|
|
391
|
+
### Slash Commands
|
|
392
|
+
|
|
393
|
+
Type `/` anywhere in the editor to open the command menu. Filter commands by typing keywords.
|
|
394
|
+
|
|
395
|
+
**Available Commands:**
|
|
396
|
+
|
|
397
|
+
| Command | Aliases | Description |
|
|
398
|
+
|---------|---------|-------------|
|
|
399
|
+
| `/text` or `/paragraph` | `/p`, `/text` | Plain text block |
|
|
400
|
+
| `/heading1` | `/h1`, `/title` | Large section heading |
|
|
401
|
+
| `/heading2` | `/h2`, `/subtitle` | Medium section heading |
|
|
402
|
+
| `/heading3` | `/h3` | Small section heading |
|
|
403
|
+
| `/bullet` | `/ul`, `/bullet`, `/-` | Bulleted list |
|
|
404
|
+
| `/numbered` | `/ol`, `/numbered`, `/1.` | Numbered list |
|
|
405
|
+
| `/todo` | `/task`, `/checkbox`, `/[]` | To-do list with checkboxes |
|
|
406
|
+
| `/image` | `/img`, `/picture`, `/photo`, `/pic` | Upload or paste an image |
|
|
407
|
+
| `/quote` | `/blockquote`, `/>` | Quote block |
|
|
408
|
+
| `/code` | `/pre`, `/snippet`, `/``` | Code block |
|
|
409
|
+
| `/divider` | `/hr`, `/line`, `/---` | Horizontal divider |
|
|
410
|
+
|
|
411
|
+
**Usage:**
|
|
412
|
+
1. Type `/` to open the menu
|
|
413
|
+
2. Type to filter (e.g., `/h1` for heading 1, `/img` for image)
|
|
414
|
+
3. Press `Enter` or click to select
|
|
415
|
+
4. Use `ArrowUp`/`ArrowDown` to navigate
|
|
416
|
+
5. Press `Escape` to close
|
|
417
|
+
|
|
418
|
+
### Selection Toolbar
|
|
419
|
+
|
|
420
|
+
The toolbar appears automatically when you select text, providing quick access to formatting options.
|
|
421
|
+
|
|
422
|
+
**Available Actions:**
|
|
423
|
+
- **Turn Into** - Convert block type (Paragraph, Heading 1-3, Bulleted List, Numbered List)
|
|
424
|
+
- **Bold** (`Cmd/Ctrl+B`) - Make text bold
|
|
425
|
+
- **Italic** (`Cmd/Ctrl+I`) - Make text italic
|
|
426
|
+
- **Underline** - Underline text
|
|
427
|
+
- **Strikethrough** - Strikethrough text
|
|
428
|
+
- **Code** - Inline code formatting
|
|
429
|
+
- **Link** - Add or edit link
|
|
430
|
+
- **Text Color** - Change text color (with recently used colors)
|
|
431
|
+
- **Background Color** - Change background color (with recently used colors)
|
|
432
|
+
|
|
433
|
+
### Block Types
|
|
434
|
+
|
|
435
|
+
The editor supports the following block types:
|
|
436
|
+
|
|
437
|
+
- **Paragraph** - Default text block
|
|
438
|
+
- **Heading 1-3** - Section headings
|
|
439
|
+
- **Bulleted List** - Unordered list with bullets
|
|
440
|
+
- **Numbered List** - Ordered list with numbers
|
|
441
|
+
- **Task List** - Checklist with checkboxes
|
|
442
|
+
- **Image** - Image block (supports base64 or URL)
|
|
443
|
+
- **Blockquote** - Quote block
|
|
444
|
+
- **Code Block** - Code snippet with syntax highlighting
|
|
445
|
+
- **Horizontal Rule** - Divider line
|
|
446
|
+
|
|
447
|
+
### Keyboard Shortcuts
|
|
448
|
+
|
|
449
|
+
| Shortcut | Action |
|
|
450
|
+
|----------|--------|
|
|
451
|
+
| `Cmd/Ctrl+B` | Toggle bold |
|
|
452
|
+
| `Cmd/Ctrl+I` | Toggle italic |
|
|
453
|
+
| `Cmd/Ctrl+Z` | Undo |
|
|
454
|
+
| `Cmd/Ctrl+Shift+Z` | Redo |
|
|
455
|
+
| `Cmd/Ctrl+A` | Select all |
|
|
456
|
+
| `Enter` | Create new block |
|
|
457
|
+
| `Arrow Up/Down` | Navigate between blocks |
|
|
458
|
+
| `Shift+Arrow` | Select text |
|
|
459
|
+
| `/` | Open slash command menu |
|
|
460
|
+
| `Escape` | Close menus/cancel drag |
|
|
461
|
+
|
|
462
|
+
### Drag & Drop
|
|
463
|
+
|
|
464
|
+
- **Drag Handle** - Appears on hover at the left edge of blocks
|
|
465
|
+
- **Visual Feedback** - Clear drop indicators show where blocks will be placed
|
|
466
|
+
- **Cancel** - Press `Escape` to cancel dragging
|
|
467
|
+
- **Multi-block Selection** - Select multiple blocks to drag together
|
|
468
|
+
|
|
469
|
+
### Color Picker
|
|
470
|
+
|
|
471
|
+
The color picker provides:
|
|
472
|
+
- **Text Colors** - 10 predefined text colors
|
|
473
|
+
- **Background Colors** - 10 predefined background colors
|
|
474
|
+
- **Recently Used** - Global history of recently used colors (persists across sessions)
|
|
475
|
+
- **Visual Distinction** - Background colors shown as solid circles, text colors shown with 'A' icon
|
|
476
|
+
- **Tooltips** - Hover over any color to see its name
|
|
477
|
+
- **Smart Positioning** - Automatically positions above or below based on available space
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## 🔧 Advanced Usage
|
|
482
|
+
|
|
483
|
+
### Setting Editor Width
|
|
484
|
+
|
|
485
|
+
By default, the editor takes 100% of the parent container width. You can customize the width using the `width` prop:
|
|
486
|
+
|
|
487
|
+
```tsx
|
|
488
|
+
// Full width (default)
|
|
489
|
+
<PubwaveEditor width="100%" />
|
|
490
|
+
|
|
491
|
+
// Fixed width
|
|
492
|
+
<PubwaveEditor width="1200px" />
|
|
493
|
+
|
|
494
|
+
// Responsive width
|
|
495
|
+
<PubwaveEditor width="90vw" />
|
|
496
|
+
<PubwaveEditor width="calc(100% - 40px)" />
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
The `width` prop accepts any valid CSS width value. When set, it will override the default `max-width` constraint.
|
|
500
|
+
|
|
501
|
+
### Programmatic Content Manipulation
|
|
502
|
+
|
|
503
|
+
```tsx
|
|
504
|
+
const editorRef = useRef<EditorAPI | null>(null);
|
|
505
|
+
|
|
506
|
+
<PubwaveEditor
|
|
507
|
+
ref={editorRef}
|
|
508
|
+
onReady={(api) => {
|
|
509
|
+
editorRef.current = api;
|
|
510
|
+
}}
|
|
511
|
+
/>
|
|
512
|
+
|
|
513
|
+
// Set content programmatically
|
|
514
|
+
editorRef.current?.setContent({
|
|
515
|
+
type: 'doc',
|
|
516
|
+
content: [
|
|
517
|
+
{
|
|
518
|
+
type: 'heading',
|
|
519
|
+
attrs: { level: 1 },
|
|
520
|
+
content: [{ type: 'text', text: 'Hello World' }],
|
|
521
|
+
},
|
|
522
|
+
],
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Get content
|
|
526
|
+
const json = editorRef.current?.getJSON();
|
|
527
|
+
const html = editorRef.current?.getHTML();
|
|
528
|
+
const text = editorRef.current?.getText();
|
|
529
|
+
|
|
530
|
+
// Apply formatting
|
|
531
|
+
editorRef.current?.toggleBold();
|
|
532
|
+
editorRef.current?.setLink('https://example.com');
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Event Handling
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
<PubwaveEditor
|
|
539
|
+
onChange={(content) => {
|
|
540
|
+
// Content changed
|
|
541
|
+
console.log('New content:', content);
|
|
542
|
+
}}
|
|
543
|
+
onSelectionChange={() => {
|
|
544
|
+
// Selection changed (toolbar may appear/disappear)
|
|
545
|
+
}}
|
|
546
|
+
onFocus={() => {
|
|
547
|
+
// Editor gained focus
|
|
548
|
+
}}
|
|
549
|
+
onBlur={() => {
|
|
550
|
+
// Editor lost focus
|
|
551
|
+
}}
|
|
552
|
+
onReady={(api) => {
|
|
553
|
+
// Editor is ready, API is available
|
|
554
|
+
console.log('Editor ready:', api);
|
|
555
|
+
}}
|
|
556
|
+
/>
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Read-only Mode
|
|
560
|
+
|
|
561
|
+
```tsx
|
|
562
|
+
<PubwaveEditor
|
|
563
|
+
editable={false}
|
|
564
|
+
content={readOnlyContent}
|
|
565
|
+
/>
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
In read-only mode:
|
|
569
|
+
- All editing affordances are hidden
|
|
570
|
+
- Toolbar does not appear
|
|
571
|
+
- Slash commands are disabled
|
|
572
|
+
- Drag handles are hidden
|
|
573
|
+
- Content is still selectable and copyable
|
|
574
|
+
|
|
575
|
+
### Custom Slash Commands
|
|
576
|
+
|
|
577
|
+
You can extend the editor with custom slash commands:
|
|
578
|
+
|
|
579
|
+
```tsx
|
|
580
|
+
import { PubwaveEditor } from '@pubwave/editor';
|
|
581
|
+
import type { SlashCommand } from '@pubwave/editor';
|
|
582
|
+
|
|
583
|
+
const customCommands: SlashCommand[] = [
|
|
584
|
+
{
|
|
585
|
+
id: 'custom-block',
|
|
586
|
+
title: 'Custom Block',
|
|
587
|
+
description: 'Insert a custom block',
|
|
588
|
+
icon: <CustomIcon />,
|
|
589
|
+
aliases: ['custom', 'cb'],
|
|
590
|
+
group: 'basic',
|
|
591
|
+
action: (editor) => {
|
|
592
|
+
// Your custom action
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
];
|
|
596
|
+
|
|
597
|
+
// Note: Custom slash commands require access to internal APIs
|
|
598
|
+
// This is an advanced feature - see source code for implementation details
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## ⚡ SSR Integration (Next.js)
|
|
604
|
+
|
|
605
|
+
The library is SSR-safe and can be imported server-side without errors. The editor component itself renders client-side only.
|
|
606
|
+
|
|
607
|
+
```tsx
|
|
608
|
+
'use client';
|
|
609
|
+
|
|
610
|
+
import { PubwaveEditor } from '@pubwave/editor';
|
|
611
|
+
import '@pubwave/editor/index.css';
|
|
612
|
+
|
|
613
|
+
export default function EditorComponent() {
|
|
614
|
+
return <PubwaveEditor />;
|
|
615
|
+
}
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
## 🐛 Troubleshooting
|
|
622
|
+
|
|
623
|
+
### Common Issues
|
|
624
|
+
|
|
625
|
+
**Q: The editor doesn't render in Next.js SSR**
|
|
626
|
+
- Make sure you're using `'use client'` directive
|
|
627
|
+
- Ensure the component is marked as a client component
|
|
628
|
+
|
|
629
|
+
**Q: Styles are not applied**
|
|
630
|
+
- Ensure you've imported the CSS: `import '@pubwave/editor/index.css'`
|
|
631
|
+
- Check that CSS custom properties are defined
|
|
632
|
+
|
|
633
|
+
**Q: Slash commands don't work**
|
|
634
|
+
- Ensure the editor is in editable mode
|
|
635
|
+
- Check that you're typing `/` in a text block (not in code blocks)
|
|
636
|
+
|
|
637
|
+
**Q: Toolbar doesn't appear**
|
|
638
|
+
- Toolbar only appears when text is selected
|
|
639
|
+
- Make sure you have a non-empty selection
|
|
640
|
+
|
|
641
|
+
**Q: Colors don't persist**
|
|
642
|
+
- Text and background colors are stored as marks in the content
|
|
643
|
+
- Ensure you're saving the full JSON content, not just HTML
|
|
644
|
+
|
|
645
|
+
**Q: Image upload doesn't work**
|
|
646
|
+
- Check that `imageUpload.handler` returns a valid URL string
|
|
647
|
+
- Verify file size is within `maxSize` limit
|
|
648
|
+
- Check browser console for error messages
|
|
649
|
+
- If custom upload fails, editor will automatically fall back to base64
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## 🤝 Contributing
|
|
654
|
+
|
|
655
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
## 📄 License
|
|
660
|
+
|
|
661
|
+
MIT © [Pubwave](https://github.com/pubwave)
|