@regal-text-editor/plugin-table 0.1.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 +883 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +462 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,883 @@
|
|
|
1
|
+
# 👑 Regal Text Editor
|
|
2
|
+
|
|
3
|
+
### A rich text editing engine built from scratch for the modern web.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Headless</strong> ·
|
|
7
|
+
<strong>Extensible</strong> ·
|
|
8
|
+
<strong>Framework-Independent</strong> ·
|
|
9
|
+
<strong>React-Ready</strong> ·
|
|
10
|
+
<strong>RTL-Aware</strong> ·
|
|
11
|
+
<strong>Security-Conscious</strong>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://regal-text-editor.vercel.app/">Live Demo</a>
|
|
16
|
+
·
|
|
17
|
+
<a href="https://www.npmjs.com/">NPM</a>
|
|
18
|
+
·
|
|
19
|
+
<a href="https://github.com/mohammad-mirzaie-gh/regal-text-editor">GitHub</a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ⚡ What is Regal?
|
|
25
|
+
|
|
26
|
+
**Regal Text Editor** is a modular rich text editing engine and toolkit for the web, built completely from scratch.
|
|
27
|
+
|
|
28
|
+
It provides a framework-independent editing core, browser DOM adapter, React bindings, customizable UI components, and a real plugin architecture — distributed as **11 independently installable npm packages**.
|
|
29
|
+
|
|
30
|
+
Regal is **not a wrapper around ProseMirror, Slate, Lexical, TipTap, or CKEditor**.
|
|
31
|
+
|
|
32
|
+
The document model, schema system, transactions, selections, commands, history, serialization, parsing, and browser adapter are all implemented inside this project.
|
|
33
|
+
|
|
34
|
+
> **Rich text, without the black box.**
|
|
35
|
+
|
|
36
|
+
### 🚀 Try it live
|
|
37
|
+
|
|
38
|
+
**[Open the Regal Text Editor Demo →](https://regal-text-editor.vercel.app/)**
|
|
39
|
+
|
|
40
|
+
The live demo runs the actual published-style React/UI stack in the browser, including the full plugin set.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## ✨ Why Regal?
|
|
45
|
+
|
|
46
|
+
Rich text editors often become tightly coupled systems where the editor engine, UI, framework, and features are difficult to separate.
|
|
47
|
+
|
|
48
|
+
Regal takes a different approach.
|
|
49
|
+
|
|
50
|
+
### 🧠 Built from scratch
|
|
51
|
+
|
|
52
|
+
No dependency on an existing rich-text editing engine.
|
|
53
|
+
|
|
54
|
+
Regal owns its:
|
|
55
|
+
|
|
56
|
+
* Document model
|
|
57
|
+
* Schema system
|
|
58
|
+
* Transactions & operations
|
|
59
|
+
* Selection model
|
|
60
|
+
* Position tracking
|
|
61
|
+
* Command system
|
|
62
|
+
* Normalization
|
|
63
|
+
* Undo / redo history
|
|
64
|
+
* HTML parsing
|
|
65
|
+
* HTML serialization
|
|
66
|
+
* Markdown serialization
|
|
67
|
+
* JSON serialization
|
|
68
|
+
* Plain-text serialization
|
|
69
|
+
* Browser DOM adapter
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### 🎛️ Headless by default
|
|
74
|
+
|
|
75
|
+
The core engine does not depend on React or the DOM.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { Editor, createBaseSchema } from "@regal-text-editor/core";
|
|
79
|
+
|
|
80
|
+
const editor = new Editor({
|
|
81
|
+
schema: createBaseSchema(),
|
|
82
|
+
plugins: [],
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This makes the core suitable for:
|
|
87
|
+
|
|
88
|
+
* Server-side document processing
|
|
89
|
+
* Document validation
|
|
90
|
+
* Format conversion
|
|
91
|
+
* Custom editors
|
|
92
|
+
* Custom rendering environments
|
|
93
|
+
* Framework-independent applications
|
|
94
|
+
|
|
95
|
+
You can use the engine without rendering an editor at all.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### 🧩 A real plugin architecture
|
|
100
|
+
|
|
101
|
+
Regal does not treat extensibility as a giant configuration object.
|
|
102
|
+
|
|
103
|
+
Plugins can register:
|
|
104
|
+
|
|
105
|
+
* Nodes
|
|
106
|
+
* Marks
|
|
107
|
+
* Commands
|
|
108
|
+
* Keyboard shortcuts
|
|
109
|
+
* HTML parsers
|
|
110
|
+
* HTML serializers
|
|
111
|
+
* Markdown serializers
|
|
112
|
+
* Editor behavior
|
|
113
|
+
|
|
114
|
+
More importantly, **the built-in features use the same plugin architecture available to consumers**.
|
|
115
|
+
|
|
116
|
+
Bold, headings, lists, links, images, code blocks and history are not privileged features hidden inside the engine.
|
|
117
|
+
|
|
118
|
+
They're plugins.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### 🎨 Theme it without CSS-in-JS
|
|
123
|
+
|
|
124
|
+
The UI is built around CSS custom properties.
|
|
125
|
+
|
|
126
|
+
Every major visual token — including colors, spacing, radii and shadows — can be overridden by the host application.
|
|
127
|
+
|
|
128
|
+
No CSS-in-JS.
|
|
129
|
+
|
|
130
|
+
No build-time theme generator.
|
|
131
|
+
|
|
132
|
+
No need to modify package source.
|
|
133
|
+
|
|
134
|
+
Two built-in themes are included:
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<div data-rte-theme="notion">
|
|
138
|
+
...
|
|
139
|
+
</div>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```html
|
|
143
|
+
<div data-rte-theme="midnight">
|
|
144
|
+
...
|
|
145
|
+
</div>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Or create your own theme by overriding the same CSS variables.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### ↔️ RTL-aware by construction
|
|
153
|
+
|
|
154
|
+
RTL is not implemented as a separate visual mode.
|
|
155
|
+
|
|
156
|
+
Regal uses logical CSS properties and logical alignment values:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
setTextAlign("start");
|
|
160
|
+
setTextAlign("center");
|
|
161
|
+
setTextAlign("end");
|
|
162
|
+
setTextAlign("justify");
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
rather than hard-coded physical directions such as `left` and `right`.
|
|
166
|
+
|
|
167
|
+
This allows the editor UI and text alignment to behave naturally under:
|
|
168
|
+
|
|
169
|
+
```html
|
|
170
|
+
<div dir="rtl">
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
> Dedicated bidi-cursor testing is still planned.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### 🔐 Security-conscious by default
|
|
178
|
+
|
|
179
|
+
Rich text editors process untrusted HTML and URLs.
|
|
180
|
+
|
|
181
|
+
Regal treats sanitization as an editing boundary concern.
|
|
182
|
+
|
|
183
|
+
URLs are validated through an allow-list that accepts:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
http:
|
|
187
|
+
https:
|
|
188
|
+
mailto:
|
|
189
|
+
tel:
|
|
190
|
+
relative URLs
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
while rejecting dangerous schemes such as:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
javascript:
|
|
197
|
+
data:
|
|
198
|
+
vbscript:
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Sanitization happens both when importing HTML **and when URLs enter through programmatic commands**.
|
|
202
|
+
|
|
203
|
+
HTML parsing also removes dangerous elements and event-handler attributes.
|
|
204
|
+
|
|
205
|
+
Text content is escaped during HTML serialization.
|
|
206
|
+
|
|
207
|
+
The editor never directly injects arbitrary external HTML into the DOM.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 🏗️ Architecture
|
|
212
|
+
|
|
213
|
+
Regal is organized into five conceptual layers.
|
|
214
|
+
|
|
215
|
+
```text
|
|
216
|
+
┌──────────────────────────┐
|
|
217
|
+
│ Application │
|
|
218
|
+
└────────────┬─────────────┘
|
|
219
|
+
│
|
|
220
|
+
┌────────────▼─────────────┐
|
|
221
|
+
│ @regal-text-editor │
|
|
222
|
+
│ /ui │
|
|
223
|
+
│ Toolbar · Popovers · UI │
|
|
224
|
+
└────────────┬─────────────┘
|
|
225
|
+
│
|
|
226
|
+
┌────────────▼─────────────┐
|
|
227
|
+
│ @regal-text-editor │
|
|
228
|
+
│ /react │
|
|
229
|
+
│ Hooks · Components │
|
|
230
|
+
└────────────┬─────────────┘
|
|
231
|
+
│
|
|
232
|
+
┌────────────────────┴────────────────────┐
|
|
233
|
+
│ │
|
|
234
|
+
┌────────▼─────────┐ ┌─────────▼────────┐
|
|
235
|
+
│ Plugins │ │ Browser │
|
|
236
|
+
│ │ │ Adapter │
|
|
237
|
+
│ Marks · Lists │ │ DOM · Selection │
|
|
238
|
+
│ Links · Images │ │ Keyboard · IME │
|
|
239
|
+
│ Code · History │ │ Clipboard · DnD │
|
|
240
|
+
└────────┬─────────┘ └─────────┬────────┘
|
|
241
|
+
│ │
|
|
242
|
+
└──────────────────┬──────────────────────┘
|
|
243
|
+
│
|
|
244
|
+
┌──────────▼───────────┐
|
|
245
|
+
│ @regal- │
|
|
246
|
+
│ text-editor/core │
|
|
247
|
+
│ │
|
|
248
|
+
│ Document Model │
|
|
249
|
+
│ Schema │
|
|
250
|
+
│ Transactions │
|
|
251
|
+
│ Selection │
|
|
252
|
+
│ Commands │
|
|
253
|
+
│ History │
|
|
254
|
+
│ Serialization │
|
|
255
|
+
│ Parsing │
|
|
256
|
+
│ Plugin System │
|
|
257
|
+
└──────────────────────┘
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Dependency philosophy
|
|
261
|
+
|
|
262
|
+
```text
|
|
263
|
+
core
|
|
264
|
+
↓
|
|
265
|
+
browser
|
|
266
|
+
↓
|
|
267
|
+
react
|
|
268
|
+
↓
|
|
269
|
+
ui
|
|
270
|
+
|
|
271
|
+
plugins → core
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The core never imports React.
|
|
275
|
+
|
|
276
|
+
The browser adapter never imports React.
|
|
277
|
+
|
|
278
|
+
The UI communicates with the editor through the public React API rather than reaching into engine internals.
|
|
279
|
+
|
|
280
|
+
This means you can replace individual layers without rewriting the entire editor.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
# 📦 Package Ecosystem
|
|
285
|
+
|
|
286
|
+
Regal is currently distributed as **11 independent packages** under the `@regal-text-editor/*` scope.
|
|
287
|
+
|
|
288
|
+
| Package | Description |
|
|
289
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
290
|
+
| `@regal-text-editor/core` | Document model, schema, transactions, selection, commands, history, plugins, serialization, parsing & sanitization |
|
|
291
|
+
| `@regal-text-editor/browser` | DOM rendering, selection mapping, keyboard, IME, clipboard & drag/drop |
|
|
292
|
+
| `@regal-text-editor/react` | React bindings, `<RichTextEditor />` and editor hooks |
|
|
293
|
+
| `@regal-text-editor/ui` | Toolbar, bubble toolbar, popovers, icons & theming |
|
|
294
|
+
| `@regal-text-editor/plugin-basic-marks` | Bold, italic, underline, strikethrough & inline code |
|
|
295
|
+
| `@regal-text-editor/plugin-basic-blocks` | Paragraphs, headings, blockquotes, HR & alignment |
|
|
296
|
+
| `@regal-text-editor/plugin-lists` | Bullet, ordered & task lists, nesting and indentation |
|
|
297
|
+
| `@regal-text-editor/plugin-link` | Link marks and URL sanitization |
|
|
298
|
+
| `@regal-text-editor/plugin-image` | Block-level images |
|
|
299
|
+
| `@regal-text-editor/plugin-code-block` | Preformatted code blocks |
|
|
300
|
+
| `@regal-text-editor/plugin-history` | Undo / redo |
|
|
301
|
+
|
|
302
|
+
All packages are currently published as **ESM**, include TypeScript declarations and source maps, and are designed for tree-shaking.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
# 🧰 Features
|
|
307
|
+
|
|
308
|
+
## ✍️ Text Formatting
|
|
309
|
+
|
|
310
|
+
* Bold
|
|
311
|
+
* Italic
|
|
312
|
+
* Underline
|
|
313
|
+
* Strikethrough
|
|
314
|
+
* Inline code
|
|
315
|
+
* Links
|
|
316
|
+
* Stored marks
|
|
317
|
+
|
|
318
|
+
### Keyboard shortcuts
|
|
319
|
+
|
|
320
|
+
| Action | Shortcut |
|
|
321
|
+
| ------------- | ----------------- |
|
|
322
|
+
| Bold | `Mod + B` |
|
|
323
|
+
| Italic | `Mod + I` |
|
|
324
|
+
| Underline | `Mod + U` |
|
|
325
|
+
| Strikethrough | `Mod + Shift + X` |
|
|
326
|
+
| Inline Code | `Mod + E` |
|
|
327
|
+
|
|
328
|
+
`Mod` resolves to `⌘` on macOS and `Ctrl` elsewhere.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 🧱 Block Content
|
|
333
|
+
|
|
334
|
+
* Paragraphs
|
|
335
|
+
* H1–H6
|
|
336
|
+
* Blockquotes
|
|
337
|
+
* Horizontal rules
|
|
338
|
+
* Logical text alignment
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
setTextAlign("start");
|
|
342
|
+
setTextAlign("center");
|
|
343
|
+
setTextAlign("end");
|
|
344
|
+
setTextAlign("justify");
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## 📋 Lists
|
|
350
|
+
|
|
351
|
+
* Bullet lists
|
|
352
|
+
* Ordered lists
|
|
353
|
+
* Task lists
|
|
354
|
+
* Nested lists
|
|
355
|
+
* Indent / outdent
|
|
356
|
+
* Custom ordered-list start values
|
|
357
|
+
|
|
358
|
+
Keyboard behavior:
|
|
359
|
+
|
|
360
|
+
```text
|
|
361
|
+
Enter → Split list item
|
|
362
|
+
Enter + empty → Exit list
|
|
363
|
+
Tab → Indent
|
|
364
|
+
Shift + Tab → Outdent
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## 🔗 Links
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
setLink(href);
|
|
373
|
+
unsetLink();
|
|
374
|
+
getActiveLinkHref(editor);
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Links are sanitized at the command boundary and during HTML parsing.
|
|
378
|
+
|
|
379
|
+
Exported links include:
|
|
380
|
+
|
|
381
|
+
```html
|
|
382
|
+
rel="noopener noreferrer"
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 🖼️ Images
|
|
388
|
+
|
|
389
|
+
Images are currently represented as **block-level nodes**.
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
insertImage(src, alt?);
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Images are URL-based and use the same URL sanitization rules as links.
|
|
396
|
+
|
|
397
|
+
Export formats:
|
|
398
|
+
|
|
399
|
+
* HTML
|
|
400
|
+
* Markdown
|
|
401
|
+
|
|
402
|
+
A built-in upload service is intentionally not included. Applications can connect their own upload infrastructure.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## 💻 Code Blocks
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
toggleCodeBlock();
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Shortcut:
|
|
413
|
+
|
|
414
|
+
```text
|
|
415
|
+
Mod + Alt + C
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Code blocks:
|
|
419
|
+
|
|
420
|
+
* Preserve whitespace
|
|
421
|
+
* Preserve line breaks
|
|
422
|
+
* Disable text marks
|
|
423
|
+
* Support a `language` attribute
|
|
424
|
+
* Import `<pre>` / `<pre><code>`
|
|
425
|
+
* Treat `Enter` as a literal line break
|
|
426
|
+
|
|
427
|
+
Syntax highlighting is not currently included.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## ↩️ Undo / Redo
|
|
432
|
+
|
|
433
|
+
```text
|
|
434
|
+
Mod + Z
|
|
435
|
+
Mod + Shift + Z
|
|
436
|
+
Mod + Y
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
History currently uses snapshots backed by Regal's persistent, structurally-shared document tree.
|
|
440
|
+
|
|
441
|
+
Rapid typing can be coalesced into a single undo step.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## 🔎 Find & Replace
|
|
446
|
+
|
|
447
|
+
Find & Replace is implemented directly in the core:
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
findMatches();
|
|
451
|
+
replaceMatch();
|
|
452
|
+
replaceAllMatches();
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
The UI is available through:
|
|
456
|
+
|
|
457
|
+
```tsx
|
|
458
|
+
<FindReplacePanel />
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Search is case-insensitive by default and does not cross block boundaries.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
# ⚛️ React
|
|
466
|
+
|
|
467
|
+
Regal provides first-class React bindings without coupling the engine itself to React.
|
|
468
|
+
|
|
469
|
+
```tsx
|
|
470
|
+
import {
|
|
471
|
+
EditorProvider,
|
|
472
|
+
RichTextEditor,
|
|
473
|
+
useEditor,
|
|
474
|
+
} from "@regal-text-editor/react";
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Available APIs include:
|
|
478
|
+
|
|
479
|
+
```text
|
|
480
|
+
<RichTextEditor />
|
|
481
|
+
<EditorProvider />
|
|
482
|
+
useEditor()
|
|
483
|
+
useEditorSelector()
|
|
484
|
+
useCommand()
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Subscriptions are built around React's `useSyncExternalStore` model.
|
|
488
|
+
|
|
489
|
+
Both controlled and uncontrolled usage are supported.
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
# 🎛️ UI
|
|
494
|
+
|
|
495
|
+
The optional UI package provides:
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
<Toolbar />
|
|
499
|
+
<BubbleToolbar />
|
|
500
|
+
<LinkButton />
|
|
501
|
+
<ImageButton />
|
|
502
|
+
<FindReplacePanel />
|
|
503
|
+
<ToolbarButton />
|
|
504
|
+
<ToolbarSeparator />
|
|
505
|
+
<ToolbarDropdown />
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
The toolbar is data-driven:
|
|
509
|
+
|
|
510
|
+
```tsx
|
|
511
|
+
<Toolbar items={defaultToolbarItems} />
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
You can also completely replace it with your own UI.
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
# 🧪 Quality
|
|
519
|
+
|
|
520
|
+
Regal currently has:
|
|
521
|
+
|
|
522
|
+
* **462 automated tests**
|
|
523
|
+
* **35 test files**
|
|
524
|
+
* **~93% statement coverage**
|
|
525
|
+
* Dedicated tests for every package
|
|
526
|
+
* Browser adapter tests
|
|
527
|
+
* React hook tests
|
|
528
|
+
* UI component tests
|
|
529
|
+
* Keyboard interaction tests
|
|
530
|
+
* IME composition tests
|
|
531
|
+
* Clipboard tests
|
|
532
|
+
* DOM selection mapping tests
|
|
533
|
+
* Full-stack jsdom integration tests
|
|
534
|
+
|
|
535
|
+
The published packages have also been verified outside the pnpm workspace using a normal `npm install` flow.
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
# 📊 Current Status
|
|
540
|
+
|
|
541
|
+
Regal is a **working, tested foundation**, not a claim of feature parity with mature editors.
|
|
542
|
+
|
|
543
|
+
The project intentionally documents its current limitations.
|
|
544
|
+
|
|
545
|
+
### ✅ Implemented
|
|
546
|
+
|
|
547
|
+
* Document model
|
|
548
|
+
* Schema
|
|
549
|
+
* Transactions
|
|
550
|
+
* Operations
|
|
551
|
+
* Selection & position tracking
|
|
552
|
+
* Commands
|
|
553
|
+
* Plugin architecture
|
|
554
|
+
* Bold / italic / underline / strike / inline code
|
|
555
|
+
* Links
|
|
556
|
+
* Paragraphs
|
|
557
|
+
* Headings
|
|
558
|
+
* Blockquotes
|
|
559
|
+
* Horizontal rules
|
|
560
|
+
* Logical text alignment
|
|
561
|
+
* Bullet / ordered / task lists
|
|
562
|
+
* List nesting
|
|
563
|
+
* Indent / outdent
|
|
564
|
+
* Block-level images
|
|
565
|
+
* Code blocks
|
|
566
|
+
* Undo / redo
|
|
567
|
+
* Find & replace
|
|
568
|
+
* HTML import/export
|
|
569
|
+
* JSON serialization
|
|
570
|
+
* Markdown export
|
|
571
|
+
* Plain-text serialization
|
|
572
|
+
* Read-only mode
|
|
573
|
+
* Placeholder / empty state
|
|
574
|
+
* Toolbar
|
|
575
|
+
* Bubble toolbar
|
|
576
|
+
* CSS-variable theming
|
|
577
|
+
* React integration
|
|
578
|
+
|
|
579
|
+
### 🟡 Partial
|
|
580
|
+
|
|
581
|
+
* Schema content matching
|
|
582
|
+
* Snapshot-based history
|
|
583
|
+
* Clipboard compatibility
|
|
584
|
+
* Drag & drop
|
|
585
|
+
* Markdown import
|
|
586
|
+
* Accessibility auditing
|
|
587
|
+
* Dedicated RTL / bidi cursor testing
|
|
588
|
+
|
|
589
|
+
### 🚧 Planned
|
|
590
|
+
|
|
591
|
+
* Tables
|
|
592
|
+
* Mentions / bookmarks
|
|
593
|
+
* Raw HTML editing
|
|
594
|
+
* Autosave helpers
|
|
595
|
+
* Slash commands
|
|
596
|
+
* Command palette
|
|
597
|
+
* Table of contents
|
|
598
|
+
* Auto-linking
|
|
599
|
+
* Smart typography
|
|
600
|
+
* Advanced spellcheck integration
|
|
601
|
+
* UI localization
|
|
602
|
+
* Syntax highlighting
|
|
603
|
+
* Block drag & drop
|
|
604
|
+
|
|
605
|
+
---
|
|
606
|
+
|
|
607
|
+
# 📥 Installation
|
|
608
|
+
|
|
609
|
+
## React
|
|
610
|
+
|
|
611
|
+
Install the packages you need:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
npm install \
|
|
615
|
+
@regal-text-editor/core \
|
|
616
|
+
@regal-text-editor/browser \
|
|
617
|
+
@regal-text-editor/react \
|
|
618
|
+
@regal-text-editor/ui \
|
|
619
|
+
@regal-text-editor/plugin-basic-blocks \
|
|
620
|
+
@regal-text-editor/plugin-basic-marks \
|
|
621
|
+
@regal-text-editor/plugin-lists \
|
|
622
|
+
@regal-text-editor/plugin-history
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
# ⚡ Quick Start
|
|
628
|
+
|
|
629
|
+
```tsx
|
|
630
|
+
import { createBaseSchema } from "@regal-text-editor/core";
|
|
631
|
+
|
|
632
|
+
import {
|
|
633
|
+
EditorProvider,
|
|
634
|
+
RichTextEditor,
|
|
635
|
+
useEditor,
|
|
636
|
+
} from "@regal-text-editor/react";
|
|
637
|
+
|
|
638
|
+
import {
|
|
639
|
+
Toolbar,
|
|
640
|
+
defaultToolbarItems,
|
|
641
|
+
} from "@regal-text-editor/ui";
|
|
642
|
+
|
|
643
|
+
import "@regal-text-editor/ui/styles.css";
|
|
644
|
+
|
|
645
|
+
import { BasicBlocksPlugin } from "@regal-text-editor/plugin-basic-blocks";
|
|
646
|
+
import { BasicMarksPlugin } from "@regal-text-editor/plugin-basic-marks";
|
|
647
|
+
import { ListsPlugin } from "@regal-text-editor/plugin-lists";
|
|
648
|
+
import { HistoryPlugin } from "@regal-text-editor/plugin-history";
|
|
649
|
+
|
|
650
|
+
function Editor() {
|
|
651
|
+
const editor = useEditor({
|
|
652
|
+
schema: createBaseSchema(),
|
|
653
|
+
|
|
654
|
+
plugins: [
|
|
655
|
+
BasicBlocksPlugin(),
|
|
656
|
+
BasicMarksPlugin(),
|
|
657
|
+
ListsPlugin(),
|
|
658
|
+
HistoryPlugin(),
|
|
659
|
+
],
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
return (
|
|
663
|
+
<EditorProvider editor={editor}>
|
|
664
|
+
<Toolbar items={defaultToolbarItems} />
|
|
665
|
+
|
|
666
|
+
<RichTextEditor
|
|
667
|
+
editor={editor}
|
|
668
|
+
placeholder="Start writing..."
|
|
669
|
+
onChange={(doc) => {
|
|
670
|
+
console.log(doc);
|
|
671
|
+
}}
|
|
672
|
+
/>
|
|
673
|
+
</EditorProvider>
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
# 🧠 Headless Usage
|
|
681
|
+
|
|
682
|
+
You don't need React or the UI package.
|
|
683
|
+
|
|
684
|
+
```ts
|
|
685
|
+
import {
|
|
686
|
+
Editor,
|
|
687
|
+
createBaseSchema,
|
|
688
|
+
} from "@regal-text-editor/core";
|
|
689
|
+
|
|
690
|
+
import {
|
|
691
|
+
BasicMarksPlugin,
|
|
692
|
+
} from "@regal-text-editor/plugin-basic-marks";
|
|
693
|
+
|
|
694
|
+
const editor = new Editor({
|
|
695
|
+
schema: createBaseSchema(),
|
|
696
|
+
|
|
697
|
+
plugins: [
|
|
698
|
+
BasicMarksPlugin(),
|
|
699
|
+
],
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
editor.commands.execute(
|
|
703
|
+
editor,
|
|
704
|
+
"toggleBold"
|
|
705
|
+
);
|
|
706
|
+
|
|
707
|
+
console.log(editor.getHTML());
|
|
708
|
+
console.log(editor.getMarkdown());
|
|
709
|
+
console.log(editor.getJSON());
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
This is useful for server-side processing, document manipulation, validation, conversion, or custom editor implementations.
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
# 🔐 Security Model
|
|
717
|
+
|
|
718
|
+
Regal treats rich-text security as a first-class design concern.
|
|
719
|
+
|
|
720
|
+
### URL allow-list
|
|
721
|
+
|
|
722
|
+
Allowed:
|
|
723
|
+
|
|
724
|
+
```text
|
|
725
|
+
http:
|
|
726
|
+
https:
|
|
727
|
+
mailto:
|
|
728
|
+
tel:
|
|
729
|
+
relative URLs
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
Rejected:
|
|
733
|
+
|
|
734
|
+
```text
|
|
735
|
+
javascript:
|
|
736
|
+
data:
|
|
737
|
+
vbscript:
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Dangerous HTML
|
|
741
|
+
|
|
742
|
+
The parser removes dangerous elements such as:
|
|
743
|
+
|
|
744
|
+
```html
|
|
745
|
+
<script>
|
|
746
|
+
<style>
|
|
747
|
+
<iframe>
|
|
748
|
+
<object>
|
|
749
|
+
<embed>
|
|
750
|
+
<link>
|
|
751
|
+
<meta>
|
|
752
|
+
<base>
|
|
753
|
+
<form>
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
Event-handler attributes such as:
|
|
757
|
+
|
|
758
|
+
```html
|
|
759
|
+
onclick
|
|
760
|
+
onload
|
|
761
|
+
onerror
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
are removed as well.
|
|
765
|
+
|
|
766
|
+
### Safe serialization
|
|
767
|
+
|
|
768
|
+
Text content is escaped during HTML serialization.
|
|
769
|
+
|
|
770
|
+
The browser adapter only renders HTML produced by Regal's own serializer from its validated document model.
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
# 🌍 Compatibility
|
|
775
|
+
|
|
776
|
+
### Node.js
|
|
777
|
+
|
|
778
|
+
Node.js `18.18+` is required for developing and building the monorepo.
|
|
779
|
+
|
|
780
|
+
### React
|
|
781
|
+
|
|
782
|
+
React `18+` is required for:
|
|
783
|
+
|
|
784
|
+
```text
|
|
785
|
+
@regal-text-editor/react
|
|
786
|
+
@regal-text-editor/ui
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
React is a peer dependency and is not bundled.
|
|
790
|
+
|
|
791
|
+
### Browser
|
|
792
|
+
|
|
793
|
+
Regal targets modern evergreen browsers supporting:
|
|
794
|
+
|
|
795
|
+
* `contentEditable`
|
|
796
|
+
* Selection API
|
|
797
|
+
* Range API
|
|
798
|
+
* `beforeinput`
|
|
799
|
+
* Clipboard APIs
|
|
800
|
+
* IME composition
|
|
801
|
+
|
|
802
|
+
HTML parsing requires a `DOMParser`-compatible environment.
|
|
803
|
+
|
|
804
|
+
---
|
|
805
|
+
|
|
806
|
+
# 📦 Module Format
|
|
807
|
+
|
|
808
|
+
Regal is currently:
|
|
809
|
+
|
|
810
|
+
* ESM-only
|
|
811
|
+
* TypeScript typed
|
|
812
|
+
* Tree-shaking friendly
|
|
813
|
+
* `.d.ts` included
|
|
814
|
+
* Source maps included
|
|
815
|
+
|
|
816
|
+
There is currently **no CommonJS build**.
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
# 🗺️ Roadmap
|
|
821
|
+
|
|
822
|
+
Regal is being developed around a simple principle:
|
|
823
|
+
|
|
824
|
+
> **The editor should be extensible without becoming tightly coupled.**
|
|
825
|
+
|
|
826
|
+
Future development will focus on:
|
|
827
|
+
|
|
828
|
+
* More document primitives
|
|
829
|
+
* More powerful plugins
|
|
830
|
+
* Better browser behavior
|
|
831
|
+
* Accessibility
|
|
832
|
+
* Advanced collaboration primitives
|
|
833
|
+
* Better serialization
|
|
834
|
+
* Developer tooling
|
|
835
|
+
* More framework integrations
|
|
836
|
+
* Richer UI primitives
|
|
837
|
+
|
|
838
|
+
The roadmap will evolve alongside the architecture.
|
|
839
|
+
|
|
840
|
+
---
|
|
841
|
+
|
|
842
|
+
# 🤝 Contributing
|
|
843
|
+
|
|
844
|
+
Contributions are welcome.
|
|
845
|
+
|
|
846
|
+
Before opening a pull request:
|
|
847
|
+
|
|
848
|
+
1. Keep changes focused.
|
|
849
|
+
2. Add or update tests.
|
|
850
|
+
3. Preserve the package boundaries.
|
|
851
|
+
4. Keep public APIs strongly typed.
|
|
852
|
+
5. Avoid unnecessary dependencies.
|
|
853
|
+
6. Document user-facing behavior.
|
|
854
|
+
7. Run the complete test suite before submitting.
|
|
855
|
+
|
|
856
|
+
```bash
|
|
857
|
+
pnpm install
|
|
858
|
+
pnpm test
|
|
859
|
+
pnpm lint
|
|
860
|
+
pnpm typecheck
|
|
861
|
+
pnpm build
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
# 📄 License
|
|
867
|
+
|
|
868
|
+
Regal Text Editor is released under the **MIT License**.
|
|
869
|
+
|
|
870
|
+
---
|
|
871
|
+
|
|
872
|
+
<div align="center">
|
|
873
|
+
|
|
874
|
+
# 👑 Regal Text Editor
|
|
875
|
+
|
|
876
|
+
### Rich text, without the black box.
|
|
877
|
+
|
|
878
|
+
Built from scratch.
|
|
879
|
+
Designed to be extended.
|
|
880
|
+
|
|
881
|
+
[Live Demo](https://regal-text-editor.vercel.app/)
|
|
882
|
+
|
|
883
|
+
</div>
|