eddyter 1.3.53 → 1.3.55

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
@@ -20,217 +20,6 @@ yarn add eddyter
20
20
  - AI chat integration (for premium plans)
21
21
  - Environment-based API configuration
22
22
 
23
- ## React Native Integration
24
-
25
- You can use Eddyter in React Native applications via WebView by loading a deployed version of the editor.
26
-
27
- ### Prerequisites
28
-
29
- ```bash
30
- npm install react-native-webview
31
- # or
32
- yarn add react-native-webview
33
- ```
34
-
35
- ### RichTextEditor Component
36
-
37
- Create a reusable `RichTextEditor` component that wraps the WebView:
38
-
39
- ```tsx
40
- import React, { useRef, useState, useCallback } from 'react';
41
- import { View, ActivityIndicator, Text, StyleSheet } from 'react-native';
42
- import { WebView, WebViewMessageEvent } from 'react-native-webview';
43
-
44
- interface RichTextEditorProps {
45
- editorBaseUrl: string;
46
- apiKey: string;
47
- initialContent?: string;
48
- theme?: 'light' | 'dark';
49
- style?: object;
50
- onChange?: (content: string) => void;
51
- onReady?: () => void;
52
- onAuthSuccess?: () => void;
53
- onAuthError?: (error: string) => void;
54
- }
55
-
56
- interface WebViewMessage {
57
- type: string;
58
- payload?: Record<string, unknown>;
59
- }
60
-
61
- export const RichTextEditor: React.FC<RichTextEditorProps> = ({
62
- editorBaseUrl,
63
- apiKey,
64
- initialContent,
65
- theme = 'light',
66
- style,
67
- onChange,
68
- onReady,
69
- onAuthSuccess,
70
- onAuthError,
71
- }) => {
72
- const webViewRef = useRef<WebView>(null);
73
- const [isLoading, setIsLoading] = useState(true);
74
- const [error, setError] = useState<string | null>(null);
75
-
76
- const buildEditorUrl = () => {
77
- const baseUrl = editorBaseUrl.replace(/\/$/, '');
78
- const params = new URLSearchParams();
79
- if (apiKey) params.append('apiKey', apiKey);
80
- if (theme) params.append('theme', theme);
81
- return `${baseUrl}?${params.toString()}`;
82
- };
83
-
84
- const handleMessage = useCallback((event: WebViewMessageEvent) => {
85
- try {
86
- const message: WebViewMessage = JSON.parse(event.nativeEvent.data);
87
-
88
- switch (message.type) {
89
- case 'EDITOR_READY':
90
- setIsLoading(false);
91
- onReady?.();
92
- // Send initial content after editor is ready
93
- if (initialContent && webViewRef.current) {
94
- webViewRef.current.postMessage(
95
- JSON.stringify({
96
- type: 'SET_CONTENT',
97
- payload: { content: initialContent },
98
- })
99
- );
100
- }
101
- break;
102
-
103
- case 'CONTENT_CHANGE':
104
- onChange?.(message.payload?.content as string || '');
105
- break;
106
-
107
- case 'AUTH_SUCCESS':
108
- onAuthSuccess?.();
109
- break;
110
-
111
- case 'AUTH_ERROR':
112
- onAuthError?.(message.payload?.error as string);
113
- break;
114
- }
115
- } catch (e) {
116
- console.warn('[RichTextEditor] Failed to parse message:', e);
117
- }
118
- }, [onChange, onReady, onAuthSuccess, onAuthError, initialContent]);
119
-
120
- const handleError = useCallback((syntheticEvent: any) => {
121
- const { nativeEvent } = syntheticEvent;
122
- setError(nativeEvent.description || 'Failed to load editor');
123
- setIsLoading(false);
124
- }, []);
125
-
126
- if (error) {
127
- return (
128
- <View style={styles.errorContainer}>
129
- <Text style={styles.errorText}>Failed to load editor</Text>
130
- <Text style={styles.errorDetail}>{error}</Text>
131
- </View>
132
- );
133
- }
134
-
135
- return (
136
- <View style={[styles.container, style]}>
137
- <WebView
138
- ref={webViewRef}
139
- source={{ uri: buildEditorUrl() }}
140
- style={styles.webview}
141
- onMessage={handleMessage}
142
- onError={handleError}
143
- javaScriptEnabled={true}
144
- domStorageEnabled={true}
145
- startInLoadingState={false}
146
- scalesPageToFit={true}
147
- allowsInlineMediaPlayback={true}
148
- keyboardDisplayRequiresUserAction={false}
149
- />
150
- {isLoading && (
151
- <View style={styles.loadingOverlay}>
152
- <ActivityIndicator size="large" color="#007AFF" />
153
- </View>
154
- )}
155
- </View>
156
- );
157
- };
158
-
159
- const styles = StyleSheet.create({
160
- container: { flex: 1 },
161
- webview: { flex: 1, backgroundColor: 'transparent' },
162
- loadingOverlay: {
163
- ...StyleSheet.absoluteFillObject,
164
- justifyContent: 'center',
165
- alignItems: 'center',
166
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
167
- },
168
- errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
169
- errorText: { fontSize: 18, fontWeight: 'bold', color: '#FF3B30' },
170
- errorDetail: { fontSize: 14, color: '#666', marginTop: 8, textAlign: 'center' },
171
- });
172
- ```
173
-
174
- ### Usage Example
175
-
176
- ```tsx
177
- import React, { useState } from 'react';
178
- import { View, KeyboardAvoidingView, Platform } from 'react-native';
179
- import { RichTextEditor } from './components/RichTextEditor';
180
-
181
- const EDITOR_CONFIG = {
182
- editorBaseUrl: 'https://your-deployed-editor-url.com',
183
- apiKey: 'your-api-key',
184
- };
185
-
186
- function NoteEditorScreen() {
187
- const [content, setContent] = useState('');
188
-
189
- return (
190
- <KeyboardAvoidingView
191
- style={{ flex: 1 }}
192
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
193
- >
194
- <RichTextEditor
195
- editorBaseUrl={EDITOR_CONFIG.editorBaseUrl}
196
- apiKey={EDITOR_CONFIG.apiKey}
197
- theme="light"
198
- initialContent="<p>Start writing...</p>"
199
- onChange={setContent}
200
- onReady={() => console.log('Editor ready')}
201
- onAuthSuccess={() => console.log('Authenticated')}
202
- onAuthError={(error) => console.error('Auth failed:', error)}
203
- />
204
- </KeyboardAvoidingView>
205
- );
206
- }
207
- ```
208
-
209
- ### Message Protocol
210
-
211
- The editor and React Native communicate via `postMessage`. Here are the supported message types:
212
-
213
- | Message Type | Direction | Description |
214
- |--------------|-----------|-------------|
215
- | `EDITOR_READY` | Editor → RN | Editor has finished loading |
216
- | `CONTENT_CHANGE` | Editor → RN | Content was modified (payload: `{ content: string }`) |
217
- | `AUTH_SUCCESS` | Editor → RN | API key authentication succeeded |
218
- | `AUTH_ERROR` | Editor → RN | Authentication failed (payload: `{ error: string }`) |
219
- | `SET_CONTENT` | RN → Editor | Set editor content (payload: `{ content: string }`) |
220
-
221
- ### Sending Content to Editor
222
-
223
- After receiving the `EDITOR_READY` message, you can programmatically set content:
224
-
225
- ```tsx
226
- webViewRef.current?.postMessage(
227
- JSON.stringify({
228
- type: 'SET_CONTENT',
229
- payload: { content: '<p>New content here</p>' },
230
- })
231
- );
232
- ```
233
-
234
23
  ## Usage
