eddyter 1.3.70 → 1.3.72

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,6 +1,6 @@
1
1
  # Eddyter
2
2
 
3
- A configurable rich text editor component with AI-powered features and API key authentication.
3
+ A powerful, configurable rich text editor built on [Lexical](https://lexical.dev/) with AI capabilities, dark mode support, and API key authentication.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,44 +8,48 @@ A configurable rich text editor component with AI-powered features and API key a
8
8
  npm install eddyter
9
9
  # or
10
10
  yarn add eddyter
11
+ # or
12
+ pnpm add eddyter
11
13
  ```
12
14
 
13
- ## Features
15
+ ### Compatibility
14
16
 
15
- - Rich text editor with extensive formatting options
16
- - API key authentication
17
- - Configurable UI components (toolbar, floating menu)
18
- - HTML view option
19
- - Support for tables, images, links, and more
20
- - AI chat integration (for premium plans)
21
- - Environment-based API configuration
17
+ | Requirement | Version |
18
+ |-------------|---------|
19
+ | React | 18.2+ or 19.x |
20
+ | React DOM | 18.2+ or 19.x |
21
+ | Node.js | 16+ |
22
22
 
23
- ## Usage
23
+ ## Quick Start
24
24
 
25
- ### Important: Importing Styles
26
- To ensure proper styling of the editor components including tables, you must import the package's CSS:
25
+ ### 1. Import styles
27
26
 
28
- ```jsx
29
- // Import the styles in your application
27
+ ```tsx
30
28
  import 'eddyter/style.css';
31
29
  ```
32
30
 
33
- ### Basic Setup
31
+ > **Important:** The stylesheet is required for tables, toolbars, and all editor components to render correctly.
32
+
33
+ ### 2. Get your API key
34
34
 
35
- ```jsx
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
36
42
  import React from 'react';
37
43
  import {
38
44
  ConfigurableEditorWithAuth,
39
45
  EditorProvider,
40
46
  defaultEditorConfig
41
47
  } from 'eddyter';
42
- // Import required styles
43
48
  import 'eddyter/style.css';
44
49
 
45
50
  function App() {
46
- const apiKey = 'your-api-key'; // Replace with your actual API key
51
+ const apiKey = process.env.NEXT_PUBLIC_EDITOR_API_KEY!;
47
52
 
48
- // Current logged-in user for comments
49
53
  const currentUser = {
50
54
  id: 'user-123',
51
55
  name: 'John Doe',
@@ -53,11 +57,6 @@ function App() {
53
57
  avatar: 'https://example.com/avatar.jpg' // optional
54
58
  };
55
59
 
56
- const handleContentChange = (html) => {
57
- console.log('Editor HTML content:', html);
58
- // Handle the HTML content (save to state, send to server, etc.)
59
- };
60
-
61
60
  return (
62
61
  <EditorProvider
63
62
  defaultFontFamilies={defaultEditorConfig.defaultFontFamilies}
@@ -65,156 +64,235 @@ function App() {
65
64
  >
66
65
  <ConfigurableEditorWithAuth
67
66
  apiKey={apiKey}
68
- onChange={handleContentChange}
69
- initialContent="<p>Welcome to the editor!</p>"
70
- mentionUserList={["Alice", "Bob", "Charlie"]}
71
- onAuthSuccess={() => console.log('Authentication successful')}
72
- onAuthError={(error) => console.error('Authentication error:', error)}
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)}
73
72
  />
74
73
  </EditorProvider>
75
74
  );
76
75
  }
77
76
  ```
78
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
+
79
151
  ## API Reference
80
152
 
81
- ### EditorProvider
153
+ ### `<EditorProvider>`
82
154
 
83
- Provides authentication and configuration context for the editor.
155
+ Provides authentication and configuration context. Must wrap the editor component.
84
156
 
85
- #### Props
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 |
86
164
 
87
- - `children`: React nodes to render
88
- - `defaultFontFamilies`: Array of font names (optional)
89
- - `currentUser`: Current logged-in user for comments (optional) - Object with `id`, `name`, `email`, and optional `avatar`
90
- - `enableLinkPreview`: Enable automatic link preview on hover (optional, default: `true`)
91
- - `apiKey`: API key for authentication (optional) - Required only if you need link preview to work immediately without opening the editor first
165
+ #### CurrentUser Type
92
166
 
93
- ### ConfigurableEditorWithAuth
167
+ ```ts
168
+ interface CurrentUser {
169
+ id: string;
170
+ name: string;
171
+ email: string;
172
+ avatar?: string;
173
+ }
174
+ ```
94
175
 
95
- The main editor component with authentication.
176
+ ### `<ConfigurableEditorWithAuth>`
96
177
 
97
- #### Props
178
+ The main editor component with authentication.
98
179
 
99
- - `apiKey`: Your API key for authentication (required)
100
- - `initialContent`: Initial HTML content for the editor (optional) - string
101
- - `onChange`: Callback function when editor content changes (optional) - receives HTML string
102
- - `defaultFontFamilies`: Array of font names for the font selector (optional)
103
- - `mentionUserList`: Array of usernames for mention functionality (optional) - Array of strings like `["Alice", "Bob", "Charlie"]`
104
- - `onAuthSuccess`: Callback function when authentication is successful (optional)
105
- - `onAuthError`: Callback function when authentication fails (optional)
106
- - `customVerifyKey`: Custom function to verify API key (optional)
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 |
107
199
 
108
200
  ## Examples
109
201
 
110
- ### Basic Editor with Authentication
202
+ ### Basic Editor
111
203
 
112
- ```jsx
113
- import React from 'react';
204
+ ```tsx
114
205
  import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
115
206
  import 'eddyter/style.css';
116
207
 
117
- function App() {
208
+ export default function BasicEditor() {
118
209
  return (
119
210
  <EditorProvider>
120
211
  <ConfigurableEditorWithAuth
121
212
  apiKey="your-api-key"
122
- onAuthSuccess={() => console.log('Authenticated')}
123
- onAuthError={(error) => console.error(error)}
213
+ onAuthSuccess={() => console.log('Ready!')}
124
214
  />
125
215
  </EditorProvider>
126
216
  );
127
217
  }
128
218
  ```
129
219
 
130
- ### Editor with Content Handling
220
+ ### Editor with State Management
131
221
 
132
- ```jsx
133
- import React, { useState } from 'react';
222
+ ```tsx
223
+ import { useState } from 'react';
134
224
  import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
135
225
  import 'eddyter/style.css';
136
226
 
137
- function App() {
138
- const [editorContent, setEditorContent] = useState('');
139
-
140
- // Current user (typically from your auth system)
141
- const currentUser = {
142
- id: 'user-456',
143
- name: 'Jane Smith',
144
- email: 'jane@example.com'
145
- };
146
-
147
- const handleContentChange = (html) => {
148
- setEditorContent(html);
149
- console.log('Current content:', html);
150
- // You can also save to localStorage, send to API, etc.
151
- };
227
+ export default function EditorWithState() {
228
+ const [content, setContent] = useState('<p>Start writing...</p>');
152
229
 
153
- const handleSave = () => {
154
- // Save the HTML content to your backend or localStorage
155
- localStorage.setItem('saved-content', editorContent);
156
- console.log('Content saved!');
157
- };
158
-
159
- const loadSavedContent = () => {
160
- const saved = '<p>Start writing your content here...</p>';
161
- return saved;
230
+ const handleSave = async () => {
231
+ await fetch('/api/save', {
232
+ method: 'POST',
233
+ headers: { 'Content-Type': 'application/json' },
234
+ body: JSON.stringify({ content })
235
+ });
162
236
  };
163
237
 
164
238
  return (
165
239
  <div>
166
- <EditorProvider currentUser={currentUser}>
240
+ <EditorProvider>
167
241
  <ConfigurableEditorWithAuth
168
242
  apiKey="your-api-key"
169
- initialContent={loadSavedContent()}
170
- onChange={handleContentChange}
171
- defaultFontFamilies={['Arial', 'Helvetica', 'Times New Roman']}
172
- mentionUserList={['Alice', 'Bob', 'Charlie']}
173
- onAuthSuccess={() => console.log('Ready to edit!')}
174
- onAuthError={(error) => console.error('Auth failed:', error)}
243
+ initialContent={content}
244
+ onChange={setContent}
175
245
  />
176
246
  </EditorProvider>
247
+ <button onClick={handleSave}>Save</button>
248
+ </div>
249
+ );
250
+ }
251
+ ```
177
252
 
178
- <button onClick={handleSave} style={{ marginTop: '10px', padding: '10px' }}>
179
- Save Content
180
- </button>
253
+ ### Editor with Comments & Mentions
181
254
 
182
- <div style={{ marginTop: '20px', padding: '10px', background: '#f5f5f5' }}>
183
- <h3>Current HTML Content:</h3>
184
- <pre>{editorContent}</pre>
185
- </div>
186
- </div>
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>
187
274
  );
188
275
  }
