@voilabs/mark 0.0.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/LICENSE +21 -0
- package/README.md +728 -0
- package/dist/components/Editor.d.ts +31 -0
- package/dist/components/Editor.d.ts.map +1 -0
- package/dist/components/editor/EditorContext.d.ts +16 -0
- package/dist/components/editor/EditorContext.d.ts.map +1 -0
- package/dist/components/editor/GlobalDragHandleMenu.d.ts +8 -0
- package/dist/components/editor/GlobalDragHandleMenu.d.ts.map +1 -0
- package/dist/components/editor/StyleBar.d.ts +26 -0
- package/dist/components/editor/StyleBar.d.ts.map +1 -0
- package/dist/components/editor/StyleEditPopover.d.ts +19 -0
- package/dist/components/editor/StyleEditPopover.d.ts.map +1 -0
- package/dist/components/editor/Toolbar.d.ts +21 -0
- package/dist/components/editor/Toolbar.d.ts.map +1 -0
- package/dist/components/editor/classNames.d.ts +136 -0
- package/dist/components/editor/classNames.d.ts.map +1 -0
- package/dist/components/editor/extensions/AdvancedImage.d.ts +22 -0
- package/dist/components/editor/extensions/AdvancedImage.d.ts.map +1 -0
- package/dist/components/editor/extensions/FontFamily.d.ts +14 -0
- package/dist/components/editor/extensions/FontFamily.d.ts.map +1 -0
- package/dist/components/editor/extensions/FontSize.d.ts +14 -0
- package/dist/components/editor/extensions/FontSize.d.ts.map +1 -0
- package/dist/components/editor/extensions/ImageNodeView.d.ts +2 -0
- package/dist/components/editor/extensions/ImageNodeView.d.ts.map +1 -0
- package/dist/components/editor/extensions/TextStyleAttributes.d.ts +10 -0
- package/dist/components/editor/extensions/TextStyleAttributes.d.ts.map +1 -0
- package/dist/components/editor/extensions/Video.d.ts +21 -0
- package/dist/components/editor/extensions/Video.d.ts.map +1 -0
- package/dist/components/editor/paragraphStyles.d.ts +42 -0
- package/dist/components/editor/paragraphStyles.d.ts.map +1 -0
- package/dist/components/editor/slash-command/commands-list.d.ts +27 -0
- package/dist/components/editor/slash-command/commands-list.d.ts.map +1 -0
- package/dist/components/editor/slash-command/commands.d.ts +15 -0
- package/dist/components/editor/slash-command/commands.d.ts.map +1 -0
- package/dist/components/editor/slash-command/suggestion.d.ts +38 -0
- package/dist/components/editor/slash-command/suggestion.d.ts.map +1 -0
- package/dist/components/editor/translations.d.ts +151 -0
- package/dist/components/editor/translations.d.ts.map +1 -0
- package/dist/components/editor/useStyleStorage.d.ts +19 -0
- package/dist/components/editor/useStyleStorage.d.ts.map +1 -0
- package/dist/components/editor/utils/upload.d.ts +5 -0
- package/dist/components/editor/utils/upload.d.ts.map +1 -0
- package/dist/components/ui/Dropdown.d.ts +16 -0
- package/dist/components/ui/Dropdown.d.ts.map +1 -0
- package/dist/components/ui/DropdownMenu.d.ts +28 -0
- package/dist/components/ui/DropdownMenu.d.ts.map +1 -0
- package/dist/components/ui/Popover.d.ts +11 -0
- package/dist/components/ui/Popover.d.ts.map +1 -0
- package/dist/components/ui/Tooltip.d.ts +7 -0
- package/dist/components/ui/Tooltip.d.ts.map +1 -0
- package/dist/index.cjs.js +151 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +22724 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/plugin.cjs.js +10 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.es.js +575 -0
- package/dist/translations/en-US.cjs.js +1 -0
- package/dist/translations/en-US.d.ts +4 -0
- package/dist/translations/en-US.d.ts.map +1 -0
- package/dist/translations/en-US.es.js +153 -0
- package/dist/translations/tr-TR.cjs.js +1 -0
- package/dist/translations/tr-TR.d.ts +4 -0
- package/dist/translations/tr-TR.d.ts.map +1 -0
- package/dist/translations/tr-TR.es.js +153 -0
- package/package.json +121 -0
package/README.md
ADDED
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
# @voilabs/mark
|
|
2
|
+
|
|
3
|
+
A modern React rich text editor library built on top of Tiptap, Tailwind CSS, Radix UI, and TypeScript.
|
|
4
|
+
|
|
5
|
+
It is designed as a reusable package rather than a demo application, and ships a polished editing shell with a formatting toolbar, slash commands, media support, paragraph styles, theme switching, translations, and a Tailwind integration helper.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Overview](#overview)
|
|
10
|
+
- [Quick Start](#quick-start)
|
|
11
|
+
- [Highlights](#highlights)
|
|
12
|
+
- [Prerequisites](#prerequisites)
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Tailwind Integration](#tailwind-integration)
|
|
15
|
+
- [API Reference](#api-reference)
|
|
16
|
+
- [Development Workflow](#development-workflow)
|
|
17
|
+
- [Build and Distribution](#build-and-distribution)
|
|
18
|
+
- [Dependency Management](#dependency-management)
|
|
19
|
+
- [Architecture](#architecture)
|
|
20
|
+
- [Project Structure](#project-structure)
|
|
21
|
+
- [Tech Stack and Dependencies](#tech-stack-and-dependencies)
|
|
22
|
+
- [Contributing](#contributing)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
|
|
27
|
+
`@voilabs/mark` is a library-first editor package that exposes a ready-to-use `Editor` component and several supporting exports for advanced integration scenarios.
|
|
28
|
+
|
|
29
|
+
The package focuses on:
|
|
30
|
+
|
|
31
|
+
- rich text authoring with Tiptap
|
|
32
|
+
- consistent, modern UI primitives using Radix UI
|
|
33
|
+
- Tailwind-friendly styling and host-project theming
|
|
34
|
+
- configurable typography and named paragraph styles
|
|
35
|
+
- image and video insertion workflows
|
|
36
|
+
- English and Turkish localization support
|
|
37
|
+
- distribution as a typed library with multiple entry points
|
|
38
|
+
|
|
39
|
+
The editor accepts initial content as HTML or Markdown, can auto-detect the incoming format, and emits content updates as Markdown through `onChange`.
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { Editor } from "@voilabs/mark";
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Basic Usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { useState } from "react";
|
|
51
|
+
import { Editor } from "@voilabs/mark";
|
|
52
|
+
import translations from "@voilabs/mark/translations/en-US";
|
|
53
|
+
|
|
54
|
+
export function ArticleEditor() {
|
|
55
|
+
const [value, setValue] = useState("# Hello world");
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Editor
|
|
59
|
+
value={value}
|
|
60
|
+
format="auto"
|
|
61
|
+
theme="auto"
|
|
62
|
+
translations={translations}
|
|
63
|
+
onChange={setValue}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### With Upload Support
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { Editor } from "@voilabs/mark";
|
|
73
|
+
|
|
74
|
+
async function uploadFile(file: File): Promise<{ url: string }> {
|
|
75
|
+
const formData = new FormData();
|
|
76
|
+
formData.append("file", file);
|
|
77
|
+
|
|
78
|
+
const response = await fetch("/api/upload", {
|
|
79
|
+
method: "POST",
|
|
80
|
+
body: formData,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
throw new Error("Upload failed");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return response.json();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function MediaEnabledEditor() {
|
|
91
|
+
return (
|
|
92
|
+
<Editor
|
|
93
|
+
value="Start writing..."
|
|
94
|
+
onUpload={uploadFile}
|
|
95
|
+
onChange={(markdown) => {
|
|
96
|
+
console.log(markdown);
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### With Custom Paragraph Styles
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import {
|
|
107
|
+
Editor,
|
|
108
|
+
DEFAULT_PARAGRAPH_STYLES,
|
|
109
|
+
type ParagraphStyle,
|
|
110
|
+
} from "@voilabs/mark";
|
|
111
|
+
|
|
112
|
+
const customStyles: ParagraphStyle[] = [
|
|
113
|
+
...DEFAULT_PARAGRAPH_STYLES,
|
|
114
|
+
{
|
|
115
|
+
id: "callout",
|
|
116
|
+
label: "Callout",
|
|
117
|
+
preview: "Callout",
|
|
118
|
+
type: "blockquote",
|
|
119
|
+
fontSize: "18px",
|
|
120
|
+
fontWeight: "600",
|
|
121
|
+
color: "#0f172a",
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
export function StyledEditor() {
|
|
126
|
+
return <Editor styles={customStyles} />;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### With Translation Overrides
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Editor } from "@voilabs/mark";
|
|
134
|
+
import trTR from "@voilabs/mark/translations/tr-TR";
|
|
135
|
+
|
|
136
|
+
export function TurkishEditor() {
|
|
137
|
+
return (
|
|
138
|
+
<Editor
|
|
139
|
+
translations={{
|
|
140
|
+
...trTR,
|
|
141
|
+
placeholder: "Yazmaya baslayin",
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Highlights
|
|
149
|
+
|
|
150
|
+
### Editing Experience
|
|
151
|
+
|
|
152
|
+
- Toolbar with undo/redo, headings, lists, quote, code block, inline marks, text alignment, links, highlight colors, font family, and font size controls
|
|
153
|
+
- Slash command menu triggered by `/` for quick insertion of text blocks, headings, lists, images, tables, quotes, and code blocks
|
|
154
|
+
- Word count footer with zoom controls and fullscreen toggle
|
|
155
|
+
- Global drag handle for block insertion, duplication, reordering, anchor-link copying, color actions, and deletion
|
|
156
|
+
|
|
157
|
+
### Media Support
|
|
158
|
+
|
|
159
|
+
- Custom image node implementation with resize handles, crop mode, rotation, object-fit frame mode, pan controls, upload state, and alt text editing
|
|
160
|
+
- Video node support for uploaded video insertion
|
|
161
|
+
- Drag-and-drop and paste image upload handling when an `onUpload` callback is provided
|
|
162
|
+
- Slash-command image insertion with file picker support or URL prompt fallback
|
|
163
|
+
|
|
164
|
+
### Styling and Customization
|
|
165
|
+
|
|
166
|
+
- Tailwind-based UI with overridable class slots via `EditorClassNames`
|
|
167
|
+
- Built-in light and dark themes with persisted theme preference in cookie/localStorage when `theme="auto"`
|
|
168
|
+
- Named paragraph styles with a Word-like quick style gallery
|
|
169
|
+
- Right-click style editing with local persistence in `localStorage`
|
|
170
|
+
- Translation override support through `translations`
|
|
171
|
+
|
|
172
|
+
### Packaging
|
|
173
|
+
|
|
174
|
+
- Typed library output generated by Vite and `vite-plugin-dts`
|
|
175
|
+
- ESM, UMD, and declaration outputs under `dist`
|
|
176
|
+
|
|
177
|
+
## Prerequisites
|
|
178
|
+
|
|
179
|
+
Before using or developing this package, make sure you have:
|
|
180
|
+
|
|
181
|
+
- Node.js 20 or newer recommended
|
|
182
|
+
- npm 10 or newer recommended
|
|
183
|
+
- a React application using React 19
|
|
184
|
+
- TypeScript 5 if you want full type support
|
|
185
|
+
|
|
186
|
+
If you plan to use the Tailwind helper export, your host application should also be configured for Tailwind CSS.
|
|
187
|
+
|
|
188
|
+
## Installation
|
|
189
|
+
|
|
190
|
+
### Library Consumers
|
|
191
|
+
|
|
192
|
+
Install the package and its peer dependencies:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npm install @voilabs/mark react react-dom typescript
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Tailwind Plugin Consumers
|
|
199
|
+
|
|
200
|
+
If you use the exported Tailwind plugin helper, also ensure your project has the typography plugin available:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
npm install @tailwindcss/typography
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Repository Setup
|
|
207
|
+
|
|
208
|
+
To work on the library itself:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
bun install
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Tailwind Integration
|
|
215
|
+
|
|
216
|
+
The editor UI is built with Tailwind utility classes. In host projects, the package export `@voilabs/mark/plugin` can help Tailwind discover the library's compiled class usage.
|
|
217
|
+
|
|
218
|
+
Example configuration:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import editorTailwindPlugin from "@voilabs/mark/plugin";
|
|
222
|
+
|
|
223
|
+
export default {
|
|
224
|
+
plugins: [editorTailwindPlugin],
|
|
225
|
+
};
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
What the plugin does:
|
|
229
|
+
|
|
230
|
+
- resolves the installed package path
|
|
231
|
+
- points Tailwind content scanning to the package `dist` files
|
|
232
|
+
- enables `tailwindcss-animate`
|
|
233
|
+
- enables `@tailwindcss/typography`
|
|
234
|
+
|
|
235
|
+
## API Reference
|
|
236
|
+
|
|
237
|
+
### `Editor`
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { Editor } from "@voilabs/mark";
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### `EditorProps`
|
|
244
|
+
|
|
245
|
+
| Prop | Type | Description |
|
|
246
|
+
| -------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
247
|
+
| `value` | `string` | Initial editor content. Supports HTML or Markdown depending on `format`. |
|
|
248
|
+
| `onChange` | `(value: string) => void` | Receives Markdown output whenever the editor updates. |
|
|
249
|
+
| `format` | `"html" \| "markdown" \| "auto"` | Controls how the initial value is interpreted. `auto` detects Markdown-like input. |
|
|
250
|
+
| `theme` | `"light" \| "dark" \| "auto"` | Forces a theme or enables persisted user switching. |
|
|
251
|
+
| `onUpload` | `(file: File) => Promise<{ url: string }>` | Upload handler used by toolbar uploads, slash-command image insertion, paste uploads, and drag/drop uploads. |
|
|
252
|
+
| `disableVideoUpload` | `boolean` | Disables video upload actions in the toolbar. |
|
|
253
|
+
| `disableImageUpload` | `boolean` | Disables image upload actions and image upload-driven insertion flows. |
|
|
254
|
+
| `classNames` | `EditorClassNames` | Fine-grained class slot overrides for editor shell, toolbar, menus, footer, drag handle, and image UI. |
|
|
255
|
+
| `translations` | `Partial<EditorTranslations>` | Partial translation override merged over built-in English strings. |
|
|
256
|
+
| `styles` | `ParagraphStyle[]` | Replaces the default quick style set shown in the toolbar style gallery. |
|
|
257
|
+
|
|
258
|
+
### `ParagraphStyle`
|
|
259
|
+
|
|
260
|
+
Each paragraph style can define:
|
|
261
|
+
|
|
262
|
+
- `id`
|
|
263
|
+
- `label`
|
|
264
|
+
- `preview`
|
|
265
|
+
- `type`
|
|
266
|
+
- `level`
|
|
267
|
+
- `fontFamily`
|
|
268
|
+
- `fontSize`
|
|
269
|
+
- `fontWeight`
|
|
270
|
+
- `fontStyle`
|
|
271
|
+
- `color`
|
|
272
|
+
- `letterSpacing`
|
|
273
|
+
- `lineHeight`
|
|
274
|
+
- `textTransform`
|
|
275
|
+
|
|
276
|
+
### `EditorClassNames`
|
|
277
|
+
|
|
278
|
+
`EditorClassNames` exposes slot-level styling hooks for nearly every visible part of the editor.
|
|
279
|
+
|
|
280
|
+
### Shell and Content Slots
|
|
281
|
+
|
|
282
|
+
| Slot | Purpose |
|
|
283
|
+
| ----------------- | ------------------------------ |
|
|
284
|
+
| `root` | Outer editor wrapper |
|
|
285
|
+
| `content` | Main scrollable content region |
|
|
286
|
+
| `contentScroller` | Content scrolling container |
|
|
287
|
+
| `contentInner` | Inner centered content wrapper |
|
|
288
|
+
| `editorContent` | `EditorContent` host container |
|
|
289
|
+
| `prose` | Tiptap prose typography layer |
|
|
290
|
+
|
|
291
|
+
### Footer Slots
|
|
292
|
+
|
|
293
|
+
| Slot | Purpose |
|
|
294
|
+
| ------------------------ | ------------------------------------ |
|
|
295
|
+
| `footer` | Footer root container |
|
|
296
|
+
| `footerWordCount` | Word count label area |
|
|
297
|
+
| `footerControls` | Footer controls wrapper |
|
|
298
|
+
| `footerButton` | Shared footer button styling |
|
|
299
|
+
| `footerButtonDisabled` | Disabled footer button state |
|
|
300
|
+
| `footerZoomGroup` | Zoom controls group |
|
|
301
|
+
| `footerZoomButton` | Zoom increment and decrement buttons |
|
|
302
|
+
| `footerZoomValue` | Zoom percentage button |
|
|
303
|
+
| `footerFullscreenButton` | Fullscreen toggle button |
|
|
304
|
+
|
|
305
|
+
### Toolbar Slots
|
|
306
|
+
|
|
307
|
+
| Slot | Purpose |
|
|
308
|
+
| ----------------------- | -------------------------------- |
|
|
309
|
+
| `toolbar` | Toolbar root container |
|
|
310
|
+
| `toolbarInner` | Toolbar scroll container |
|
|
311
|
+
| `toolbarGroup` | Toolbar button group wrapper |
|
|
312
|
+
| `toolbarButton` | Shared toolbar button styling |
|
|
313
|
+
| `toolbarButtonActive` | Active toolbar button state |
|
|
314
|
+
| `toolbarButtonDisabled` | Disabled toolbar button state |
|
|
315
|
+
| `toolbarButtonIcon` | Toolbar button icon wrapper |
|
|
316
|
+
| `toolbarTrigger` | Dropdown trigger buttons |
|
|
317
|
+
| `toolbarTriggerActive` | Active dropdown trigger state |
|
|
318
|
+
| `toolbarTriggerIcon` | Dropdown trigger icon wrapper |
|
|
319
|
+
| `toolbarTriggerLabel` | Dropdown trigger label text |
|
|
320
|
+
| `toolbarTriggerChevron` | Dropdown trigger chevron |
|
|
321
|
+
| `toolbarSeparator` | Separator between toolbar groups |
|
|
322
|
+
| `toolbarFileInput` | Hidden media file input |
|
|
323
|
+
| `toolbarUploadButton` | Upload button |
|
|
324
|
+
| `toolbarUploadIcon` | Upload button icon |
|
|
325
|
+
| `toolbarUploadLabel` | Upload button label |
|
|
326
|
+
|
|
327
|
+
### Style Bar Slots
|
|
328
|
+
|
|
329
|
+
| Slot | Purpose |
|
|
330
|
+
| -------------------- | ----------------------------- |
|
|
331
|
+
| `styleBar` | Quick style gallery container |
|
|
332
|
+
| `styleBarItem` | Individual style card |
|
|
333
|
+
| `styleBarItemActive` | Active style card |
|
|
334
|
+
|
|
335
|
+
### Style Editor Slots
|
|
336
|
+
|
|
337
|
+
| Slot | Purpose |
|
|
338
|
+
| ---------------------- | ------------------------------------ |
|
|
339
|
+
| `styleEditPopover` | Right-click style edit popover |
|
|
340
|
+
| `styleEditInput` | Inputs inside the style editor |
|
|
341
|
+
| `styleEditSaveButton` | Save button inside the style editor |
|
|
342
|
+
| `styleEditResetButton` | Reset button inside the style editor |
|
|
343
|
+
|
|
344
|
+
### Font Family Slots
|
|
345
|
+
|
|
346
|
+
| Slot | Purpose |
|
|
347
|
+
| ----------------------------- | ---------------------------- |
|
|
348
|
+
| `toolbarFontFamilyTrigger` | Font family combobox trigger |
|
|
349
|
+
| `toolbarFontFamilyPopover` | Font family popover body |
|
|
350
|
+
| `toolbarFontFamilySearch` | Font family search input |
|
|
351
|
+
| `toolbarFontFamilyList` | Font family list container |
|
|
352
|
+
| `toolbarFontFamilyItem` | Individual font option |
|
|
353
|
+
| `toolbarFontFamilyItemActive` | Active font option |
|
|
354
|
+
|
|
355
|
+
### Font Size Slots
|
|
356
|
+
|
|
357
|
+
| Slot | Purpose |
|
|
358
|
+
| ------------------------------ | ------------------------------ |
|
|
359
|
+
| `toolbarFontSizeControl` | Font size control wrapper |
|
|
360
|
+
| `toolbarFontSizeControlActive` | Active font size control state |
|
|
361
|
+
| `toolbarFontSizeIcon` | Font size icon wrapper |
|
|
362
|
+
| `toolbarFontSizeInput` | Font size text input |
|
|
363
|
+
| `toolbarFontSizeUnit` | Font size unit label |
|
|
364
|
+
| `toolbarFontSizeStepButton` | Increase and decrease buttons |
|
|
365
|
+
| `toolbarFontSizeResetButton` | Reset font size button |
|
|
366
|
+
|
|
367
|
+
### Link Popover Slots
|
|
368
|
+
|
|
369
|
+
| Slot | Purpose |
|
|
370
|
+
| ------------------------- | ---------------------------- |
|
|
371
|
+
| `toolbarLinkButton` | Link toolbar button |
|
|
372
|
+
| `toolbarLinkPopoverBody` | Link popover content wrapper |
|
|
373
|
+
| `toolbarLinkPopoverLabel` | Link popover heading |
|
|
374
|
+
| `toolbarLinkInput` | Link URL input |
|
|
375
|
+
| `toolbarLinkActions` | Link action buttons wrapper |
|
|
376
|
+
| `toolbarLinkApplyButton` | Apply link button |
|
|
377
|
+
| `toolbarLinkUnlinkButton` | Remove link button |
|
|
378
|
+
|
|
379
|
+
### Highlight Slots
|
|
380
|
+
|
|
381
|
+
| Slot | Purpose |
|
|
382
|
+
| ------------------------------ | -------------------------------- |
|
|
383
|
+
| `toolbarHighlightButton` | Highlight toolbar button |
|
|
384
|
+
| `toolbarHighlightIndicator` | Color indicator under the button |
|
|
385
|
+
| `toolbarHighlightPopoverBody` | Highlight palette popover body |
|
|
386
|
+
| `toolbarHighlightPopoverLabel` | Highlight palette label |
|
|
387
|
+
| `toolbarHighlightGrid` | Highlight swatch grid |
|
|
388
|
+
| `toolbarHighlightSwatch` | Individual highlight swatch |
|
|
389
|
+
| `toolbarHighlightSwatchActive` | Active highlight swatch |
|
|
390
|
+
| `toolbarHighlightClearButton` | Clear highlight button |
|
|
391
|
+
|
|
392
|
+
### Shared Overlay Slots
|
|
393
|
+
|
|
394
|
+
| Slot | Purpose |
|
|
395
|
+
| --------------------------- | --------------------------------- |
|
|
396
|
+
| `tooltipContent` | Tooltip content container |
|
|
397
|
+
| `tooltipArrow` | Tooltip arrow |
|
|
398
|
+
| `popoverContent` | Shared popover content container |
|
|
399
|
+
| `dropdownContent` | Main dropdown menu content |
|
|
400
|
+
| `dropdownSubContent` | Nested dropdown menu content |
|
|
401
|
+
| `dropdownSubTrigger` | Nested dropdown trigger item |
|
|
402
|
+
| `dropdownSubTriggerChevron` | Nested dropdown chevron |
|
|
403
|
+
| `dropdownItem` | Standard dropdown item |
|
|
404
|
+
| `dropdownItemActive` | Active dropdown item state |
|
|
405
|
+
| `dropdownItemIcon` | Dropdown item icon |
|
|
406
|
+
| `dropdownItemLabel` | Dropdown item label |
|
|
407
|
+
| `dropdownItemCheck` | Dropdown selected-state checkmark |
|
|
408
|
+
| `dropdownCheckboxItem` | Checkbox dropdown item |
|
|
409
|
+
| `dropdownRadioItem` | Radio dropdown item |
|
|
410
|
+
| `dropdownIndicator` | Dropdown indicator wrapper |
|
|
411
|
+
| `dropdownLabel` | Dropdown section label |
|
|
412
|
+
| `dropdownSeparator` | Dropdown separator |
|
|
413
|
+
| `dropdownShortcut` | Dropdown shortcut hint text |
|
|
414
|
+
|
|
415
|
+
### Slash Command Slots
|
|
416
|
+
|
|
417
|
+
| Slot | Purpose |
|
|
418
|
+
| ------------------------ | ---------------------------- |
|
|
419
|
+
| `slashCommandMenu` | Slash command menu container |
|
|
420
|
+
| `slashCommandItem` | Slash command item |
|
|
421
|
+
| `slashCommandItemActive` | Active slash command item |
|
|
422
|
+
| `slashCommandIcon` | Slash command icon wrapper |
|
|
423
|
+
| `slashCommandTitle` | Slash command item label |
|
|
424
|
+
| `slashCommandEmpty` | Empty state wrapper |
|
|
425
|
+
| `slashCommandEmptyIcon` | Empty state icon |
|
|
426
|
+
|
|
427
|
+
### Drag Handle Slots
|
|
428
|
+
|
|
429
|
+
| Slot | Purpose |
|
|
430
|
+
| ----------------------------- | ----------------------------------- |
|
|
431
|
+
| `dragHandleRoot` | Floating drag handle root |
|
|
432
|
+
| `dragHandleAddButton` | Add-block button |
|
|
433
|
+
| `dragHandleMenuButton` | Grip/menu button |
|
|
434
|
+
| `dragHandleMenuContent` | Drag handle dropdown content |
|
|
435
|
+
| `dragHandleSubContent` | Nested drag handle dropdown content |
|
|
436
|
+
| `dragHandleColorSectionLabel` | Text/background color section label |
|
|
437
|
+
| `dragHandleColorTextPreview` | Text color preview glyph |
|
|
438
|
+
| `dragHandleColorSwatch` | Background color swatch |
|
|
439
|
+
| `dragHandleCheckIcon` | Selected-state check icon |
|
|
440
|
+
| `dragHandleDeleteItem` | Delete item styling |
|
|
441
|
+
|
|
442
|
+
### Image Node Slots
|
|
443
|
+
|
|
444
|
+
| Slot | Purpose |
|
|
445
|
+
| ------------------------------ | ---------------------------- |
|
|
446
|
+
| `imageNodeWrapper` | Outer image node wrapper |
|
|
447
|
+
| `imageContainer` | Image selection frame |
|
|
448
|
+
| `imageCropArea` | Crop area wrapper |
|
|
449
|
+
| `imageCropToolbar` | Crop mode toolbar |
|
|
450
|
+
| `imageCropButton` | Shared crop toolbar button |
|
|
451
|
+
| `imageCropCancelButton` | Cancel crop button |
|
|
452
|
+
| `imageCropApplyButton` | Apply crop button |
|
|
453
|
+
| `imageFrame` | Image frame container |
|
|
454
|
+
| `imageElement` | Rendered `<img>` element |
|
|
455
|
+
| `imageLoadingOverlay` | Upload/loading overlay |
|
|
456
|
+
| `imageLoadingBox` | Loader badge container |
|
|
457
|
+
| `imageLoadingIcon` | Loader icon |
|
|
458
|
+
| `imagePanHintOverlay` | Pan hint overlay |
|
|
459
|
+
| `imagePanHint` | Pan hint text |
|
|
460
|
+
| `imageToolbar` | Image hover toolbar |
|
|
461
|
+
| `imageToolbarButton` | Shared image toolbar button |
|
|
462
|
+
| `imageToolbarSeparator` | Divider inside image toolbar |
|
|
463
|
+
| `imageDeleteButton` | Image delete button |
|
|
464
|
+
| `imageAltButton` | Alt text button |
|
|
465
|
+
| `imageAltPopover` | Alt text popover body |
|
|
466
|
+
| `imageAltLabel` | Alt text popover label |
|
|
467
|
+
| `imageAltInput` | Alt text input |
|
|
468
|
+
| `imageAltActions` | Alt text action row |
|
|
469
|
+
| `imageAltApplyButton` | Apply alt text button |
|
|
470
|
+
| `imageAltClearButton` | Clear alt text button |
|
|
471
|
+
| `imageResizeHandle` | Shared resize handle styling |
|
|
472
|
+
| `imageResizeHandleRight` | Right resize handle |
|
|
473
|
+
| `imageResizeHandleBottom` | Bottom resize handle |
|
|
474
|
+
| `imageResizeHandleBottomRight` | Bottom-right resize handle |
|
|
475
|
+
| `imageModeBadge` | Mode hint wrapper |
|
|
476
|
+
| `imageModeText` | Mode hint label |
|
|
477
|
+
|
|
478
|
+
This API is intended for design system integration without forcing a fork of the editor internals.
|
|
479
|
+
|
|
480
|
+
### Translations
|
|
481
|
+
|
|
482
|
+
The library ships with:
|
|
483
|
+
|
|
484
|
+
- `en-US`
|
|
485
|
+
- `tr-TR`
|
|
486
|
+
|
|
487
|
+
You can either import a full locale object or pass a partial object to override selected labels.
|
|
488
|
+
|
|
489
|
+
## Development Workflow
|
|
490
|
+
|
|
491
|
+
This repository is configured as a library project, not as a local preview app.
|
|
492
|
+
|
|
493
|
+
### Install Dependencies
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
bun install
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Development Build Watch
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
bun run dev
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
This runs:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
vite build --watch
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Use this mode when you want incremental rebuilds of the library output while developing the package.
|
|
512
|
+
|
|
513
|
+
### Production Build
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
bun run build
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
This runs:
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
vite build && tsc -p tsconfig.build.json
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
The command generates JavaScript bundles and declaration files into `dist/`.
|
|
526
|
+
|
|
527
|
+
## Build and Distribution
|
|
528
|
+
|
|
529
|
+
The Vite configuration builds multiple library entry points:
|
|
530
|
+
|
|
531
|
+
- `src/index.ts`
|
|
532
|
+
- `src/plugin.ts`
|
|
533
|
+
- `src/translations/en-US.ts`
|
|
534
|
+
- `src/translations/tr-TR.ts`
|
|
535
|
+
|
|
536
|
+
The package metadata exposes:
|
|
537
|
+
|
|
538
|
+
- main editor entry
|
|
539
|
+
- Tailwind plugin entry
|
|
540
|
+
- locale entries
|
|
541
|
+
|
|
542
|
+
Distributed assets include:
|
|
543
|
+
|
|
544
|
+
- ESM bundles
|
|
545
|
+
- UMD bundles
|
|
546
|
+
- `.d.ts` type declarations
|
|
547
|
+
|
|
548
|
+
The package is configured to publish:
|
|
549
|
+
|
|
550
|
+
- `dist`
|
|
551
|
+
- `README.md`
|
|
552
|
+
- `LICENSE`
|
|
553
|
+
- `package.json`
|
|
554
|
+
|
|
555
|
+
## Dependency Management
|
|
556
|
+
|
|
557
|
+
### Peer Dependencies
|
|
558
|
+
|
|
559
|
+
The package declares the following peers:
|
|
560
|
+
|
|
561
|
+
- `react`
|
|
562
|
+
- `react-dom`
|
|
563
|
+
- `typescript`
|
|
564
|
+
|
|
565
|
+
### Runtime Dependencies
|
|
566
|
+
|
|
567
|
+
The runtime layer relies heavily on:
|
|
568
|
+
|
|
569
|
+
- Tiptap extensions and editor packages
|
|
570
|
+
- Radix UI primitives
|
|
571
|
+
- Tailwind merge helpers
|
|
572
|
+
- icon and motion libraries
|
|
573
|
+
- media and popup utilities such as `react-easy-crop` and `tippy.js`
|
|
574
|
+
|
|
575
|
+
### Development Dependencies
|
|
576
|
+
|
|
577
|
+
Development and build-time tooling includes:
|
|
578
|
+
|
|
579
|
+
- Babel and Rollup-related packages
|
|
580
|
+
- Vite and React integration tooling
|
|
581
|
+
- TypeScript
|
|
582
|
+
- declaration generation plugins
|
|
583
|
+
|
|
584
|
+
### Lockfiles
|
|
585
|
+
|
|
586
|
+
The repository currently includes both `package-lock.json` and `bun.lock`, which suggests npm and Bun have both been used during development. This README uses Bun for repository workflows and npm for consumer installation examples.
|
|
587
|
+
|
|
588
|
+
## Architecture
|
|
589
|
+
|
|
590
|
+
The codebase is organized as a reusable component library with a single primary editor shell and a set of supporting modules.
|
|
591
|
+
|
|
592
|
+
### High-Level Design
|
|
593
|
+
|
|
594
|
+
1. `src/components/Editor.tsx` creates the Tiptap editor instance and composes the full editor shell.
|
|
595
|
+
2. `src/components/editor/Toolbar.tsx` provides the main authoring controls and style management UI.
|
|
596
|
+
3. `src/components/editor/GlobalDragHandleMenu.tsx` adds block-level actions beside the editing surface.
|
|
597
|
+
4. `src/components/editor/extensions/*` defines custom Tiptap extensions for images, video, font size, font family, and extra text-style attributes.
|
|
598
|
+
5. `src/components/editor/slash-command/*` implements the `/` suggestion system.
|
|
599
|
+
6. `src/components/editor/EditorContext.tsx` shares theme, translations, class names, and styles with nested UI primitives.
|
|
600
|
+
7. `src/plugin.ts` exports a Tailwind helper plugin intended to help host apps include the editor's utility classes.
|
|
601
|
+
|
|
602
|
+
### Content Flow
|
|
603
|
+
|
|
604
|
+
The editor follows this runtime flow:
|
|
605
|
+
|
|
606
|
+
1. Initial `value` is read as HTML, Markdown, or auto-detected content.
|
|
607
|
+
2. Tiptap extensions are registered, including the custom image/video and typography extensions.
|
|
608
|
+
3. User actions update the internal ProseMirror document.
|
|
609
|
+
4. `onChange` is called with `editor.getMarkdown()`.
|
|
610
|
+
|
|
611
|
+
Important behavior:
|
|
612
|
+
|
|
613
|
+
- Input supports HTML, Markdown, or auto-detection via the `format` prop.
|
|
614
|
+
- Output is Markdown on every update, regardless of the original input format.
|
|
615
|
+
- Auto theme mode persists the chosen theme using the `VOILABS_REACT_EDITOR_THEME` key.
|
|
616
|
+
- Style overrides are stored locally using the `voilabs-editor-style-overrides` key.
|
|
617
|
+
|
|
618
|
+
### Extensibility Points
|
|
619
|
+
|
|
620
|
+
- `onUpload` for custom media upload pipelines
|
|
621
|
+
- `styles` for custom paragraph style sets
|
|
622
|
+
- `classNames` for granular visual overrides
|
|
623
|
+
- `translations` for partial locale overrides
|
|
624
|
+
- exported Tiptap extensions such as `AdvancedImage`, `FontSize`, and `Video`
|
|
625
|
+
|
|
626
|
+
## Project Structure
|
|
627
|
+
|
|
628
|
+
```text
|
|
629
|
+
editor/
|
|
630
|
+
|- src/
|
|
631
|
+
| |- components/
|
|
632
|
+
| | |- Editor.tsx
|
|
633
|
+
| | |- editor/
|
|
634
|
+
| | | |- EditorContext.tsx
|
|
635
|
+
| | | |- Toolbar.tsx
|
|
636
|
+
| | | |- StyleBar.tsx
|
|
637
|
+
| | | |- StyleEditPopover.tsx
|
|
638
|
+
| | | |- GlobalDragHandleMenu.tsx
|
|
639
|
+
| | | |- classNames.ts
|
|
640
|
+
| | | |- paragraphStyles.ts
|
|
641
|
+
| | | |- translations.ts
|
|
642
|
+
| | | |- useStyleStorage.ts
|
|
643
|
+
| | | |- extensions/
|
|
644
|
+
| | | | |- AdvancedImage.tsx
|
|
645
|
+
| | | | |- ImageNodeView.tsx
|
|
646
|
+
| | | | |- FontFamily.ts
|
|
647
|
+
| | | | |- FontSize.ts
|
|
648
|
+
| | | | |- TextStyleAttributes.ts
|
|
649
|
+
| | | | `- Video.tsx
|
|
650
|
+
| | | `- slash-command/
|
|
651
|
+
| | | |- commands.ts
|
|
652
|
+
| | | |- commands-list.tsx
|
|
653
|
+
| | | `- suggestion.tsx
|
|
654
|
+
| | `- ui/
|
|
655
|
+
| | |- Dropdown.tsx
|
|
656
|
+
| | |- DropdownMenu.tsx
|
|
657
|
+
| | |- Popover.tsx
|
|
658
|
+
| | `- Tooltip.tsx
|
|
659
|
+
| |- lib/
|
|
660
|
+
| | `- utils.ts
|
|
661
|
+
| |- translations/
|
|
662
|
+
| | |- en-US.ts
|
|
663
|
+
| | `- tr-TR.ts
|
|
664
|
+
| |- index.ts
|
|
665
|
+
| `- plugin.ts
|
|
666
|
+
|- package.json
|
|
667
|
+
|- vite.config.ts
|
|
668
|
+
|- tsconfig.json
|
|
669
|
+
`- tsconfig.build.json
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
## Tech Stack and Dependencies
|
|
673
|
+
|
|
674
|
+
### Core Runtime
|
|
675
|
+
|
|
676
|
+
- React 19
|
|
677
|
+
- Tiptap 3 ecosystem
|
|
678
|
+
- ProseMirror through `@tiptap/pm`
|
|
679
|
+
- Tailwind CSS 4
|
|
680
|
+
- Radix UI popover, dropdown menu, and tooltip primitives
|
|
681
|
+
- `lucide-react` for editor icons
|
|
682
|
+
- `react-easy-crop` for image cropping
|
|
683
|
+
- `tippy.js` for slash command popup positioning
|
|
684
|
+
|
|
685
|
+
### Build Tooling
|
|
686
|
+
|
|
687
|
+
- Vite library mode
|
|
688
|
+
- TypeScript 5
|
|
689
|
+
- `vite-plugin-dts` for declaration generation
|
|
690
|
+
- `@vitejs/plugin-react`
|
|
691
|
+
- `@tailwindcss/vite`
|
|
692
|
+
|
|
693
|
+
### Noteworthy Implementation Details
|
|
694
|
+
|
|
695
|
+
- The editor uses a custom `image` node implementation instead of the default Tiptap image extension.
|
|
696
|
+
- The `Video` extension renders native HTML `<video>` nodes with controls enabled.
|
|
697
|
+
- The toolbar uses context-aware command selection so inline formatting can apply to the current word even when no explicit range is selected.
|
|
698
|
+
- The drag handle menu performs block-level actions by resolving the top-level node under the cursor.
|
|
699
|
+
- The Tailwind helper plugin tries to resolve the installed package path dynamically and scans the built `dist` files for content extraction.
|
|
700
|
+
|
|
701
|
+
## Contributing
|
|
702
|
+
|
|
703
|
+
Contributions should preserve the package's library-first design, typed public API, and editor usability.
|
|
704
|
+
|
|
705
|
+
Recommended contribution process:
|
|
706
|
+
|
|
707
|
+
1. Fork or branch from the latest mainline source.
|
|
708
|
+
2. Install dependencies with `npm install`.
|
|
709
|
+
3. Make focused changes with clear intent.
|
|
710
|
+
4. Verify the package builds successfully with `npm run build`.
|
|
711
|
+
5. Update documentation when behavior or public API changes.
|
|
712
|
+
6. Open a pull request with a concise explanation of the change.
|
|
713
|
+
|
|
714
|
+
Suggested contribution guidelines:
|
|
715
|
+
|
|
716
|
+
- keep public API changes intentional and documented
|
|
717
|
+
- avoid breaking translation keys without updating shipped locales
|
|
718
|
+
- preserve Markdown output behavior unless a deliberate API change is introduced
|
|
719
|
+
- keep Tailwind class slot compatibility in mind when changing UI structure
|
|
720
|
+
- prefer incremental, reviewable pull requests
|
|
721
|
+
|
|
722
|
+
At the moment, the repository does not define dedicated lint or test scripts in `package.json`, so build validation is the primary verification step.
|
|
723
|
+
|
|
724
|
+
## License
|
|
725
|
+
|
|
726
|
+
This project is licensed under the MIT License.
|
|
727
|
+
|
|
728
|
+
See [LICENSE](./LICENSE) for the full text.
|