235
24
 
236
25
  ### Important: Importing Styles
@@ -478,6 +267,221 @@ To disable link preview entirely:
478
267
  </EditorProvider>
479
268
  ```
480
269
 
270
+ ---
271
+
272
+ ## React Native Integration
273
+
274
+ You can use Eddyter in React Native applications via WebView by loading a deployed version of the editor.
275
+
276
+ ### Prerequisites
277
+
278
+ ```bash
279
+ npm install react-native-webview
280
+ # or
281
+ yarn add react-native-webview
282
+ ```
283
+
284
+ ### RichTextEditor Component
285
+
286
+ Create a reusable `RichTextEditor` component that wraps the WebView:
287
+
288
+ ```tsx
289
+ import React, { useRef, useState, useCallback } from 'react';
290
+ import { View, ActivityIndicator, Text, StyleSheet } from 'react-native';
291
+ import { WebView, WebViewMessageEvent } from 'react-native-webview';
292
+
293
+ interface RichTextEditorProps {
294
+ editorBaseUrl: string;
295
+ apiKey: string;
296
+ initialContent?: string;
297
+ theme?: 'light' | 'dark';
298
+ style?: object;
299
+ onChange?: (content: string) => void;
300
+ onReady?: () => void;
301
+ onAuthSuccess?: () => void;
302
+ onAuthError?: (error: string) => void;
303
+ }
304
+
305
+ interface WebViewMessage {
306
+ type: string;
307
+ payload?: Record<string, unknown>;
308
+ }
309
+
310
+ export const RichTextEditor: React.FC<RichTextEditorProps> = ({
311
+ editorBaseUrl,
312
+ apiKey,
313
+ initialContent,
314
+ theme = 'light',
315
+ style,
316
+ onChange,
317
+ onReady,
318
+ onAuthSuccess,
319
+ onAuthError,
320
+ }) => {
321
+ const webViewRef = useRef<WebView>(null);
322
+ const [isLoading, setIsLoading] = useState(true);
323
+ const [error, setError] = useState<string | null>(null);
324
+
325
+ const buildEditorUrl = () => {
326
+ const baseUrl = editorBaseUrl.replace(/\/$/, '');
327
+ const params = new URLSearchParams();
328
+ if (apiKey) params.append('apiKey', apiKey);
329
+ if (theme) params.append('theme', theme);
330
+ return `${baseUrl}?${params.toString()}`;
331
+ };
332
+
333
+ const handleMessage = useCallback((event: WebViewMessageEvent) => {
334
+ try {
335
+ const message: WebViewMessage = JSON.parse(event.nativeEvent.data);
336
+
337
+ switch (message.type) {
338
+ case 'EDITOR_READY':
339
+ setIsLoading(false);
340
+ onReady?.();
341
+ // Send initial content after editor is ready
342
+ if (initialContent && webViewRef.current) {
343
+ webViewRef.current.postMessage(
344
+ JSON.stringify({
345
+ type: 'SET_CONTENT',
346
+ payload: { content: initialContent },
347
+ })
348
+ );
349
+ }
350
+ break;
351
+
352
+ case 'CONTENT_CHANGE':
353
+ onChange?.(message.payload?.content as string || '');
354
+ break;
355
+
356
+ case 'AUTH_SUCCESS':
357
+ onAuthSuccess?.();
358
+ break;
359
+
360
+ case 'AUTH_ERROR':
361
+ onAuthError?.(message.payload?.error as string);
362
+ break;
363
+ }
364
+ } catch (e) {
365
+ console.warn('[RichTextEditor] Failed to parse message:', e);
366
+ }
367
+ }, [onChange, onReady, onAuthSuccess, onAuthError, initialContent]);
368
+
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
+ return (
385
+ <View style={[styles.container, style]}>
386
+ <WebView
387
+ ref={webViewRef}
388
+ source={{ uri: buildEditorUrl() }}
389
+ style={styles.webview}
390
+ onMessage={handleMessage}
391
+ onError={handleError}
392
+ javaScriptEnabled={true}
393
+ domStorageEnabled={true}
394
+ startInLoadingState={false}
395
+ scalesPageToFit={true}
396
+ allowsInlineMediaPlayback={true}
397
+ keyboardDisplayRequiresUserAction={false}
398
+ />
399
+ {isLoading && (
400
+ <View style={styles.loadingOverlay}>
401
+ <ActivityIndicator size="large" color="#007AFF" />
402
+ </View>
403
+ )}
404
+ </View>
405
+ );
406
+ };
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
+ ```
457
+
458
+ ### Message Protocol
459
+
460
+ The editor and React Native communicate via `postMessage`. Here are the supported message types:
461
+
462
+ | Message Type | Direction | Description |
463
+ |--------------|-----------|-------------|
464
+ | `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 }`) |
469
+
470
+ ### Sending Content to Editor
471
+
472
+ After receiving the `EDITOR_READY` message, you can programmatically set content:
473
+
474
+ ```tsx
475
+ webViewRef.current?.postMessage(
476
+ JSON.stringify({
477
+ type: 'SET_CONTENT',
478
+ payload: { content: '<p>New content here</p>' },
479
+ })
480
+ );
481
+ ```
482
+
483
+ ---
484
+
481
485
  ## License
482
486
 
483
487
  Eddyter is **proprietary software**.
@@ -17,6 +17,9 @@ export declare const apiEndpoints: {
17
17
  transcript: {
18
18
  voiceTranscript: string;
19
19
  endSession: string;
20
+ uploadAudio: string;
21
+ status: string;
22
+ endBatchSession: string;
20
23
  };
21
24
  linkPreview: {
22
25
  getPreview: string;
@@ -1,3 +1,14 @@
1
+ export declare const STREAMING_LANGUAGES: {
2
+ code: string;
3
+ name: string;
4
+ flag: string;
5
+ }[];
6
+ export declare const BATCH_ONLY_LANGUAGES: {
7
+ code: string;
8
+ name: string;
9
+ flag: string;
10
+ }[];
11
+ export declare const isBatchOnlyLanguage: (code: string) => boolean;
1
12
  export declare class ApiKeyRequiredError extends Error {
2
13
  code: string;
3
14
  constructor(message: string, code: string);
@@ -19,3 +30,39 @@ export declare class CreditError extends Error {
19
30
  * @returns Credit deduction result
20
31
  */
21
32
  export declare const endVoiceSession: (durationSeconds: number, apiKey?: string) => Promise<any>;
33
+ export interface BatchTranscriptionResult {
34
+ transcriptId: string;
35
+ status: string;
36
+ }
37
+ export interface TranscriptionStatusResult {
38
+ status: 'queued' | 'processing' | 'completed' | 'error';
39
+ text?: string;
40
+ error?: string;
41
+ }
42
+ /**
43
+ * Upload audio for batch transcription (Hindi and other non-streaming languages)
44
+ * @param audioBlob - The recorded audio as a Blob
45
+ * @param languageCode - The language code (e.g., 'hi' for Hindi)
46
+ * @param apiKey - License key for authentication
47
+ */
48
+ export declare const uploadAudioForTranscription: (audioBlob: Blob, languageCode: string, apiKey?: string) => Promise<BatchTranscriptionResult>;
49
+ /**
50
+ * Get transcription status and result
51
+ * @param transcriptId - The ID of the transcription job
52
+ * @param apiKey - License key for authentication
53
+ */
54
+ export declare const getTranscriptionStatus: (transcriptId: string, apiKey?: string) => Promise<TranscriptionStatusResult>;
55
+ /**
56
+ * Poll for transcription completion with timeout
57
+ * @param transcriptId - The ID of the transcription job
58
+ * @param apiKey - License key for authentication
59
+ * @param maxAttempts - Maximum polling attempts (default: 120 = 2 minutes)
60
+ * @param pollInterval - Interval between polls in ms (default: 1000)
61
+ */
62
+ export declare const pollForTranscription: (transcriptId: string, apiKey?: string, maxAttempts?: number, pollInterval?: number) => Promise<TranscriptionStatusResult>;
63
+ /**
64
+ * End batch transcription session and deduct credits
65
+ * @param durationSeconds - Duration of the audio in seconds
66
+ * @param apiKey - License key for authentication
67
+ */
68
+ export declare const endBatchSession: (durationSeconds: number, apiKey?: string) => Promise<any>;