inkora 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 +509 -0
- package/dist/index.cjs +4986 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +4946 -0
- package/dist/index.js.map +1 -0
- package/package.json +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tiptap-reach-editor contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
# Inkora
|
|
2
|
+
|
|
3
|
+
> Plug-and-play rich text editor for React — built on TipTap v3 and ProseMirror.
|
|
4
|
+
|
|
5
|
+
Inkora gives you a production-ready WYSIWYG editor in one import. No configuration required to get started, fully customisable when you need it. Ships with a full menu-bar editor, a lightweight toolbar editor, and a read-only viewer — all styled with CSS variables so they adapt to any design system.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of contents
|
|
10
|
+
|
|
11
|
+
- [Features](#features)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Quick start](#quick-start)
|
|
14
|
+
- [Components](#components)
|
|
15
|
+
- [InkoraEditor](#inkoraeditor)
|
|
16
|
+
- [InkoraBasicEditor](#inkorabasiceditor)
|
|
17
|
+
- [InkoraViewer](#inkoraviewer)
|
|
18
|
+
- [Props reference](#props-reference)
|
|
19
|
+
- [Media uploads](#media-uploads)
|
|
20
|
+
- [@ Mentions](#-mentions)
|
|
21
|
+
- [Theming with CSS variables](#theming-with-css-variables)
|
|
22
|
+
- [Dark mode](#dark-mode)
|
|
23
|
+
- [Using your own editor setup](#using-your-own-editor-setup)
|
|
24
|
+
- [Keyboard shortcuts](#keyboard-shortcuts)
|
|
25
|
+
- [Built-in extensions](#built-in-extensions)
|
|
26
|
+
- [Framework notes](#framework-notes)
|
|
27
|
+
- [Contributing](#contributing)
|
|
28
|
+
- [License](#license)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
**Formatting**
|
|
35
|
+
- Bold, Italic, Underline, Strikethrough, Inline code
|
|
36
|
+
- Superscript, Subscript
|
|
37
|
+
- Font family, font size (8 – 96 pt)
|
|
38
|
+
- Text colour, highlight colour (multi-colour)
|
|
39
|
+
- Text alignment (left, centre, right, justify)
|
|
40
|
+
- Line height
|
|
41
|
+
- Clear all formatting
|
|
42
|
+
|
|
43
|
+
**Structure**
|
|
44
|
+
- Headings H1 – H6
|
|
45
|
+
- Bullet list, ordered list, task list (checkboxes)
|
|
46
|
+
- Nested indentation
|
|
47
|
+
- Blockquote
|
|
48
|
+
- Horizontal rule
|
|
49
|
+
- Code block with syntax highlighting (100+ languages via `lowlight`)
|
|
50
|
+
- Callout blocks (info, success, warning, danger) with emoji icons
|
|
51
|
+
|
|
52
|
+
**Media**
|
|
53
|
+
- Image insert — file upload or paste URL; resize, align, shape (rect / circle), frame, filters
|
|
54
|
+
- Video insert — file upload or URL
|
|
55
|
+
- Audio insert — file upload
|
|
56
|
+
- YouTube / Vimeo embed
|
|
57
|
+
- GIF picker (curated + URL paste)
|
|
58
|
+
- SVG shape inserter (circle, square, star, arrows …)
|
|
59
|
+
|
|
60
|
+
**Tables**
|
|
61
|
+
- Insert via grid picker (up to 10 × 10)
|
|
62
|
+
- Add / delete rows and columns
|
|
63
|
+
- Merge and split cells
|
|
64
|
+
- Resizable columns
|
|
65
|
+
|
|
66
|
+
**Advanced**
|
|
67
|
+
- LaTeX math formulas (KaTeX, optional peer dep)
|
|
68
|
+
- @ Mentions with custom data source
|
|
69
|
+
- `#` Hashtags
|
|
70
|
+
- Emoji shortcodes (`:heart:` → ❤️, `:smile:` → 😊, `:rocket:` → 🚀, `:fire:` → 🔥)
|
|
71
|
+
- Hyperlinks with modal dialog
|
|
72
|
+
- Global drag-handle for reordering blocks
|
|
73
|
+
- Bubble menu (formatting pop-up on text selection)
|
|
74
|
+
- Character / word count
|
|
75
|
+
- Auto-save with debounce
|
|
76
|
+
- Fullscreen mode
|
|
77
|
+
- HTML source editor
|
|
78
|
+
- Undo / Redo
|
|
79
|
+
|
|
80
|
+
**Themes**
|
|
81
|
+
- Light and dark mode — switch at runtime
|
|
82
|
+
- All colours exposed as CSS variables (`--rte-*`) for easy overriding
|
|
83
|
+
- Zero Tailwind dependency — safe in any project
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
Install Inkora and its peer dependencies.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm install inkora \
|
|
93
|
+
@tiptap/core @tiptap/pm @tiptap/react @tiptap/starter-kit \
|
|
94
|
+
@tiptap/extension-character-count \
|
|
95
|
+
@tiptap/extension-code-block-lowlight \
|
|
96
|
+
@tiptap/extension-color \
|
|
97
|
+
@tiptap/extension-dropcursor \
|
|
98
|
+
@tiptap/extension-font-family \
|
|
99
|
+
@tiptap/extension-gapcursor \
|
|
100
|
+
@tiptap/extension-highlight \
|
|
101
|
+
@tiptap/extension-image \
|
|
102
|
+
@tiptap/extension-link \
|
|
103
|
+
@tiptap/extension-mention \
|
|
104
|
+
@tiptap/extension-placeholder \
|
|
105
|
+
@tiptap/extension-subscript \
|
|
106
|
+
@tiptap/extension-superscript \
|
|
107
|
+
@tiptap/extension-table \
|
|
108
|
+
@tiptap/extension-task-item \
|
|
109
|
+
@tiptap/extension-task-list \
|
|
110
|
+
@tiptap/extension-text-align \
|
|
111
|
+
@tiptap/extension-text-style \
|
|
112
|
+
@tiptap/extension-underline \
|
|
113
|
+
@tiptap/extension-youtube \
|
|
114
|
+
react react-dom
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Math formulas (optional)**
|
|
118
|
+
|
|
119
|
+
If you want KaTeX math blocks, install the optional peer dependency and import the KaTeX stylesheet once in your app root:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm install katex
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
import 'katex/dist/katex.min.css';
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Quick start
|
|
132
|
+
|
|
133
|
+
```jsx
|
|
134
|
+
import { InkoraEditor } from 'inkora';
|
|
135
|
+
|
|
136
|
+
export default function App() {
|
|
137
|
+
return (
|
|
138
|
+
<InkoraEditor
|
|
139
|
+
onChange={(json) => console.log(json)}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
That is all. The editor mounts with a full toolbar, dark-mode toggle, and every extension enabled.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Components
|
|
150
|
+
|
|
151
|
+
### InkoraEditor
|
|
152
|
+
|
|
153
|
+
Full-featured editor with menu bar, format bar, bubble menu, media support, tables, math, and more.
|
|
154
|
+
|
|
155
|
+
```jsx
|
|
156
|
+
import { InkoraEditor } from 'inkora';
|
|
157
|
+
|
|
158
|
+
<InkoraEditor
|
|
159
|
+
initialContent={myDocumentJson}
|
|
160
|
+
theme="light"
|
|
161
|
+
width="100%"
|
|
162
|
+
minHeight={500}
|
|
163
|
+
onChange={(json) => setContent(json)}
|
|
164
|
+
onSave={(json) => saveToDatabase(json)}
|
|
165
|
+
onUpload={async (file) => {
|
|
166
|
+
const url = await uploadToStorage(file);
|
|
167
|
+
return { src: url };
|
|
168
|
+
}}
|
|
169
|
+
resolveMediaUrl={(src) => `https://cdn.example.com/${src}`}
|
|
170
|
+
onToggleTheme={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### InkoraBasicEditor
|
|
177
|
+
|
|
178
|
+
Lightweight editor with a single compact toolbar. Ideal for comments, short-form inputs, and forms.
|
|
179
|
+
|
|
180
|
+
```jsx
|
|
181
|
+
import { InkoraBasicEditor } from 'inkora';
|
|
182
|
+
|
|
183
|
+
<InkoraBasicEditor
|
|
184
|
+
value={content}
|
|
185
|
+
onChange={setContent}
|
|
186
|
+
minHeight={150}
|
|
187
|
+
theme="light"
|
|
188
|
+
/>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Render saved content read-only by passing `readOnly`:
|
|
192
|
+
|
|
193
|
+
```jsx
|
|
194
|
+
<InkoraBasicEditor value={content} readOnly />
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### InkoraViewer
|
|
200
|
+
|
|
201
|
+
Read-only renderer for TipTap JSON. Renders the same visual styles as the editor with no editing controls. Shows a shimmer skeleton during SSR / before hydration.
|
|
202
|
+
|
|
203
|
+
```jsx
|
|
204
|
+
import { InkoraViewer } from 'inkora';
|
|
205
|
+
|
|
206
|
+
<InkoraViewer content={savedDocumentJson} theme="light" />
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Props reference
|
|
212
|
+
|
|
213
|
+
### InkoraEditor
|
|
214
|
+
|
|
215
|
+
| Prop | Type | Default | Description |
|
|
216
|
+
|------|------|---------|-------------|
|
|
217
|
+
| `initialContent` | `Object` | `undefined` | TipTap JSON document to pre-populate the editor. |
|
|
218
|
+
| `onChange` | `(json: Object) => void` | — | Fired on every keystroke with the current TipTap JSON. |
|
|
219
|
+
| `onSave` | `(json: Object) => void` | — | Auto-save callback. Fires after 1 s of idle typing. |
|
|
220
|
+
| `onUpload` | `(file: File) => Promise<{ src: string }>` | — | Media upload handler. Must return a promise resolving to `{ src }`. |
|
|
221
|
+
| `resolveMediaUrl` | `(src: string) => string` | — | Maps stored `src` values to live, displayable URLs. |
|
|
222
|
+
| `onToggleTheme` | `() => void` | — | Called when the user clicks the dark-mode toggle button. |
|
|
223
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | Controls the colour scheme. |
|
|
224
|
+
| `placeholder` | `string` | `'Start writing…'` | Empty-state placeholder text. |
|
|
225
|
+
| `width` | `string \| number` | `'100%'` | CSS width of the outer container. |
|
|
226
|
+
| `height` | `string \| number` | `undefined` | Fixed height for the scrollable content area. |
|
|
227
|
+
| `minHeight` | `number` | `420` | Minimum height (px) of the scrollable content area. |
|
|
228
|
+
| `mentionOptions` | `Object` | `{}` | Forwarded to the TipTap Mention extension's `suggestion` config. |
|
|
229
|
+
|
|
230
|
+
### InkoraBasicEditor
|
|
231
|
+
|
|
232
|
+
| Prop | Type | Default | Description |
|
|
233
|
+
|------|------|---------|-------------|
|
|
234
|
+
| `value` | `Object` | `undefined` | TipTap JSON document. |
|
|
235
|
+
| `onChange` | `(json: Object) => void` | — | Fired on every change with the current TipTap JSON. |
|
|
236
|
+
| `onSave` | `(json: Object) => void` | — | Auto-save callback (debounced 1 s). |
|
|
237
|
+
| `placeholder` | `string` | `'Start writing…'` | Empty-state placeholder text. |
|
|
238
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | Controls the colour scheme. |
|
|
239
|
+
| `width` | `string \| number` | `'100%'` | CSS width of the outer container. |
|
|
240
|
+
| `height` | `string \| number` | `undefined` | Fixed height for the content area. |
|
|
241
|
+
| `minHeight` | `number` | `150` | Minimum height (px) of the content area. |
|
|
242
|
+
| `readOnly` | `boolean` | `false` | Hides the toolbar and disables all editing. |
|
|
243
|
+
|
|
244
|
+
### InkoraViewer
|
|
245
|
+
|
|
246
|
+
| Prop | Type | Default | Description |
|
|
247
|
+
|------|------|---------|-------------|
|
|
248
|
+
| `content` | `Object` | `undefined` | TipTap JSON document to display. |
|
|
249
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | Controls the colour scheme. |
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Media uploads
|
|
254
|
+
|
|
255
|
+
Inkora does not upload files itself — you provide the upload function. The editor calls it with the selected `File` and expects a promise that resolves to `{ src: string }`.
|
|
256
|
+
|
|
257
|
+
```jsx
|
|
258
|
+
<InkoraEditor
|
|
259
|
+
onUpload={async (file) => {
|
|
260
|
+
// Example: S3 presigned-URL upload
|
|
261
|
+
const { url, key } = await fetch('/api/upload-url', {
|
|
262
|
+
method: 'POST',
|
|
263
|
+
body: JSON.stringify({ name: file.name, type: file.type }),
|
|
264
|
+
headers: { 'Content-Type': 'application/json' },
|
|
265
|
+
}).then(r => r.json());
|
|
266
|
+
|
|
267
|
+
await fetch(url, { method: 'PUT', body: file });
|
|
268
|
+
|
|
269
|
+
// Return the key that gets stored in the TipTap document JSON
|
|
270
|
+
return { src: key };
|
|
271
|
+
}}
|
|
272
|
+
resolveMediaUrl={(src) => `https://your-bucket.s3.amazonaws.com/${src}`}
|
|
273
|
+
/>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`resolveMediaUrl` is called at render time to convert stored keys back into displayable URLs. If your `onUpload` already returns a full absolute URL, you can omit `resolveMediaUrl`.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## @ Mentions
|
|
281
|
+
|
|
282
|
+
Pass a `mentionOptions` prop with an async `items` function. The editor calls it as the user types after `@`.
|
|
283
|
+
|
|
284
|
+
```jsx
|
|
285
|
+
<InkoraEditor
|
|
286
|
+
mentionOptions={{
|
|
287
|
+
suggestion: {
|
|
288
|
+
items: async ({ query }) => {
|
|
289
|
+
const users = await fetchUsers(query);
|
|
290
|
+
// Must return an array of objects with at least { id, label }
|
|
291
|
+
return users.map(u => ({ id: u.id, label: u.name }));
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
}}
|
|
295
|
+
/>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The mention node renders as `<span class="rte-mention">` in HTML output. Style it in your own stylesheet:
|
|
299
|
+
|
|
300
|
+
```css
|
|
301
|
+
.rte-mention {
|
|
302
|
+
background: #d3e3fd;
|
|
303
|
+
color: #0b57d0;
|
|
304
|
+
border-radius: 4px;
|
|
305
|
+
padding: 1px 4px;
|
|
306
|
+
font-weight: 500;
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Theming with CSS variables
|
|
313
|
+
|
|
314
|
+
All colours are driven by CSS custom properties on the wrapper element. Override any variable in your own stylesheet:
|
|
315
|
+
|
|
316
|
+
```css
|
|
317
|
+
/* Example: purple accent */
|
|
318
|
+
.inkora-editor {
|
|
319
|
+
--rte-accent: #7c3aed;
|
|
320
|
+
--rte-accent-soft: #ede9fe;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Full variable reference**
|
|
325
|
+
|
|
326
|
+
| Variable | Light | Dark | Role |
|
|
327
|
+
|----------|-------|------|------|
|
|
328
|
+
| `--rte-page` | `#ffffff` | `#1f2023` | Editor content background |
|
|
329
|
+
| `--rte-bar` | `#ffffff` | `#26272b` | Toolbar / panel background |
|
|
330
|
+
| `--rte-pill` | `#f6f8fc` | `#2b2c30` | Subtle chip / button fill |
|
|
331
|
+
| `--rte-hover` | `rgba(60,64,67,.09)` | `rgba(255,255,255,.09)` | Button hover background |
|
|
332
|
+
| `--rte-border` | `#e4e7eb` | `#3c4043` | Panel and input borders |
|
|
333
|
+
| `--rte-ink` | `#202124` | `#e8eaed` | Primary text colour |
|
|
334
|
+
| `--rte-muted` | `#5f6368` | `#9aa0a6` | Secondary / disabled text |
|
|
335
|
+
| `--rte-accent` | `#0b57d0` | `#8ab4f8` | Links, active states, focus rings |
|
|
336
|
+
| `--rte-accent-soft` | `#d3e3fd` | `#1e3a5f` | Selected-node highlight tint |
|
|
337
|
+
| `--rte-shadow` | subtle | deeper | Outer box shadow |
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Dark mode
|
|
342
|
+
|
|
343
|
+
Pass `theme="dark"` to switch the colour scheme. The component is fully controlled — your app owns the state:
|
|
344
|
+
|
|
345
|
+
```jsx
|
|
346
|
+
const [theme, setTheme] = useState('light');
|
|
347
|
+
|
|
348
|
+
<InkoraEditor
|
|
349
|
+
theme={theme}
|
|
350
|
+
onToggleTheme={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
|
|
351
|
+
/>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The built-in toggle button in the toolbar calls `onToggleTheme` when clicked. Wire it up to update your `theme` state so the prop changes and the editor re-renders with the new scheme.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Using your own editor setup
|
|
359
|
+
|
|
360
|
+
**Extension factory**
|
|
361
|
+
|
|
362
|
+
`createEditorExtensions` returns the full array of TipTap extensions used internally. Use it when building a custom `useEditor` instance:
|
|
363
|
+
|
|
364
|
+
```jsx
|
|
365
|
+
import { createEditorExtensions, editorStyles } from 'inkora';
|
|
366
|
+
import { useEditor, EditorContent } from '@tiptap/react';
|
|
367
|
+
import { useMemo } from 'react';
|
|
368
|
+
|
|
369
|
+
function MyEditor({ placeholder }) {
|
|
370
|
+
const extensions = useMemo(
|
|
371
|
+
() => createEditorExtensions({ placeholder, isEditable: true }),
|
|
372
|
+
[placeholder]
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
const editor = useEditor({ extensions });
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<>
|
|
379
|
+
<style dangerouslySetInnerHTML={{ __html: editorStyles }} />
|
|
380
|
+
<EditorContent editor={editor} />
|
|
381
|
+
</>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**`createEditorExtensions` options**
|
|
387
|
+
|
|
388
|
+
| Key | Type | Default | Description |
|
|
389
|
+
|-----|------|---------|-------------|
|
|
390
|
+
| `placeholder` | `string` | `'Start writing…'` | Placeholder shown in the empty editor. |
|
|
391
|
+
| `mentionOptions` | `Object` | `{}` | Forwarded into the Mention extension's `suggestion` config. |
|
|
392
|
+
| `isEditable` | `boolean` | `true` | Pass `false` to skip loading the drag-handle extension. |
|
|
393
|
+
|
|
394
|
+
**Styles**
|
|
395
|
+
|
|
396
|
+
`editorStyles` is a plain CSS string containing all `.rte-*` class definitions. The built-in components inject it automatically. Only import it manually if you are building a custom editor without `InkoraEditor` / `InkoraBasicEditor`.
|
|
397
|
+
|
|
398
|
+
```jsx
|
|
399
|
+
import { editorStyles } from 'inkora';
|
|
400
|
+
|
|
401
|
+
<style dangerouslySetInnerHTML={{ __html: editorStyles }} />
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Keyboard shortcuts
|
|
407
|
+
|
|
408
|
+
> Substitute `⌘` with `Ctrl` on Windows / Linux.
|
|
409
|
+
|
|
410
|
+
| Shortcut | Action |
|
|
411
|
+
|----------|--------|
|
|
412
|
+
| `⌘ B` | Bold |
|
|
413
|
+
| `⌘ I` | Italic |
|
|
414
|
+
| `⌘ U` | Underline |
|
|
415
|
+
| `⌘ Shift S` | Strikethrough |
|
|
416
|
+
| `⌘ K` | Insert / edit link |
|
|
417
|
+
| `⌘ E` | Inline code |
|
|
418
|
+
| `⌘ Z` | Undo |
|
|
419
|
+
| `⌘ Y` | Redo |
|
|
420
|
+
| `⌘ A` | Select all |
|
|
421
|
+
| `⌘ ,` | Subscript |
|
|
422
|
+
| `⌘ .` | Superscript |
|
|
423
|
+
| `⌘ \` | Clear all formatting |
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Built-in extensions
|
|
428
|
+
|
|
429
|
+
These TipTap extensions are loaded automatically. You do not need to configure them individually unless you use `createEditorExtensions` directly.
|
|
430
|
+
|
|
431
|
+
| Extension | What it provides |
|
|
432
|
+
|-----------|-----------------|
|
|
433
|
+
| `StarterKit` | Document, paragraph, headings, bold, italic, code, blockquote, HR, lists, history (undo/redo) |
|
|
434
|
+
| `Underline` | Underline mark |
|
|
435
|
+
| `Highlight` | Multi-colour highlight marks |
|
|
436
|
+
| `TextStyle` | Inline style container (required by Color and FontFamily) |
|
|
437
|
+
| `Color` | Text colour |
|
|
438
|
+
| `FontFamily` | Font family |
|
|
439
|
+
| `FontSize` *(custom)* | Font size (8 – 96 pt) |
|
|
440
|
+
| `LineHeight` *(custom)* | Line height per block |
|
|
441
|
+
| `TextAlign` | Left / centre / right / justify alignment on blocks |
|
|
442
|
+
| `TaskList` + `TaskItem` | Interactive checkbox lists (nested supported) |
|
|
443
|
+
| `Link` | Hyperlink mark — opens in new tab, `rel="noopener noreferrer"` |
|
|
444
|
+
| `Image` *(extended)* | Images with resize handles, shape, frame, filters, alignment |
|
|
445
|
+
| `Table` + rows / cells | Full table support with resizable columns |
|
|
446
|
+
| `Subscript` | Subscript mark |
|
|
447
|
+
| `Superscript` | Superscript mark |
|
|
448
|
+
| `Callout` *(custom)* | Coloured callout blocks (blue, green, yellow, red) |
|
|
449
|
+
| `Video` *(custom)* | Block video player with resize / shape / filters |
|
|
450
|
+
| `Audio` *(custom)* | Block audio player |
|
|
451
|
+
| `Math` *(custom)* | KaTeX block math formulas (click to edit) |
|
|
452
|
+
| `Youtube` *(extended)* | YouTube / Vimeo embeds |
|
|
453
|
+
| `Hashtag` *(custom)* | `#tag` input rule |
|
|
454
|
+
| `Mention` | `@mention` with configurable suggestion source |
|
|
455
|
+
| `Placeholder` | Ghost placeholder text |
|
|
456
|
+
| `CharacterCount` | Character and word count data |
|
|
457
|
+
| `Dropcursor` | Drag-and-drop position indicator |
|
|
458
|
+
| `Gapcursor` | Cursor in gaps between block nodes |
|
|
459
|
+
| `CodeBlockLowlight` *(extended)* | Fenced code blocks with syntax highlighting and collapse toggle |
|
|
460
|
+
| `GlobalDragHandle` | Drag handle on the left of every block node |
|
|
461
|
+
| `EmojiInputRules` *(custom)* | `:heart:` → ❤️ `:smile:` → 😊 `:rocket:` → 🚀 `:fire:` → 🔥 |
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Framework notes
|
|
466
|
+
|
|
467
|
+
**Next.js (App Router)**
|
|
468
|
+
|
|
469
|
+
All Inkora components include `'use client'` at the top. Import them inside a client component, or lazy-load to skip SSR:
|
|
470
|
+
|
|
471
|
+
```jsx
|
|
472
|
+
'use client';
|
|
473
|
+
import { InkoraEditor } from 'inkora';
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
```jsx
|
|
477
|
+
// Skip SSR entirely
|
|
478
|
+
import dynamic from 'next/dynamic';
|
|
479
|
+
|
|
480
|
+
const InkoraEditor = dynamic(
|
|
481
|
+
() => import('inkora').then(m => m.InkoraEditor),
|
|
482
|
+
{ ssr: false }
|
|
483
|
+
);
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Vite / Create React App**
|
|
487
|
+
|
|
488
|
+
No special configuration needed. Import and use directly.
|
|
489
|
+
|
|
490
|
+
**KaTeX stylesheet**
|
|
491
|
+
|
|
492
|
+
If you use math blocks, add the KaTeX stylesheet once at your app root:
|
|
493
|
+
|
|
494
|
+
```js
|
|
495
|
+
// _app.js, layout.js, or main.jsx
|
|
496
|
+
import 'katex/dist/katex.min.css';
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Contributing
|
|
502
|
+
|
|
503
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to set up the dev environment, submit pull requests, and report bugs.
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## License
|
|
508
|
+
|
|
509
|
+
MIT © Inkora contributors
|