189
276
  ```
190
277
 
191
278
  ### Custom API Key Verification
192
279
 
193
- ```jsx
194
- import React from 'react';
280
+ ```tsx
195
281
  import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
196
282
  import 'eddyter/style.css';
197
283
 
198
- function App() {
199
- const customVerifyKey = async (apiKey) => {
284
+ export default function EditorWithCustomAuth() {
285
+ const customVerifyKey = async (apiKey: string) => {
200
286
  try {
201
287
  const response = await fetch('/api/verify-key', {
202
288
  method: 'POST',
203
289
  headers: { 'Content-Type': 'application/json' },
204
290
  body: JSON.stringify({ apiKey })
205
291
  });
206
-
207
292
  const data = await response.json();
208
-
209
- return {
210
- success: data.valid,
211
- message: data.message || 'API key verified'
212
- };
213
- } catch (error) {
214
- return {
215
- success: false,
216
- message: 'Failed to verify API key'
217
- };
293
+ return { success: data.valid, message: data.message || 'Verified' };
294
+ } catch {
295
+ return { success: false, message: 'Verification failed' };
218
296
  }
219
297
  };
220
298
 
@@ -223,71 +301,43 @@ function App() {
223
301
  <ConfigurableEditorWithAuth
224
302
  apiKey="your-api-key"
225
303
  customVerifyKey={customVerifyKey}
226
- onChange={(html) => console.log('Content changed:', html)}
227
304
  />
228
305
  </EditorProvider>
229
306
  );
230
307
  }
231
308
  ```
