magic-editor-x 1.0.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 +21 -0
- package/README.md +890 -0
- package/dist/_chunks/App-B1FgOsWa.mjs +2143 -0
- package/dist/_chunks/App-mtrlABtd.js +2146 -0
- package/dist/_chunks/LicensePage-BnyWSrWs.js +375 -0
- package/dist/_chunks/LicensePage-CWH-AFR-.mjs +373 -0
- package/dist/_chunks/LiveCollaborationPanel-DbDHwr2C.js +222 -0
- package/dist/_chunks/LiveCollaborationPanel-ryjcDAA7.mjs +220 -0
- package/dist/_chunks/Settings-Bk9bxJTy.js +440 -0
- package/dist/_chunks/Settings-D-V2MLVm.mjs +438 -0
- package/dist/_chunks/de-CSrHZWEb.mjs +295 -0
- package/dist/_chunks/de-CzSo1oD2.js +295 -0
- package/dist/_chunks/en-DuQun2v4.mjs +295 -0
- package/dist/_chunks/en-DxIkVPUh.js +295 -0
- package/dist/_chunks/es-DAQ_97zx.js +273 -0
- package/dist/_chunks/es-DEB0CA8S.mjs +273 -0
- package/dist/_chunks/fr-Bqkhvdx2.mjs +273 -0
- package/dist/_chunks/fr-ChPabvNP.js +273 -0
- package/dist/_chunks/getTranslation-C4uWR0DB.mjs +50985 -0
- package/dist/_chunks/getTranslation-D35vbDap.js +51001 -0
- package/dist/_chunks/index-B5MzUyo0.mjs +2541 -0
- package/dist/_chunks/index-BRVqbnOb.mjs +4450 -0
- package/dist/_chunks/index-BiLy_f7C.js +2540 -0
- package/dist/_chunks/index-CQx7-dFP.js +4472 -0
- package/dist/_chunks/pt-BMoYltav.mjs +273 -0
- package/dist/_chunks/pt-Cm74LpyZ.js +273 -0
- package/dist/_chunks/tools-CjnQJ9w2.mjs +2155 -0
- package/dist/_chunks/tools-DNt2tioN.js +2186 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/server/index.js +2554 -0
- package/dist/server/index.mjs +2544 -0
- package/dist/style.css +164 -0
- package/package.json +122 -0
- package/pics/collab-magiceditorX.png +0 -0
- package/pics/editorX.png +0 -0
- package/pics/liveCollabwidget1.png +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
# Magic Editor X
|
|
2
|
+
|
|
3
|
+
**Advanced Block-Based Content Editor for Strapi v5 with Real-Time Collaboration**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/magic-editor-x)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://strapi.io)
|
|
8
|
+
[](https://editorjs.io)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Introduction
|
|
13
|
+
|
|
14
|
+
Magic Editor X is a production-ready Strapi v5 Custom Field that brings the power of Editor.js to your content management workflow. Unlike traditional WYSIWYG replacements or plugins that override Strapi's default editor, Magic Editor X integrates as a **proper Custom Field** in Strapi's Content-Type Builder, giving you complete control over when and where to use it.
|
|
15
|
+
|
|
16
|
+
Built on proven technologies like Editor.js for the editing interface and Y.js for conflict-free real-time collaboration, this plugin provides a modern, extensible content editing experience that scales from solo developers to large editorial teams.
|
|
17
|
+
|
|
18
|
+
### Why Magic Editor X?
|
|
19
|
+
|
|
20
|
+
**For Developers:**
|
|
21
|
+
- Clean JSON output instead of unstructured HTML
|
|
22
|
+
- 25+ pre-configured Editor.js tools out of the box
|
|
23
|
+
- Type-safe content structure with versioned schemas
|
|
24
|
+
- Real-time collaboration with CRDT (Conflict-free Replicated Data Types)
|
|
25
|
+
- Full TypeScript support with proper type definitions
|
|
26
|
+
- Extensible architecture for custom block types
|
|
27
|
+
|
|
28
|
+
**For Content Teams:**
|
|
29
|
+
- Familiar Medium-style block editor experience
|
|
30
|
+
- Live collaborative editing with presence indicators
|
|
31
|
+
- Keyboard shortcuts for fast content creation
|
|
32
|
+
- Inline formatting without toolbar clutter
|
|
33
|
+
- Media Library integration for asset management
|
|
34
|
+
- AI-powered content suggestions (premium)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Screenshots
|
|
39
|
+
|
|
40
|
+
### Editor Interface
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
### Real-Time Collaboration
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
### Collaboration Widget
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Architecture Overview
|
|
52
|
+
|
|
53
|
+
Magic Editor X follows a clean client-server architecture with three main components:
|
|
54
|
+
|
|
55
|
+
### Client-Side (Admin Panel)
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
admin/
|
|
59
|
+
├── src/
|
|
60
|
+
│ ├── components/
|
|
61
|
+
│ │ ├── EditorJS/ # Main editor component
|
|
62
|
+
│ │ ├── EditorTools/ # Custom tools (Button, Hyperlink, AI)
|
|
63
|
+
│ │ ├── MediaLib/ # Strapi Media Library adapter
|
|
64
|
+
│ │ └── LiveCollaborationPanel.jsx # Collaboration UI
|
|
65
|
+
│ ├── hooks/
|
|
66
|
+
│ │ ├── useMagicCollaboration.js # Y.js & Socket.io integration
|
|
67
|
+
│ │ ├── useAIAssistant.js # AI features
|
|
68
|
+
│ │ └── useLicense.js # License management
|
|
69
|
+
│ ├── config/
|
|
70
|
+
│ │ └── tools.js # Editor.js tools configuration
|
|
71
|
+
│ └── index.js # Plugin registration
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Key Technologies:**
|
|
75
|
+
- **React 18** - UI components with hooks
|
|
76
|
+
- **Editor.js 2.31** - Block-based editor core
|
|
77
|
+
- **Y.js 13.6** - CRDT for real-time sync
|
|
78
|
+
- **Socket.io-client 4.8** - WebSocket communication
|
|
79
|
+
- **IndexedDB** - Local persistence via y-indexeddb
|
|
80
|
+
|
|
81
|
+
### Server-Side (Strapi Backend)
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
server/
|
|
85
|
+
├── src/
|
|
86
|
+
│ ├── controllers/
|
|
87
|
+
│ │ ├── editor-controller.js # Image upload, link previews
|
|
88
|
+
│ │ ├── collaboration-controller.js # Permission management
|
|
89
|
+
│ │ ├── realtime-controller.js # Session tokens
|
|
90
|
+
│ │ └── license-controller.js # License validation
|
|
91
|
+
│ ├── services/
|
|
92
|
+
│ │ ├── realtime-service.js # Y.js document management
|
|
93
|
+
│ │ ├── access-service.js # Permission checks
|
|
94
|
+
│ │ ├── license-service.js # License API
|
|
95
|
+
│ │ └── snapshot-service.js # Document snapshots
|
|
96
|
+
│ ├── routes/
|
|
97
|
+
│ │ ├── admin.js # Admin panel routes
|
|
98
|
+
│ │ └── content-api.js # Public API routes
|
|
99
|
+
│ └── config/
|
|
100
|
+
│ └── index.js # Plugin configuration
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Key Technologies:**
|
|
104
|
+
- **Socket.io 4.8** - WebSocket server
|
|
105
|
+
- **Y.js 13.6** - Server-side CRDT state
|
|
106
|
+
- **Open Graph Scraper** - Link metadata extraction
|
|
107
|
+
- **Strapi 5.31** - Backend framework
|
|
108
|
+
|
|
109
|
+
### Data Flow
|
|
110
|
+
|
|
111
|
+
1. **Editor Changes** → Y.js Document (CRDT) → Socket.io → Server → Other Clients
|
|
112
|
+
2. **Image Upload** → Strapi Controller → Strapi Upload Service → Media Library
|
|
113
|
+
3. **Link Preview** → Backend Scraper → OpenGraph Metadata → Client
|
|
114
|
+
4. **Collaboration** → Permission Check → Session Token → WebSocket Connection
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Features Deep Dive
|
|
119
|
+
|
|
120
|
+
### 1. Block-Based Content Structure
|
|
121
|
+
|
|
122
|
+
Magic Editor X stores content as structured JSON, not HTML. Each block has a unique ID, type, and data object:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"time": 1699999999999,
|
|
127
|
+
"blocks": [
|
|
128
|
+
{
|
|
129
|
+
"id": "abc123",
|
|
130
|
+
"type": "header",
|
|
131
|
+
"data": {
|
|
132
|
+
"text": "Hello World",
|
|
133
|
+
"level": 2
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"id": "def456",
|
|
138
|
+
"type": "paragraph",
|
|
139
|
+
"data": {
|
|
140
|
+
"text": "This is <b>bold</b> and <i>italic</i> text."
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"id": "ghi789",
|
|
145
|
+
"type": "list",
|
|
146
|
+
"data": {
|
|
147
|
+
"style": "unordered",
|
|
148
|
+
"items": [
|
|
149
|
+
{
|
|
150
|
+
"content": "First item",
|
|
151
|
+
"items": []
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"content": "Second item with nested list",
|
|
155
|
+
"items": [
|
|
156
|
+
{
|
|
157
|
+
"content": "Nested item",
|
|
158
|
+
"items": []
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
"version": "2.31.0"
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Benefits:**
|
|
171
|
+
- Predictable, type-safe content structure
|
|
172
|
+
- Easy to parse, transform, and render
|
|
173
|
+
- Version control friendly (no HTML diffs)
|
|
174
|
+
- Can be validated against JSON schemas
|
|
175
|
+
- Simple to migrate between systems
|
|
176
|
+
|
|
177
|
+
### 2. Real-Time Collaboration (CRDT Technology)
|
|
178
|
+
|
|
179
|
+
Magic Editor X uses Y.js, a battle-tested CRDT implementation, to enable true real-time collaboration without conflicts:
|
|
180
|
+
|
|
181
|
+
**How It Works:**
|
|
182
|
+
|
|
183
|
+
1. **Y.Doc Creation** - Each content entry gets a shared Y.js document
|
|
184
|
+
2. **Local Changes** - Edits create CRDT operations (not plain text diffs)
|
|
185
|
+
3. **Sync Protocol** - Operations are sent via Socket.io to the server
|
|
186
|
+
4. **Server Broadcast** - Server distributes operations to all connected clients
|
|
187
|
+
5. **Automatic Merge** - Y.js guarantees conflict-free merges (no "last write wins")
|
|
188
|
+
6. **Persistence** - Changes are stored in IndexedDB for offline capability
|
|
189
|
+
|
|
190
|
+
**Example: Collaborative Editing Flow**
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
// Client A types "Hello" at position 0
|
|
194
|
+
const update = Y.encodeStateAsUpdate(yDoc);
|
|
195
|
+
socket.emit('collab:update', update);
|
|
196
|
+
|
|
197
|
+
// Server broadcasts to all clients
|
|
198
|
+
socket.broadcast.to(roomId).emit('collab:update', update);
|
|
199
|
+
|
|
200
|
+
// Client B applies the update
|
|
201
|
+
Y.applyUpdate(yDoc, update);
|
|
202
|
+
// Editor automatically re-renders with "Hello"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Presence & Cursors:**
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
// Awareness protocol for presence
|
|
209
|
+
awareness.setLocalStateField('cursor', {
|
|
210
|
+
blockId: 'abc123',
|
|
211
|
+
position: 42,
|
|
212
|
+
user: {
|
|
213
|
+
id: 'user-1',
|
|
214
|
+
name: 'John Doe',
|
|
215
|
+
color: '#FF6B6B'
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Other clients receive cursor updates
|
|
220
|
+
awareness.on('change', ({ added, updated, removed }) => {
|
|
221
|
+
// Render remote cursor indicators
|
|
222
|
+
renderCursors(awareness.getStates());
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 3. 25+ Editor.js Tools
|
|
227
|
+
|
|
228
|
+
Magic Editor X comes with a comprehensive collection of tools, categorized for easy reference:
|
|
229
|
+
|
|
230
|
+
#### Block Tools (21 Tools)
|
|
231
|
+
|
|
232
|
+
| Tool | Description | Package | Shortcut |
|
|
233
|
+
|------|-------------|---------|----------|
|
|
234
|
+
| **Header** | H1-H6 headings with alignment | `@editorjs/header` | `CMD+SHIFT+H` |
|
|
235
|
+
| **Paragraph** | Text blocks with inline formatting | `@editorjs/paragraph` | - |
|
|
236
|
+
| **Nested List** | Multi-level ordered/unordered lists | `@editorjs/nested-list` | `CMD+SHIFT+L` |
|
|
237
|
+
| **Checklist** | Interactive todo lists | `@editorjs/checklist` | `CMD+SHIFT+C` |
|
|
238
|
+
| **Quote** | Blockquotes with caption | `@editorjs/quote` | `CMD+SHIFT+Q` |
|
|
239
|
+
| **Warning** | Alert boxes | `@editorjs/warning` | `CMD+SHIFT+W` |
|
|
240
|
+
| **Code** | Basic code blocks | `@editorjs/code` | `CMD+SHIFT+P` |
|
|
241
|
+
| **Code (Highlight)** | Syntax-highlighted code | `@calumk/editorjs-codeflask` | - |
|
|
242
|
+
| **Delimiter** | Visual section separator | `@editorjs/delimiter` | `CMD+SHIFT+D` |
|
|
243
|
+
| **Table** | Create/edit tables | `@editorjs/table` | `CMD+SHIFT+T` |
|
|
244
|
+
| **Embed** | YouTube, Vimeo, Twitter, etc. | `@editorjs/embed` | - |
|
|
245
|
+
| **Raw HTML** | Insert raw HTML | `@editorjs/raw` | - |
|
|
246
|
+
| **Image** | Upload by file or URL | `@editorjs/image` | - |
|
|
247
|
+
| **Simple Image** | Image by URL only | `@editorjs/simple-image` | - |
|
|
248
|
+
| **Link Preview** | Rich link cards with metadata | `@editorjs/link` | - |
|
|
249
|
+
| **Attaches** | File attachments | `@editorjs/attaches` | - |
|
|
250
|
+
| **Media Library** | Strapi Media Library picker | Custom | - |
|
|
251
|
+
| **Personality** | Author/person cards | `@editorjs/personality` | - |
|
|
252
|
+
| **Alert** | Colored alert messages | `editorjs-alert` | `CMD+SHIFT+A` |
|
|
253
|
+
| **Toggle** | Collapsible content blocks | `editorjs-toggle-block` | - |
|
|
254
|
+
| **Button** | CTA buttons | Custom | - |
|
|
255
|
+
|
|
256
|
+
#### Inline Tools (7 Tools)
|
|
257
|
+
|
|
258
|
+
| Tool | Description | Package | Shortcut |
|
|
259
|
+
|------|-------------|---------|----------|
|
|
260
|
+
| **Bold** | Bold text | Built-in | `CMD+B` |
|
|
261
|
+
| **Italic** | Italic text | Built-in | `CMD+I` |
|
|
262
|
+
| **Marker** | Highlight text | `@editorjs/marker` | `CMD+SHIFT+M` |
|
|
263
|
+
| **Inline Code** | Code formatting | `@editorjs/inline-code` | `CMD+SHIFT+I` |
|
|
264
|
+
| **Underline** | Underline text | `@editorjs/underline` | `CMD+U` |
|
|
265
|
+
| **Strikethrough** | Strike through text | `@sotaproject/strikethrough` | `CMD+SHIFT+S` |
|
|
266
|
+
| **Hyperlink** | Links with target/rel | Custom | `CMD+K` |
|
|
267
|
+
| **Tooltip** | Add tooltips to text | `editorjs-tooltip` | - |
|
|
268
|
+
|
|
269
|
+
#### Block Tunes (3 Tunes)
|
|
270
|
+
|
|
271
|
+
| Tune | Description | Package |
|
|
272
|
+
|------|-------------|---------|
|
|
273
|
+
| **Alignment** | Left, center, right, justify | `editorjs-text-alignment-blocktune` |
|
|
274
|
+
| **Indent** | Multi-level indentation | `editorjs-indent-tune` |
|
|
275
|
+
| **Text Variant** | Call-out, citation, details styles | `@editorjs/text-variant-tune` |
|
|
276
|
+
|
|
277
|
+
#### Plugins (2 Plugins)
|
|
278
|
+
|
|
279
|
+
| Plugin | Description | Package |
|
|
280
|
+
|--------|-------------|---------|
|
|
281
|
+
| **Undo/Redo** | History management | `editorjs-undo` |
|
|
282
|
+
| **Drag & Drop** | Reorder blocks by dragging | `editorjs-drag-drop` |
|
|
283
|
+
|
|
284
|
+
### 4. Media Library Integration
|
|
285
|
+
|
|
286
|
+
Seamless integration with Strapi's built-in Media Library:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
// Custom MediaLibAdapter bridges Editor.js and Strapi
|
|
290
|
+
class MediaLibAdapter {
|
|
291
|
+
static get toolbox() {
|
|
292
|
+
return {
|
|
293
|
+
title: 'Media Library',
|
|
294
|
+
icon: '<svg>...</svg>'
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
render() {
|
|
299
|
+
const button = document.createElement('button');
|
|
300
|
+
button.textContent = 'Choose from Media Library';
|
|
301
|
+
button.onclick = () => {
|
|
302
|
+
// Opens Strapi's Media Library modal
|
|
303
|
+
this.config.mediaLibToggleFunc();
|
|
304
|
+
};
|
|
305
|
+
return button;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
save(blockContent) {
|
|
309
|
+
return {
|
|
310
|
+
url: blockContent.url,
|
|
311
|
+
caption: blockContent.caption,
|
|
312
|
+
alt: blockContent.alt
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### 5. AI-Powered Features (Premium)
|
|
319
|
+
|
|
320
|
+
Built-in AI assistant for content enhancement:
|
|
321
|
+
|
|
322
|
+
**Grammar Check:**
|
|
323
|
+
```javascript
|
|
324
|
+
// Automatically fix grammar and spelling
|
|
325
|
+
aiAssistant.check(text) → corrected text
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Style Improvement:**
|
|
329
|
+
```javascript
|
|
330
|
+
// Make text more professional, casual, or friendly
|
|
331
|
+
aiAssistant.rewrite(text, style: 'professional') → improved text
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Content Generation:**
|
|
335
|
+
```javascript
|
|
336
|
+
// Generate content based on prompts
|
|
337
|
+
aiAssistant.generate(prompt) → generated content
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Installation & Setup
|
|
343
|
+
|
|
344
|
+
### 1. Install Package
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
npm install magic-editor-x
|
|
348
|
+
# or
|
|
349
|
+
yarn add magic-editor-x
|
|
350
|
+
# or
|
|
351
|
+
pnpm add magic-editor-x
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 2. Enable Plugin
|
|
355
|
+
|
|
356
|
+
Create or update `config/plugins.ts` (or `.js`):
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
export default () => ({
|
|
360
|
+
'magic-editor-x': {
|
|
361
|
+
enabled: true,
|
|
362
|
+
config: {
|
|
363
|
+
// Editor Configuration
|
|
364
|
+
enabledTools: [
|
|
365
|
+
'header', 'paragraph', 'list', 'checklist', 'quote',
|
|
366
|
+
'warning', 'code', 'delimiter', 'table', 'embed',
|
|
367
|
+
'raw', 'image', 'mediaLib', 'linkTool'
|
|
368
|
+
],
|
|
369
|
+
|
|
370
|
+
// Upload Limits
|
|
371
|
+
maxImageSize: 10 * 1024 * 1024, // 10MB
|
|
372
|
+
allowedImageTypes: [
|
|
373
|
+
'image/jpeg', 'image/png', 'image/gif',
|
|
374
|
+
'image/webp', 'image/svg+xml'
|
|
375
|
+
],
|
|
376
|
+
|
|
377
|
+
// Link Previews
|
|
378
|
+
linkPreviewTimeout: 10000, // 10 seconds
|
|
379
|
+
|
|
380
|
+
// Real-Time Collaboration
|
|
381
|
+
collaboration: {
|
|
382
|
+
enabled: true,
|
|
383
|
+
sessionTTL: 2 * 60 * 1000, // 2 minutes
|
|
384
|
+
wsPath: '/magic-editor-x/realtime',
|
|
385
|
+
wsUrl: null, // Auto-detect or set custom URL
|
|
386
|
+
allowedOrigins: ['http://localhost:1337'],
|
|
387
|
+
allowedAdminRoles: ['strapi-super-admin'],
|
|
388
|
+
allowedAdminUserIds: [],
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### 3. Content Security Policy (Optional)
|
|
396
|
+
|
|
397
|
+
For link previews with external images, update `config/middlewares.ts`:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
export default [
|
|
401
|
+
'strapi::logger',
|
|
402
|
+
'strapi::errors',
|
|
403
|
+
{
|
|
404
|
+
name: 'strapi::security',
|
|
405
|
+
config: {
|
|
406
|
+
contentSecurityPolicy: {
|
|
407
|
+
useDefaults: true,
|
|
408
|
+
directives: {
|
|
409
|
+
'connect-src': ["'self'", 'https:'],
|
|
410
|
+
'img-src': [
|
|
411
|
+
"'self'",
|
|
412
|
+
'data:',
|
|
413
|
+
'blob:',
|
|
414
|
+
'market-assets.strapi.io',
|
|
415
|
+
'*', // Allow external images for link previews
|
|
416
|
+
],
|
|
417
|
+
'media-src': [
|
|
418
|
+
"'self'",
|
|
419
|
+
'data:',
|
|
420
|
+
'blob:',
|
|
421
|
+
'market-assets.strapi.io',
|
|
422
|
+
],
|
|
423
|
+
upgradeInsecureRequests: null,
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
'strapi::cors',
|
|
429
|
+
'strapi::poweredBy',
|
|
430
|
+
'strapi::query',
|
|
431
|
+
'strapi::body',
|
|
432
|
+
'strapi::session',
|
|
433
|
+
'strapi::favicon',
|
|
434
|
+
'strapi::public',
|
|
435
|
+
];
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### 4. Build & Start
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# Build admin panel with plugin
|
|
442
|
+
npm run build
|
|
443
|
+
|
|
444
|
+
# Start in development mode
|
|
445
|
+
npm run develop
|
|
446
|
+
|
|
447
|
+
# Or in production
|
|
448
|
+
npm run start
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### 5. Add Field to Content Type
|
|
452
|
+
|
|
453
|
+
1. Navigate to **Content-Type Builder**
|
|
454
|
+
2. Select a content type or create a new one
|
|
455
|
+
3. Click **Add another field**
|
|
456
|
+
4. Go to the **Custom** tab
|
|
457
|
+
5. Select **Magic Editor X**
|
|
458
|
+
6. Configure field name and options
|
|
459
|
+
7. Click **Finish** and save the content type
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Configuration Reference
|
|
464
|
+
|
|
465
|
+
### Plugin Configuration
|
|
466
|
+
|
|
467
|
+
All options for `config/plugins.ts`:
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
interface MagicEditorXConfig {
|
|
471
|
+
// Tool Selection
|
|
472
|
+
enabledTools?: string[]; // Default: all tools
|
|
473
|
+
|
|
474
|
+
// Upload Settings
|
|
475
|
+
maxImageSize?: number; // Bytes, default: 10MB
|
|
476
|
+
allowedImageTypes?: string[]; // MIME types
|
|
477
|
+
|
|
478
|
+
// Link Preview Settings
|
|
479
|
+
linkPreviewTimeout?: number; // Milliseconds, default: 10000
|
|
480
|
+
|
|
481
|
+
// Collaboration Settings
|
|
482
|
+
collaboration?: {
|
|
483
|
+
enabled: boolean; // Default: true
|
|
484
|
+
sessionTTL: number; // Milliseconds, default: 120000 (2 min)
|
|
485
|
+
wsPath: string; // WebSocket path, default: '/magic-editor-x/realtime'
|
|
486
|
+
wsUrl: string | null; // Custom WebSocket URL (for proxies)
|
|
487
|
+
allowedOrigins: string[]; // CORS origins
|
|
488
|
+
allowedAdminRoles: string[]; // Roles that can collaborate
|
|
489
|
+
allowedAdminUserIds: number[]; // Specific user IDs
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Field Options
|
|
495
|
+
|
|
496
|
+
When adding the field in Content-Type Builder:
|
|
497
|
+
|
|
498
|
+
**Base Settings:**
|
|
499
|
+
- **Placeholder** - Placeholder text (default: "Start writing or press Tab...")
|
|
500
|
+
- **Required** - Make field mandatory
|
|
501
|
+
- **Private** - Exclude from API responses
|
|
502
|
+
|
|
503
|
+
**Advanced Settings:**
|
|
504
|
+
- **Minimum Height** - Editor height in pixels (default: 300)
|
|
505
|
+
- **Max Length** - Maximum characters (optional)
|
|
506
|
+
- **Min Length** - Minimum characters (optional)
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## API Reference
|
|
511
|
+
|
|
512
|
+
### Public API Endpoints
|
|
513
|
+
|
|
514
|
+
These endpoints are available for content-api usage:
|
|
515
|
+
|
|
516
|
+
#### Fetch Link Metadata
|
|
517
|
+
|
|
518
|
+
```http
|
|
519
|
+
GET /api/magic-editor-x/link?url=https://example.com
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Response:**
|
|
523
|
+
```json
|
|
524
|
+
{
|
|
525
|
+
"success": 1,
|
|
526
|
+
"meta": {
|
|
527
|
+
"title": "Example Domain",
|
|
528
|
+
"description": "Example domain for documentation",
|
|
529
|
+
"image": {
|
|
530
|
+
"url": "https://example.com/og-image.jpg"
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
#### Upload Image by File
|
|
537
|
+
|
|
538
|
+
```http
|
|
539
|
+
POST /api/magic-editor-x/image/byFile
|
|
540
|
+
Content-Type: multipart/form-data
|
|
541
|
+
|
|
542
|
+
files.image: <file>
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
**Response:**
|
|
546
|
+
```json
|
|
547
|
+
{
|
|
548
|
+
"success": 1,
|
|
549
|
+
"file": {
|
|
550
|
+
"url": "https://cdn.example.com/image.jpg",
|
|
551
|
+
"name": "image.jpg",
|
|
552
|
+
"size": 123456
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
#### Upload Image by URL
|
|
558
|
+
|
|
559
|
+
```http
|
|
560
|
+
POST /api/magic-editor-x/image/byUrl
|
|
561
|
+
Content-Type: application/json
|
|
562
|
+
|
|
563
|
+
{
|
|
564
|
+
"url": "https://example.com/image.jpg"
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**Response:**
|
|
569
|
+
```json
|
|
570
|
+
{
|
|
571
|
+
"success": 1,
|
|
572
|
+
"file": {
|
|
573
|
+
"url": "https://cdn.example.com/image.jpg",
|
|
574
|
+
"name": "image.jpg",
|
|
575
|
+
"size": 123456
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Admin API Endpoints
|
|
581
|
+
|
|
582
|
+
These endpoints require authentication and admin permissions:
|
|
583
|
+
|
|
584
|
+
#### License Management
|
|
585
|
+
|
|
586
|
+
```http
|
|
587
|
+
GET /magic-editor-x/license/status
|
|
588
|
+
GET /magic-editor-x/license/limits
|
|
589
|
+
POST /magic-editor-x/license/auto-create
|
|
590
|
+
POST /magic-editor-x/license/store-key
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
#### Collaboration Permissions
|
|
594
|
+
|
|
595
|
+
```http
|
|
596
|
+
GET /magic-editor-x/collaboration/permissions
|
|
597
|
+
POST /magic-editor-x/collaboration/permissions
|
|
598
|
+
PUT /magic-editor-x/collaboration/permissions/:id
|
|
599
|
+
DELETE /magic-editor-x/collaboration/permissions/:id
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
#### Real-Time Sessions
|
|
603
|
+
|
|
604
|
+
```http
|
|
605
|
+
POST /magic-editor-x/realtime/session-token
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**Request:**
|
|
609
|
+
```json
|
|
610
|
+
{
|
|
611
|
+
"roomId": "api::article.article|abc123|content",
|
|
612
|
+
"fieldName": "content"
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Response:**
|
|
617
|
+
```json
|
|
618
|
+
{
|
|
619
|
+
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
620
|
+
"expiresAt": 1699999999999
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
## Collaboration System
|
|
627
|
+
|
|
628
|
+
### Permission Model
|
|
629
|
+
|
|
630
|
+
Magic Editor X uses a flexible role-based permission system:
|
|
631
|
+
|
|
632
|
+
**Roles:**
|
|
633
|
+
- **Owner** - Full control (edit, delete, manage permissions)
|
|
634
|
+
- **Editor** - Can edit content
|
|
635
|
+
- **Viewer** - Read-only access
|
|
636
|
+
|
|
637
|
+
**Permission Scope:**
|
|
638
|
+
- Per content type (e.g., `api::article.article`)
|
|
639
|
+
- Per user or role
|
|
640
|
+
- Inherits from Strapi's built-in RBAC
|
|
641
|
+
|
|
642
|
+
### Managing Collaborators
|
|
643
|
+
|
|
644
|
+
Navigate to **Plugins > Magic Editor X > Collaboration** to:
|
|
645
|
+
|
|
646
|
+
1. **Add Collaborator**
|
|
647
|
+
- Select user
|
|
648
|
+
- Choose content type
|
|
649
|
+
- Assign role (Viewer, Editor, Owner)
|
|
650
|
+
|
|
651
|
+
2. **Update Permission**
|
|
652
|
+
- Change role
|
|
653
|
+
- Modify content type access
|
|
654
|
+
|
|
655
|
+
3. **Remove Collaborator**
|
|
656
|
+
- Revoke access
|
|
657
|
+
|
|
658
|
+
### Collaboration Limits
|
|
659
|
+
|
|
660
|
+
Limits are enforced based on your license:
|
|
661
|
+
|
|
662
|
+
| License | Concurrent Collaborators |
|
|
663
|
+
|---------|-------------------------|
|
|
664
|
+
| FREE | 2 |
|
|
665
|
+
| PREMIUM | 10 |
|
|
666
|
+
| ADVANCED | Unlimited |
|
|
667
|
+
|
|
668
|
+
**Note:** The editor itself is completely free. You only pay for extended collaboration features.
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## Pricing
|
|
673
|
+
|
|
674
|
+
Magic Editor X follows a freemium model:
|
|
675
|
+
|
|
676
|
+
| Feature | FREE | PREMIUM | ADVANCED |
|
|
677
|
+
|---------|------|---------|----------|
|
|
678
|
+
| **Price** | $0 | $9.90/mo | $24.90/mo |
|
|
679
|
+
| Full Editor | Yes | Yes | Yes |
|
|
680
|
+
| All 25+ Tools | Yes | Yes | Yes |
|
|
681
|
+
| Real-Time Sync | Yes | Yes | Yes |
|
|
682
|
+
| Collaborators | 2 | 10 | Unlimited |
|
|
683
|
+
| Version History | - | Yes | Yes |
|
|
684
|
+
| AI Assistant | - | Pay-per-use | Included |
|
|
685
|
+
| Priority Support | - | Email | Email + Chat |
|
|
686
|
+
| Custom Blocks | - | - | Yes |
|
|
687
|
+
|
|
688
|
+
**Get started:** https://store.magicdx.dev/
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
692
|
+
## Development & Extension
|
|
693
|
+
|
|
694
|
+
### Custom Block Types
|
|
695
|
+
|
|
696
|
+
Create your own block tools:
|
|
697
|
+
|
|
698
|
+
```javascript
|
|
699
|
+
// custom-tools/MyCustomTool.js
|
|
700
|
+
export default class MyCustomTool {
|
|
701
|
+
static get toolbox() {
|
|
702
|
+
return {
|
|
703
|
+
title: 'My Tool',
|
|
704
|
+
icon: '<svg>...</svg>'
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
constructor({ data, api, config }) {
|
|
709
|
+
this.data = data;
|
|
710
|
+
this.api = api;
|
|
711
|
+
this.config = config;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
render() {
|
|
715
|
+
const wrapper = document.createElement('div');
|
|
716
|
+
wrapper.classList.add('my-custom-tool');
|
|
717
|
+
// Add your UI
|
|
718
|
+
return wrapper;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
save(blockContent) {
|
|
722
|
+
return {
|
|
723
|
+
// Your data structure
|
|
724
|
+
text: blockContent.querySelector('input').value
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
static get sanitize() {
|
|
729
|
+
return {
|
|
730
|
+
text: {} // Sanitizer config
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
**Register in `config/tools.js`:**
|
|
737
|
+
|
|
738
|
+
```javascript
|
|
739
|
+
import MyCustomTool from './custom-tools/MyCustomTool';
|
|
740
|
+
|
|
741
|
+
export const getTools = ({ mediaLibToggleFunc, pluginId }) => {
|
|
742
|
+
return {
|
|
743
|
+
// ... existing tools
|
|
744
|
+
myCustomTool: {
|
|
745
|
+
class: MyCustomTool,
|
|
746
|
+
config: {
|
|
747
|
+
// Your config
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
};
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### Extending Collaboration
|
|
755
|
+
|
|
756
|
+
Hook into collaboration events:
|
|
757
|
+
|
|
758
|
+
```javascript
|
|
759
|
+
// In your component
|
|
760
|
+
const { awareness } = useMagicCollaboration({
|
|
761
|
+
enabled: true,
|
|
762
|
+
roomId: 'my-room',
|
|
763
|
+
onRemoteUpdate: () => {
|
|
764
|
+
console.log('Remote update received');
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
// Listen to presence changes
|
|
769
|
+
awareness.on('change', ({ added, updated, removed }) => {
|
|
770
|
+
console.log('Users joined:', added);
|
|
771
|
+
console.log('Users updated:', updated);
|
|
772
|
+
console.log('Users left:', removed);
|
|
773
|
+
});
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
---
|
|
777
|
+
|
|
778
|
+
## Troubleshooting
|
|
779
|
+
|
|
780
|
+
### Common Issues
|
|
781
|
+
|
|
782
|
+
**1. Editor Not Loading**
|
|
783
|
+
|
|
784
|
+
Check browser console for errors. Common causes:
|
|
785
|
+
- Plugin not enabled in `config/plugins.ts`
|
|
786
|
+
- Build not run after installation (`npm run build`)
|
|
787
|
+
- CSP blocking external resources
|
|
788
|
+
|
|
789
|
+
**2. Image Upload Failing**
|
|
790
|
+
|
|
791
|
+
Verify:
|
|
792
|
+
- Strapi Upload plugin is enabled
|
|
793
|
+
- File size within limits (default 10MB)
|
|
794
|
+
- File type is allowed
|
|
795
|
+
- User has upload permissions
|
|
796
|
+
|
|
797
|
+
**3. Collaboration Not Working**
|
|
798
|
+
|
|
799
|
+
Debug checklist:
|
|
800
|
+
- WebSocket connection established (check Network tab)
|
|
801
|
+
- Session token valid (check `/realtime/session-token` response)
|
|
802
|
+
- User has collaboration permissions
|
|
803
|
+
- Firewall/proxy allows WebSocket connections
|
|
804
|
+
|
|
805
|
+
**4. Link Previews Not Showing**
|
|
806
|
+
|
|
807
|
+
Ensure:
|
|
808
|
+
- CSP allows `img-src: '*'` in middlewares config
|
|
809
|
+
- Target site returns OpenGraph metadata
|
|
810
|
+
- Link preview timeout not too short (default: 10s)
|
|
811
|
+
|
|
812
|
+
### Enable Debug Logging
|
|
813
|
+
|
|
814
|
+
Add to `config/plugins.ts`:
|
|
815
|
+
|
|
816
|
+
```typescript
|
|
817
|
+
{
|
|
818
|
+
'magic-editor-x': {
|
|
819
|
+
config: {
|
|
820
|
+
debug: true // Enable verbose logging
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
## Roadmap
|
|
829
|
+
|
|
830
|
+
- **Version History** - Track all content changes with restore capability
|
|
831
|
+
- **Custom Blocks API** - Simplified API for creating custom tools
|
|
832
|
+
- **Advanced AI** - Content suggestions, auto-completion, tone analysis
|
|
833
|
+
- **Comments & Annotations** - Inline comments for editorial workflow
|
|
834
|
+
- **Offline Mode** - Full offline editing with sync on reconnect
|
|
835
|
+
- **Import/Export** - Markdown, HTML, DOCX conversion
|
|
836
|
+
- **Templates** - Pre-built content templates
|
|
837
|
+
|
|
838
|
+
---
|
|
839
|
+
|
|
840
|
+
## Resources
|
|
841
|
+
|
|
842
|
+
- **Documentation:** https://docs.magicdx.dev/
|
|
843
|
+
- **Store:** https://store.magicdx.dev/
|
|
844
|
+
- **GitHub:** https://github.com/Schero94/magic-editor-x
|
|
845
|
+
- **Strapi Custom Fields:** https://docs.strapi.io/cms/features/custom-fields
|
|
846
|
+
- **Editor.js:** https://editorjs.io/
|
|
847
|
+
- **Y.js:** https://docs.yjs.dev/
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## Contributing
|
|
852
|
+
|
|
853
|
+
Contributions are welcome! Please:
|
|
854
|
+
|
|
855
|
+
1. Fork the repository
|
|
856
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
857
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
858
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
859
|
+
5. Open a Pull Request
|
|
860
|
+
|
|
861
|
+
**Development Setup:**
|
|
862
|
+
|
|
863
|
+
```bash
|
|
864
|
+
git clone https://github.com/Schero94/magic-editor-x.git
|
|
865
|
+
cd magic-editor-x
|
|
866
|
+
npm install
|
|
867
|
+
npm run watch:link
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
---
|
|
871
|
+
|
|
872
|
+
## License
|
|
873
|
+
|
|
874
|
+
MIT License - See [LICENSE](LICENSE) for details
|
|
875
|
+
|
|
876
|
+
Copyright (c) 2024 Schero D.
|
|
877
|
+
|
|
878
|
+
---
|
|
879
|
+
|
|
880
|
+
## Support
|
|
881
|
+
|
|
882
|
+
- **Email:** support@magicdx.dev
|
|
883
|
+
- **Issues:** https://github.com/Schero94/magic-editor-x/issues
|
|
884
|
+
- **Discord:** https://discord.gg/magicdx
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
**Built with dedication by [Schero D.](https://github.com/Schero94)**
|
|
889
|
+
|
|
890
|
+
Part of the MagicDX Plugin Suite for Strapi
|