eddyter 1.3.73 → 1.3.75

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 (29) hide show
  1. package/LICENSE +24 -24
  2. package/README.md +478 -478
  3. package/dist/EddyterIcon.svg +24 -24
  4. package/dist/{ImageResizer-FrE13F54.js → ImageResizer-o0eMm1Mg.js} +56 -50
  5. package/dist/api/ai/aiAgentService.d.ts +22 -1
  6. package/dist/api/config/endpoints.d.ts +3 -0
  7. package/dist/assets/style.css +1 -1
  8. package/dist/{babel-CCPWkrf4.js → babel-B9hn44Wo.js} +726 -1302
  9. package/dist/components/VideoView/index.d.ts +3 -1
  10. package/dist/constants.d.ts +2 -1
  11. package/dist/{estree-CxUPh9wa.js → estree-CocPn_Md.js} +529 -917
  12. package/dist/hooks/useBlockFormat.d.ts +1 -0
  13. package/dist/{html-CmniStvG.js → html-CxCicOef.js} +350 -589
  14. package/dist/{html2pdf.bundle-CQue4YDW.js → html2pdf.bundle-CVq-OpZt.js} +2778 -3797
  15. package/dist/{html2pdf.bundle.min-BEj2NT3U.js → html2pdf.bundle.min-BxzIoi3T.js} +3405 -5221
  16. package/dist/{index-CX3cfSUQ.js → index-CtPRZTab.js} +27 -18
  17. package/dist/index-Cuv9ugJL.js +381 -0
  18. package/dist/{index-CfRDm1jv.js → index-DxEP36zG.js} +12228 -12186
  19. package/dist/{index-BO5ICrpG.js → index-eRyVFO7x.js} +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/{markdown-B0mEGGfQ.js → markdown-BUjgWFLu.js} +578 -1015
  22. package/dist/nodes/GeneratingImageNode.d.ts +30 -0
  23. package/dist/nodes/VideoNode.d.ts +10 -3
  24. package/dist/{postcss-B0bxXf7u.js → postcss-CGIcwj_g.js} +615 -1065
  25. package/dist/{standalone-DmuJV5rn.js → standalone-C0qguT38.js} +350 -596
  26. package/dist/{typescript-DZlC_9M8.js → typescript-BM7wk6k-.js} +1114 -1806
  27. package/dist/ui/Icons.d.ts +2 -1
  28. package/package.json +149 -152
  29. package/dist/index-Buj5fA92.js +0 -274
