neuphlo-editor 1.1.0 → 1.1.1

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.
Files changed (2) hide show
  1. package/README.md +453 -15
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,20 +1,458 @@
1
- Neuphlo Editor
2
- ===============
1
+ # Neuphlo Editor
3
2
 
4
- Lightweight React wrapper around Tiptap with sensible defaults.
3
+ A lightweight, feature-rich React wrapper around [Tiptap](https://tiptap.dev) with sensible defaults, built-in styling, and powerful image upload capabilities.
5
4
 
6
- CSS integration
7
- ----------------
5
+ [![npm version](https://img.shields.io/npm/v/neuphlo-editor.svg)](https://www.npmjs.com/package/neuphlo-editor)
6
+ [![License](https://img.shields.io/npm/l/neuphlo-editor.svg)](https://github.com/Neuphlo/neuphlo-editor/blob/main/LICENSE)
8
7
 
9
- - Import global CSS once near your app root:
10
- import 'neuphlo-editor/styles.css';
8
+ ## Features
11
9
 
12
- Highlight.js theme (optional)
13
- -----------------------------
14
- - We no longer inject a theme by default. If you want a default theme, also import:
15
- import 'neuphlo-editor/highlight.css';
16
- - Or import any other `highlight.js` theme you prefer.
10
+ - 🎨 **Beautiful defaults** - Pre-styled editor with clean, modern design
11
+ - 🖼️ **Image upload** - Integrated image support with custom upload handlers
12
+ - 📐 **Image controls** - Resize and align images with intuitive controls
13
+ - ⌨️ **Slash commands** - Quick formatting with `/` commands
14
+ - 💬 **Bubble menus** - Context-aware formatting menus
15
+ - 🎯 **TypeScript** - Full TypeScript support with type definitions
16
+ - 🎨 **Syntax highlighting** - Code blocks with highlight.js support
17
+ - 🔗 **Link management** - Easy link insertion and editing
18
+ - 📦 **Lightweight** - Minimal bundle size with tree-shaking support
17
19
 
18
- Notes
19
- -----
20
- - All styles are namespaced (e.g., `.nph-editor`) to avoid colliding with your app.
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install neuphlo-editor
24
+ # or
25
+ pnpm add neuphlo-editor
26
+ # or
27
+ yarn add neuphlo-editor
28
+ ```
29
+
30
+ ### Peer Dependencies
31
+
32
+ Make sure you have the required peer dependencies installed:
33
+
34
+ ```bash
35
+ npm install react react-dom @tiptap/react @tiptap/pm
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ```tsx
41
+ import { Editor } from 'neuphlo-editor'
42
+ import 'neuphlo-editor/styles.css'
43
+
44
+ function MyApp() {
45
+ return <Editor content="<p>Start editing...</p>" />
46
+ }
47
+ ```
48
+
49
+ ## Image Upload
50
+
51
+ Neuphlo Editor makes it easy to add image upload functionality with your own backend:
52
+
53
+ ```tsx
54
+ import { Editor } from 'neuphlo-editor'
55
+ import 'neuphlo-editor/styles.css'
56
+
57
+ function MyApp() {
58
+ const handleImageUpload = async (file: File): Promise<string> => {
59
+ // Upload to your backend (S3, Cloudinary, custom API, etc.)
60
+ const formData = new FormData()
61
+ formData.append('file', file)
62
+
63
+ const response = await fetch('/api/upload', {
64
+ method: 'POST',
65
+ body: formData,
66
+ })
67
+
68
+ const { url } = await response.json()
69
+ return url // Return the permanent image URL
70
+ }
71
+
72
+ return <Editor uploadImage={handleImageUpload} />
73
+ }
74
+ ```
75
+
76
+ ### Image Upload Methods
77
+
78
+ Users can insert images in multiple ways:
79
+
80
+ 1. **Slash command** - Type `/image` and press Enter
81
+ 2. **Image menu** - Click the upload button in the image bubble menu
82
+ 3. **Drag & drop** - Drag image files directly into the editor
83
+ 4. **Paste** - Copy and paste images from clipboard
84
+
85
+ ### Image Controls
86
+
87
+ When an image is selected, a bubble menu appears with controls to:
88
+
89
+ - **Resize** - Adjust size from 25% to 100% with a slider
90
+ - **Align** - Position left, center, or right
91
+ - **Replace** - Upload a new image
92
+ - **Delete** - Remove the image
93
+
94
+ ## Upload Handler Examples
95
+
96
+ ### AWS S3
97
+
98
+ ```tsx
99
+ const handleImageUpload = async (file: File): Promise<string> => {
100
+ // Get presigned URL from your API
101
+ const response = await fetch('/api/presigned-url', {
102
+ method: 'POST',
103
+ headers: { 'Content-Type': 'application/json' },
104
+ body: JSON.stringify({
105
+ fileName: file.name,
106
+ fileType: file.type,
107
+ }),
108
+ })
109
+
110
+ const { uploadUrl, imageUrl } = await response.json()
111
+
112
+ // Upload to S3
113
+ await fetch(uploadUrl, {
114
+ method: 'PUT',
115
+ body: file,
116
+ headers: { 'Content-Type': file.type },
117
+ })
118
+
119
+ return imageUrl
120
+ }
121
+ ```
122
+
123
+ ### Cloudinary
124
+
125
+ ```tsx
126
+ const handleImageUpload = async (file: File): Promise<string> => {
127
+ const formData = new FormData()
128
+ formData.append('file', file)
129
+ formData.append('upload_preset', 'your_upload_preset')
130
+
131
+ const response = await fetch(
132
+ `https://api.cloudinary.com/v1_1/your_cloud_name/image/upload`,
133
+ {
134
+ method: 'POST',
135
+ body: formData,
136
+ }
137
+ )
138
+
139
+ const data = await response.json()
140
+ return data.secure_url
141
+ }
142
+ ```
143
+
144
+ ### Custom Backend
145
+
146
+ ```tsx
147
+ const handleImageUpload = async (file: File): Promise<string> => {
148
+ const formData = new FormData()
149
+ formData.append('file', file)
150
+
151
+ const response = await fetch('/api/upload', {
152
+ method: 'POST',
153
+ body: formData,
154
+ })
155
+
156
+ if (!response.ok) {
157
+ throw new Error('Upload failed')
158
+ }
159
+
160
+ const { url } = await response.json()
161
+ return url
162
+ }
163
+ ```
164
+
165
+ ## API Reference
166
+
167
+ ### Editor Props
168
+
169
+ | Prop | Type | Default | Description |
170
+ |------|------|---------|-------------|
171
+ | `content` | `string` | `""` | Initial HTML content |
172
+ | `className` | `string` | `undefined` | CSS class for the editor wrapper |
173
+ | `editable` | `boolean` | `true` | Whether the editor is editable |
174
+ | `immediatelyRender` | `boolean` | `false` | Render immediately on mount |
175
+ | `showTextMenu` | `boolean` | `true` | Show text formatting bubble menu |
176
+ | `showImageMenu` | `boolean` | `true` | Show image controls bubble menu |
177
+ | `showSlashMenu` | `boolean` | `true` | Show slash command menu |
178
+ | `extensions` | `Extension[]` | `[]` | Additional Tiptap extensions |
179
+ | `uploadImage` | `(file: File) => Promise<string>` | `undefined` | Image upload handler |
180
+ | `onUpdate` | `({ editor }) => void` | `undefined` | Called when content changes |
181
+ | `onCreate` | `({ editor }) => void` | `undefined` | Called when editor is created |
182
+
183
+ ### Example with Props
184
+
185
+ ```tsx
186
+ <Editor
187
+ content="<p>Hello world!</p>"
188
+ className="my-editor"
189
+ editable={true}
190
+ showTextMenu={true}
191
+ showImageMenu={true}
192
+ showSlashMenu={true}
193
+ uploadImage={handleImageUpload}
194
+ onUpdate={({ editor }) => {
195
+ const html = editor.getHTML()
196
+ console.log('Content updated:', html)
197
+ }}
198
+ onCreate={({ editor }) => {
199
+ console.log('Editor created:', editor)
200
+ }}
201
+ />
202
+ ```
203
+
204
+ ## Styling
205
+
206
+ ### Basic Styling
207
+
208
+ Import the default styles in your app:
209
+
210
+ ```tsx
211
+ import 'neuphlo-editor/styles.css'
212
+ ```
213
+
214
+ ### Syntax Highlighting (Optional)
215
+
216
+ For code block syntax highlighting, import the highlight.js theme:
217
+
218
+ ```tsx
219
+ import 'neuphlo-editor/styles.css'
220
+ import 'neuphlo-editor/highlight.css'
221
+ ```
222
+
223
+ Or use any other highlight.js theme:
224
+
225
+ ```tsx
226
+ import 'neuphlo-editor/styles.css'
227
+ import 'highlight.js/styles/github-dark.css'
228
+ ```
229
+
230
+ ### Custom Styling
231
+
232
+ All styles are namespaced with `.nph-` prefix to avoid conflicts. You can override them:
233
+
234
+ ```css
235
+ .nph-editor {
236
+ min-height: 400px;
237
+ border: 1px solid #e5e7eb;
238
+ border-radius: 0.5rem;
239
+ padding: 1rem;
240
+ }
241
+
242
+ .nph-editor img {
243
+ max-width: 100%;
244
+ border-radius: 0.5rem;
245
+ }
246
+ ```
247
+
248
+ ## Available Features
249
+
250
+ ### Text Formatting
251
+
252
+ - **Bold** (`Ctrl+B` / `Cmd+B`)
253
+ - **Italic** (`Ctrl+I` / `Cmd+I`)
254
+ - **Strike** (`Ctrl+Shift+X` / `Cmd+Shift+X`)
255
+ - **Code** (inline code)
256
+
257
+ ### Blocks
258
+
259
+ - Headings (H1-H6)
260
+ - Bullet lists
261
+ - Ordered lists
262
+ - Blockquotes
263
+ - Code blocks with syntax highlighting
264
+
265
+ ### Slash Commands
266
+
267
+ Type `/` to open the command menu:
268
+
269
+ - `/bold` - Bold text
270
+ - `/italic` - Italic text
271
+ - `/strike` - Strikethrough
272
+ - `/h1` - Heading 1
273
+ - `/h2` - Heading 2
274
+ - `/h3` - Heading 3
275
+ - `/h4` - Heading 4
276
+ - `/list` - Bullet list
277
+ - `/ol` - Ordered list
278
+ - `/quote` - Blockquote
279
+ - `/code` - Inline code
280
+ - `/codeblock` - Code block
281
+ - `/image` - Insert image
282
+
283
+ ### Links
284
+
285
+ - Insert/edit links via the bubble menu
286
+ - Open links in new tab
287
+ - Remove links
288
+
289
+ ### Images
290
+
291
+ - Upload via slash command (`/image`)
292
+ - Drag and drop images
293
+ - Paste images from clipboard
294
+ - Resize images (25%-100%)
295
+ - Align images (left, center, right)
296
+ - Replace images
297
+ - Delete images
298
+
299
+ ## Advanced Usage
300
+
301
+ ### Custom Extensions
302
+
303
+ Add your own Tiptap extensions:
304
+
305
+ ```tsx
306
+ import { Editor } from 'neuphlo-editor'
307
+ import { Underline } from '@tiptap/extension-underline'
308
+
309
+ <Editor
310
+ extensions={[Underline]}
311
+ content="<p>Now you can <u>underline</u> text!</p>"
312
+ />
313
+ ```
314
+
315
+ ### Accessing the Editor Instance
316
+
317
+ ```tsx
318
+ import { useRef } from 'react'
319
+ import { Editor } from 'neuphlo-editor'
320
+
321
+ function MyApp() {
322
+ const editorRef = useRef(null)
323
+
324
+ const handleCreate = ({ editor }) => {
325
+ editorRef.current = editor
326
+ }
327
+
328
+ const getContent = () => {
329
+ if (editorRef.current) {
330
+ const html = editorRef.current.getHTML()
331
+ const json = editorRef.current.getJSON()
332
+ console.log('HTML:', html)
333
+ console.log('JSON:', json)
334
+ }
335
+ }
336
+
337
+ return (
338
+ <>
339
+ <Editor onCreate={handleCreate} />
340
+ <button onClick={getContent}>Get Content</button>
341
+ </>
342
+ )
343
+ }
344
+ ```
345
+
346
+ ### Controlled Content
347
+
348
+ ```tsx
349
+ import { useState } from 'react'
350
+ import { Editor } from 'neuphlo-editor'
351
+
352
+ function MyApp() {
353
+ const [content, setContent] = useState('<p>Initial content</p>')
354
+
355
+ return (
356
+ <Editor
357
+ content={content}
358
+ onUpdate={({ editor }) => {
359
+ setContent(editor.getHTML())
360
+ }}
361
+ />
362
+ )
363
+ }
364
+ ```
365
+
366
+ ## Error Handling
367
+
368
+ Handle upload errors gracefully:
369
+
370
+ ```tsx
371
+ const handleImageUpload = async (file: File): Promise<string> => {
372
+ try {
373
+ // Validate file size (e.g., max 5MB)
374
+ if (file.size > 5 * 1024 * 1024) {
375
+ throw new Error('File size must be less than 5MB')
376
+ }
377
+
378
+ // Validate file type
379
+ if (!file.type.startsWith('image/')) {
380
+ throw new Error('File must be an image')
381
+ }
382
+
383
+ // Upload logic
384
+ const formData = new FormData()
385
+ formData.append('file', file)
386
+
387
+ const response = await fetch('/api/upload', {
388
+ method: 'POST',
389
+ body: formData,
390
+ })
391
+
392
+ if (!response.ok) {
393
+ throw new Error('Upload failed')
394
+ }
395
+
396
+ const { url } = await response.json()
397
+ return url
398
+ } catch (error) {
399
+ console.error('Image upload error:', error)
400
+ // You might want to show a toast notification here
401
+ throw error
402
+ }
403
+ }
404
+ ```
405
+
406
+ ## TypeScript
407
+
408
+ The package is fully typed with TypeScript:
409
+
410
+ ```tsx
411
+ import type { Editor as TiptapEditor } from '@tiptap/core'
412
+ import { Editor } from 'neuphlo-editor'
413
+
414
+ type UploadImageFn = (file: File) => Promise<string>
415
+
416
+ const handleImageUpload: UploadImageFn = async (file) => {
417
+ // Your upload logic
418
+ return 'https://example.com/image.jpg'
419
+ }
420
+
421
+ function MyApp() {
422
+ const handleCreate = ({ editor }: { editor: TiptapEditor }) => {
423
+ console.log('Editor created:', editor)
424
+ }
425
+
426
+ return (
427
+ <Editor
428
+ uploadImage={handleImageUpload}
429
+ onCreate={handleCreate}
430
+ />
431
+ )
432
+ }
433
+ ```
434
+
435
+ ## Browser Support
436
+
437
+ Neuphlo Editor works in all modern browsers that support:
438
+ - FileReader API
439
+ - Drag and Drop API
440
+ - Clipboard API
441
+
442
+ Supported browsers:
443
+ - Chrome 60+
444
+ - Firefox 55+
445
+ - Safari 11+
446
+ - Edge 79+
447
+
448
+ ## License
449
+
450
+ MIT
451
+
452
+ ## Contributing
453
+
454
+ Contributions are welcome! Please feel free to submit a Pull Request.
455
+
456
+ ## Support
457
+
458
+ If you encounter any issues or have questions, please file an issue on the [GitHub repository](https://github.com/Neuphlo/neuphlo-editor/issues).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuphlo-editor",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "private": false,
5
5
  "description": "A lightweight React wrapper around Tiptap with sensible defaults and image upload support.",
6
6
  "type": "module",