funda-ui 4.7.585 → 4.7.601

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.
@@ -1,277 +0,0 @@
1
- /**
2
- * Stream Controller
3
- *
4
- * @usage:
5
-
6
- // Use in component
7
- const streamController = useStreamController({
8
- onChunk: async (chunk: string, index: number) => {
9
- // start (Execute it only once)
10
- if (index === 0) {
11
-
12
- }
13
-
14
- // Streaming data is JSON split by rows
15
- const lines = chunk.split("\n").filter(line => line.trim() !== "");
16
-
17
- // Process each data chunk
18
- console.log('Received chunk:', chunk);
19
- },
20
- onComplete: async (lastContent: string) => {
21
- // Process when stream is completed
22
- console.log('Stream completed');
23
-
24
- // Display AI reply
25
- console.log('AI reply:', lastContent);
26
-
27
- },
28
- onError: (error) => {
29
- // Error handling
30
- console.error('Stream error:', error);
31
- },
32
- onAbort: () => {
33
- // Abort processing
34
- console.log('Stream aborted');
35
- }
36
- });
37
-
38
- // Start stream
39
- const response = await fetch(url);
40
- await streamController.start(response);
41
-
42
- // Pause stream
43
- streamController.pause();
44
-
45
- // Resume stream
46
- streamController.resume();
47
-
48
- // Abort stream
49
- streamController.abort();
50
-
51
- // Check status
52
- const isActive = streamController.isActive();
53
- const isPaused = streamController.isPaused();
54
-
55
- */
56
-
57
-
58
- import { useRef, useCallback, useEffect } from 'react';
59
-
60
- export interface StreamControllerOptions {
61
- onChunk?: (chunk: string, index: number) => void;
62
- onComplete?: (lastContent: string) => void;
63
- onError?: (error: any) => void;
64
- onAbort?: () => void;
65
- }
66
-
67
- export interface StreamController {
68
- start: (response: Response) => Promise<void>;
69
- pause: () => void;
70
- resume: () => void;
71
- abort: () => void;
72
- isActive: () => boolean;
73
- isPaused: () => boolean;
74
- }
75
-
76
- export const useStreamController = (options: StreamControllerOptions = {}): StreamController => {
77
- const streamController = useRef<ReadableStreamDefaultController | null>(null);
78
- const reader = useRef<ReadableStreamDefaultReader | null>(null);
79
- const activeStream = useRef<ReadableStream | null>(null);
80
- const responseReader = useRef<ReadableStreamDefaultReader | null>(null);
81
- const paused = useRef<boolean>(false);
82
- const active = useRef<boolean>(false);
83
- const abortController = useRef<AbortController>(new AbortController());
84
- const textDecoder = useRef<TextDecoder>(new TextDecoder("utf-8")); // Get the decoding of UTF8
85
-
86
- // To avoid the "Uncaught (in promise) TypeError: Failed to execute 'cancel' on 'ReadableStream': Cannot cancel a locked stream" error,
87
- // (1) you need to safely release the reader.
88
- // (2) cleanup() also requires asynchronous state
89
- const releaseReader = useCallback(async (readerRef: React.MutableRefObject<ReadableStreamDefaultReader | null>) => {
90
- if (readerRef.current) {
91
- try {
92
- await readerRef.current.cancel();
93
- } catch (e) {
94
- console.warn('Error cancelling reader:', e);
95
- }
96
-
97
- try {
98
- readerRef.current.releaseLock();
99
- } catch (e) {
100
- console.warn('Error releasing reader lock:', e);
101
- }
102
- readerRef.current = null;
103
- }
104
- }, []);
105
-
106
- const cleanup = useCallback(async () => {
107
- // First release all readers
108
- await releaseReader(reader);
109
- await releaseReader(responseReader);
110
-
111
- // Then try to cancel the stream
112
- if (activeStream.current) {
113
- try {
114
- await activeStream.current.cancel();
115
- } catch (e) {
116
- console.warn('Error cancelling stream:', e);
117
- }
118
- activeStream.current = null;
119
- }
120
-
121
- streamController.current = null;
122
- active.current = false;
123
- paused.current = false;
124
- }, [releaseReader]);
125
-
126
- // Process chunks of data
127
- const processChunk = useCallback(async (chunk: string, index: number) => {
128
- try {
129
- options.onChunk?.(chunk, index);
130
- } catch (error) {
131
- options.onError?.(error);
132
- }
133
- }, [options]);
134
-
135
- // Start processing the stream
136
- const startProcessing = useCallback(async () => {
137
- if (!reader.current || !active.current) return;
138
-
139
- //
140
- let counter = 0;
141
-
142
-
143
- // Store the final content and bind it to loading
144
- let lastContent: string = '';
145
-
146
- while (active.current) {
147
- try {
148
-
149
- if (paused.current) {
150
- await new Promise(resolve => setTimeout(resolve, 100));
151
- continue;
152
- }
153
-
154
- const { done, value } = await reader.current.read();
155
-
156
- if (done) {
157
- options.onComplete?.(lastContent);
158
- await cleanup();
159
- break;
160
- }
161
-
162
- // Decode the content
163
- const chunkStr = textDecoder.current.decode(value as Uint8Array, { stream: true });
164
- lastContent += chunkStr;
165
-
166
- await processChunk(chunkStr, counter);
167
- counter++;
168
-
169
- } catch (error: any) {
170
- if (error.name === 'AbortError') {
171
- options.onAbort?.();
172
- } else {
173
- options.onError?.(error);
174
- }
175
- await cleanup();
176
- break;
177
- }
178
-
179
- }
180
- }, [options, cleanup, processChunk]);
181
-
182
-
183
- // Start streaming
184
- const start = useCallback(async (response: Response) => {
185
- await cleanup();
186
-
187
- // Get Reader
188
- reader.current = response.body!.getReader();
189
-
190
- try {
191
-
192
- const stream = new ReadableStream({
193
- start(controller) {
194
- streamController.current = controller;
195
- },
196
- async pull(controller) {
197
- try {
198
- const { done, value } = await reader.current!.read();
199
-
200
- if (done) {
201
- controller.close();
202
- return;
203
- }
204
-
205
- // Decode the content
206
- const chunkStr = textDecoder.current.decode(value as Uint8Array, { stream: true });
207
-
208
- controller.enqueue(chunkStr);
209
- } catch (error) {
210
- controller.error(error);
211
- }
212
- },
213
- cancel() {
214
- response.body?.cancel();
215
- }
216
- });
217
-
218
- activeStream.current = stream;
219
- active.current = true;
220
- paused.current = false;
221
-
222
- // Start processing immediately
223
- await startProcessing();
224
- } catch (error) {
225
- options.onError?.(error);
226
- cleanup();
227
- }
228
-
229
-
230
- }, [options, cleanup, startProcessing]);
231
-
232
-
233
-
234
- // Pause streaming
235
- const pause = useCallback(() => {
236
- paused.current = true;
237
- }, []);
238
-
239
- // Resume streaming
240
- const resume = useCallback(() => {
241
- paused.current = false;
242
- }, []);
243
-
244
- // Abort streaming
245
- const abort = useCallback(async () => {
246
- abortController.current.abort();
247
- await cleanup();
248
- }, [cleanup]);
249
-
250
- // Check if stream is active
251
- const isActive = useCallback(() => {
252
- return active.current;
253
- }, []);
254
-
255
- // Check if stream is paused
256
- const isPaused = useCallback(() => {
257
- return paused.current;
258
- }, []);
259
-
260
- // Cleanup on unmount
261
- useEffect(() => {
262
- return () => {
263
- cleanup().catch(console.error);
264
- };
265
- }, [cleanup]);
266
-
267
- return {
268
- start,
269
- pause,
270
- resume,
271
- abort,
272
- isActive,
273
- isPaused
274
- };
275
- };
276
-
277
- export default useStreamController;