232
309
 
233
- ## Link Preview Feature
234
-
235
- Eddyter includes automatic link preview on hover. When users hover over links in content wrapped by `EditorProvider`, a preview popup shows the link's title, description, and image.
236
-
237
- ### How It Works
238
-
239
- - **Inside the editor**: Link preview works automatically after authentication
240
- - **Outside the editor** (preview mode, saved content): Link preview works if the user has opened the editor at least once in the session (authentication stores the API key)
241
-
242
- ### Preview-Only Scenarios
243
-
244
- If your application displays saved content without ever opening the editor (e.g., a read-only view), you need to pass `apiKey` to `EditorProvider`:
245
-
246
- ```jsx
247
- import React from 'react';
248
- import { EditorProvider } from 'eddyter';
249
- import 'eddyter/style.css';
310
+ ## Link Preview
250
311
 
251
- function ContentPreviewPage({ savedHtml }) {
252
- return (
253
- <EditorProvider apiKey="your-api-key">
254
- <div dangerouslySetInnerHTML={{ __html: savedHtml }} />
255
- </EditorProvider>
256
- );
257
- }
258
- ```
312
+ The editor includes automatic link preview on hover.
259
313
 
260
- ### Disabling Link Preview
314
+ - **Inside the editor**: Works automatically after authentication
315
+ - **Read-only content**: Pass `apiKey` to `EditorProvider`
316
+ - **Disable**: Set `enableLinkPreview={false}` on `EditorProvider`
261
317
 
262
- To disable link preview entirely:
318
+ ```tsx
319
+ // Read-only content with link preview
320
+ <EditorProvider apiKey="your-api-key">
321
+ <div dangerouslySetInnerHTML={{ __html: savedHtml }} />
322
+ </EditorProvider>
263
323
 
264
- ```jsx
324
+ // Disable link preview
265
325
  <EditorProvider enableLinkPreview={false}>
266
- {/* Your content */}
326
+ {/* content */}
267
327
  </EditorProvider>
268
328
  ```
269
329
 
270
- ---
271
-
272
330
  ## React Native Integration
273
331
 
274
- You can use Eddyter in React Native applications via WebView by loading a deployed version of the editor.
275
-
276
- ### Prerequisites
332
+ Use Eddyter in React Native via WebView by loading a deployed version of the editor.
277
333
 
278
334
  ```bash
