markdown-text-editor 1.7.0 → 1.9.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 +1 -1
- package/README.md +60 -12
- package/dist/markdown-text-editor.es.js +885 -654
- package/dist/markdown-text-editor.es.js.map +1 -1
- package/dist/markdown-text-editor.min.js +74 -72
- package/dist/markdown-text-editor.min.js.map +1 -1
- package/dist/markdown-text-editor.umd.js +74 -72
- package/dist/markdown-text-editor.umd.js.map +1 -1
- package/package.json +15 -9
- package/types/index.d.ts +102 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Markdown Text Editor
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### Toolbar, live preview and WYSIWYG on the textarea you already have
|
|
4
4
|
|
|
5
5
|
[![npm installs][npm_installs]](https://www.npmjs.com/package/markdown-text-editor)
|
|
6
6
|
[![Jsdelivr hits][jsdelivr]](https://cdn.jsdelivr.net/npm/markdown-text-editor)
|
|
@@ -13,27 +13,34 @@ A lightweight, embeddable JavaScript Markdown editor that transforms a standard
|
|
|
13
13
|
|
|
14
14
|
A native-first Markdown editor built on a standard textarea. No data binding, no API — just drop it in and your forms keep working as-is.
|
|
15
15
|
|
|
16
|
+
**Works standalone.** No Frutjam, no Tailwind, no framework required, the styles are bundled.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
<sub>Hybrid mode. Text is styled as you type and the Markdown stays where it is, because this is your `<textarea>`, not a copy of it.</sub>
|
|
21
|
+
|
|
16
22
|
> **No complex APIs. No data binding. No JSON schemas.** Just a `<textarea>` that types Markdown and submits like any normal form field — enhanced with a rich toolbar, live preview, and WYSIWYG hybrid mode.
|
|
17
23
|
|
|
18
24
|
## ⭐ Why developers choose this over EasyMDE / SimpleMDE
|
|
19
25
|
|
|
20
|
-
Most JavaScript markdown editors (EasyMDE, SimpleMDE, CodeMirror-based editors)
|
|
26
|
+
Most JavaScript markdown editors (EasyMDE, SimpleMDE, CodeMirror-based editors) hide your `<textarea>` and edit a copy, writing the value back when the form is submitted. That holds up until something else needs the field: a `required` input the browser cannot focus blocks the submit entirely, and `.value` or `FormData` read before submit returns an empty string, which breaks htmx, Turbo, Livewire, autosave and unsaved-changes guards.
|
|
21
27
|
|
|
22
28
|
**MarkdownEditor is different.** It sits transparently on top of your existing `<textarea>`:
|
|
23
29
|
|
|
24
30
|
| Feature | MarkdownEditor | EasyMDE / SimpleMDE |
|
|
25
31
|
|---|---|---|
|
|
26
|
-
| Native `<textarea>` preserved | ✅ | ❌
|
|
27
|
-
|
|
|
28
|
-
|
|
|
32
|
+
| Native `<textarea>` preserved | ✅ | ❌ Hidden, edited as a copy |
|
|
33
|
+
| Works with `required` fields | ✅ | ❌ Browser blocks the submit |
|
|
34
|
+
| `.value` correct before submit | ✅ | ❌ Empty until the form submits |
|
|
35
|
+
| Serialises with `FormData`, htmx, Turbo | ✅ | ❌ Needs the editor's own API |
|
|
29
36
|
| WYSIWYG hybrid mode | ✅ | ❌ |
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
| RTL support | ✅ | ❌ |
|
|
37
|
+
| Inline event handlers (CSP) | ✅ None | Some |
|
|
38
|
+
| RTL support | ✅ Built in | Via CodeMirror's `direction` option |
|
|
33
39
|
| Built-in Find & Replace | ✅ | ❌ |
|
|
34
40
|
| Swap the markdown parser | ✅ | ❌ |
|
|
35
41
|
| Keyboard shortcuts | ✅ | Partial |
|
|
36
42
|
| Dark mode / theming | ✅ | Limited |
|
|
43
|
+
| Bundle size, gzipped | 54 KB | 107 KB (JS + CSS) |
|
|
37
44
|
|
|
38
45
|
## 🚀 Quick Start
|
|
39
46
|
|
|
@@ -89,13 +96,14 @@ That's it. Form submission, `.value` access, and all native textarea behaviour w
|
|
|
89
96
|
- 🔄 **Undo / Redo** — Full diff-based history with exact cursor restoration. Works with `Ctrl+Z`, `Ctrl+Y`, `Ctrl+Shift+Z`
|
|
90
97
|
- ♿ **Accessible by Default** — `role="toolbar"`, `aria-pressed`, `aria-disabled`, `disabled`, screen-reader-friendly SVGs, and correct focus restoration on modal close
|
|
91
98
|
- 🛡️ **XSS Safe** — Preview output sanitized via [DOMPurify](https://github.com/cure53/DOMPurify) before rendering, including the output of a custom renderer
|
|
92
|
-
- 🛡️ **
|
|
99
|
+
- 🛡️ **No inline scripts** — No inline event handlers and no `eval`, so `script-src` needs no exception. The stylesheet is currently injected as a `<style>` element, which a strict `style-src` blocks ([#42](https://github.com/nezanuha/markdown-text-editor/issues/42))
|
|
93
100
|
- 🌍 **RTL Support** — Native Right-to-Left support for Arabic, Urdu, Farsi, and other RTL languages
|
|
94
101
|
- 🌙 **Dark Mode & Theming** — Inherits `data-theme` from any ancestor element. Built-in light, dark, snowberry, and darkberry themes. Fully customizable via CSS variables
|
|
102
|
+
- 🧩 **Custom Tools** — Add your own toolbar button with `{ custom: { title, icon, action } }`, or extend `MarkdownEditor.Tool` when it builds its own markup. Keyboard shortcuts, translation and teardown work exactly as they do for the built-ins. No fork, no patching
|
|
95
103
|
- 🎛️ **Modular Toolbar** — Pick exactly which tools appear and in what order
|
|
96
104
|
- 🟦 **TypeScript Ready** — Definitions ship with the package. Options, toolbar entries and variable shapes are all checked, so a mistyped tool name is a compile error rather than a silently missing button
|
|
97
105
|
- 📦 **Universal Module Support** — ESM, CommonJS, UMD, and IIFE. Works with Vite, webpack, Rollup, or directly via `<script src>` CDN — no configuration needed
|
|
98
|
-
- 🚀 **High Performance** — ~
|
|
106
|
+
- 🚀 **High Performance** — ~54KB gzipped (253KB minified). Debounced preview, cached layout calculations, conflict-free Tab/Enter handling for large documents
|
|
99
107
|
|
|
100
108
|
## 🛠 Developer Workflow
|
|
101
109
|
|
|
@@ -133,6 +141,46 @@ const editor = new MarkdownEditor('#markdown-editor', {
|
|
|
133
141
|
});
|
|
134
142
|
```
|
|
135
143
|
|
|
144
|
+
### Add your own toolbar button
|
|
145
|
+
|
|
146
|
+
Most tools are just a title, an icon and a function. No class, no fork:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
new MarkdownEditor('#markdown-editor', {
|
|
150
|
+
toolbar: ['bold', 'italic', {
|
|
151
|
+
custom: {
|
|
152
|
+
title: 'Callout', // tooltip, and the .callout-btn class
|
|
153
|
+
icon: '<svg>…</svg>',
|
|
154
|
+
shortcut: 'Ctrl+Shift+C', // optional, shown in the tooltip
|
|
155
|
+
action(editor) {
|
|
156
|
+
editor.insertText('> [!NOTE]\n> ');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}, 'preview']
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Use `editor.insertText(text, caretOffset, trailingLength)` rather than writing to the textarea yourself — it replaces the selection, restores focus, moves the caret, re-renders the preview, fires `onChange`, and keeps undo working.
|
|
164
|
+
|
|
165
|
+
A tool that builds its own markup, such as a dropdown or a dialog, extends the base class instead:
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
class EmbedTool extends MarkdownEditor.Tool {
|
|
169
|
+
constructor(editor) {
|
|
170
|
+
super(editor, 'Embed video');
|
|
171
|
+
this.button = this.createButton('<svg>…</svg>');
|
|
172
|
+
}
|
|
173
|
+
applySyntax(event) {
|
|
174
|
+
// MarkdownEditor.modal is the same dialog the link and image tools use
|
|
175
|
+
const dialog = MarkdownEditor.modal(event, 'fj:max-w-sm', '…', 'Embed');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
new MarkdownEditor('#markdown-editor', { toolbar: ['bold', EmbedTool, 'preview'] });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Two helpers exist for tools that need them: `editor.renderMarkdown(md)` returns sanitized HTML exactly as the preview does, and `editor.createToolbar(textarea, ['bold', 'italic'])` builds a real toolbar bound to another textarea, for a tool collecting rich text in its own dialog.
|
|
183
|
+
|
|
136
184
|
### Tear down in SPAs
|
|
137
185
|
|
|
138
186
|
```javascript
|
|
@@ -149,7 +197,7 @@ useEffect(() => {
|
|
|
149
197
|
}, []);
|
|
150
198
|
```
|
|
151
199
|
|
|
152
|
-
Use `defaultValue`, not `value`. The editor writes to the textarea directly, so a controlled binding (or Vue's `:value`) would overwrite what the user is typing. Full React and Vue examples are in the [documentation](https://frutjam.com/
|
|
200
|
+
Use `defaultValue`, not `value`. The editor writes to the textarea directly, so a controlled binding (or Vue's `:value`) would overwrite what the user is typing. Full React and Vue examples are in the [documentation](https://frutjam.com/plugins/markdown-editor).
|
|
153
201
|
|
|
154
202
|
The same two rules cover Svelte, Angular and anything else: don't bind the value, and call `destroy()` on unmount. In Angular, `onChange` fires outside the zone, so wrap it in `zone.run()` for change detection to notice.
|
|
155
203
|
|