oneuxi-editor 1.0.0 → 1.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.
Files changed (2) hide show
  1. package/README.md +521 -0
  2. package/package.json +7 -3
package/README.md ADDED
@@ -0,0 +1,521 @@
1
+ # oneuxi-editor
2
+
3
+ A production-ready rich text editor for React applications, built on the Lexical engine. Designed for SaaS products, internal tools, and content platforms that need a dependable editor without the weight of legacy alternatives.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/oneuxi-editor.svg)](https://www.npmjs.com/package/oneuxi-editor)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Telegram](https://img.shields.io/badge/Telegram-Join%20Community-2CA5E0?logo=telegram)](https://t.me/thedebuglab)
8
+
9
+ ---
10
+
11
+ ## Why oneuxi-editor
12
+
13
+ Most rich text solutions either carry significant legacy weight or are overly opinionated about their design system. `oneuxi-editor` is built to be dropped into any React project with a single import, minimal configuration, and a clean public API. It is SSR-safe, fully tree-shakeable, and ships without any bundled peer dependencies beyond Lexical itself.
14
+
15
+ It works independently of the `oneuxi` UI component library. If you use `oneuxi` elsewhere, they share a consistent design language. If you do not, `oneuxi-editor` stands entirely on its own.
16
+
17
+ ---
18
+
19
+ ## Table of Contents
20
+
21
+ - [Installation](#installation)
22
+ - [Basic Usage](#basic-usage)
23
+ - [Controlled and Uncontrolled Modes](#controlled-and-uncontrolled-modes)
24
+ - [Output Formats](#output-formats)
25
+ - [Toolbar Configuration](#toolbar-configuration)
26
+ - [Themes](#themes)
27
+ - [Editor Height](#editor-height)
28
+ - [Bubble Toolbar](#bubble-toolbar)
29
+ - [HTML Source Mode](#html-source-mode)
30
+ - [Images and File Uploads](#images-and-file-uploads)
31
+ - [Tables](#tables)
32
+ - [Code Blocks](#code-blocks)
33
+ - [Mentions](#mentions)
34
+ - [Slash Commands](#slash-commands)
35
+ - [Paste Cleanup](#paste-cleanup)
36
+ - [Security and Sanitization](#security-and-sanitization)
37
+ - [SSR and Next.js](#ssr-and-nextjs)
38
+ - [Imperative API](#imperative-api)
39
+ - [Props Reference](#props-reference)
40
+ - [Accessibility](#accessibility)
41
+ - [Browser Support](#browser-support)
42
+ - [Bundle Size](#bundle-size)
43
+ - [AI Features — Coming Soon](#ai-features--coming-soon)
44
+ - [Community & Support](#community--support)
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ npm install oneuxi-editor
52
+ ```
53
+
54
+ Import the stylesheet once at your application root:
55
+
56
+ ```tsx
57
+ import "oneuxi-editor/styles.css"
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Basic Usage
63
+
64
+ The simplest form of the editor requires no configuration. It renders with a default toolbar and outputs HTML on every change.
65
+
66
+ ```tsx
67
+ import { Editor } from "oneuxi-editor"
68
+ import "oneuxi-editor/styles.css"
69
+
70
+ export default function App() {
71
+ return (
72
+ <Editor
73
+ placeholder="Start writing..."
74
+ onChange={(html) => console.log(html)}
75
+ />
76
+ )
77
+ }
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Controlled and Uncontrolled Modes
83
+
84
+ **Controlled** — pass `value` and `onChange` to keep the content in sync with your state. Useful for forms, autosave, and live preview panels.
85
+
86
+ ```tsx
87
+ import { useState } from "react"
88
+ import { Editor } from "oneuxi-editor"
89
+
90
+ export default function ControlledEditor() {
91
+ const [html, setHtml] = useState("<p>Initial content</p>")
92
+
93
+ return <Editor value={html} onChange={setHtml} />
94
+ }
95
+ ```
96
+
97
+ **Uncontrolled** — pass `defaultValue` to set initial content without managing state. Useful for simple forms where you only need the final value on submit.
98
+
99
+ ```tsx
100
+ import { useRef } from "react"
101
+ import { Editor } from "oneuxi-editor"
102
+ import type { EditorRef } from "oneuxi-editor"
103
+
104
+ export default function UncontrolledForm() {
105
+ const editorRef = useRef<EditorRef>(null)
106
+
107
+ const handleSubmit = () => {
108
+ const html = editorRef.current?.getHTML()
109
+ console.log(html)
110
+ }
111
+
112
+ return (
113
+ <>
114
+ <Editor ref={editorRef} defaultValue="<p>Draft</p>" />
115
+ <button onClick={handleSubmit}>Submit</button>
116
+ </>
117
+ )
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Output Formats
124
+
125
+ The editor supports three output formats, controlled by the `output` prop.
126
+
127
+ **HTML** (default) — returns sanitized HTML as a string.
128
+
129
+ ```tsx
130
+ <Editor output="html" onChange={(html) => console.log(html)} />
131
+ ```
132
+
133
+ **JSON** — returns the Lexical editor state as a JSON string. Useful for round-trip serialization.
134
+
135
+ ```tsx
136
+ <Editor
137
+ output="json"
138
+ onChange={(json) => {
139
+ const state = JSON.parse(json)
140
+ console.log(state)
141
+ }}
142
+ />
143
+ ```
144
+
145
+ **Text** — returns plain text with all markup stripped.
146
+
147
+ ```tsx
148
+ <Editor output="text" onChange={(text) => console.log(text)} />
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Toolbar Configuration
154
+
155
+ Pass an array of toolbar item names to control which controls are shown and in what order. Use `"|"` as a separator.
156
+
157
+ ```tsx
158
+ <Editor
159
+ toolbar={[
160
+ "undo", "redo", "|",
161
+ "heading", "|",
162
+ "bold", "italic", "underline", "strike", "|",
163
+ "align", "|",
164
+ "bulletList", "orderedList", "checkList", "|",
165
+ "link", "image", "table", "codeBlock", "blockquote", "hr", "|",
166
+ "sourceMode", "findReplace", "fullscreen"
167
+ ]}
168
+ />
169
+ ```
170
+
171
+ Available items: `undo`, `redo`, `heading`, `bold`, `italic`, `underline`, `strike`, `code`, `subscript`, `superscript`, `clearFormatting`, `align`, `bulletList`, `orderedList`, `checkList`, `indent`, `outdent`, `link`, `image`, `table`, `codeBlock`, `blockquote`, `hr`, `sourceMode`, `findReplace`, `fullscreen`.
172
+
173
+ To render without any toolbar (headless mode):
174
+
175
+ ```tsx
176
+ <Editor toolbar={false} bubbleToolbar={false} />
177
+ ```
178
+
179
+ You can also insert custom React components directly into the toolbar array:
180
+
181
+ ```tsx
182
+ import { WordCountDisplay } from "./WordCountDisplay"
183
+
184
+ <Editor toolbar={["bold", "italic", WordCountDisplay]} />
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Themes
190
+
191
+ Three built-in theme modes are available. The editor respects your OS preference when set to `"system"`.
192
+
193
+ ```tsx
194
+ <Editor theme="light" />
195
+ <Editor theme="dark" />
196
+ <Editor theme="system" /> {/* follows OS dark mode setting */}
197
+ ```
198
+
199
+ For brand-specific styling, pass a theme object with any combination of CSS custom property overrides:
200
+
201
+ ```tsx
202
+ <Editor
203
+ theme={{
204
+ mode: "dark",
205
+ primary: "#7c3aed",
206
+ background: "#0f172a",
207
+ surface: "#1e293b",
208
+ border: "#334155",
209
+ radius: "12px",
210
+ fontFamily: "'Inter', sans-serif",
211
+ fontSize: "15px"
212
+ }}
213
+ />
214
+ ```
215
+
216
+ All theme values map to scoped CSS custom properties (`--oue-*`) applied inline on the editor container. This means multiple editors on the same page can have different themes without conflicting.
217
+
218
+ ---
219
+
220
+ ## Editor Height
221
+
222
+ By default, the editor grows with its content. To constrain the height and enable scrolling within the content area, pass a `height` prop. The toolbar remains pinned at the top while the content area scrolls independently.
223
+
224
+ ```tsx
225
+ {/* Fixed pixel height */}
226
+ <Editor height={400} />
227
+
228
+ {/* CSS string value */}
229
+ <Editor height="50vh" />
230
+
231
+ {/* Minimum height with auto-grow */}
232
+ <Editor minHeight={200} />
233
+ ```
234
+
235
+ Both `height` and `minHeight` accept numbers (interpreted as pixels) or any valid CSS length string.
236
+
237
+ ---
238
+
239
+ ## Bubble Toolbar
240
+
241
+ A contextual toolbar that appears above selected text. Configure the items it shows:
242
+
243
+ ```tsx
244
+ <Editor bubbleToolbar={["bold", "italic", "underline", "strike", "link"]} />
245
+ ```
246
+
247
+ Disable it entirely:
248
+
249
+ ```tsx
250
+ <Editor bubbleToolbar={false} />
251
+ ```
252
+
253
+ ---
254
+
255
+ ## HTML Source Mode
256
+
257
+ Toggle between the visual editor and raw HTML source:
258
+
259
+ ```tsx
260
+ <Editor sourceMode />
261
+ ```
262
+
263
+ The `sourceMode` toolbar button can also be included in the toolbar config to let users switch at runtime. Changes in either view stay in sync.
264
+
265
+ ---
266
+
267
+ ## Images and File Uploads
268
+
269
+ Images can be inserted via URL or through a custom upload handler. The upload handler receives a `File` object and must return a Promise resolving to `{ src, alt?, width?, height? }`.
270
+
271
+ ```tsx
272
+ import { Editor, ImageExtension } from "oneuxi-editor"
273
+
274
+ const imageExtension = ImageExtension.configure({
275
+ upload: async (file: File) => {
276
+ const form = new FormData()
277
+ form.append("file", file)
278
+ const res = await fetch("/api/upload", { method: "POST", body: form })
279
+ const data = await res.json()
280
+ return { src: data.url, alt: file.name }
281
+ },
282
+ maxSize: 5 * 1024 * 1024,
283
+ allowedTypes: ["image/jpeg", "image/png", "image/webp", "image/gif"]
284
+ })
285
+
286
+ <Editor extensions={[imageExtension]} />
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Tables
292
+
293
+ Insert and edit HTML tables. Cells support all standard formatting (bold, links, lists).
294
+
295
+ ```tsx
296
+ import { Editor } from "oneuxi-editor"
297
+
298
+ {/* Tables are included in the default editor — use the toolbar button */}
299
+ <Editor toolbar={["table"]} />
300
+ ```
301
+
302
+ ---
303
+
304
+ ## Code Blocks
305
+
306
+ Syntax-highlighted code blocks are included out of the box. Use the `codeBlock` toolbar item to insert one, or type a fenced code block in source mode.
307
+
308
+ ```tsx
309
+ <Editor toolbar={["codeBlock"]} />
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Mentions
315
+
316
+ Trigger user mentions with `@`. Provide a search function that returns a list of users.
317
+
318
+ ```tsx
319
+ import { Editor, MentionExtension } from "oneuxi-editor"
320
+
321
+ const mentionExt = MentionExtension.configure({
322
+ trigger: "@",
323
+ search: async (query: string) => {
324
+ const res = await fetch(`/api/users?q=${query}`)
325
+ return res.json() // [{ id, name, username, avatar? }]
326
+ }
327
+ })
328
+
329
+ <Editor extensions={[mentionExt]} />
330
+ ```
331
+
332
+ ---
333
+
334
+ ## Slash Commands
335
+
336
+ Type `/` to open a command menu. Register custom commands or use the built-in set.
337
+
338
+ ```tsx
339
+ import { Editor, SlashCommandExtension } from "oneuxi-editor"
340
+
341
+ <Editor extensions={[SlashCommandExtension]} />
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Paste Cleanup
347
+
348
+ Pasting from external office suites or rich document sources is handled automatically. The editor strips proprietary markup (`MsoNormal`, `mso-*`, inline styled wrapper spans) while preserving the semantic structure: headings, lists, bold, italic, links, and tables come through intact.
349
+
350
+ No configuration is required. Paste cleanup runs silently on every paste event.
351
+
352
+ ---
353
+
354
+ ## Security and Sanitization
355
+
356
+ All HTML imported or pasted into the editor is sanitized by DOMPurify before parsing. The following are blocked unconditionally:
357
+
358
+ - `<script>` tags and inline event handlers (`onerror`, `onload`, etc.)
359
+ - `javascript:` URLs in `<a href>` attributes
360
+ - `<iframe>`, `<object>`, `<embed>`, and similar embeds
361
+
362
+ You can extend or restrict the allowed tag set via the `html` prop:
363
+
364
+ ```tsx
365
+ <Editor
366
+ html={{
367
+ allowedTags: ["p", "strong", "em", "a", "ul", "ol", "li", "h1", "h2", "h3"],
368
+ allowedAttributes: {
369
+ "a": ["href", "target", "rel"]
370
+ }
371
+ }}
372
+ />
373
+ ```
374
+
375
+ ---
376
+
377
+ ## SSR and Next.js
378
+
379
+ The editor is SSR-safe. It does not access `window`, `document`, or the DOM at module import time. System theme detection uses `useSyncExternalStore` with a stable server snapshot to prevent hydration mismatches in Next.js App Router and Pages Router.
380
+
381
+ No dynamic import wrappers or `ssr: false` configuration is needed.
382
+
383
+ ---
384
+
385
+ ## Imperative API
386
+
387
+ Use a `ref` to call editor methods programmatically from outside the component.
388
+
389
+ ```tsx
390
+ import { useRef } from "react"
391
+ import { Editor } from "oneuxi-editor"
392
+ import type { EditorRef } from "oneuxi-editor"
393
+
394
+ function MyPage() {
395
+ const editorRef = useRef<EditorRef>(null)
396
+
397
+ return (
398
+ <>
399
+ <Editor ref={editorRef} />
400
+ <button onClick={() => editorRef.current?.clear()}>Clear</button>
401
+ <button onClick={() => console.log(editorRef.current?.getHTML())}>
402
+ Get HTML
403
+ </button>
404
+ </>
405
+ )
406
+ }
407
+ ```
408
+
409
+ Available methods:
410
+
411
+ | Method | Description |
412
+ |--------|-------------|
413
+ | `focus()` | Focus the editor |
414
+ | `blur()` | Remove focus from the editor |
415
+ | `clear()` | Clear all content |
416
+ | `getHTML()` | Return current content as HTML string |
417
+ | `setHTML(html)` | Replace content with the provided HTML |
418
+ | `getText()` | Return plain text content |
419
+ | `getJSON()` | Return Lexical editor state as a JSON object |
420
+ | `setJSON(json)` | Replace content from a Lexical state object |
421
+ | `isEmpty()` | Return true if the editor has no content |
422
+ | `isDirty()` | Return true if content has changed since last clean mark |
423
+ | `markClean()` | Reset the dirty state |
424
+ | `undo()` | Undo the last change |
425
+ | `redo()` | Redo the last undone change |
426
+
427
+ ---
428
+
429
+ ## Props Reference
430
+
431
+ | Prop | Type | Default | Description |
432
+ |------|------|---------|-------------|
433
+ | `value` | `string` | — | Controlled HTML content |
434
+ | `defaultValue` | `string` | — | Initial uncontrolled content |
435
+ | `output` | `"html" \| "json" \| "text"` | `"html"` | Format of onChange output |
436
+ | `placeholder` | `string` | `"Start writing..."` | Placeholder text |
437
+ | `readOnly` | `boolean` | `false` | Disable all editing |
438
+ | `disabled` | `boolean` | `false` | Disable and apply disabled styling |
439
+ | `theme` | `"light" \| "dark" \| "system" \| ThemeObject` | `"system"` | Editor color theme |
440
+ | `height` | `string \| number` | — | Fixed editor height; enables scrolling |
441
+ | `minHeight` | `string \| number` | — | Minimum editor height |
442
+ | `toolbar` | `ToolbarItem[] \| false` | default set | Toolbar items or false to hide |
443
+ | `bubbleToolbar` | `ToolbarItem[] \| false` | default set | Bubble toolbar items |
444
+ | `sourceMode` | `boolean` | `false` | Start in HTML source mode |
445
+ | `fullscreen` | `boolean` | `false` | Start in fullscreen mode |
446
+ | `characterCount` | `boolean` | `false` | Show character count in footer |
447
+ | `wordCount` | `boolean` | `false` | Show word count in footer |
448
+ | `maxLength` | `number` | — | Maximum character limit |
449
+ | `extensions` | `EditorExtension[]` | — | Custom or built-in extensions |
450
+ | `preset` | `"basic" \| "full"` | — | Load a predefined extension set |
451
+ | `html` | `HTMLPolicy` | — | DOMPurify allow-list configuration |
452
+ | `autosave` | `AutosaveConfig` | — | Autosave callback and debounce delay |
453
+ | `icons` | `CustomIconMap` | — | Override default toolbar icons |
454
+ | `onChange` | `(output: string) => void` | — | Fires on every content change |
455
+ | `onUpdate` | `(update: EditorUpdate) => void` | — | Full update object with html, text, json |
456
+ | `onError` | `(error: Error) => void` | — | Lexical runtime error handler |
457
+ | `onDirtyChange` | `(isDirty: boolean) => void` | — | Fires when dirty state changes |
458
+ | `onCharacterCountChange` | `(count: number) => void` | — | Fires on character count change |
459
+
460
+ ---
461
+
462
+ ## Accessibility
463
+
464
+ The toolbar renders with `role="toolbar"` and `aria-label` attributes on every control. Interactive elements have unique, descriptive `aria-label` values for screen reader compatibility. Keyboard navigation within the editor follows standard `contenteditable` conventions.
465
+
466
+ ---
467
+
468
+ ## Browser Support
469
+
470
+ The editor targets the last two stable releases of:
471
+
472
+ - Chrome and Edge
473
+ - Firefox
474
+ - Safari
475
+ - Mobile Chrome and Safari
476
+
477
+ ---
478
+
479
+ ## Bundle Size
480
+
481
+ The editor ships as separate ESM and CJS builds with full source maps. Lexical itself is a peer dependency and is not included in these figures.
482
+
483
+ | Output | Raw | Gzipped |
484
+ |--------|-----|---------|
485
+ | ESM (`index.mjs`) | 75.9 KB | 15.2 KB |
486
+ | CJS (`index.js`) | 85.0 KB | 15.8 KB |
487
+ | CSS (`styles.css`) | 10.0 KB | 2.4 KB |
488
+
489
+ Extensions that are not imported are not included in your production bundle.
490
+
491
+ ---
492
+
493
+ ## AI Features — Coming Soon
494
+
495
+ The next phase of `oneuxi-editor` introduces a set of AI-assisted writing capabilities designed to integrate directly into the editing experience. These features will work with any OpenAI-compatible API endpoint and will require no changes to your existing editor integration.
496
+
497
+ Planned capabilities include:
498
+
499
+ **Inline completions** — The editor will offer sentence and paragraph completions as you type, accepting them with a single key.
500
+
501
+ **Selection-based actions** — Selecting text will expose actions to rephrase, expand, summarize, simplify, or change tone, inline without opening a separate panel.
502
+
503
+ **Content generation** — A prompt bar for generating drafts, outlines, and structured content from a short description.
504
+
505
+ **Grammar and clarity suggestions** — Passive, non-intrusive suggestions that highlight improvements without interrupting the writing flow.
506
+
507
+ **Custom AI commands** — Register your own AI actions tied to your backend, using the same extension API as other oneuxi-editor features.
508
+
509
+ The AI module will be optional and tree-shakeable. It will not add to your bundle size unless explicitly imported.
510
+
511
+ ---
512
+
513
+ ## Community & Support
514
+
515
+ Join our Telegram channel for updates, discussions, and support: [https://t.me/thedebuglab](https://t.me/thedebuglab)
516
+
517
+ ---
518
+
519
+ ## License
520
+
521
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oneuxi-editor",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A production-ready, lightweight, modular rich text and HTML editor for React applications built on Lexical",
5
5
  "author": "thedebuglab",
6
6
  "license": "MIT",
@@ -39,7 +39,8 @@
39
39
  "./styles.css": "./dist/styles.css"
40
40
  },
41
41
  "files": [
42
- "dist"
42
+ "dist",
43
+ "README.md"
43
44
  ],
44
45
  "scripts": {
45
46
  "build": "tsup",
@@ -49,7 +50,10 @@
49
50
  "test:watch": "vitest",
50
51
  "test:e2e": "playwright test",
51
52
  "prepublishOnly": "npm run build && npm run typecheck && npm run test",
52
- "release": "npm publish --access public"
53
+ "release": "npm publish --access public",
54
+ "release:patch": "npm version patch && npm publish --access public",
55
+ "release:minor": "npm version minor && npm publish --access public",
56
+ "release:major": "npm version major && npm publish --access public"
53
57
  },
54
58
  "peerDependencies": {
55
59
  "react": "^18.0.0 || ^19.0.0",