smartrte-react 1.0.0-beta.9 โ†’ 1.1.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 CHANGED
@@ -1,880 +1,374 @@
1
- # Smart RTE (Rich Text Editor) - React
1
+ # smartrte-react
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/smartrte-react.svg)](https://www.npmjs.com/package/smartrte-react)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A powerful, feature-rich Rich Text Editor built for React applications with support for tables, formulas (LaTeX/KaTeX), media management, and advanced text formatting.
6
+ A rich text editor for React, built on a document-model core rather than raw `contentEditable` state โ€” tables, LaTeX/KaTeX formulas, media, DOCX/PDF/Markdown import-export, per-tool toolbar visibility, and host-owned integration points for version history, comments, and suggestions/track-changes.
7
7
 
8
- ## ๐ŸŒŸ Features
8
+ It pairs with [`smartrte-core`](https://www.npmjs.com/package/smartrte-core), a framework-agnostic document engine โ€” you only need this package to use it from React.
9
9
 
10
- - **๐Ÿ“ Rich Text Editing**: Full-featured WYSIWYG editor with all standard formatting options
11
- - **๐Ÿ“Š Advanced Table Support**: Create, edit, merge, split cells, and customize tables
12
- - **๐Ÿ”ข Mathematical Formulas**: LaTeX/KaTeX integration for rendering mathematical expressions
13
- - **๐Ÿ–ผ๏ธ Media Management**: Image upload, resize, drag-and-drop, and custom media manager integration
14
- - **๐ŸŽจ Styling Options**: Font sizes (8-96pt), text colors, background colors, and more
15
- - **๐Ÿ”— Link Management**: Easy insertion and editing of hyperlinks
16
- - **๐Ÿ“ฑ Responsive**: Works seamlessly across different screen sizes
17
- - **โšก Lightweight**: Minimal dependencies, optimized for performance
18
- - **๐ŸŽฏ TypeScript Support**: Fully typed for better developer experience
19
- - **๐Ÿ”ง Customizable**: Toggle features on/off, custom media managers, and more
10
+ ## Contents
20
11
 
21
- ## ๐Ÿ“ฆ Installation
12
+ - [Install](#install)
13
+ - [Quick start](#quick-start)
14
+ - [Which component do I use?](#which-component-do-i-use-canonicalauthorityeditor-vs-classiceditor)
15
+ - [Props](#props)
16
+ - [Toolbar customization](#toolbar-customization)
17
+ - [Capability presets](#capability-presets-table-onoff)
18
+ - [Host-owned providers](#host-owned-providers-media-versions-comments-suggestions)
19
+ - [Imperative handle](#imperative-handle-ref)
20
+ - [Import & export formats](#import--export-formats)
21
+ - [Theming](#theming)
22
+ - [Standalone / non-React embed](#standalone--non-react-embed)
23
+ - [Security](#security)
24
+ - [Browser support](#browser-support)
25
+ - [Development](#development)
26
+ - [Contributing](#contributing)
27
+ - [License](#license)
22
28
 
23
- ### Using npm
29
+ ## Install
24
30
 
25
31
  ```bash
26
32
  npm install smartrte-react
33
+ # or: pnpm add smartrte-react / yarn add smartrte-react
27
34
  ```
28
35
 
29
- ### Using yarn
36
+ `react` and `react-dom` (`>=18`) are peer dependencies โ€” install them if your project doesn't already have them. No separate CSS import is required; the editor injects its own stylesheet on mount.
30
37
 
31
- ```bash
32
- yarn add smartrte-react
33
- ```
34
-
35
- ### Using pnpm
36
-
37
- ```bash
38
- pnpm add smartrte-react
39
- ```
40
-
41
- ## ๐Ÿš€ Quick Start
42
-
43
- ### ๐ŸŽฎ Live Demo
44
-
45
- Try the editor instantly in your browser:
46
-
47
- - **[Live Demo](https://playground-k9l44ah7t-ayush1852017s-projects.vercel.app/)** (Deployed Version)
48
- - **[CodeSandbox Playground](https://codesandbox.io/s/github/ayush1852017/smart-rte/tree/master/packages/react/playground)** (Interactive)
49
-
50
- ### Basic Usage
38
+ ## Quick start
51
39
 
52
40
  ```tsx
53
- import React, { useState } from 'react';
54
- import { ClassicEditor } from 'smartrte-react';
41
+ import { useState } from "react";
42
+ import { CanonicalAuthorityEditor } from "smartrte-react";
55
43
 
56
44
  function App() {
57
- const [content, setContent] = useState('<p>Start typing...</p>');
45
+ const [content, setContent] = useState("<p>Start typingโ€ฆ</p>");
58
46
 
59
47
  return (
60
- <div>
61
- <ClassicEditor
62
- value={content}
63
- onChange={(html) => setContent(html)}
64
- placeholder="Type hereโ€ฆ"
65
- />
66
- </div>
48
+ <CanonicalAuthorityEditor
49
+ defaultValue={content}
50
+ onHtmlChange={setContent}
51
+ placeholder="Type hereโ€ฆ"
52
+ />
67
53
  );
68
54
  }
69
-
70
- export default App;
71
55
  ```
72
56
 
73
- ## ๐Ÿ“š Documentation
57
+ `defaultValue` is uncontrolled โ€” it seeds the editor once on mount, not on every render (see [Props](#props) for why, and how to programmatically replace content later via the imperative handle).
74
58
 
75
- ### Component API
59
+ ## Which component do I use? `CanonicalAuthorityEditor` vs `ClassicEditor`
76
60
 
77
- #### ClassicEditor Props
78
-
79
- | Prop | Type | Default | Description |
80
- |------|------|---------|-------------|
81
- | `value` | `string` | `undefined` | HTML content of the editor |
82
- | `onChange` | `(html: string) => void` | `undefined` | Callback fired when content changes |
83
- | `placeholder` | `string` | `"Type hereโ€ฆ"` | Placeholder text when editor is empty |
84
- | `minHeight` | `number \| string` | `200` | Minimum height of the editor (in pixels) |
85
- | `maxHeight` | `number \| string` | `500` | Maximum height of the editor (in pixels) |
86
- | `readOnly` | `boolean` | `false` | Make the editor read-only |
87
- | `table` | `boolean` | `true` | Enable/disable table functionality |
88
- | `media` | `boolean` | `true` | Enable/disable media/image functionality |
89
- | `formula` | `boolean` | `true` | Enable/disable formula/LaTeX functionality |
90
- | `features` | `CoreFeatureConfig` | standard preset | Enable or disable individual core plugins |
91
- | `plugins` | `ReactEditorPlugin[]` | standard preset | Use an exact custom plugin set; replaces the standard preset |
92
- | `formats` | `EditorFormatConfig` | all built-ins | Enable or disable HTML, Markdown, DOCX, and PDF adapters |
93
- | `formatDefinitions` | `EditorFormatDefinition[]` | built-ins | Add or replace import/export adapters |
94
- | `mediaManager` | `MediaManagerAdapter` | `undefined` | Custom media manager for handling images |
95
- | `fonts` | `{ name: string; value: string }[]` | Default web-safe fonts | Custom font list for the toolbar |
96
- | `defaultFont` | `string` | `undefined` | Default font family for the editor content |
97
- | `theme` | `"light" \| "dark"` | `"light"` | Built-in theme mode |
98
- | `className` | `string` | `undefined` | Custom CSS class for theming via CSS variable overrides |
99
- | `tools` | `Partial<ToolbarTools>` | every tool `true` | Hide individual toolbar tools (Bold, Video, Version history, etc.) โ€” see [Hiding individual toolbar tools](#hiding-individual-toolbar-tools) below |
100
-
101
- ### Advanced Examples
102
-
103
- #### Feature plugins
104
-
105
- The editor uses the same plugin registry for core commands and React toolbar
106
- state. For the architecture and custom plugin API, see
107
- [`docs/PLUGIN_ARCHITECTURE.md`](../../docs/PLUGIN_ARCHITECTURE.md).
61
+ - **`CanonicalAuthorityEditor`** โ€” the actual editor. Everything in this guide (tools, providers, presets, the imperative handle) is its API. Use this for anything new.
62
+ - **`ClassicEditor`** โ€” a thin backwards-compatibility wrapper around `CanonicalAuthorityEditor`, kept for integrations written against the package's older `value`/`onChange: (html: string) => void` shape. It forwards everything it can (`tools`, `mediaProvider`, `versionProvider`, etc.) but silently ignores a handful of props from an even older, now-retired plugin system (`features`, `plugins`, `formats`, `fonts`, `theme`, `mediaManager` as an adapter object). If you're starting fresh, use `CanonicalAuthorityEditor` directly โ€” `ClassicEditor` exists so old call sites keep compiling, not as a recommended entry point.
108
63
 
109
64
  ```tsx
110
- import { ClassicEditor } from 'smartrte-react';
111
-
112
- <ClassicEditor
113
- features={{
114
- table: false,
115
- media: false,
116
- formula: false,
117
- checklist: true,
118
- }}
119
- />
65
+ // Legacy-compatible shape - only use this if migrating an existing integration
66
+ import { ClassicEditor } from "smartrte-react";
67
+
68
+ <ClassicEditor value={htmlString} onChange={(html) => setHtmlString(html)} />
120
69
  ```
121
70
 
122
- #### Hiding individual toolbar tools
71
+ ## Props
72
+
73
+ The commonly-used `CanonicalAuthorityEditor` props:
123
74
 
124
- The `table`/`media`/`formula` props above turn off a whole *capability* โ€” no
125
- tables anywhere, or media/formulas removed from the schema entirely. If you
126
- just want a **smaller toolbar** while keeping every capability intact (e.g.
127
- hide Video, Audio, Version history, and Review for a specific product
128
- surface, without touching what content the editor can actually store), use
129
- `tools` instead. It's a plain object: name the tools you want off, everything
130
- else stays on by default. No wrapping, no CSS overrides, no forking the
131
- component.
75
+ | Prop | Type | Default | Description |
76
+ |---|---|---|---|
77
+ | `defaultValue` | `string \| PersistedEditorDocument` | `undefined` | Initial content (HTML string or a previously-saved document envelope). Uncontrolled after mount โ€” see [Imperative handle](#imperative-handle-ref) to replace content later. |
78
+ | `onChange` | `(change: SmartEditorChange) => void` | `undefined` | Fires per transaction with the structured change event. |
79
+ | `onHtmlChange` | `(html: string) => void` | `undefined` | Debounced (~250ms after the last edit) plain-HTML serialization โ€” the simplest way to persist content as a string. |
80
+ | `preset` | `"full" \| "simple"` | `"full"` | Construction-time capability preset โ€” `"simple"` excludes the table plugin entirely (schema-level, not just hidden in the toolbar). See [Capability presets](#capability-presets-table-onoff). |
81
+ | `tools` | `Partial<ToolbarTools>` | every tool `true` | Hide individual toolbar tools without touching document capability. See [Toolbar customization](#toolbar-customization). |
82
+ | `mediaProvider` | `MediaProvider` | `undefined` | Host-owned upload/search/remove boundary for images, video, and audio. Absent โ‡’ media tools don't render. |
83
+ | `mediaManager` | `boolean` | `true` when `mediaProvider` is set | Use the library/search/duplicate-detection picker for images (vs. the plain file-input default). |
84
+ | `mediaPicker` | `MediaPickerComponent` | built-in file picker | Replace the default file-picker UI for video/audio (and images, if `mediaManager` is `false`). |
85
+ | `versionProvider` | `VersionProvider` | `undefined` | Host-owned save/list/load/remove boundary for version history. Absent โ‡’ Version History tool doesn't render. |
86
+ | `commentProvider` | `CommentProvider` | `undefined` | Host-owned boundary for comment threads. Absent โ‡’ comment tools/markers don't render. |
87
+ | `suggestionProvider` | `SuggestionProvider` | `undefined` | Host-owned boundary for *structural* suggestions (track-changes). Absent โ‡’ suggestion tools/markers don't render. |
88
+ | `authorId` | `string` | `"anonymous"` | Attributed to new comment replies and suggestions. |
89
+ | `renderFormulaHtml` | `boolean` | `false` | Bake real KaTeX-rendered HTML into `onHtmlChange`'s formula markup instead of an empty placeholder โ€” turn this on if you render that HTML anywhere outside the editor (email, PDF export, a read-only view without KaTeX loaded). |
90
+ | `onClipboardDiagnostic` | `(report: ClipboardDiagnosticReport) => void` | `undefined` | Inspect what a paste was parsed as / why it was rejected โ€” useful while debugging a host's own copy sources. |
91
+ | `placeholder` | `string` | `undefined` | Placeholder text shown when the editor is empty. |
92
+ | `minHeight` / `maxHeight` | `number \| string` | `undefined` | Editing-surface height bounds. |
93
+ | `readOnly` | `boolean` | `false` | Disables editing; toolbar tools become inert. |
94
+ | `className` | `string` | `undefined` | Extra class(es) on the editor's root element โ€” this is also how you enable [dark mode](#theming). |
95
+ | `onRuntime` | `(runtime: CanonicalEditorRuntime) => void` | `undefined` | Escape hatch for tests/diagnostics; not part of the stable editing contract. |
96
+
97
+ `ClassicEditor` accepts the same props under `value`/`onChange: (html) => void` instead of `defaultValue`/`onHtmlChange`, plus a legacy `table?: boolean` (equivalent to `preset={table === false ? "simple" : "full"}`).
98
+
99
+ ## Toolbar customization
100
+
101
+ `tools` hides individual toolbar entries โ€” Bold, Video, Version history, whatever you name โ€” without touching what the document itself can *store*. Every tool defaults to visible; only name the ones you want off:
132
102
 
133
103
  ```tsx
134
- import { ClassicEditor } from 'smartrte-react';
135
-
136
- <ClassicEditor
137
- tools={{
138
- video: false,
139
- audio: false,
140
- versionHistory: false,
141
- comments: false,
142
- suggestions: false,
143
- }}
104
+ <CanonicalAuthorityEditor
105
+ tools={{ video: false, audio: false, versionHistory: false, comments: false, suggestions: false }}
144
106
  />
145
107
  ```
146
108
 
147
- **How it composes with everything else:** a tool only ever needs *both*
148
- things to be true to show up โ€” your `tools` flag says yes, *and* whatever
149
- that tool actually depends on is present. So:
109
+ `tools` can only ever hide something, never conjure it into existence โ€” a tool still needs its underlying capability to actually be there:
150
110
 
151
- - `tools.video`/`tools.audio`/`tools.image` also need a `mediaProvider` (or
152
- `mediaManager`) to be configured at all โ€” turning the flag on can't make
153
- media insertion appear out of nowhere if you never wired up a provider.
154
- - `tools.versionHistory` also needs a `versionProvider`; `tools.comments`
155
- needs a `commentProvider`; `tools.suggestions` needs a `suggestionProvider`.
156
- - `tools.insertTable` also needs the table capability to actually be enabled
157
- (i.e. you haven't set `table={false}`) โ€” you can't use `tools` to bring
158
- back a capability you turned off elsewhere.
111
+ - `image`/`video`/`audio` also need `mediaProvider` configured.
112
+ - `versionHistory` also needs `versionProvider`; `comments` needs `commentProvider`; `suggestions` needs `suggestionProvider`.
113
+ - `insertTable` also needs the table capability enabled (i.e. you haven't set `preset="simple"`).
159
114
 
160
- In other words: `tools` can only ever hide something, never force something
161
- into existence that isn't otherwise configured. This means it's always safe
162
- to leave `tools` unset โ€” every consumer who doesn't pass it sees the exact
163
- toolbar they see today.
115
+ This means it's always safe to leave `tools` unset โ€” a consumer who never passes it sees every tool their other configuration already supports.
164
116
 
165
117
  **Every toggleable key**, grouped the way they appear in the toolbar:
166
118
 
167
119
  ```ts
168
120
  interface ToolbarTools {
169
121
  // Text formatting
170
- bold, italic, underline, strikethrough, code,
171
- superscript, subscript, textColor, backgroundColor, fontSize, fontFamily,
122
+ bold: boolean; italic: boolean; underline: boolean; strikethrough: boolean; code: boolean;
123
+ superscript: boolean; subscript: boolean; textColor: boolean; backgroundColor: boolean;
124
+ fontSize: boolean; fontFamily: boolean;
172
125
 
173
126
  // Paragraph
174
- blockType, // the Paragraph/Heading/Code block dropdown
175
- alignLeft, alignCenter, alignRight, alignJustify, quote,
127
+ blockType: boolean; // the Paragraph/Heading 1-6/Code block dropdown
128
+ alignLeft: boolean; alignCenter: boolean; alignRight: boolean; alignJustify: boolean;
129
+ lineHeight: boolean; // the line-spacing dropdown (1/1.15/1.5/2/2.5 presets plus a custom value)
130
+ quote: boolean;
176
131
 
177
132
  // Lists
178
- bulletedList, numberedList, checklist,
179
- listPreset, // the numbered-list style picker (1,2,3 / a,b,c / i,ii,iii / A,B,C / I,II,III)
133
+ bulletedList: boolean; numberedList: boolean; checklist: boolean;
134
+ listPreset: boolean; // the named marker-preset picker (decimal/alpha/roman/outline/bullet glyphs)
180
135
 
181
136
  // Insert
182
- link, removeLink, image, video, audio, insertFormula, specialCharacters, insertTable,
137
+ link: boolean; removeLink: boolean;
138
+ image: boolean; video: boolean; audio: boolean; // each requires mediaProvider
139
+ insertFormula: boolean; specialCharacters: boolean;
140
+ horizontalLine: boolean; // inserts a divider (<hr>)
141
+ pageBreak: boolean; // a print/export pagination marker, distinct from horizontalLine
142
+ insertTable: boolean; // requires the table capability (preset)
183
143
 
184
144
  // Document
185
- import,
186
- saveAsHtml, saveAsMarkdown, saveAsWord, saveAsPdf, saveAsSmartRte,
187
- versionHistory, comments, suggestions,
145
+ import: boolean;
146
+ saveAsHtml: boolean; saveAsMarkdown: boolean; saveAsWord: boolean; saveAsPdf: boolean; saveAsSmartRte: boolean;
147
+ versionHistory: boolean; // requires versionProvider
148
+ comments: boolean; // requires commentProvider
149
+ suggestions: boolean; // requires suggestionProvider
188
150
 
189
151
  // History
190
- undo, redo,
152
+ undo: boolean; redo: boolean;
191
153
  }
192
154
  ```
193
155
 
194
- A few notes on what's *not* in this list, on purpose: actions that only ever
195
- apply to something you already have selected โ€” moving a block up/down,
196
- indenting a list item, adding/removing a table row or column, resizing a
197
- selected image โ€” aren't individually toggleable. They only make sense as
198
- part of using the tool that created them (a table, a list, an image), so
199
- they follow that tool's own visibility rather than needing a separate flag
200
- each. If you turn off `insertTable`, its row/column tools go with it
201
- automatically โ€” there's no separate flag to remember.
156
+ Purely contextual actions that only ever act on something already selected โ€” moving a block up/down, indenting a list item, adding/removing a table row, resizing a selected image โ€” aren't individually toggleable; they follow their owning tool's visibility (turn off `insertTable` and its row/column actions go with it, with no separate flag to remember).
202
157
 
203
- If you're using the exported `CanonicalAuthorityEditor` directly instead of
204
- `ClassicEditor`, `tools` works exactly the same way. And if you're using the
205
- standalone embed (`window.SmartRTE.ClassicEditor.init(...)`), pass `tools`
206
- in the same options object you pass `target`/`value`/etc.
158
+ ## Capability presets (table on/off)
207
159
 
208
- ```ts
209
- window.SmartRTE.ClassicEditor.init({
210
- target: document.getElementById('editor'),
211
- tools: { video: false, audio: false },
212
- });
213
- ```
214
-
215
- #### Complete Example with All Features
160
+ `preset` is a construction-time, host/integrator-level setting (there's no in-editor UI for a user to change their own preset) โ€” it decides which plugins the document's *schema* is built with, not just what the toolbar shows:
216
161
 
217
162
  ```tsx
218
- import React, { useState } from 'react';
219
- import { ClassicEditor, MediaManagerAdapter } from 'smartrte-react';
220
-
221
- // Custom media manager implementation
222
- const customMediaManager: MediaManagerAdapter = {
223
- async search(query) {
224
- // Implement your media search logic
225
- const response = await fetch(`/api/media/search?q=${query.text}`);
226
- const data = await response.json();
227
- return {
228
- items: data.items.map(item => ({
229
- id: item.id,
230
- url: item.url,
231
- thumbnailUrl: item.thumbnailUrl,
232
- title: item.title,
233
- })),
234
- };
235
- },
236
- async upload(file) {
237
- // Implement your file upload logic
238
- const formData = new FormData();
239
- formData.append('file', file);
240
- const response = await fetch('/api/media/upload', {
241
- method: 'POST',
242
- body: formData,
243
- });
244
- const data = await response.json();
245
- return {
246
- id: data.id,
247
- url: data.url,
248
- thumbnailUrl: data.thumbnailUrl,
249
- title: data.title,
250
- };
251
- },
252
- };
253
-
254
- function AdvancedEditor() {
255
- const [content, setContent] = useState('');
256
-
257
- return (
258
- <ClassicEditor
259
- value={content}
260
- onChange={(html) => {
261
- console.log('Content changed:', html);
262
- setContent(html);
263
- }}
264
- placeholder="Start editing..."
265
- minHeight={300}
266
- maxHeight={800}
267
- table={true}
268
- media={true}
269
- formula={true}
270
- mediaManager={customMediaManager}
271
- />
272
- );
273
- }
274
-
275
- export default AdvancedEditor;
163
+ <CanonicalAuthorityEditor preset="simple" /> // excludes the table plugin entirely
164
+ <CanonicalAuthorityEditor preset="full" /> // default - excludes nothing
276
165
  ```
277
166
 
278
- #### Read-Only Mode
167
+ `"simple"` exists for content that should never contain tables at all (e.g. a short-answer question editor) โ€” `preset="simple"` and `tools={{ insertTable: false }}` are not equivalent: the latter only hides the button, the former means the schema itself will reject a pasted or imported table.
279
168
 
280
- ```tsx
281
- import { ClassicEditor } from 'smartrte-react';
169
+ ## Host-owned providers (media, versions, comments, suggestions)
282
170
 
283
- function ReadOnlyEditor({ content }) {
284
- return (
285
- <ClassicEditor
286
- value={content}
287
- readOnly={true}
288
- minHeight={200}
289
- />
290
- );
291
- }
292
- ```
171
+ Four features are opt-in via a provider interface the *host* implements โ€” the package never holds storage credentials, a socket, or a database connection itself. Absent provider โ‡’ that feature's toolbar entries simply don't render; nothing crashes or shows a broken control.
293
172
 
294
- #### Minimal Editor (No Tables, Media, or Formulas)
173
+ ```ts
174
+ interface MediaProvider {
175
+ upload(file: File, opts?: { signal?: AbortSignal }): Promise<{ url: string; id: string }>;
176
+ search(query: string, filters?: MediaFilters, page?: number): Promise<MediaItem[]>;
177
+ remove(id: string): Promise<void>;
178
+ }
295
179
 
296
- ```tsx
297
- import { ClassicEditor } from 'smartrte-react';
180
+ interface VersionProvider {
181
+ save(version: DocumentVersion): Promise<VersionListEntry>;
182
+ list(): Promise<readonly VersionListEntry[]>;
183
+ load(id: string): Promise<DocumentVersion>;
184
+ remove(id: string): Promise<void>;
185
+ }
298
186
 
299
- function MinimalEditor() {
300
- const [content, setContent] = useState('');
187
+ interface CommentProvider {
188
+ list(): Promise<readonly CommentThread[]>;
189
+ save(thread: CommentThread): Promise<void>;
190
+ remove(threadId: string): Promise<void>;
191
+ }
301
192
 
302
- return (
303
- <ClassicEditor
304
- value={content}
305
- onChange={setContent}
306
- table={false}
307
- media={false}
308
- formula={false}
309
- placeholder="Simple text editor"
310
- />
311
- );
193
+ interface SuggestionProvider {
194
+ list(): Promise<readonly StructuralSuggestion[]>;
195
+ save(suggestion: StructuralSuggestion): Promise<void>;
196
+ remove(suggestionId: string): Promise<void>;
312
197
  }
313
198
  ```
314
199
 
315
- #### Next.js Integration
316
-
317
- For Next.js applications, you may need to use dynamic imports to avoid SSR issues:
318
-
319
200
  ```tsx
320
- import dynamic from 'next/dynamic';
321
- import { useState } from 'react';
322
-
323
- const ClassicEditor = dynamic(
324
- () => import('smartrte-react').then(mod => mod.ClassicEditor),
325
- { ssr: false }
326
- );
327
-
328
- export default function Page() {
329
- const [content, setContent] = useState('');
330
-
331
- return (
332
- <div>
333
- <ClassicEditor
334
- value={content}
335
- onChange={setContent}
336
- placeholder="Start typing..."
337
- />
338
- </div>
339
- );
340
- }
201
+ <CanonicalAuthorityEditor
202
+ mediaProvider={{
203
+ async upload(file) {
204
+ const body = new FormData();
205
+ body.append("file", file);
206
+ const res = await fetch("/api/media", { method: "POST", body });
207
+ return res.json(); // { url, id }
208
+ },
209
+ async search(query) {
210
+ const res = await fetch(`/api/media?q=${encodeURIComponent(query)}`);
211
+ return res.json();
212
+ },
213
+ async remove(id) {
214
+ await fetch(`/api/media/${id}`, { method: "DELETE" });
215
+ },
216
+ }}
217
+ />
341
218
  ```
342
219
 
343
- ## ๐Ÿ”ง Features Deep Dive
344
-
345
- ### Text Formatting
346
-
347
- The editor supports all standard text formatting options:
348
-
349
- - **Bold**, *Italic*, <u>Underline</u>, ~~Strikethrough~~
350
- - Font sizes from 8pt to 96pt
351
- - Text color and background color
352
- - Headings (H1-H6)
353
- - Paragraph, blockquote, code block
354
- - Ordered and unordered lists
355
- - Text alignment (left, center, right, justify)
356
- - Superscript and subscript
357
-
358
- ### Tables
359
-
360
- Full-featured table support includes:
361
-
362
- - Create tables with custom rows and columns
363
- - Add/delete rows and columns
364
- - Merge and split cells
365
- - Toggle header rows/cells
366
- - Cell background colors
367
- - Cell borders toggle
368
- - Right-click context menu for table operations
369
- - Keyboard navigation (Tab, Shift+Tab, Arrow keys)
220
+ Your `upload` implementation must independently validate file type, size, and content server-side โ€” the editor applies only a best-effort client-side allow-list check as a UX nicety, not a security boundary.
370
221
 
371
- **Keyboard Shortcuts:**
372
- - `Tab` - Move to next cell
373
- - `Shift+Tab` - Move to previous cell
374
- - `Arrow keys` - Navigate between cells
375
- - Right-click on cell - Open context menu
222
+ There's a fifth contract, **`CollabTransport`** (real-time multi-writer editing), exported for hosts building against it โ€” it defines `sendTransaction`/`onRemoteTransaction`/`onPresenceUpdate`/`sendPresence`/`getRevisionHistory`, but isn't wired into the editor's runtime yet. Without one connected, the editor behaves exactly as it does today: single-writer, no rebase path ever triggers.
376
223
 
377
- ### Mathematical Formulas
224
+ ## Imperative handle (ref)
378
225
 
379
- LaTeX/KaTeX support for mathematical expressions:
226
+ `CanonicalAuthorityEditor`/`ClassicEditor` forward a `SmartEditorHandle` ref for everything `defaultValue`/props alone can't do โ€” replacing content programmatically, reading the current document, and version snapshots:
380
227
 
381
228
  ```tsx
382
- // The editor automatically loads KaTeX
383
- // Users can insert formulas using the formula button
384
- // Examples of supported LaTeX:
385
- // - E=mc^2
386
- // - \frac{a}{b}
387
- // - \sqrt{x}
388
- // - \sum_{i=1}^{n} x_i
389
- ```
229
+ import { useRef } from "react";
230
+ import { CanonicalAuthorityEditor, type SmartEditorHandle } from "smartrte-react";
390
231
 
391
- **Required External Dependency:**
232
+ function Editor() {
233
+ const ref = useRef<SmartEditorHandle>(null);
392
234
 
393
- To use formulas, include KaTeX in your HTML:
235
+ const loadDocument = (doc) => ref.current?.replaceValue(doc, { keepSelection: false });
236
+ const currentDoc = () => ref.current?.getValue();
394
237
 
395
- ```html
396
- <!-- In your public/index.html or _app.tsx -->
397
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css">
398
- <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"></script>
238
+ return <CanonicalAuthorityEditor ref={ref} />;
239
+ }
399
240
  ```
400
241
 
401
- ### Media Management
402
-
403
- Built-in image support with optional custom media manager:
404
-
405
- **Default behavior:**
406
- - Local file upload
407
- - Drag and drop images
408
- - Image resize handles
409
- - Right-click context menu for image operations
410
-
411
- **Custom Media Manager Implementation:**
412
-
413
- ```typescript
414
- import { MediaManagerAdapter, MediaItem, MediaSearchQuery } from 'smartrte-react';
415
-
416
- const myMediaManager: MediaManagerAdapter = {
417
- async search(query: MediaSearchQuery) {
418
- // Search your media library
419
- return {
420
- items: [/* array of MediaItem */],
421
- hasMore: false,
422
- nextPage: undefined,
423
- };
424
- },
425
-
426
- async upload(file: File) {
427
- // Upload file to your server
428
- return {
429
- id: 'unique-id',
430
- url: 'https://example.com/image.jpg',
431
- thumbnailUrl: 'https://example.com/thumb.jpg',
432
- title: 'Image title',
433
- };
434
- },
435
- };
242
+ ```ts
243
+ interface SmartEditorHandle {
244
+ getValue(): PersistedEditorDocument;
245
+ replaceValue(doc: PersistedEditorDocument, opts?: { keepSelection?: boolean }): void;
246
+ isDirty(): boolean;
247
+ markSaved(revision: number): void;
248
+ getRevision(): number;
249
+ focus(): void;
250
+ executeOperations(operations: readonly SmartOperation[], opts?: ExecuteOperationsOptions): void;
251
+ createCheckpoint(): SmartEditorCheckpoint;
252
+ restoreCheckpoint(checkpoint: SmartEditorCheckpoint): void;
253
+ saveVersion(opts?: { label?: string; authorId?: string }): DocumentVersion;
254
+ restoreVersion(version: DocumentVersion, opts?: { keepSelection?: boolean }): void;
255
+ }
436
256
  ```
437
257
 
438
- ## ๐ŸŽจ Theming & Dark Mode
258
+ `saveVersion`/`restoreVersion` are the same operations the toolbar's Version History panel calls โ€” use them directly if you want your own save-version UI instead of (or alongside) the built-in one.
439
259
 
440
- The editor uses CSS custom properties (CSS variables) for all colors, making it fully customizable. No hardcoded colors โ€” everything can be themed.
260
+ ## Import & export formats
441
261
 
442
- ### Quick Start: Dark Mode
262
+ The toolbar's "Import" and "Save as ..." tools cover HTML, Markdown, DOCX (Word), PDF, and the package's own JSON document format out of the box โ€” no extra setup. DOCX import preserves real Word styling (fonts, colors, spacing) where possible; PDF export prints the same HTML the editor renders, so formulas, tables, and images all appear as they do live.
443
263
 
444
- ```tsx
445
- <ClassicEditor theme="dark" onChange={handleChange} />
446
- ```
264
+ For a custom import/export pipeline (e.g. converting on a server, or a "Save as..." flow outside the toolbar), the underlying codecs are re-exported from `smartrte-core/foundation`: `exportDocxDocument`, `importDocxDocumentWithMammoth`, `importStyledDocxDocument`, `buildPdfPrintDocument`, `importPdfDocument`, and the format-fidelity contract (`builtInFormatFidelity`) describing exactly what's lossless vs. lossy per format.
447
265
 
448
- ### Custom Themes via CSS
266
+ ## Theming
449
267
 
450
- Apply a custom class and override any CSS variables:
268
+ The editor uses CSS custom properties for every color โ€” there's no `theme` prop; dark mode is a CSS class.
451
269
 
452
270
  ```tsx
453
- <ClassicEditor className="my-theme" onChange={handleChange} />
271
+ <CanonicalAuthorityEditor className="srte-dark" />
454
272
  ```
455
273
 
456
274
  ```css
457
- .my-theme {
458
- --srte-bg: #1a1a2e;
459
- --srte-text: #eaeaea;
460
- --srte-border: #3a3a5c;
461
- /* Override only the variables you need */
275
+ /* Or follow system preference yourself and toggle the class conditionally */
276
+ @media (prefers-color-scheme: dark) {
277
+ .srte-editor:not(.srte-dark) { /* your own light/dark logic here */ }
462
278
  }
463
279
  ```
464
280
 
465
- ### Responding to System Preference
466
-
467
- ```tsx
468
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
469
-
470
- <ClassicEditor theme={prefersDark ? 'dark' : 'light'} />
471
- ```
472
-
473
- Or purely via CSS (without the `theme` prop):
281
+ Override individual variables (scoped to your own class, composed alongside `srte-dark` or standalone) to build a custom palette:
474
282
 
475
283
  ```css
476
- @media (prefers-color-scheme: dark) {
477
- .srte-editor {
478
- --srte-bg: #1e1e1e;
479
- --srte-text: #e0e0e0;
480
- --srte-border: #3a3a3a;
481
- /* ... */
482
- }
284
+ .my-theme {
285
+ --srte-background: #1a1a2e;
286
+ --srte-foreground: #eaeaea;
287
+ --srte-border: #3a3a5c;
288
+ --srte-accent: #7c3aed;
289
+ /* override only what you need - everything else falls back to the default */
483
290
  }
484
291
  ```
485
292
 
486
- ### Available CSS Custom Properties
487
-
488
- | Variable | Default (Light) | Description |
489
- |---|---|---|
490
- | `--srte-bg` | `#ffffff` | Editor container & content background |
491
- | `--srte-text` | `#111111` | Primary UI text color |
492
- | `--srte-text-muted` | `#4b5563` | Secondary/subtle text |
493
- | `--srte-border` | `#dddddd` | Standard border color |
494
- | `--srte-border-light` | `#eeeeee` | Light separator borders |
495
- | `--srte-toolbar-bg` | `#ffffff` | Toolbar background |
496
- | `--srte-input-bg` | `#ffffff` | Button, input, select backgrounds |
497
- | `--srte-input-text` | `#111111` | Button, input, select text |
498
- | `--srte-input-border` | `#e5e7eb` | Button, input, select borders |
499
- | `--srte-modal-backdrop` | `rgba(0,0,0,0.35)` | Modal overlay background |
500
- | `--srte-modal-bg` | `#ffffff` | Modal/dialog background |
501
- | `--srte-modal-text` | `#000000` | Modal text color |
502
- | `--srte-menu-bg` | `#ffffff` | Context menu background |
503
- | `--srte-menu-text` | `#111111` | Context menu text |
504
- | `--srte-menu-shadow` | `0 8px 24px rgba(0,0,0,0.18)` | Menu box-shadow |
505
- | `--srte-accent` | `#1e90ff` | Accent/selection color |
506
- | `--srte-accent-bg` | `rgba(30,144,255,0.15)` | Accent background (translucent) |
507
- | `--srte-danger` | `#dc2626` | Destructive action color |
508
- | `--srte-primary` | `#2563eb` | Primary action button color |
509
- | `--srte-surface-subtle` | `#f3f4f6` | Subtle surface (dropzone, preset buttons) |
510
- | `--srte-on-primary` | `#ffffff` | Text on primary/danger buttons |
511
- | `--srte-cancel-bg` | `#f3f4f6` | Cancel button background |
512
-
513
- ### Important Notes
514
-
515
- - **User content colors are preserved** โ€” colors set via the color picker (text/background) are inline styles on content elements and are not affected by theming.
516
- - **Color picker swatches are not themed** โ€” they display actual color values regardless of theme.
517
- - **The theme only affects editor chrome** โ€” toolbar, dialogs, menus, and container. User content remains unchanged.
518
-
519
- ## ๐Ÿ› ๏ธ Development
520
-
521
- ### Prerequisites
522
-
523
- - Node.js 18+
524
- - pnpm 9.10.0+
525
-
526
- ### Setting Up Development Environment
527
-
528
- 1. **Clone the repository**
529
-
530
- ```bash
531
- git clone https://github.com/ayush1852017/smart-rte.git
532
- cd smart-rte
533
- ```
534
-
535
- 2. **Install dependencies**
536
-
537
- ```bash
538
- pnpm install
539
- ```
540
-
541
- 3. **Build the project**
542
-
543
- ```bash
544
- # Build TypeScript packages
545
- pnpm build
546
- ```
547
-
548
- 4. **Run the development playground**
549
-
550
- ```bash
551
- cd packages/react/playground
552
- pnpm install
553
- pnpm dev
554
- ```
555
-
556
- The playground will be available at `http://localhost:5173`
293
+ | Variable | Description |
294
+ |---|---|
295
+ | `--srte-background` / `--srte-canvas` | Toolbar/chrome background vs. editing-surface background |
296
+ | `--srte-foreground` / `--srte-muted-foreground` | Primary vs. secondary text |
297
+ | `--srte-border` | Standard border color |
298
+ | `--srte-ring` | Focus ring color |
299
+ | `--srte-accent` / `--srte-accent-bg` | Selection/active-state color and its translucent background |
300
+ | `--srte-primary` / `--srte-on-primary` | Primary action button background/text |
301
+ | `--srte-danger` | Destructive action color |
302
+ | `--srte-modal-bg` / `--srte-modal-backdrop` | Dialog background and overlay |
303
+ | `--srte-menu-bg` / `--srte-menu-shadow` | Dropdown/context-menu background and shadow |
304
+ | `--srte-code-bg` / `--srte-code-text` | Code block colors |
557
305
 
558
- ### Project Structure
306
+ These fall back to sensible defaults, and also read from common shadcn/ui-style tokens (`--card`, `--background`, `--foreground`, `--muted`, `--border`, `--ring`) if your app already defines those โ€” so a Tailwind/shadcn app may need no overrides at all. Colors set via the color picker (text/background) are inline styles on content and are unaffected by theming โ€” only editor chrome (toolbar, dialogs, menus) is themed.
559
307
 
560
- ```
561
- smart-rte/
562
- โ”œโ”€โ”€ packages/
563
- โ”‚ โ””โ”€โ”€ react/ # Main React package (smartrte-react)
564
- โ”‚ โ”œโ”€โ”€ src/
565
- โ”‚ โ”‚ โ”œโ”€โ”€ components/
566
- โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ ClassicEditor.tsx # Main editor component
567
- โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ MediaManager.tsx # Media management component
568
- โ”‚ โ”‚ โ””โ”€โ”€ index.ts
569
- โ”‚ โ”œโ”€โ”€ playground/ # Development playground
570
- โ”‚ โ””โ”€โ”€ package.json
571
- โ”œโ”€โ”€ dart/ # Flutter/Dart packages
572
- โ”‚ โ”œโ”€โ”€ smartrte_flutter/ # Flutter WebView integration
573
- โ”‚ โ””โ”€โ”€ example_app/ # Flutter example
574
- โ””โ”€โ”€ package.json
575
- ```
308
+ ## Standalone / non-React embed
576
309
 
577
- ### Building for Production
578
-
579
- ```bash
580
- # Build the React package
581
- cd packages/react
582
- pnpm build
583
-
584
- # This creates:
585
- # - dist/index.js - ES module
586
- # - dist/index.d.ts - TypeScript definitions
587
- # - dist/embed.js - Standalone embed bundle
588
- ```
310
+ For a host that isn't a React app (or embeds via WebView โ€” the [Flutter package](https://github.com/ayush1852017/smart-rte/tree/master/dart/smartrte_flutter) uses exactly this), a global-script build is available:
589
311
 
590
- ### Running Tests
591
-
592
- ```bash
593
- # Run vitest
594
- pnpm test
595
-
596
- # Run E2E tests with Playwright
597
- pnpm e2e
598
- ```
599
-
600
- ### Running Storybook
312
+ ```ts
313
+ import "smartrte-react/standalone/classic-editor-embed";
601
314
 
602
- ```bash
603
- cd packages/react
604
- pnpm storybook
315
+ window.SmartRTE.ClassicEditor.init({
316
+ target: document.getElementById("editor"),
317
+ value: "<p>Hello</p>",
318
+ tools: { video: false, audio: false },
319
+ onChange: (html) => console.log(html),
320
+ });
605
321
  ```
606
322
 
607
- Storybook will be available at `http://localhost:6006`
608
-
609
- ## ๐Ÿ“ Publishing
323
+ Returns a controller: `{ setHtml, getHtml, focus, blur, destroy }`.
610
324
 
611
- ### For Package Maintainers
325
+ ## Security
612
326
 
613
- The package is published to npm as `smartrte-react`.
327
+ The editor outputs HTML and never persists anything itself โ€” storage, credentials, and the actual save are always the host's. Pasted HTML is sanitized on the way in (DOMPurify), but **always sanitize before rendering elsewhere**: if you take the editor's HTML output and `dangerouslySetInnerHTML` it in a different context (an email, a public page), treat it the same as any other user-generated HTML.
614
328
 
615
- ```bash
616
- # Make sure you're in packages/react
617
- cd packages/react
329
+ ```tsx
330
+ import DOMPurify from "dompurify";
618
331
 
619
- # Update version in package.json
620
- # Then publish
621
- pnpm publish
332
+ function DisplayContent({ html }: { html: string }) {
333
+ return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />;
334
+ }
622
335
  ```
623
336
 
624
- The `prepublishOnly` script automatically runs `build:all` before publishing.
625
-
626
- ### Version Management
627
-
628
- We follow [Semantic Versioning](https://semver.org/):
629
-
630
- - **MAJOR** version for incompatible API changes
631
- - **MINOR** version for backwards-compatible functionality
632
- - **PATCH** version for backwards-compatible bug fixes
633
-
634
- ## ๐Ÿค Contributing
337
+ Found a security issue? Please email support@openstash.in rather than opening a public issue.
635
338
 
636
- We welcome contributions! Here's how you can help:
339
+ ## Browser support
637
340
 
638
- ### Reporting Bugs
341
+ Chromium, Firefox, and WebKit (Safari) โ€” the full end-to-end suite runs against all three, headless and current, on every change.
639
342
 
640
- 1. Check if the bug has already been reported in [Issues](https://github.com/ayush1852017/smart-rte/issues)
641
- 2. If not, create a new issue with:
642
- - Clear title and description
643
- - Steps to reproduce
644
- - Expected vs actual behavior
645
- - Screenshots if applicable
646
- - Your environment (browser, OS, React version)
343
+ ## Development
647
344
 
648
- ### Suggesting Features
649
-
650
- 1. Check [existing feature requests](https://github.com/ayush1852017/smart-rte/issues?q=is%3Aissue+label%3Aenhancement)
651
- 2. Create a new issue with:
652
- - Clear description of the feature
653
- - Use cases
654
- - Proposed API (if applicable)
655
-
656
- ### Pull Requests
657
-
658
- 1. **Fork** the repository
659
- 2. **Create** a feature branch (`git checkout -b feature/amazing-feature`)
660
- 3. **Make** your changes
661
- 4. **Test** your changes thoroughly
662
- 5. **Commit** with clear messages (`git commit -m 'Add amazing feature'`)
663
- 6. **Push** to your fork (`git push origin feature/amazing-feature`)
664
- 7. **Open** a Pull Request
665
-
666
- #### PR Guidelines
667
-
668
- - Follow the existing code style
669
- - Add tests for new features
670
- - Update documentation
671
- - Keep PRs focused on a single feature/fix
672
- - Write clear commit messages
673
-
674
- ### Development Workflow
345
+ This package lives in a pnpm workspace monorepo alongside `smartrte-core`.
675
346
 
676
347
  ```bash
677
- # 1. Create a feature branch
678
- git checkout -b feature/my-feature
679
-
680
- # 2. Make changes and test
681
- pnpm dev # Run playground
682
- pnpm test # Run tests
683
-
684
- # 3. Build to ensure no errors
685
- pnpm build
686
-
687
- # 4. Commit and push
688
- git add .
689
- git commit -m "feat: add my feature"
690
- git push origin feature/my-feature
691
-
692
- # 5. Create PR on GitHub
348
+ git clone https://github.com/ayush1852017/smart-rte.git
349
+ cd smart-rte
350
+ pnpm install
351
+ pnpm build # builds every package
693
352
  ```
694
353
 
695
- ## ๐Ÿ› Troubleshooting
696
-
697
- ### Common Issues
698
-
699
- #### Issue: Editor not showing/rendering
700
-
701
- **Solution:** Make sure React and React-DOM are installed as peer dependencies:
702
-
703
354
  ```bash
704
- npm install react@18 react-dom@18
705
- ```
706
-
707
- #### Issue: Formula rendering not working
708
-
709
- **Solution:** Ensure KaTeX is loaded in your HTML:
710
-
711
- ```html
712
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css">
713
- <script src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"></script>
355
+ # Live playground (aliased to workspace source, not the built dist - edits hot-reload)
356
+ cd packages/react/playground
357
+ pnpm install
358
+ pnpm dev # http://localhost:5173
714
359
  ```
715
360
 
716
- #### Issue: TypeScript errors
717
-
718
- **Solution:** Make sure you have the latest type definitions:
719
-
720
361
  ```bash
721
- npm install --save-dev @types/react@18 @types/react-dom@18
722
- ```
723
-
724
- #### Issue: Build errors in Next.js
725
-
726
- **Solution:** Use dynamic imports to disable SSR:
727
-
728
- ```tsx
729
- const ClassicEditor = dynamic(
730
- () => import('smartrte-react').then(mod => mod.ClassicEditor),
731
- { ssr: false }
732
- );
733
- ```
734
-
735
- #### Issue: Images not uploading
736
-
737
- **Solution:** Check that the `media` prop is set to `true` and implement a custom `mediaManager` if you need server-side uploads.
738
-
739
- ## ๐Ÿ” Security
740
-
741
- ### Reporting Security Issues
742
-
743
- If you discover a security vulnerability, please email [support@openstash.in] instead of using the issue tracker.
744
-
745
- ### Content Sanitization
746
-
747
- **โš ๏ธ Important:** The editor outputs raw HTML. Always sanitize user-generated content before displaying it to prevent XSS attacks.
748
-
749
- Recommended libraries:
750
- - [DOMPurify](https://github.com/cure53/DOMPurify)
751
- - [sanitize-html](https://github.com/apostrophecms/sanitize-html)
752
-
753
- Example:
754
-
755
- ```tsx
756
- import DOMPurify from 'dompurify';
757
-
758
- function DisplayContent({ html }) {
759
- const sanitized = DOMPurify.sanitize(html);
760
- return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
761
- }
762
- ```
763
-
764
- ## ๐Ÿ“„ License
765
-
766
- This project is licensed under the MIT License - see the [LICENSE](../../dart/smartrte_flutter/LICENSE) file for details.
767
-
768
- ```
769
- MIT License
770
-
771
- Copyright (c) 2025 Smart RTE Contributors
772
-
773
- Permission is hereby granted, free of charge, to any person obtaining a copy
774
- of this software and associated documentation files (the "Software"), to deal
775
- in the Software without restriction, including without limitation the rights
776
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
777
- copies of the Software, and to permit persons to whom the Software is
778
- furnished to do so, subject to the following conditions:
779
-
780
- The above copyright notice and this permission notice shall be included in all
781
- copies or substantial portions of the Software.
782
-
783
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
784
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
785
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
786
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
787
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
788
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
789
- SOFTWARE.
362
+ # From packages/react
363
+ pnpm test # vitest unit suite
364
+ pnpm e2e # Playwright, all 3 browsers
365
+ pnpm storybook # component stories, http://localhost:6006
790
366
  ```
791
367
 
792
- ## ๐Ÿ‘ฅ Authors & Contributors
793
-
794
- - **Smart RTE Team** - Initial work and maintenance
795
-
796
- See the list of [contributors](https://github.com/ayush1852017/smart-rte/contributors) who participated in this project.
797
-
798
- ## ๐Ÿ™ Acknowledgments
799
-
800
- - [KaTeX](https://katex.org/) - For mathematical formula rendering
801
- - [React](https://reactjs.org/) - The UI library
802
- - [Vite](https://vitejs.dev/) - Build tool
803
- - All our amazing [contributors](https://github.com/ayush1852017/smart-rte/contributors)
804
-
805
- ## ๐Ÿ“ž Support
806
-
807
- - **Documentation:** You're reading it! ๐Ÿ“–
808
- - **Issues:** [GitHub Issues](https://github.com/ayush1852017/smart-rte/issues)
809
- - **Discussions:** [GitHub Discussions](https://github.com/ayush1852017/smart-rte/discussions)
810
- - **Twitter:** [@smartrte](https://twitter.com/smartrte) (if applicable)
811
-
812
- ## ๐Ÿ—บ๏ธ Roadmap
813
-
814
- ### Current Version (0.2.x)
815
-
816
- - โœ… Rich text editing
817
- - โœ… Table support
818
- - โœ… Formula support (LaTeX/KaTeX)
819
- - โœ… Media management
820
- - โœ… TypeScript support
821
- - โœ… Dark mode & theming (CSS custom properties)
822
-
823
- ### Upcoming Features
824
-
825
- - ๐Ÿ”„ Collaborative editing
826
- - ๐Ÿ”„ Undo/Redo improvements
827
- - ๐Ÿ”„ Code syntax highlighting
828
- - ๐Ÿ”„ Markdown import/export
829
- - ๐Ÿ”„ Custom toolbar configuration
830
- - ๐Ÿ”„ Mobile optimization
831
- - ๐Ÿ”„ Accessibility improvements (ARIA labels, keyboard shortcuts)
832
-
833
- ## ๐Ÿ“Š Browser Support
834
-
835
- | Browser | Version |
836
- |---------|---------|
837
- | Chrome | Last 2 versions |
838
- | Firefox | Last 2 versions |
839
- | Safari | Last 2 versions |
840
- | Edge | Last 2 versions |
841
-
842
- ## ๐Ÿ”— Related Packages
843
-
844
- - **smartrte-flutter** - Flutter/Dart WebView implementation
845
-
846
- ## ๐Ÿ’ก Tips & Best Practices
847
-
848
- 1. **Performance**: For large documents, consider implementing lazy loading or pagination
849
- 2. **State Management**: Use React state or a state management library (Redux, Zustand) for complex applications
850
- 3. **Validation**: Always validate and sanitize HTML content before storing or displaying
851
- 4. **Accessibility**: Test with screen readers and keyboard navigation
852
- 5. **Mobile**: Test on mobile devices as touch interactions may differ
853
- 6. **Auto-save**: Implement auto-save functionality to prevent data loss
854
-
855
- ## ๐ŸŽ“ Learning Resources
856
-
857
- ### For Entry-Level Developers
858
-
859
- 1. **Getting Started with React**: [React Official Tutorial](https://react.dev/learn)
860
- 2. **Understanding Rich Text Editors**: [MDN ContentEditable](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable)
861
- 3. **TypeScript Basics**: [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html)
862
-
863
- ### For Mid-Level Developers
864
-
865
- 1. **Advanced React Patterns**: Hooks, Context, Performance Optimization
866
- 2. **Component Design**: Building reusable, maintainable components
867
- 3. **State Management**: When and how to use external state management
868
-
869
- ### For Senior Developers
870
-
871
- 1. **Architecture**: Designing scalable editor implementations
872
- 2. **Performance**: Optimization techniques for large documents
873
- 3. **Extensibility**: Building plugin systems and custom extensions
874
- 4. **Cross-platform**: Adapting the editor for different frameworks
368
+ ## Contributing
875
369
 
876
- ---
370
+ Issues and PRs are welcome at [github.com/ayush1852017/smart-rte](https://github.com/ayush1852017/smart-rte/issues). For a bug report, include a minimal repro, expected vs. actual behavior, and your browser/OS. For a PR: keep it focused on one change, add test coverage (unit and/or a Playwright spec, matching whichever existing test file is closest to what you touched), and run `pnpm build && pnpm test` before pushing.
877
371
 
878
- **Happy Editing! ๐ŸŽ‰**
372
+ ## License
879
373
 
880
- If you find this package useful, please consider giving it a โญ on [GitHub](https://github.com/ayush1852017/smart-rte)!
374
+ MIT โ€” see [LICENSE](https://github.com/ayush1852017/smart-rte/blob/master/LICENSE).