279
335
  npm install react-native-webview
280
- # or
281
- yarn add react-native-webview
282
336
  ```
283
337
 
284
- ### RichTextEditor Component
285
-
286
- Create a reusable `RichTextEditor` component that wraps the WebView:
287
-
288
338
  ```tsx
289
339
  import React, { useRef, useState, useCallback } from 'react';
290
- import { View, ActivityIndicator, Text, StyleSheet } from 'react-native';
340
+ import { View, ActivityIndicator, KeyboardAvoidingView, Platform } from 'react-native';
291
341
  import { WebView, WebViewMessageEvent } from 'react-native-webview';
292
342
 
293
343
  interface RichTextEditorProps {
@@ -302,11 +352,6 @@ interface RichTextEditorProps {
302
352
  onAuthError?: (error: string) => void;
303
353
  }
304
354
 
305
- interface WebViewMessage {
306
- type: string;
307
- payload?: Record<string, unknown>;
308
- }
309
-
310
355
  export const RichTextEditor: React.FC<RichTextEditorProps> = ({
311
356
  editorBaseUrl,
312
357
  apiKey,
@@ -320,7 +365,6 @@ export const RichTextEditor: React.FC<RichTextEditorProps> = ({
320
365
  }) => {
321
366
  const webViewRef = useRef<WebView>(null);
322
367
  const [isLoading, setIsLoading] = useState(true);
323
- const [error, setError] = useState<string | null>(null);
324
368
 
325
369
  const buildEditorUrl = () => {
326
370
  const baseUrl = editorBaseUrl.replace(/\/$/, '');
@@ -332,33 +376,25 @@ export const RichTextEditor: React.FC<RichTextEditorProps> = ({
332
376
 
333
377
  const handleMessage = useCallback((event: WebViewMessageEvent) => {
334
378
  try {
335
- const message: WebViewMessage = JSON.parse(event.nativeEvent.data);
336
-
379
+ const message = JSON.parse(event.nativeEvent.data);
337
380
  switch (message.type) {
338
381
  case 'EDITOR_READY':
339
382
  setIsLoading(false);
340
383
  onReady?.();
341
- // Send initial content after editor is ready
342
384
  if (initialContent && webViewRef.current) {
343
385
  webViewRef.current.postMessage(
344
- JSON.stringify({
345
- type: 'SET_CONTENT',
346
- payload: { content: initialContent },
347
- })
386
+ JSON.stringify({ type: 'SET_CONTENT', payload: { content: initialContent } })
348
387
  );
349
388
  }
350
389
  break;
351
-
352
390
  case 'CONTENT_CHANGE':
353
- onChange?.(message.payload?.content as string || '');
391
+ onChange?.(message.payload?.content || '');
354
392
  break;
355
-
356
393
  case 'AUTH_SUCCESS':
357
394
  onAuthSuccess?.();
358
395
  break;
359
-
360
396
  case 'AUTH_ERROR':
361
- onAuthError?.(message.payload?.error as string);
397
+ onAuthError?.(message.payload?.error);
362
398
  break;
363
399
  }
364
400
  } catch (e) {
@@ -366,121 +402,70 @@ export const RichTextEditor: React.FC<RichTextEditorProps> = ({
366
402
  }
367
403
  }, [onChange, onReady, onAuthSuccess, onAuthError, initialContent]);
368
404
 
369
- const handleError = useCallback((syntheticEvent: any) => {
370
- const { nativeEvent } = syntheticEvent;
371
- setError(nativeEvent.description || 'Failed to load editor');
372
- setIsLoading(false);
373
- }, []);
374
-
375
- if (error) {
376
- return (
377
- <View style={styles.errorContainer}>
378
- <Text style={styles.errorText}>Failed to load editor</Text>
379
- <Text style={styles.errorDetail}>{error}</Text>
380
- </View>
381
- );
382
- }
383
-
384
405
  return (
385
- <View style={[styles.container, style]}>
406
+ <View style={[{ flex: 1 }, style]}>
386
407
  <WebView
387
408
  ref={webViewRef}
388
409
  source={{ uri: buildEditorUrl() }}
389
- style={styles.webview}
410
+ style={{ flex: 1 }}
390
411
  onMessage={handleMessage}
391
- onError={handleError}
392
412
  javaScriptEnabled={true}
393
413
  domStorageEnabled={true}
394
- startInLoadingState={false}
395
- scalesPageToFit={true}
396
- allowsInlineMediaPlayback={true}
397
414
  keyboardDisplayRequiresUserAction={false}
398
415
  />
399
416
  {isLoading && (
400
- <View style={styles.loadingOverlay}>
401
- <ActivityIndicator size="large" color="#007AFF" />
417
+ <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center' }}>
418
+ <ActivityIndicator size="large" />
402
419
  </View>
403
420
  )}
404
421
  </View>
405
422
  );
406
423
  };
407
-
408
- const styles = StyleSheet.create({
409
- container: { flex: 1 },
410
- webview: { flex: 1, backgroundColor: 'transparent' },
411
- loadingOverlay: {
412
- ...StyleSheet.absoluteFillObject,
413
- justifyContent: 'center',
414
- alignItems: 'center',
415
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
416
- },
417
- errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
418
- errorText: { fontSize: 18, fontWeight: 'bold', color: '#FF3B30' },
419
- errorDetail: { fontSize: 14, color: '#666', marginTop: 8, textAlign: 'center' },
420
- });
421
- ```
422
-
423
- ### Usage Example
424
-
425
- ```tsx
426
- import React, { useState } from 'react';
427
- import { View, KeyboardAvoidingView, Platform } from 'react-native';
428
- import { RichTextEditor } from './components/RichTextEditor';
429
-
430
- const EDITOR_CONFIG = {
431
- editorBaseUrl: 'https://your-deployed-editor-url.com',
432
- apiKey: 'your-api-key',
433
- };
434
-
435
- function NoteEditorScreen() {
436
- const [content, setContent] = useState('');
437
-
438
- return (
439
- <KeyboardAvoidingView
440
- style={{ flex: 1 }}
441
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
442
- >
443
- <RichTextEditor
444
- editorBaseUrl={EDITOR_CONFIG.editorBaseUrl}
445
- apiKey={EDITOR_CONFIG.apiKey}
446
- theme="light"
447
- initialContent="<p>Start writing...</p>"
448
- onChange={setContent}
449
- onReady={() => console.log('Editor ready')}
450
- onAuthSuccess={() => console.log('Authenticated')}
451
- onAuthError={(error) => console.error('Auth failed:', error)}
452
- />
453
- </KeyboardAvoidingView>
454
- );
455
- }
456
424
  ```
457
425
 
458
426
  ### Message Protocol
459
427
 
460
- The editor and React Native communicate via `postMessage`. Here are the supported message types:
461
-
462
428
  | Message Type | Direction | Description |
463
- |--------------|-----------|-------------|
429
+ |---|---|---|
464
430
  | `EDITOR_READY` | Editor → RN | Editor has finished loading |
465
- | `CONTENT_CHANGE` | Editor → RN | Content was modified (payload: `{ content: string }`) |
466
- | `AUTH_SUCCESS` | Editor → RN | API key authentication succeeded |
467
- | `AUTH_ERROR` | Editor → RN | Authentication failed (payload: `{ error: string }`) |
468
- | `SET_CONTENT` | RN → Editor | Set editor content (payload: `{ content: string }`) |
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 }`) |
469
435
 
470
- ### Sending Content to Editor
436
+ ## Exports
471
437
 
472
- After receiving the `EDITOR_READY` message, you can programmatically set content:
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';
473
446
 
474
- ```tsx
475
- webViewRef.current?.postMessage(
476
- JSON.stringify({
477
- type: 'SET_CONTENT',
478
- payload: { content: '<p>New content here</p>' },
479
- })
480
- );
481
- ```
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';
482
455
 
483
- ---
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
+ ```
484
469
 
485
470
  ## License
486
471
 
@@ -490,4 +475,4 @@ Eddyter is **proprietary software**.
490
475
  - **Commercial use requires a paid license**
491
476
  - SaaS, redistribution, and competing products are prohibited without permission
492
477
 
493
- For commercial licensing, contact: licensing@yourcompany.com
478
+ For commercial licensing, visit [eddyter.com](https://www.eddyter.com/)