effitor 0.2.0 → 0.2.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.
- package/README.md +217 -0
- package/package.json +15 -15
package/README.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://ausprain.github.io/effitor/"><img src="https://raw.githubusercontent.com/ausprain/effitor/main/docs/assets/title.webp" alt="Effitor - Efficient Editor"></a>
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<a href="#">English</a> |
|
|
6
|
+
<a href="https://github.com/Ausprain/effitor/blob/main/README_zh.md">中文</a>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
# Effitor
|
|
10
|
+
|
|
11
|
+
Effitor is a high-performance, pluggable web rich text editor that embraces modern web standards and aims for the **ultimate editing experience**. It can be used as an out-of-the-box editor library or as an underlying framework for building customized editors.
|
|
12
|
+
|
|
13
|
+
> ⚠️ Note: Currently in early exploration phase (v0.x)
|
|
14
|
+
>
|
|
15
|
+
> - Not yet production-ready stable, APIs may change;
|
|
16
|
+
> - Only supports modern desktop browsers (Chrome, Edge, Firefox, Safari);
|
|
17
|
+
> - Does not support collaborative editing (not a design goal).
|
|
18
|
+
|
|
19
|
+
## Why Choose Effitor?
|
|
20
|
+
|
|
21
|
+
Unlike other editors, Effitor has **editing efficiency and experience** as its core goal—hence the name: _**effi**cient edi**tor**_.
|
|
22
|
+
|
|
23
|
+
It's built on `contenteditable`, implemented through the most basic DOM operations, and **almost completely takes over browser's default behavior for `contenteditable`** (except for IME), enabling highly flexible and customizable editing logic.
|
|
24
|
+
|
|
25
|
+
If you:
|
|
26
|
+
|
|
27
|
+
- Don't need collaborative editing, spell checking, or auto-completion;
|
|
28
|
+
- Don't strictly require consistent document structure;
|
|
29
|
+
- Want to quickly build or customize an editor with minimal learning cost
|
|
30
|
+
|
|
31
|
+
Then Effitor is perfect for you. It has no complex abstract models, just familiarize yourself with basic DOM operations, and you can quickly build your own editor. Of course, you can also use the default configuration directly, "out of the box".
|
|
32
|
+
|
|
33
|
+
Additionally, Effitor's core package `@effitor/core` already includes extensive editing experience optimizations and will continue to iterate.
|
|
34
|
+
|
|
35
|
+
### 🚀 Notable Features
|
|
36
|
+
|
|
37
|
+
#### 1. More Reasonable Format Continuation
|
|
38
|
+
|
|
39
|
+
Most editors are like a set of painting tools: users need to manually switch brushes (toggle styles: like `Ctrl+B` for bold). If bold text is at the end of a paragraph, subsequent input will **automatically inherit the style**, requiring another key press (switching to normal brush) to exit—this has become an "industry convention," but it's not a good experience. For example, if a piece of text is bold+italic+underline, you need to press: `Ctrl+B+I+U` or another clear-style combination key to cancel all styles.
|
|
40
|
+
|
|
41
|
+
Effitor tries to change this: **style switching is done by the editor, not the user**. The core idea is: users insert special nodes through hotkeys or hotstrings, and the editor automatically applies specific styles or executes corresponding behaviors based on the cursor position and editor configuration (plugins, etc.).
|
|
42
|
+
|
|
43
|
+
- Introduces the concept of "effect elements," where all key nodes, including style nodes (like bold, italic), are "effect elements";
|
|
44
|
+
- Pressing `Tab` inside a style "effect element" allows you to jump out of the current style;
|
|
45
|
+
- Double-pressing space allows you to jump out of the outermost style nesting.
|
|
46
|
+
- Developers can customize "effect elements" and their internal editing behaviors (plugin-based).
|
|
47
|
+
|
|
48
|
+
#### 2. Built-in Hotstring
|
|
49
|
+
|
|
50
|
+
Similar to AutoHotkey's hotstring functionality, but without external tools and with cross-platform support.
|
|
51
|
+
|
|
52
|
+
- Supports custom hotstring rules;
|
|
53
|
+
- Efficient matching, even with thousands of configured rules, without affecting performance;
|
|
54
|
+
- Perfectly solves the problem of no AutoHotkey alternative on macOS.
|
|
55
|
+
|
|
56
|
+
#### 3. Automatic Half-width Punctuation Conversion for IME
|
|
57
|
+
|
|
58
|
+
When Chinese users use Chinese input methods to write markdown, they may often need to switch input methods or Chinese/English modes to insert correct markdown syntax characters (like `` ` ``, `[]`, instead of `·`, `【】`). On Windows, this is no issue, but on macOS, frequently switching input methods is an operation that extremely affects the editing experience.
|
|
59
|
+
|
|
60
|
+
Effitor provides two solutions:
|
|
61
|
+
|
|
62
|
+
- **Lightweight mode**: After entering a full-width symbol, press space again to automatically replace it with half-width (e.g., `·` → `` ` ``, `!` → `!`);
|
|
63
|
+
- **Automatic mode** (configurable): Automatically convert full-width characters to corresponding half-width when inserted;
|
|
64
|
+
- Can also completely disable this feature.
|
|
65
|
+
|
|
66
|
+
#### 4. Language-aware Caret Control
|
|
67
|
+
|
|
68
|
+
Browsers natively support `Alt + ←/→` to jump the caret by "semantic words," but this depends on system language.
|
|
69
|
+
|
|
70
|
+
Effitor integrates this capability into the editor:
|
|
71
|
+
|
|
72
|
+
- Can set editor language through API;
|
|
73
|
+
- Supports `Alt + ←/→` to skip words;
|
|
74
|
+
- Supports `Alt + Backspace` to delete entire semantic words.
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
npm install effitor
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
ESM only:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { Effitor } from "effitor";
|
|
88
|
+
import {
|
|
89
|
+
useCounterAssist,
|
|
90
|
+
useDialogAssist,
|
|
91
|
+
useDropdownAssist,
|
|
92
|
+
useMessageAssist,
|
|
93
|
+
usePopupAssist,
|
|
94
|
+
} from "effitor/assists";
|
|
95
|
+
import {
|
|
96
|
+
useMarkPlugin,
|
|
97
|
+
useHeadingPlugin,
|
|
98
|
+
useListPlugin,
|
|
99
|
+
useCodePlugin,
|
|
100
|
+
useLinkPlugin,
|
|
101
|
+
useMediaPlugin,
|
|
102
|
+
useTablePlugin,
|
|
103
|
+
useBlockquotePlugin,
|
|
104
|
+
} from "effitor/plugins";
|
|
105
|
+
|
|
106
|
+
const host = document.getElementById("host") as HTMLDivElement | null;
|
|
107
|
+
const editor = new Effitor({
|
|
108
|
+
assists: [
|
|
109
|
+
useCounterAssist(),
|
|
110
|
+
useDialogAssist(),
|
|
111
|
+
useDropdownAssist(),
|
|
112
|
+
useMessageAssist(),
|
|
113
|
+
usePopupAssist(),
|
|
114
|
+
],
|
|
115
|
+
plugins: [
|
|
116
|
+
useMarkPlugin(),
|
|
117
|
+
useHeadingPlugin(),
|
|
118
|
+
useListPlugin(),
|
|
119
|
+
useLinkPlugin(),
|
|
120
|
+
useMediaPlugin(),
|
|
121
|
+
await useCodePlugin(),
|
|
122
|
+
useTablePlugin(),
|
|
123
|
+
useBlockquotePlugin(),
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (host) {
|
|
128
|
+
editor.mount(host);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Demo
|
|
133
|
+
|
|
134
|
+
[Live Demo](https://effitor.top/)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Performance
|
|
139
|
+
|
|
140
|
+
### Editors supporting basic markdown, loading 300k characters HTML (no tables and code blocks), and typing 1000 characters at the end paragraph
|
|
141
|
+
|
|
142
|
+
| Editor | Duration (s) | LCP (ms) | CLS | INP (ms) | INPs (ms) | Memory (MB) |
|
|
143
|
+
| ------- | ------------ | -------- | --- | -------- | --------- | ----------- |
|
|
144
|
+
| effitor | 45.6 | 499.2 | 0 | 59.2 | 110.6 | 44.4 |
|
|
145
|
+
| lexical | 73.4 | 281.6 | 0 | 145.6 | 120.8 | 83.2 |
|
|
146
|
+
| tiptap | 52.7 | 216.8 | 0.2 | 64 | 61.3 | 20.6 |
|
|
147
|
+
|
|
148
|
+
### Editors supporting basic markdown, loading 1 million characters HTML (no tables and code blocks), and typing 1000 characters at the end paragraph
|
|
149
|
+
|
|
150
|
+
| Editor | Duration (s) | LCP (ms) | CLS | INP (ms) | INPs (ms) | Memory (MB) |
|
|
151
|
+
| ------- | ------------ | -------- | --- | -------- | --------- | ----------- |
|
|
152
|
+
| effitor | 50.5 | 1206.4 | 0 | 80 | 149.9 | 35.2 |
|
|
153
|
+
| tiptap | 62.3 | 755.2 | 0.2 | 84.8 | 92.4 | 58.2 |
|
|
154
|
+
|
|
155
|
+
> From [effitor performance tests](./examples/benchmark/)
|
|
156
|
+
> Test environment: macOS: 8x[Apple M3] 16GB, nodejs: v24.3.0, playwright: 1.56.1
|
|
157
|
+
>
|
|
158
|
+
> - LCP reflects the total time for editor initialization and loading corresponding content, where effitor includes the time for loading the shiki highlighter;
|
|
159
|
+
> - INP reflects the response time after stable user input, the lower the better, indicating lower latency after stable input;
|
|
160
|
+
> - INPs is the average of INP metric recorded values, the lower the better, indicating more stable editor response, the higher the larger average response latency or existence of some operation causing high latency;
|
|
161
|
+
> - Duration is the total time for the editor to complete all operations, after subtracting LCP it's the time to simulate typing 1000 characters, the lower the better, indicating faster editor response and higher efficiency;
|
|
162
|
+
> - Memory is the memory size occupied by the editor, where effitor includes the shiki highlighter (~15MB).
|
|
163
|
+
|
|
164
|
+
## Feature Overview
|
|
165
|
+
|
|
166
|
+
- ✅ Out of the box
|
|
167
|
+
- ✅ Framework-agnostic, highly customizable
|
|
168
|
+
- ✅ Built-in undo/redo stack
|
|
169
|
+
- 🔄 Built-in cursor/selection history (in development)
|
|
170
|
+
- ✅ Built-in hotkeys and hotstrings
|
|
171
|
+
- ✅ Light/dark theme switching
|
|
172
|
+
- ✅ Partial Markdown, native HTML conversion
|
|
173
|
+
- ✅ Built-in assistants
|
|
174
|
+
- ✅ `assist-counter`: Word count
|
|
175
|
+
- ✅ `assist-dialog`: Dialog
|
|
176
|
+
- ✅ `assist-dropdown`: Dropdown menu
|
|
177
|
+
- ✅ `assist-message`: Message notification
|
|
178
|
+
- ✅ `assist-popup`: Popup and floating tools
|
|
179
|
+
- ✅ Built-in plugins
|
|
180
|
+
- ✅ `plugin-heading`: Headings
|
|
181
|
+
- ✅ `plugin-mark`: Highlights (bold, italic, strikethrough, etc.)
|
|
182
|
+
- ✅ `plugin-link`: Links
|
|
183
|
+
- ✅ `plugin-list`: Ordered/unordered/task lists
|
|
184
|
+
- ✅ `plugin-media`: Media (images/audio/video)
|
|
185
|
+
- ✅ `plugin-code`: Code blocks (supports HTML, LaTeX rendering)
|
|
186
|
+
- ✅ `plugin-table`: Tables
|
|
187
|
+
- ✅ `plugin-blockquote`: Blockquotes (paragraph groups | columns)
|
|
188
|
+
- Other assistants or plugins
|
|
189
|
+
- 🔄 `assist-ai`: AI assistant (in development)
|
|
190
|
+
- 📐 `plugin-math`: Math formulas (planned)
|
|
191
|
+
- 🎨 `plugin-excalidraw`: excalidraw whiteboard (in development)
|
|
192
|
+
|
|
193
|
+
## Design Philosophy
|
|
194
|
+
|
|
195
|
+
- **No abstract data model**: Direct DOM manipulation, reducing learning cost;
|
|
196
|
+
- **Deeply customizable `contenteditable`**: Except for IME and clipboard, all behaviors are controlled by Effitor;
|
|
197
|
+
- **Based on web standards**: Follows [`Input Events Level 2`](https://www.w3.org/TR/input-events-2/), [`Selection API`](https://www.w3.org/TR/selection-api/), [`Range`](https://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html) and other specifications.
|
|
198
|
+
|
|
199
|
+
## Limitations
|
|
200
|
+
|
|
201
|
+
- **No collaborative editing support**: Due to no intermediate data model, collaborative implementation is extremely difficult (but not a design goal anyway);
|
|
202
|
+
- **Browser compatibility differences**: Although most edge cases have been handled, individual subtle behaviors may vary by browser;
|
|
203
|
+
- **Accessibility (a11y) support to be improved**: Heavy use of custom elements, ARIA support is still under construction.
|
|
204
|
+
|
|
205
|
+
## Final Words
|
|
206
|
+
|
|
207
|
+
Effitor is still in the exploration phase, and some APIs may be adjusted at any time. For example, we initially built Effitor based on Shadow DOM, but in practice we found: **current Shadow DOM still has many compatibility and interaction issues in rich text editing scenarios**, and eventually had to abandon this approach.
|
|
208
|
+
|
|
209
|
+
We plan to complete the last major refactoring in **v0.3.0**, supplement complete tests, and push Effitor towards maturity and stability.
|
|
210
|
+
|
|
211
|
+
## Documentation
|
|
212
|
+
|
|
213
|
+
[Documentation](https://github.com/Ausprain/effitor/blob/main/docs/Index.md)
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
[MIT](https://choosealicense.com/licenses/mit/)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effitor",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "An elegant and efficient editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"editor",
|
|
@@ -62,20 +62,20 @@
|
|
|
62
62
|
"build": "tsup"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@effitor/core": "
|
|
66
|
-
"@effitor/assist-counter": "
|
|
67
|
-
"@effitor/assist-dialog": "
|
|
68
|
-
"@effitor/assist-dropdown": "
|
|
69
|
-
"@effitor/assist-message": "
|
|
70
|
-
"@effitor/assist-popup": "
|
|
71
|
-
"@effitor/plugin-blockquote": "
|
|
72
|
-
"@effitor/plugin-code": "
|
|
73
|
-
"@effitor/plugin-heading": "
|
|
74
|
-
"@effitor/plugin-link": "
|
|
75
|
-
"@effitor/plugin-list": "
|
|
76
|
-
"@effitor/plugin-mark": "
|
|
77
|
-
"@effitor/plugin-media": "
|
|
78
|
-
"@effitor/plugin-table": "
|
|
65
|
+
"@effitor/core": "0.2.0",
|
|
66
|
+
"@effitor/assist-counter": "0.2.0",
|
|
67
|
+
"@effitor/assist-dialog": "0.2.0",
|
|
68
|
+
"@effitor/assist-dropdown": "0.2.0",
|
|
69
|
+
"@effitor/assist-message": "0.2.0",
|
|
70
|
+
"@effitor/assist-popup": "0.2.0",
|
|
71
|
+
"@effitor/plugin-blockquote": "0.2.0",
|
|
72
|
+
"@effitor/plugin-code": "0.2.0",
|
|
73
|
+
"@effitor/plugin-heading": "0.2.0",
|
|
74
|
+
"@effitor/plugin-link": "0.2.0",
|
|
75
|
+
"@effitor/plugin-list": "0.2.0",
|
|
76
|
+
"@effitor/plugin-mark": "0.2.0",
|
|
77
|
+
"@effitor/plugin-media": "0.2.0",
|
|
78
|
+
"@effitor/plugin-table": "0.2.0"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"fs-extra": "^11.3.1",
|