package/README.md CHANGED
@@ -1,478 +1,478 @@
1
- # Eddyter
2
-
3
- A powerful, configurable rich text editor built on [Lexical](https://lexical.dev/) with AI capabilities, dark mode support, and API key authentication.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install eddyter
9
- # or
10
- yarn add eddyter
11
- # or
12
- pnpm add eddyter
13
- ```
14
-
15
- ### Compatibility
16
-
17
- | Requirement | Version |
18
- |-------------|---------|
19
- | React | 18.2+ or 19.x |
20
- | React DOM | 18.2+ or 19.x |
21
- | Node.js | 16+ |
22
-
23
- ## Quick Start
24
-
25
- ### 1. Import styles
26
-
27
- ```tsx
28
- import 'eddyter/style.css';
29
- ```
30
-
31
- > **Important:** The stylesheet is required for tables, toolbars, and all editor components to render correctly.
32
-
33
- ### 2. Get your API key
34
-
35
- 1. Create an account at [eddyter.com](https://www.eddyter.com/)
36
- 2. Navigate to [License Keys](https://www.eddyter.com/user/license-key) in your dashboard
37
- 3. Copy your API key
38
-
39
- ### 3. Add the editor
40
-
41
- ```tsx
42
- import React from 'react';
43
- import {
44
- ConfigurableEditorWithAuth,
45
- EditorProvider,
46
- defaultEditorConfig
47
- } from 'eddyter';
48
- import 'eddyter/style.css';
49
-
50
- function App() {
51
- const apiKey = process.env.NEXT_PUBLIC_EDITOR_API_KEY!;
52
-
53
- const currentUser = {
54
- id: 'user-123',
55
- name: 'John Doe',
56
- email: 'john@example.com',
57
- avatar: 'https://example.com/avatar.jpg' // optional
58
- };
59
-
60
- return (
61
- <EditorProvider
62
- defaultFontFamilies={defaultEditorConfig.defaultFontFamilies}
63
- currentUser={currentUser}
64
- >
65
- <ConfigurableEditorWithAuth
66
- apiKey={apiKey}
67
- onChange={(html) => console.log('Content:', html)}
68
- initialContent="<p>Start writing...</p>"
69
- mentionUserList={['Alice', 'Bob', 'Charlie']}
70
- onAuthSuccess={() => console.log('Editor ready!')}
71
- onAuthError={(error) => console.error('Auth failed:', error)}
72
- />
73
- </EditorProvider>
74
- );
75
- }
76
- ```
77
-
78
- ## Features
79
-
80
- ### Text & Formatting
81
- - Bold, italic, underline, strikethrough, subscript, superscript
82
- - Text color and background highlight with color picker
83
- - 20+ font families with adjustable font sizes
84
- - Text alignment (left, center, right, justify)
85
- - Line height and letter spacing controls
86
-
87
- ### Lists & Structure
88
- - Bullet lists, numbered lists (decimal, alpha, roman)
89
- - Interactive checklists with strikethrough
90
- - Headings (H1-H6), blockquotes
91
- - Horizontal rules
92
-
93
- ### Tables
94
- - Insert/delete rows and columns, merge cells
95
- - Drag-to-resize columns and rows
96
- - Header row styling
97
- - Row striping with custom colors
98
- - Right-click context menu for table actions
99
-
100
- ### Media
101
- - Image upload with drag-drop and 8-point resize handles
102
- - Video embed with drag-drop and paste support
103
- - File attachments (downloadable files)
104
- - Link insertion with floating editor
105
- - Automatic link preview on hover
106
- - Rich embeds for external content (YouTube, etc.)
107
-
108
- ### AI Features (Premium)
109
- - AI Chat assistant for content help
110
- - Smart autocomplete (AI-powered text suggestions)
111
- - Real-time grammar check and corrections
112
- - Text enhancement (improve, shorten, expand)
113
- - Tone adjustment (formal, casual, professional)
114
- - AI image generation from text prompts
115
-
116
- ### Advanced
117
- - Slash commands (`/` for quick formatting)
118
- - @Mentions with customizable user list
119
- - Inline comments with bubble UI and sidebar
120
- - Note panels (info, warning, error, success)
121
- - Code blocks with syntax highlighting
122
- - Interactive charts
123
- - Digital signature capture
124
- - Voice input / transcription
125
- - Export to PDF
126
- - HTML view toggle
127
- - Drag-and-drop block reordering
128
- - Markdown shortcuts
129
-
130
- ### Dark Mode
131
-
132
- The editor automatically detects your app's theme:
133
- - Checks for `dark` class on `<html>` or `<body>`
134
- - Falls back to `prefers-color-scheme: dark` system preference
135
- - Or set explicitly via the `darkMode` prop on `EditorProvider`
136
-
137
- ### Preview Mode
138
-
139
- Display saved editor content in read-only mode with interactive features:
140
-
141
- ```tsx
142
- <ConfigurableEditorWithAuth
143
- apiKey={apiKey}
144
- mode="preview"
145
- initialContent={savedHtml}
146
- onPreviewClick={() => setMode('edit')}
147
- previewClassName="my-preview-styles"
148
- />
149
- ```
150
-
151
- ## API Reference
152
-
153
- ### `<EditorProvider>`
154
-
155
- Provides authentication and configuration context. Must wrap the editor component.
156
-
157
- | Prop | Type | Required | Description |
158
- |------|------|----------|-------------|
159
- | `children` | `ReactNode` | Yes | Editor component to render |
160
- | `defaultFontFamilies` | `string[]` | No | Font family names for the font selector |
161
- | `currentUser` | `CurrentUser` | No | Current user for comments feature |
162
- | `enableLinkPreview` | `boolean` | No | Enable link preview on hover (default: `true`) |
163
- | `apiKey` | `string` | No | API key for link preview in read-only mode |
164
-
165
- #### CurrentUser Type
166
-
167
- ```ts
168
- interface CurrentUser {
169
- id: string;
170
- name: string;
171
- email: string;
172
- avatar?: string;
173
- }
174
- ```
175
-
176
- ### `<ConfigurableEditorWithAuth>`
177
-
178
- The main editor component with authentication.
179
-
180
- | Prop | Type | Required | Description |
181
- |------|------|----------|-------------|
182
- | `apiKey` | `string` | Yes | Your API key for authentication |
183
- | `initialContent` | `string` | No | Initial HTML content |
184
- | `onChange` | `(html: string) => void` | No | Content change callback |
185
- | `defaultFontFamilies` | `string[]` | No | Font names for the font selector |
186
- | `mentionUserList` | `string[]` | No | Usernames for @mention feature |
187
- | `onAuthSuccess` | `() => void` | No | Called when authentication succeeds |
188
- | `onAuthError` | `(error: string) => void` | No | Called when authentication fails |
189
- | `customVerifyKey` | `(key: string) => Promise<ApiResponse>` | No | Custom key verification function |
190
- | `mode` | `"edit" \| "preview"` | No | Editor mode (default: `"edit"`) |
191
- | `previewClassName` | `string` | No | CSS class for preview container |
192
- | `previewStyle` | `React.CSSProperties` | No | Inline styles for preview container |
193
- | `onPreviewClick` | `() => void` | No | Click handler for preview mode |
194
- | `enableReactNativeBridge` | `boolean` | No | Enable React Native WebView bridge |
195
- | `onEditorReady` | `() => void` | No | Called when editor is fully loaded |
196
- | `onFocus` | `() => void` | No | Called on editor focus |
197
- | `onBlur` | `() => void` | No | Called on editor blur |
198
- | `onHeightChange` | `(height: number) => void` | No | Called when editor height changes |
199
-
200
- ## Examples
201
-
202
- ### Basic Editor
203
-
204
- ```tsx
205
- import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
206
- import 'eddyter/style.css';
207
-
208
- export default function BasicEditor() {
209
- return (
210
- <EditorProvider>
211
- <ConfigurableEditorWithAuth
212
- apiKey="your-api-key"
213
- onAuthSuccess={() => console.log('Ready!')}
214
- />
215
- </EditorProvider>
216
- );
217
- }
218
- ```
219
-
220
- ### Editor with State Management
221
-
222
- ```tsx
223
- import { useState } from 'react';
224
- import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
225
- import 'eddyter/style.css';
226
-
227
- export default function EditorWithState() {
228
- const [content, setContent] = useState('<p>Start writing...</p>');
229
-
230
- const handleSave = async () => {
231
- await fetch('/api/save', {
232
- method: 'POST',
233
- headers: { 'Content-Type': 'application/json' },
234
- body: JSON.stringify({ content })
235
- });
236
- };
237
-
238
- return (
239
- <div>
240
- <EditorProvider>
241
- <ConfigurableEditorWithAuth
242
- apiKey="your-api-key"
243
- initialContent={content}
244
- onChange={setContent}
245
- />
246
- </EditorProvider>
247
- <button onClick={handleSave}>Save</button>
248
- </div>
249
- );
250
- }
251
- ```
252
-
253
- ### Editor with Comments & Mentions
254
-
255
- ```tsx
256
- import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
257
- import 'eddyter/style.css';
258
-
259
- export default function EditorWithComments({ user }) {
260
- const currentUser = {
261
- id: user.id,
262
- name: user.name,
263
- email: user.email,
264
- avatar: user.avatarUrl
265
- };
266
-
267
- return (
268
- <EditorProvider currentUser={currentUser}>
269
- <ConfigurableEditorWithAuth
270
- apiKey="your-api-key"
271
- mentionUserList={['Alice', 'Bob', 'Charlie']}
272
- />
273
- </EditorProvider>
274
- );
275
- }
276
- ```
277
-
278
- ### Custom API Key Verification
279
-
280
- ```tsx
281
- import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
282
- import 'eddyter/style.css';
283
-
284
- export default function EditorWithCustomAuth() {
285
- const customVerifyKey = async (apiKey: string) => {
286
- try {
287
- const response = await fetch('/api/verify-key', {
288
- method: 'POST',
289
- headers: { 'Content-Type': 'application/json' },
290
- body: JSON.stringify({ apiKey })
291
- });
292
- const data = await response.json();
293
- return { success: data.valid, message: data.message || 'Verified' };
294
- } catch {
295
- return { success: false, message: 'Verification failed' };
296
- }
297
- };
298
-
299
- return (
300
- <EditorProvider>
301
- <ConfigurableEditorWithAuth
302
- apiKey="your-api-key"
303
- customVerifyKey={customVerifyKey}
304
- />
305
- </EditorProvider>
306
- );
307
- }
308
- ```
309
-
310
- ## Link Preview
311
-
312
- The editor includes automatic link preview on hover.
313
-
314
- - **Inside the editor**: Works automatically after authentication
315
- - **Read-only content**: Pass `apiKey` to `EditorProvider`
316
- - **Disable**: Set `enableLinkPreview={false}` on `EditorProvider`
317
-
318
- ```tsx
319
- // Read-only content with link preview
320
- <EditorProvider apiKey="your-api-key">
321
- <div dangerouslySetInnerHTML={{ __html: savedHtml }} />
322
- </EditorProvider>
323
-
324
- // Disable link preview
325
- <EditorProvider enableLinkPreview={false}>
326
- {/* content */}
327
- </EditorProvider>
328
- ```
329
-
330
- ## React Native Integration
331
-
332
- Use Eddyter in React Native via WebView by loading a deployed version of the editor.
333
-
334
- ```bash
335
- npm install react-native-webview
336
- ```
337
-
338
- ```tsx
339
- import React, { useRef, useState, useCallback } from 'react';
340
- import { View, ActivityIndicator, KeyboardAvoidingView, Platform } from 'react-native';
341
- import { WebView, WebViewMessageEvent } from 'react-native-webview';
342
-
343
- interface RichTextEditorProps {
344
- editorBaseUrl: string;
345
- apiKey: string;
346
- initialContent?: string;
347
- theme?: 'light' | 'dark';
348
- style?: object;
349
- onChange?: (content: string) => void;
350
- onReady?: () => void;
351
- onAuthSuccess?: () => void;
352
- onAuthError?: (error: string) => void;
353
- }
354
-
355
- export const RichTextEditor: React.FC<RichTextEditorProps> = ({
356
- editorBaseUrl,
357
- apiKey,
358
- initialContent,
359
- theme = 'light',
360
- style,
361
- onChange,
362
- onReady,
363
- onAuthSuccess,
364
- onAuthError,
365
- }) => {
366
- const webViewRef = useRef<WebView>(null);
367
- const [isLoading, setIsLoading] = useState(true);
368
-
369
- const buildEditorUrl = () => {
370
- const baseUrl = editorBaseUrl.replace(/\/$/, '');
371
- const params = new URLSearchParams();
372
- if (apiKey) params.append('apiKey', apiKey);
373
- if (theme) params.append('theme', theme);
374
- return `${baseUrl}?${params.toString()}`;
375
- };
376
-
377
- const handleMessage = useCallback((event: WebViewMessageEvent) => {
378
- try {
379
- const message = JSON.parse(event.nativeEvent.data);
380
- switch (message.type) {
381
- case 'EDITOR_READY':
382
- setIsLoading(false);
383
- onReady?.();
384
- if (initialContent && webViewRef.current) {
385
- webViewRef.current.postMessage(
386
- JSON.stringify({ type: 'SET_CONTENT', payload: { content: initialContent } })
387
- );
388
- }
389
- break;
390
- case 'CONTENT_CHANGE':
391
- onChange?.(message.payload?.content || '');
392
- break;
393
- case 'AUTH_SUCCESS':
394
- onAuthSuccess?.();
395
- break;
396
- case 'AUTH_ERROR':
397
- onAuthError?.(message.payload?.error);
398
- break;
399
- }
400
- } catch (e) {
401
- console.warn('[RichTextEditor] Failed to parse message:', e);
402
- }
403
- }, [onChange, onReady, onAuthSuccess, onAuthError, initialContent]);
404
-
405
- return (
406
- <View style={[{ flex: 1 }, style]}>
407
- <WebView
408
- ref={webViewRef}
409
- source={{ uri: buildEditorUrl() }}
410
- style={{ flex: 1 }}
411
- onMessage={handleMessage}
412
- javaScriptEnabled={true}
413
- domStorageEnabled={true}
414
- keyboardDisplayRequiresUserAction={false}
415
- />
416
- {isLoading && (
417
- <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center' }}>
418
- <ActivityIndicator size="large" />
419
- </View>
420
- )}
421
- </View>
422
- );
423
- };
424
- ```
425
-
426
- ### Message Protocol
427
-
428
- | Message Type | Direction | Description |
429
- |---|---|---|
430
- | `EDITOR_READY` | Editor → RN | Editor has finished loading |
431
- | `CONTENT_CHANGE` | Editor → RN | Content was modified (`{ content: string }`) |
432
- | `AUTH_SUCCESS` | Editor → RN | Authentication succeeded |
433
- | `AUTH_ERROR` | Editor → RN | Authentication failed (`{ error: string }`) |
434
- | `SET_CONTENT` | RN → Editor | Set editor content (`{ content: string }`) |
435
-
436
- ## Exports
437
-
438
- ```ts
439
- // Components
440
- import {
441
- ConfigurableEditorWithAuth, // Main editor with auth
442
- ConfigurableEditor, // Editor without auth wrapper
443
- EditorProvider, // Context provider
444
- LinkPreviewHover, // Standalone link preview component
445
- } from 'eddyter';
446
-
447
- // Hooks & utilities
448
- import {
449
- useEditor, // Access editor context
450
- useHtmlView, // Access HTML view state
451
- verifyApiKey, // Verify API key programmatically
452
- useReactNativeBridge, // React Native bridge hook
453
- isReactNativeWebView, // Check if running in RN WebView
454
- } from 'eddyter';
455
-
456
- // Config
457
- import { defaultEditorConfig } from 'eddyter';
458
-
459
- // Types
460
- import type {
461
- CurrentUser,
462
- EditorConfigTypes,
463
- LinkPreviewHoverProps,
464
- ReactNativeBridgeConfig,
465
- ReactNativeMessage,
466
- ReactNativeMessageType,
467
- } from 'eddyter';
468
- ```
469
-
470
- ## License
471
-
472
- Eddyter is **proprietary software**.
473
-
474
- - Free for evaluation and non-commercial use
475
- - **Commercial use requires a paid license**
476
- - SaaS, redistribution, and competing products are prohibited without permission
477
-
478
- For commercial licensing, visit [eddyter.com](https://www.eddyter.com/)
1
+ # Eddyter
2
+
3
+ A powerful, configurable rich text editor built on [Lexical](https://lexical.dev/) with AI capabilities, dark mode support, and API key authentication.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install eddyter
9
+ # or
10
+ yarn add eddyter
11
+ # or
12
+ pnpm add eddyter
13
+ ```
14
+
15
+ ### Compatibility
16
+
17
+ | Requirement | Version |
18
+ |-------------|---------|
19
+ | React | 18.2+ or 19.x |
20
+ | React DOM | 18.2+ or 19.x |
21
+ | Node.js | 16+ |
22
+
23
+ ## Quick Start
24
+
25
+ ### 1. Import styles
26
+
27
+ ```tsx
28
+ import 'eddyter/style.css';
29
+ ```
30
+
31
+ > **Important:** The stylesheet is required for tables, toolbars, and all editor components to render correctly.
32
+
33
+ ### 2. Get your API key
34
+
35
+ 1. Create an account at [eddyter.com](https://www.eddyter.com/)
36
+ 2. Navigate to [License Keys](https://www.eddyter.com/user/license-key) in your dashboard
37
+ 3. Copy your API key
38
+
39
+ ### 3. Add the editor
40
+
41
+ ```tsx
42
+ import React from 'react';
43
+ import {
44
+ ConfigurableEditorWithAuth,
45
+ EditorProvider,
46
+ defaultEditorConfig
47
+ } from 'eddyter';
48
+ import 'eddyter/style.css';
49
+
50
+ function App() {
51
+ const apiKey = process.env.NEXT_PUBLIC_EDITOR_API_KEY!;
52
+
53
+ const currentUser = {
54
+ id: 'user-123',
55
+ name: 'John Doe',
56
+ email: 'john@example.com',
57
+ avatar: 'https://example.com/avatar.jpg' // optional
58
+ };
59
+
60
+ return (
61
+ <EditorProvider
62
+ defaultFontFamilies={defaultEditorConfig.defaultFontFamilies}
63
+ currentUser={currentUser}
64
+ >
65
+ <ConfigurableEditorWithAuth
66
+ apiKey={apiKey}
67
+ onChange={(html) => console.log('Content:', html)}
68
+ initialContent="<p>Start writing...</p>"
69
+ mentionUserList={['Alice', 'Bob', 'Charlie']}
70
+ onAuthSuccess={() => console.log('Editor ready!')}
71
+ onAuthError={(error) => console.error('Auth failed:', error)}
72
+ />
73
+ </EditorProvider>
74
+ );
75
+ }
76
+ ```
77
+
78
+ ## Features
79
+
80
+ ### Text & Formatting
81
+ - Bold, italic, underline, strikethrough, subscript, superscript
82
+ - Text color and background highlight with color picker
83
+ - 20+ font families with adjustable font sizes
84
+ - Text alignment (left, center, right, justify)
85
+ - Line height and letter spacing controls
86
+
87
+ ### Lists & Structure
88
+ - Bullet lists, numbered lists (decimal, alpha, roman)
89
+ - Interactive checklists with strikethrough
90
+ - Headings (H1-H6), blockquotes
91
+ - Horizontal rules
92
+
93
+ ### Tables
94
+ - Insert/delete rows and columns, merge cells
95
+ - Drag-to-resize columns and rows
96
+ - Header row styling
97
+ - Row striping with custom colors
98
+ - Right-click context menu for table actions
99
+
100
+ ### Media
101
+ - Image upload with drag-drop and 8-point resize handles
102
+ - Video embed with drag-drop and paste support
103
+ - File attachments (downloadable files)
104
+ - Link insertion with floating editor
105
+ - Automatic link preview on hover
106
+ - Rich embeds for external content (YouTube, etc.)
107
+
108
+ ### AI Features (Premium)
109
+ - AI Chat assistant for content help
110
+ - Smart autocomplete (AI-powered text suggestions)
111
+ - Real-time grammar check and corrections
112
+ - Text enhancement (improve, shorten, expand)
113
+ - Tone adjustment (formal, casual, professional)
114
+ - AI image generation from text prompts
115
+
116
+ ### Advanced
117
+ - Slash commands (`/` for quick formatting)
118
+ - @Mentions with customizable user list
119
+ - Inline comments with bubble UI and sidebar
120
+ - Note panels (info, warning, error, success)
121
+ - Code blocks with syntax highlighting
122
+ - Interactive charts
123
+ - Digital signature capture
124
+ - Voice input / transcription
125
+ - Export to PDF
126
+ - HTML view toggle
127
+ - Drag-and-drop block reordering
128
+ - Markdown shortcuts
129
+
130
+ ### Dark Mode
131
+
132
+ The editor automatically detects your app's theme:
133
+ - Checks for `dark` class on `<html>` or `<body>`
134
+ - Falls back to `prefers-color-scheme: dark` system preference
135
+ - Or set explicitly via the `darkMode` prop on `EditorProvider`
136
+
137
+ ### Preview Mode
138
+
139
+ Display saved editor content in read-only mode with interactive features:
140
+
141
+ ```tsx
142
+ <ConfigurableEditorWithAuth
143
+ apiKey={apiKey}
144
+ mode="preview"
145
+ initialContent={savedHtml}
146
+ onPreviewClick={() => setMode('edit')}
147
+ previewClassName="my-preview-styles"
148
+ />
149
+ ```
150
+
151
+ ## API Reference
152
+
153
+ ### `<EditorProvider>`
154
+
155
+ Provides authentication and configuration context. Must wrap the editor component.
156
+
157
+ | Prop | Type | Required | Description |
158
+ |------|------|----------|-------------|
159
+ | `children` | `ReactNode` | Yes | Editor component to render |
160
+ | `defaultFontFamilies` | `string[]` | No | Font family names for the font selector |
161
+ | `currentUser` | `CurrentUser` | No | Current user for comments feature |
162
+ | `enableLinkPreview` | `boolean` | No | Enable link preview on hover (default: `true`) |
163
+ | `apiKey` | `string` | No | API key for link preview in read-only mode |
164
+
165
+ #### CurrentUser Type
166
+
167
+ ```ts
168
+ interface CurrentUser {
169
+ id: string;
170
+ name: string;
171
+ email: string;
172
+ avatar?: string;
173
+ }
174
+ ```
175
+
176
+ ### `<ConfigurableEditorWithAuth>`
177
+
178
+ The main editor component with authentication.
179
+
180
+ | Prop | Type | Required | Description |
181
+ |------|------|----------|-------------|
182
+ | `apiKey` | `string` | Yes | Your API key for authentication |
183
+ | `initialContent` | `string` | No | Initial HTML content |
184
+ | `onChange` | `(html: string) => void` | No | Content change callback |
185
+ | `defaultFontFamilies` | `string[]` | No | Font names for the font selector |
186
+ | `mentionUserList` | `string[]` | No | Usernames for @mention feature |
187
+ | `onAuthSuccess` | `() => void` | No | Called when authentication succeeds |
188
+ | `onAuthError` | `(error: string) => void` | No | Called when authentication fails |
189
+ | `customVerifyKey` | `(key: string) => Promise<ApiResponse>` | No | Custom key verification function |
190
+ | `mode` | `"edit" \| "preview"` | No | Editor mode (default: `"edit"`) |
191
+ | `previewClassName` | `string` | No | CSS class for preview container |
192
+ | `previewStyle` | `React.CSSProperties` | No | Inline styles for preview container |
193
+ | `onPreviewClick` | `() => void` | No | Click handler for preview mode |
194
+ | `enableReactNativeBridge` | `boolean` | No | Enable React Native WebView bridge |
195
+ | `onEditorReady` | `() => void` | No | Called when editor is fully loaded |
196
+ | `onFocus` | `() => void` | No | Called on editor focus |
197
+ | `onBlur` | `() => void` | No | Called on editor blur |
198
+ | `onHeightChange` | `(height: number) => void` | No | Called when editor height changes |
199
+
200
+ ## Examples
201
+
202
+ ### Basic Editor
203
+
204
+ ```tsx
205
+ import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
206
+ import 'eddyter/style.css';
207
+
208
+ export default function BasicEditor() {
209
+ return (
210
+ <EditorProvider>
211
+ <ConfigurableEditorWithAuth
212
+ apiKey="your-api-key"
213
+ onAuthSuccess={() => console.log('Ready!')}
214
+ />
215
+ </EditorProvider>
216
+ );
217
+ }
218
+ ```
219
+
220
+ ### Editor with State Management
221
+
222
+ ```tsx
223
+ import { useState } from 'react';
224
+ import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
225
+ import 'eddyter/style.css';
226
+
227
+ export default function EditorWithState() {
228
+ const [content, setContent] = useState('<p>Start writing...</p>');
229
+
230
+ const handleSave = async () => {
231
+ await fetch('/api/save', {
232
+ method: 'POST',
233
+ headers: { 'Content-Type': 'application/json' },
234
+ body: JSON.stringify({ content })
235
+ });
236
+ };
237
+
238
+ return (
239
+ <div>
240
+ <EditorProvider>
241
+ <ConfigurableEditorWithAuth
242
+ apiKey="your-api-key"
243
+ initialContent={content}
244
+ onChange={setContent}
245
+ />
246
+ </EditorProvider>
247
+ <button onClick={handleSave}>Save</button>
248
+ </div>
249
+ );
250
+ }
251
+ ```
252
+
253
+ ### Editor with Comments & Mentions
254
+
255
+ ```tsx
256
+ import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
257
+ import 'eddyter/style.css';
258
+
259
+ export default function EditorWithComments({ user }) {
260
+ const currentUser = {
261
+ id: user.id,
262
+ name: user.name,
263
+ email: user.email,
264
+ avatar: user.avatarUrl
265
+ };
266
+
267
+ return (
268
+ <EditorProvider currentUser={currentUser}>
269
+ <ConfigurableEditorWithAuth
270
+ apiKey="your-api-key"
271
+ mentionUserList={['Alice', 'Bob', 'Charlie']}
272
+ />
273
+ </EditorProvider>
274
+ );
275
+ }
276
+ ```
277
+
278
+ ### Custom API Key Verification
279
+
280
+ ```tsx
281
+ import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
282
+ import 'eddyter/style.css';
283
+
284
+ export default function EditorWithCustomAuth() {
285
+ const customVerifyKey = async (apiKey: string) => {
286
+ try {
287
+ const response = await fetch('/api/verify-key', {
288
+ method: 'POST',
289
+ headers: { 'Content-Type': 'application/json' },
290
+ body: JSON.stringify({ apiKey })
291
+ });
292
+ const data = await response.json();
293
+ return { success: data.valid, message: data.message || 'Verified' };
294
+ } catch {
295
+ return { success: false, message: 'Verification failed' };
296
+ }
297
+ };
298
+
299
+ return (
300
+ <EditorProvider>
301
+ <ConfigurableEditorWithAuth
302
+ apiKey="your-api-key"
303
+ customVerifyKey={customVerifyKey}
304
+ />
305
+ </EditorProvider>
306
+ );
307
+ }
308
+ ```
309
+
310
+ ## Link Preview
311
+
312
+ The editor includes automatic link preview on hover.
313
+
314
+ - **Inside the editor**: Works automatically after authentication
315
+ - **Read-only content**: Pass `apiKey` to `EditorProvider`
316
+ - **Disable**: Set `enableLinkPreview={false}` on `EditorProvider`
317
+
318
+ ```tsx
319
+ // Read-only content with link preview
320
+ <EditorProvider apiKey="your-api-key">
321
+ <div dangerouslySetInnerHTML={{ __html: savedHtml }} />
322
+ </EditorProvider>
323
+
324
+ // Disable link preview
325
+ <EditorProvider enableLinkPreview={false}>
326
+ {/* content */}
327
+ </EditorProvider>
328
+ ```
329
+
330
+ ## React Native Integration
331
+
332
+ Use Eddyter in React Native via WebView by loading a deployed version of the editor.
333
+
334
+ ```bash
335
+ npm install react-native-webview
336
+ ```
337
+
338
+ ```tsx
339
+ import React, { useRef, useState, useCallback } from 'react';
340
+ import { View, ActivityIndicator, KeyboardAvoidingView, Platform } from 'react-native';
341
+ import { WebView, WebViewMessageEvent } from 'react-native-webview';
342
+
343
+ interface RichTextEditorProps {
344
+ editorBaseUrl: string;
345
+ apiKey: string;
346
+ initialContent?: string;
347
+ theme?: 'light' | 'dark';
348
+ style?: object;
349
+ onChange?: (content: string) => void;
350
+ onReady?: () => void;
351
+ onAuthSuccess?: () => void;
352
+ onAuthError?: (error: string) => void;
353
+ }
354
+
355
+ export const RichTextEditor: React.FC<RichTextEditorProps> = ({
356
+ editorBaseUrl,
357
+ apiKey,
358
+ initialContent,
359
+ theme = 'light',
360
+ style,
361
+ onChange,
362
+ onReady,
363
+ onAuthSuccess,
364
+ onAuthError,
365
+ }) => {
366
+ const webViewRef = useRef<WebView>(null);
367
+ const [isLoading, setIsLoading] = useState(true);
368
+
369
+ const buildEditorUrl = () => {
370
+ const baseUrl = editorBaseUrl.replace(/\/$/, '');
371
+ const params = new URLSearchParams();
372
+ if (apiKey) params.append('apiKey', apiKey);
373
+ if (theme) params.append('theme', theme);
374
+ return `${baseUrl}?${params.toString()}`;
375
+ };
376
+
377
+ const handleMessage = useCallback((event: WebViewMessageEvent) => {
378
+ try {
379
+ const message = JSON.parse(event.nativeEvent.data);
380
+ switch (message.type) {
381
+ case 'EDITOR_READY':
382
+ setIsLoading(false);
383
+ onReady?.();
384
+ if (initialContent && webViewRef.current) {
385
+ webViewRef.current.postMessage(
386
+ JSON.stringify({ type: 'SET_CONTENT', payload: { content: initialContent } })
387
+ );
388
+ }
389
+ break;
390
+ case 'CONTENT_CHANGE':
391
+ onChange?.(message.payload?.content || '');
392
+ break;
393
+ case 'AUTH_SUCCESS':
394
+ onAuthSuccess?.();
395
+ break;
396
+ case 'AUTH_ERROR':
397
+ onAuthError?.(message.payload?.error);
398
+ break;
399
+ }
400
+ } catch (e) {
401
+ console.warn('[RichTextEditor] Failed to parse message:', e);
402
+ }
403
+ }, [onChange, onReady, onAuthSuccess, onAuthError, initialContent]);
404
+
405
+ return (
406
+ <View style={[{ flex: 1 }, style]}>
407
+ <WebView
408
+ ref={webViewRef}
409
+ source={{ uri: buildEditorUrl() }}
410
+ style={{ flex: 1 }}
411
+ onMessage={handleMessage}
412
+ javaScriptEnabled={true}
413
+ domStorageEnabled={true}
414
+ keyboardDisplayRequiresUserAction={false}
415
+ />
416
+ {isLoading && (
417
+ <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center' }}>
418
+ <ActivityIndicator size="large" />
419
+ </View>
420
+ )}
421
+ </View>
422
+ );
423
+ };
424
+ ```
425
+
426
+ ### Message Protocol
427
+
428
+ | Message Type | Direction | Description |
429
+ |---|---|---|
430
+ | `EDITOR_READY` | Editor → RN | Editor has finished loading |
431
+ | `CONTENT_CHANGE` | Editor → RN | Content was modified (`{ content: string }`) |
432
+ | `AUTH_SUCCESS` | Editor → RN | Authentication succeeded |
433
+ | `AUTH_ERROR` | Editor → RN | Authentication failed (`{ error: string }`) |
434
+ | `SET_CONTENT` | RN → Editor | Set editor content (`{ content: string }`) |
435
+
436
+ ## Exports
437
+
438
+ ```ts
439
+ // Components
440
+ import {
441
+ ConfigurableEditorWithAuth, // Main editor with auth
442
+ ConfigurableEditor, // Editor without auth wrapper
443
+ EditorProvider, // Context provider
444
+ LinkPreviewHover, // Standalone link preview component
445
+ } from 'eddyter';
446
+
447
+ // Hooks & utilities
448
+ import {
449
+ useEditor, // Access editor context
450
+ useHtmlView, // Access HTML view state
451
+ verifyApiKey, // Verify API key programmatically
452
+ useReactNativeBridge, // React Native bridge hook
453
+ isReactNativeWebView, // Check if running in RN WebView
454
+ } from 'eddyter';
455
+
456
+ // Config
457
+ import { defaultEditorConfig } from 'eddyter';
458
+
459
+ // Types
460
+ import type {
461
+ CurrentUser,
462
+ EditorConfigTypes,
463
+ LinkPreviewHoverProps,
464
+ ReactNativeBridgeConfig,
465
+ ReactNativeMessage,
466
+ ReactNativeMessageType,
467
+ } from 'eddyter';
468
+ ```
469
+
470
+ ## License
471
+
472
+ Eddyter is **proprietary software**.
473
+
474
+ - Free for evaluation and non-commercial use
475
+ - **Commercial use requires a paid license**
476
+ - SaaS, redistribution, and competing products are prohibited without permission
477
+
478
+ For commercial licensing, visit [eddyter.com](https://www.eddyter.com/)