hookery 0.0.1 → 1.0.0
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 +72 -11
- package/dist/bridges/auth0.d.mts +16 -0
- package/dist/bridges/auth0.d.ts +16 -0
- package/dist/bridges/auth0.js +3015 -0
- package/dist/bridges/auth0.js.map +1 -0
- package/dist/bridges/auth0.mjs +2977 -0
- package/dist/bridges/auth0.mjs.map +1 -0
- package/dist/bridges/axios.d.mts +17 -0
- package/dist/bridges/axios.d.ts +17 -0
- package/dist/bridges/axios.js +15351 -0
- package/dist/bridges/axios.js.map +1 -0
- package/dist/bridges/axios.mjs +15347 -0
- package/dist/bridges/axios.mjs.map +1 -0
- package/dist/bridges/clerk.d.mts +1 -0
- package/dist/bridges/clerk.d.ts +1 -0
- package/dist/bridges/clerk.js +5991 -0
- package/dist/bridges/clerk.js.map +1 -0
- package/dist/bridges/clerk.mjs +5985 -0
- package/dist/bridges/clerk.mjs.map +1 -0
- package/dist/bridges/firebase.d.mts +14 -0
- package/dist/bridges/firebase.d.ts +14 -0
- package/dist/bridges/firebase.js +52 -0
- package/dist/bridges/firebase.js.map +1 -0
- package/dist/bridges/firebase.mjs +25 -0
- package/dist/bridges/firebase.mjs.map +1 -0
- package/dist/bridges/jotai.d.mts +11 -0
- package/dist/bridges/jotai.d.ts +11 -0
- package/dist/bridges/jotai.js +870 -0
- package/dist/bridges/jotai.js.map +1 -0
- package/dist/bridges/jotai.mjs +827 -0
- package/dist/bridges/jotai.mjs.map +1 -0
- package/dist/bridges/motion.d.mts +6 -0
- package/dist/bridges/motion.d.ts +6 -0
- package/dist/bridges/motion.js +3752 -0
- package/dist/bridges/motion.js.map +1 -0
- package/dist/bridges/motion.mjs +3721 -0
- package/dist/bridges/motion.mjs.map +1 -0
- package/dist/bridges/next.d.mts +10 -0
- package/dist/bridges/next.d.ts +10 -0
- package/dist/bridges/next.js +2588 -0
- package/dist/bridges/next.js.map +1 -0
- package/dist/bridges/next.mjs +2582 -0
- package/dist/bridges/next.mjs.map +1 -0
- package/dist/bridges/redux.d.mts +15 -0
- package/dist/bridges/redux.d.ts +15 -0
- package/dist/bridges/redux.js +410 -0
- package/dist/bridges/redux.js.map +1 -0
- package/dist/bridges/redux.mjs +402 -0
- package/dist/bridges/redux.mjs.map +1 -0
- package/dist/bridges/remix.d.mts +1 -0
- package/dist/bridges/remix.d.ts +1 -0
- package/dist/bridges/remix.js +2215 -0
- package/dist/bridges/remix.js.map +1 -0
- package/dist/bridges/remix.mjs +2174 -0
- package/dist/bridges/remix.mjs.map +1 -0
- package/dist/bridges/stripe.d.mts +15 -0
- package/dist/bridges/stripe.d.ts +15 -0
- package/dist/bridges/stripe.js +1572 -0
- package/dist/bridges/stripe.js.map +1 -0
- package/dist/bridges/stripe.mjs +1556 -0
- package/dist/bridges/stripe.mjs.map +1 -0
- package/dist/bridges/supabase.d.mts +13 -0
- package/dist/bridges/supabase.d.ts +13 -0
- package/dist/bridges/supabase.js +51 -0
- package/dist/bridges/supabase.js.map +1 -0
- package/dist/bridges/supabase.mjs +24 -0
- package/dist/bridges/supabase.mjs.map +1 -0
- package/dist/bridges/tanstack.d.mts +3 -0
- package/dist/bridges/tanstack.d.ts +3 -0
- package/dist/bridges/tanstack.js +1319 -0
- package/dist/bridges/tanstack.js.map +1 -0
- package/dist/bridges/tanstack.mjs +1281 -0
- package/dist/bridges/tanstack.mjs.map +1 -0
- package/dist/bridges/yup.d.mts +16 -0
- package/dist/bridges/yup.d.ts +16 -0
- package/dist/bridges/yup.js +80 -0
- package/dist/bridges/yup.js.map +1 -0
- package/dist/bridges/yup.mjs +43 -0
- package/dist/bridges/yup.mjs.map +1 -0
- package/dist/bridges/zod.d.mts +19 -0
- package/dist/bridges/zod.d.ts +19 -0
- package/dist/bridges/zod.js +66 -0
- package/dist/bridges/zod.js.map +1 -0
- package/dist/bridges/zod.mjs +39 -0
- package/dist/bridges/zod.mjs.map +1 -0
- package/dist/bridges/zustand.d.mts +14 -0
- package/dist/bridges/zustand.d.ts +14 -0
- package/dist/bridges/zustand.js +58 -0
- package/dist/bridges/zustand.js.map +1 -0
- package/dist/bridges/zustand.mjs +21 -0
- package/dist/bridges/zustand.mjs.map +1 -0
- package/dist/index.d.mts +3124 -8
- package/dist/index.d.ts +3124 -8
- package/dist/index.js +4290 -10
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4172 -7
- package/dist/index.mjs.map +1 -0
- package/package.json +131 -6
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,4176 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.ts
|
|
2
|
+
import { useEffect, useLayoutEffect } from "react";
|
|
3
|
+
|
|
4
|
+
// src/utils/index.ts
|
|
5
|
+
var isServer = typeof window === "undefined";
|
|
6
|
+
var isBrowser = !isServer;
|
|
7
|
+
var noop = () => {
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.ts
|
|
11
|
+
var useIsomorphicLayoutEffect = isServer ? useEffect : useLayoutEffect;
|
|
12
|
+
|
|
13
|
+
// src/hooks/useToggle/useToggle.ts
|
|
14
|
+
import { useCallback, useState } from "react";
|
|
15
|
+
function useToggle(options = {}) {
|
|
16
|
+
const { initialValue = false } = options;
|
|
17
|
+
const [value, setValue] = useState(initialValue);
|
|
18
|
+
const toggle = useCallback(() => setValue((prev) => !prev), []);
|
|
19
|
+
const setTrue = useCallback(() => setValue(true), []);
|
|
20
|
+
const setFalse = useCallback(() => setValue(false), []);
|
|
21
|
+
return {
|
|
22
|
+
value,
|
|
23
|
+
toggle,
|
|
24
|
+
setTrue,
|
|
25
|
+
setFalse,
|
|
26
|
+
setValue
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/hooks/useCounter/useCounter.ts
|
|
31
|
+
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
32
|
+
function useCounter(options = {}) {
|
|
33
|
+
const { initialValue = 0, min, max, step = 1 } = options;
|
|
34
|
+
const clamp = useCallback2(
|
|
35
|
+
(value) => {
|
|
36
|
+
let result = value;
|
|
37
|
+
if (min !== void 0 && result < min) result = min;
|
|
38
|
+
if (max !== void 0 && result > max) result = max;
|
|
39
|
+
return result;
|
|
40
|
+
},
|
|
41
|
+
[min, max]
|
|
42
|
+
);
|
|
43
|
+
const [count, setCount] = useState2(() => clamp(initialValue));
|
|
44
|
+
const increment = useCallback2(() => {
|
|
45
|
+
setCount((prev) => clamp(prev + step));
|
|
46
|
+
}, [clamp, step]);
|
|
47
|
+
const decrement = useCallback2(() => {
|
|
48
|
+
setCount((prev) => clamp(prev - step));
|
|
49
|
+
}, [clamp, step]);
|
|
50
|
+
const reset = useCallback2(() => {
|
|
51
|
+
setCount(clamp(initialValue));
|
|
52
|
+
}, [clamp, initialValue]);
|
|
53
|
+
const set = useCallback2(
|
|
54
|
+
(value) => {
|
|
55
|
+
setCount(clamp(value));
|
|
56
|
+
},
|
|
57
|
+
[clamp]
|
|
58
|
+
);
|
|
59
|
+
return {
|
|
60
|
+
count,
|
|
61
|
+
increment,
|
|
62
|
+
decrement,
|
|
63
|
+
reset,
|
|
64
|
+
set
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/hooks/usePrevious/usePrevious.ts
|
|
69
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
70
|
+
function usePrevious(value) {
|
|
71
|
+
const ref = useRef(void 0);
|
|
72
|
+
useEffect2(() => {
|
|
73
|
+
ref.current = value;
|
|
74
|
+
}, [value]);
|
|
75
|
+
return ref.current;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/hooks/useMount/useMount.ts
|
|
79
|
+
import { useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
80
|
+
function useMount(callback) {
|
|
81
|
+
const callbackRef = useRef2(callback);
|
|
82
|
+
callbackRef.current = callback;
|
|
83
|
+
useEffect3(() => {
|
|
84
|
+
return callbackRef.current();
|
|
85
|
+
}, []);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/hooks/useUnmount/useUnmount.ts
|
|
89
|
+
import { useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
90
|
+
function useUnmount(callback) {
|
|
91
|
+
const callbackRef = useRef3(callback);
|
|
92
|
+
callbackRef.current = callback;
|
|
93
|
+
useEffect4(() => {
|
|
94
|
+
return () => {
|
|
95
|
+
callbackRef.current();
|
|
96
|
+
};
|
|
97
|
+
}, []);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/hooks/useDebounce/useDebounce.ts
|
|
101
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef4, useState as useState3 } from "react";
|
|
102
|
+
function useDebounce(value, options = {}) {
|
|
103
|
+
const { delay = 500, leading = false, trailing = true } = options;
|
|
104
|
+
const [debouncedValue, setDebouncedValue] = useState3(value);
|
|
105
|
+
const [isPending, setIsPending] = useState3(false);
|
|
106
|
+
const timeoutRef = useRef4(null);
|
|
107
|
+
const leadingRef = useRef4(true);
|
|
108
|
+
const latestValueRef = useRef4(value);
|
|
109
|
+
latestValueRef.current = value;
|
|
110
|
+
const cancel = useCallback3(() => {
|
|
111
|
+
if (timeoutRef.current) {
|
|
112
|
+
clearTimeout(timeoutRef.current);
|
|
113
|
+
timeoutRef.current = null;
|
|
114
|
+
}
|
|
115
|
+
setIsPending(false);
|
|
116
|
+
}, []);
|
|
117
|
+
const flush = useCallback3(() => {
|
|
118
|
+
cancel();
|
|
119
|
+
setDebouncedValue(latestValueRef.current);
|
|
120
|
+
}, [cancel]);
|
|
121
|
+
useEffect5(() => {
|
|
122
|
+
if (leading && leadingRef.current) {
|
|
123
|
+
leadingRef.current = false;
|
|
124
|
+
setDebouncedValue(value);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
setIsPending(true);
|
|
128
|
+
if (timeoutRef.current) {
|
|
129
|
+
clearTimeout(timeoutRef.current);
|
|
130
|
+
}
|
|
131
|
+
timeoutRef.current = setTimeout(() => {
|
|
132
|
+
if (trailing) {
|
|
133
|
+
setDebouncedValue(value);
|
|
134
|
+
}
|
|
135
|
+
setIsPending(false);
|
|
136
|
+
timeoutRef.current = null;
|
|
137
|
+
}, delay);
|
|
138
|
+
return () => {
|
|
139
|
+
if (timeoutRef.current) {
|
|
140
|
+
clearTimeout(timeoutRef.current);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}, [value, delay, leading, trailing]);
|
|
144
|
+
useEffect5(() => {
|
|
145
|
+
return () => {
|
|
146
|
+
if (timeoutRef.current) {
|
|
147
|
+
clearTimeout(timeoutRef.current);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}, []);
|
|
151
|
+
return {
|
|
152
|
+
debouncedValue,
|
|
153
|
+
isPending,
|
|
154
|
+
cancel,
|
|
155
|
+
flush
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/hooks/useThrottle/useThrottle.ts
|
|
160
|
+
import { useEffect as useEffect6, useRef as useRef5, useState as useState4 } from "react";
|
|
161
|
+
function useThrottle(value, options = {}) {
|
|
162
|
+
const { interval = 500, leading = true, trailing = true } = options;
|
|
163
|
+
const [throttledValue, setThrottledValue] = useState4(value);
|
|
164
|
+
const [isPending, setIsPending] = useState4(false);
|
|
165
|
+
const lastExecuted = useRef5(0);
|
|
166
|
+
const timeoutRef = useRef5(null);
|
|
167
|
+
const latestValue = useRef5(value);
|
|
168
|
+
const isFirstRun = useRef5(true);
|
|
169
|
+
latestValue.current = value;
|
|
170
|
+
useEffect6(() => {
|
|
171
|
+
const now = Date.now();
|
|
172
|
+
const elapsed = now - lastExecuted.current;
|
|
173
|
+
if (isFirstRun.current && leading) {
|
|
174
|
+
isFirstRun.current = false;
|
|
175
|
+
setThrottledValue(value);
|
|
176
|
+
lastExecuted.current = now;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
isFirstRun.current = false;
|
|
180
|
+
if (elapsed >= interval) {
|
|
181
|
+
if (timeoutRef.current) {
|
|
182
|
+
clearTimeout(timeoutRef.current);
|
|
183
|
+
timeoutRef.current = null;
|
|
184
|
+
}
|
|
185
|
+
setThrottledValue(value);
|
|
186
|
+
lastExecuted.current = now;
|
|
187
|
+
setIsPending(false);
|
|
188
|
+
} else if (trailing) {
|
|
189
|
+
setIsPending(true);
|
|
190
|
+
if (timeoutRef.current) {
|
|
191
|
+
clearTimeout(timeoutRef.current);
|
|
192
|
+
}
|
|
193
|
+
timeoutRef.current = setTimeout(() => {
|
|
194
|
+
setThrottledValue(latestValue.current);
|
|
195
|
+
lastExecuted.current = Date.now();
|
|
196
|
+
setIsPending(false);
|
|
197
|
+
timeoutRef.current = null;
|
|
198
|
+
}, interval - elapsed);
|
|
199
|
+
}
|
|
200
|
+
return () => {
|
|
201
|
+
if (timeoutRef.current) {
|
|
202
|
+
clearTimeout(timeoutRef.current);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}, [value, interval, leading, trailing]);
|
|
206
|
+
useEffect6(() => {
|
|
207
|
+
return () => {
|
|
208
|
+
if (timeoutRef.current) {
|
|
209
|
+
clearTimeout(timeoutRef.current);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}, []);
|
|
213
|
+
return {
|
|
214
|
+
throttledValue,
|
|
215
|
+
isPending
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/hooks/useLocalStorage/useLocalStorage.ts
|
|
220
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useState as useState5 } from "react";
|
|
221
|
+
function useLocalStorage(key, initialValue, options = {}) {
|
|
222
|
+
const {
|
|
223
|
+
serializer = JSON.stringify,
|
|
224
|
+
deserializer = JSON.parse,
|
|
225
|
+
syncAcrossTabs = true
|
|
226
|
+
} = options;
|
|
227
|
+
const readValue = useCallback4(() => {
|
|
228
|
+
if (isServer) {
|
|
229
|
+
return initialValue;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const item = window.localStorage.getItem(key);
|
|
233
|
+
return item !== null ? deserializer(item) : initialValue;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.warn(`Error reading localStorage key "${key}":`, error);
|
|
236
|
+
return initialValue;
|
|
237
|
+
}
|
|
238
|
+
}, [key, initialValue, deserializer]);
|
|
239
|
+
const [storedValue, setStoredValue] = useState5(readValue);
|
|
240
|
+
const [exists, setExists] = useState5(() => {
|
|
241
|
+
if (isServer) return false;
|
|
242
|
+
return window.localStorage.getItem(key) !== null;
|
|
243
|
+
});
|
|
244
|
+
const setValue = useCallback4(
|
|
245
|
+
(value) => {
|
|
246
|
+
try {
|
|
247
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
248
|
+
setStoredValue(valueToStore);
|
|
249
|
+
setExists(true);
|
|
250
|
+
if (!isServer) {
|
|
251
|
+
window.localStorage.setItem(key, serializer(valueToStore));
|
|
252
|
+
window.dispatchEvent(
|
|
253
|
+
new StorageEvent("storage", {
|
|
254
|
+
key,
|
|
255
|
+
newValue: serializer(valueToStore)
|
|
256
|
+
})
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.warn(`Error setting localStorage key "${key}":`, error);
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
[key, serializer, storedValue]
|
|
264
|
+
);
|
|
265
|
+
const remove = useCallback4(() => {
|
|
266
|
+
try {
|
|
267
|
+
setStoredValue(initialValue);
|
|
268
|
+
setExists(false);
|
|
269
|
+
if (!isServer) {
|
|
270
|
+
window.localStorage.removeItem(key);
|
|
271
|
+
}
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.warn(`Error removing localStorage key "${key}":`, error);
|
|
274
|
+
}
|
|
275
|
+
}, [key, initialValue]);
|
|
276
|
+
useEffect7(() => {
|
|
277
|
+
if (isServer || !syncAcrossTabs) return;
|
|
278
|
+
const handleStorageChange = (event) => {
|
|
279
|
+
if (event.key === key && event.newValue !== null) {
|
|
280
|
+
try {
|
|
281
|
+
setStoredValue(deserializer(event.newValue));
|
|
282
|
+
setExists(true);
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
285
|
+
} else if (event.key === key && event.newValue === null) {
|
|
286
|
+
setStoredValue(initialValue);
|
|
287
|
+
setExists(false);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
window.addEventListener("storage", handleStorageChange);
|
|
291
|
+
return () => {
|
|
292
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
293
|
+
};
|
|
294
|
+
}, [key, initialValue, deserializer, syncAcrossTabs]);
|
|
295
|
+
return {
|
|
296
|
+
value: storedValue,
|
|
297
|
+
setValue,
|
|
298
|
+
remove,
|
|
299
|
+
exists
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/hooks/useSessionStorage/useSessionStorage.ts
|
|
304
|
+
import { useCallback as useCallback5, useEffect as useEffect8, useState as useState6 } from "react";
|
|
305
|
+
function useSessionStorage(key, initialValue, options = {}) {
|
|
306
|
+
const {
|
|
307
|
+
serializer = JSON.stringify,
|
|
308
|
+
deserializer = JSON.parse,
|
|
309
|
+
initializeWithValue = false
|
|
310
|
+
} = options;
|
|
311
|
+
const [storedValue, setStoredValue] = useState6(() => {
|
|
312
|
+
if (isServer) {
|
|
313
|
+
return typeof initialValue === "function" ? initialValue() : initialValue;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
const item = window.sessionStorage.getItem(key);
|
|
317
|
+
if (item && !initializeWithValue) {
|
|
318
|
+
return deserializer(item);
|
|
319
|
+
}
|
|
320
|
+
const value = typeof initialValue === "function" ? initialValue() : initialValue;
|
|
321
|
+
if (initializeWithValue) {
|
|
322
|
+
window.sessionStorage.setItem(key, serializer(value));
|
|
323
|
+
}
|
|
324
|
+
return value;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error(`Error reading sessionStorage key "${key}":`, error);
|
|
327
|
+
return typeof initialValue === "function" ? initialValue() : initialValue;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
const setValue = useCallback5(
|
|
331
|
+
(value) => {
|
|
332
|
+
try {
|
|
333
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
334
|
+
setStoredValue(valueToStore);
|
|
335
|
+
if (!isServer) {
|
|
336
|
+
window.sessionStorage.setItem(key, serializer(valueToStore));
|
|
337
|
+
window.dispatchEvent(new Event("session-storage"));
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error(`Error setting sessionStorage key "${key}":`, error);
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
[key, storedValue, serializer]
|
|
344
|
+
);
|
|
345
|
+
const removeValue = useCallback5(() => {
|
|
346
|
+
try {
|
|
347
|
+
setStoredValue(
|
|
348
|
+
typeof initialValue === "function" ? initialValue() : initialValue
|
|
349
|
+
);
|
|
350
|
+
if (!isServer) {
|
|
351
|
+
window.sessionStorage.removeItem(key);
|
|
352
|
+
window.dispatchEvent(new Event("session-storage"));
|
|
353
|
+
}
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error(`Error removing sessionStorage key "${key}":`, error);
|
|
356
|
+
}
|
|
357
|
+
}, [key, initialValue]);
|
|
358
|
+
useEffect8(() => {
|
|
359
|
+
if (isServer) return;
|
|
360
|
+
const handleStorageChange = (e) => {
|
|
361
|
+
if (e.key === key && e.storageArea === window.sessionStorage) {
|
|
362
|
+
try {
|
|
363
|
+
if (e.newValue === null) {
|
|
364
|
+
setStoredValue(
|
|
365
|
+
typeof initialValue === "function" ? initialValue() : initialValue
|
|
366
|
+
);
|
|
367
|
+
} else {
|
|
368
|
+
setStoredValue(deserializer(e.newValue));
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error(`Error syncing sessionStorage key "${key}":`, error);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
window.addEventListener("storage", handleStorageChange);
|
|
376
|
+
return () => window.removeEventListener("storage", handleStorageChange);
|
|
377
|
+
}, [key, initialValue, deserializer]);
|
|
378
|
+
return [storedValue, setValue, removeValue];
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/hooks/useTimeout/useTimeout.ts
|
|
382
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef6, useState as useState7 } from "react";
|
|
383
|
+
function useTimeout(callback, options) {
|
|
384
|
+
const { delay, autoStart = true } = options;
|
|
385
|
+
const [isRunning, setIsRunning] = useState7(autoStart);
|
|
386
|
+
const [isComplete, setIsComplete] = useState7(false);
|
|
387
|
+
const callbackRef = useRef6(callback);
|
|
388
|
+
const timeoutRef = useRef6(null);
|
|
389
|
+
callbackRef.current = callback;
|
|
390
|
+
const stop = useCallback6(() => {
|
|
391
|
+
if (timeoutRef.current) {
|
|
392
|
+
clearTimeout(timeoutRef.current);
|
|
393
|
+
timeoutRef.current = null;
|
|
394
|
+
}
|
|
395
|
+
setIsRunning(false);
|
|
396
|
+
}, []);
|
|
397
|
+
const start = useCallback6(() => {
|
|
398
|
+
stop();
|
|
399
|
+
setIsComplete(false);
|
|
400
|
+
setIsRunning(true);
|
|
401
|
+
timeoutRef.current = setTimeout(() => {
|
|
402
|
+
callbackRef.current();
|
|
403
|
+
setIsRunning(false);
|
|
404
|
+
setIsComplete(true);
|
|
405
|
+
timeoutRef.current = null;
|
|
406
|
+
}, delay);
|
|
407
|
+
}, [delay, stop]);
|
|
408
|
+
const reset = useCallback6(() => {
|
|
409
|
+
stop();
|
|
410
|
+
setIsComplete(false);
|
|
411
|
+
}, [stop]);
|
|
412
|
+
useEffect9(() => {
|
|
413
|
+
if (autoStart) {
|
|
414
|
+
start();
|
|
415
|
+
}
|
|
416
|
+
return () => {
|
|
417
|
+
if (timeoutRef.current) {
|
|
418
|
+
clearTimeout(timeoutRef.current);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}, [delay]);
|
|
422
|
+
return {
|
|
423
|
+
isRunning,
|
|
424
|
+
isComplete,
|
|
425
|
+
start,
|
|
426
|
+
stop,
|
|
427
|
+
reset
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/hooks/useInterval/useInterval.ts
|
|
432
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useRef as useRef7, useState as useState8 } from "react";
|
|
433
|
+
function useInterval(callback, options) {
|
|
434
|
+
const { delay, autoStart = true, immediate = false } = options;
|
|
435
|
+
const [isRunning, setIsRunning] = useState8(autoStart);
|
|
436
|
+
const [count, setCount] = useState8(0);
|
|
437
|
+
const callbackRef = useRef7(callback);
|
|
438
|
+
const intervalRef = useRef7(null);
|
|
439
|
+
callbackRef.current = callback;
|
|
440
|
+
const stop = useCallback7(() => {
|
|
441
|
+
if (intervalRef.current) {
|
|
442
|
+
clearInterval(intervalRef.current);
|
|
443
|
+
intervalRef.current = null;
|
|
444
|
+
}
|
|
445
|
+
setIsRunning(false);
|
|
446
|
+
}, []);
|
|
447
|
+
const start = useCallback7(() => {
|
|
448
|
+
stop();
|
|
449
|
+
setIsRunning(true);
|
|
450
|
+
if (immediate) {
|
|
451
|
+
callbackRef.current();
|
|
452
|
+
setCount((c) => c + 1);
|
|
453
|
+
}
|
|
454
|
+
intervalRef.current = setInterval(() => {
|
|
455
|
+
callbackRef.current();
|
|
456
|
+
setCount((c) => c + 1);
|
|
457
|
+
}, delay);
|
|
458
|
+
}, [delay, immediate, stop]);
|
|
459
|
+
const reset = useCallback7(() => {
|
|
460
|
+
setCount(0);
|
|
461
|
+
}, []);
|
|
462
|
+
useEffect10(() => {
|
|
463
|
+
if (autoStart) {
|
|
464
|
+
start();
|
|
465
|
+
}
|
|
466
|
+
return () => {
|
|
467
|
+
if (intervalRef.current) {
|
|
468
|
+
clearInterval(intervalRef.current);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
}, [delay]);
|
|
472
|
+
return {
|
|
473
|
+
isRunning,
|
|
474
|
+
count,
|
|
475
|
+
start,
|
|
476
|
+
stop,
|
|
477
|
+
reset
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/hooks/useUpdateEffect/useUpdateEffect.ts
|
|
482
|
+
import { useEffect as useEffect11, useRef as useRef8 } from "react";
|
|
483
|
+
function useUpdateEffect(effect, deps) {
|
|
484
|
+
const isFirstMount = useRef8(true);
|
|
485
|
+
useEffect11(() => {
|
|
486
|
+
if (isFirstMount.current) {
|
|
487
|
+
isFirstMount.current = false;
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
return effect();
|
|
491
|
+
}, deps);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/hooks/useIsMounted/useIsMounted.ts
|
|
495
|
+
import { useCallback as useCallback8, useEffect as useEffect12, useRef as useRef9 } from "react";
|
|
496
|
+
function useIsMounted() {
|
|
497
|
+
const isMountedRef = useRef9(false);
|
|
498
|
+
useEffect12(() => {
|
|
499
|
+
isMountedRef.current = true;
|
|
500
|
+
return () => {
|
|
501
|
+
isMountedRef.current = false;
|
|
502
|
+
};
|
|
503
|
+
}, []);
|
|
504
|
+
return useCallback8(() => isMountedRef.current, []);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/hooks/useStableCallback/useStableCallback.ts
|
|
508
|
+
import { useCallback as useCallback9, useEffect as useEffect13, useRef as useRef10 } from "react";
|
|
509
|
+
function useStableCallback(callback) {
|
|
510
|
+
const callbackRef = useRef10(callback);
|
|
511
|
+
useEffect13(() => {
|
|
512
|
+
callbackRef.current = callback;
|
|
513
|
+
});
|
|
514
|
+
return useCallback9((...args) => {
|
|
515
|
+
return callbackRef.current(...args);
|
|
516
|
+
}, []);
|
|
517
|
+
}
|
|
518
|
+
var useStableCallback_default = useStableCallback;
|
|
519
|
+
|
|
520
|
+
// src/hooks/useMediaQuery/useMediaQuery.ts
|
|
521
|
+
import { useCallback as useCallback10, useEffect as useEffect14, useState as useState9 } from "react";
|
|
522
|
+
function useMediaQuery(query, options = {}) {
|
|
523
|
+
const { defaultValue = false, initializeOnMount = false } = options;
|
|
524
|
+
const getMatches = useCallback10(() => {
|
|
525
|
+
if (isServer) {
|
|
526
|
+
return defaultValue;
|
|
527
|
+
}
|
|
528
|
+
return window.matchMedia(query).matches;
|
|
529
|
+
}, [query, defaultValue]);
|
|
530
|
+
const [matches, setMatches] = useState9(() => {
|
|
531
|
+
if (initializeOnMount) {
|
|
532
|
+
return defaultValue;
|
|
533
|
+
}
|
|
534
|
+
return getMatches();
|
|
535
|
+
});
|
|
536
|
+
useEffect14(() => {
|
|
537
|
+
if (isServer) return;
|
|
538
|
+
const mediaQueryList = window.matchMedia(query);
|
|
539
|
+
setMatches(mediaQueryList.matches);
|
|
540
|
+
const handleChange = (event) => {
|
|
541
|
+
setMatches(event.matches);
|
|
542
|
+
};
|
|
543
|
+
if (mediaQueryList.addEventListener) {
|
|
544
|
+
mediaQueryList.addEventListener("change", handleChange);
|
|
545
|
+
} else {
|
|
546
|
+
mediaQueryList.addListener(handleChange);
|
|
547
|
+
}
|
|
548
|
+
return () => {
|
|
549
|
+
if (mediaQueryList.removeEventListener) {
|
|
550
|
+
mediaQueryList.removeEventListener("change", handleChange);
|
|
551
|
+
} else {
|
|
552
|
+
mediaQueryList.removeListener(handleChange);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}, [query]);
|
|
556
|
+
return matches;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/hooks/useWindowSize/useWindowSize.ts
|
|
560
|
+
import { useCallback as useCallback11, useEffect as useEffect15, useState as useState10 } from "react";
|
|
561
|
+
function useWindowSize(options = {}) {
|
|
562
|
+
const { debounce = 0, initialWidth = 0, initialHeight = 0 } = options;
|
|
563
|
+
const getSize = useCallback11(() => {
|
|
564
|
+
if (isServer) {
|
|
565
|
+
return { width: initialWidth, height: initialHeight };
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
width: window.innerWidth,
|
|
569
|
+
height: window.innerHeight
|
|
570
|
+
};
|
|
571
|
+
}, [initialWidth, initialHeight]);
|
|
572
|
+
const [windowSize, setWindowSize] = useState10(getSize);
|
|
573
|
+
useEffect15(() => {
|
|
574
|
+
if (isServer) return;
|
|
575
|
+
let timeoutId = null;
|
|
576
|
+
const handleResize = () => {
|
|
577
|
+
if (debounce > 0) {
|
|
578
|
+
if (timeoutId) {
|
|
579
|
+
clearTimeout(timeoutId);
|
|
580
|
+
}
|
|
581
|
+
timeoutId = setTimeout(() => {
|
|
582
|
+
setWindowSize(getSize());
|
|
583
|
+
}, debounce);
|
|
584
|
+
} else {
|
|
585
|
+
setWindowSize(getSize());
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
setWindowSize(getSize());
|
|
589
|
+
window.addEventListener("resize", handleResize);
|
|
590
|
+
return () => {
|
|
591
|
+
window.removeEventListener("resize", handleResize);
|
|
592
|
+
if (timeoutId) {
|
|
593
|
+
clearTimeout(timeoutId);
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
}, [debounce, getSize]);
|
|
597
|
+
return windowSize;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// src/hooks/useClickOutside/useClickOutside.ts
|
|
601
|
+
import { useEffect as useEffect16, useRef as useRef11 } from "react";
|
|
602
|
+
function useClickOutside(ref, callback, options = {}) {
|
|
603
|
+
const { events = ["mousedown", "touchstart"], enabled = true } = options;
|
|
604
|
+
const callbackRef = useRef11(callback);
|
|
605
|
+
callbackRef.current = callback;
|
|
606
|
+
useEffect16(() => {
|
|
607
|
+
if (isServer || !enabled) return;
|
|
608
|
+
const handleClick = (event) => {
|
|
609
|
+
const el = ref.current;
|
|
610
|
+
if (!el || el.contains(event.target)) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
callbackRef.current(event);
|
|
614
|
+
};
|
|
615
|
+
events.forEach((eventName) => {
|
|
616
|
+
document.addEventListener(eventName, handleClick);
|
|
617
|
+
});
|
|
618
|
+
return () => {
|
|
619
|
+
events.forEach((eventName) => {
|
|
620
|
+
document.removeEventListener(eventName, handleClick);
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
}, [ref, events, enabled]);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// src/hooks/useHover/useHover.ts
|
|
627
|
+
import { useCallback as useCallback12, useRef as useRef12, useState as useState11 } from "react";
|
|
628
|
+
function useHover() {
|
|
629
|
+
const [isHovered, setIsHovered] = useState11(false);
|
|
630
|
+
const nodeRef = useRef12(null);
|
|
631
|
+
const handleMouseEnter = useCallback12(() => {
|
|
632
|
+
setIsHovered(true);
|
|
633
|
+
}, []);
|
|
634
|
+
const handleMouseLeave = useCallback12(() => {
|
|
635
|
+
setIsHovered(false);
|
|
636
|
+
}, []);
|
|
637
|
+
const ref = useCallback12(
|
|
638
|
+
(node) => {
|
|
639
|
+
if (nodeRef.current) {
|
|
640
|
+
nodeRef.current.removeEventListener("mouseenter", handleMouseEnter);
|
|
641
|
+
nodeRef.current.removeEventListener("mouseleave", handleMouseLeave);
|
|
642
|
+
}
|
|
643
|
+
if (node) {
|
|
644
|
+
node.addEventListener("mouseenter", handleMouseEnter);
|
|
645
|
+
node.addEventListener("mouseleave", handleMouseLeave);
|
|
646
|
+
}
|
|
647
|
+
nodeRef.current = node;
|
|
648
|
+
},
|
|
649
|
+
[handleMouseEnter, handleMouseLeave]
|
|
650
|
+
);
|
|
651
|
+
return {
|
|
652
|
+
isHovered,
|
|
653
|
+
ref,
|
|
654
|
+
hoverProps: {
|
|
655
|
+
onMouseEnter: handleMouseEnter,
|
|
656
|
+
onMouseLeave: handleMouseLeave
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/hooks/useKeyPress/useKeyPress.ts
|
|
662
|
+
import { useCallback as useCallback13, useEffect as useEffect17, useState as useState12 } from "react";
|
|
663
|
+
function useKeyPress(targetKey, callback, options = {}) {
|
|
664
|
+
const {
|
|
665
|
+
target = "window",
|
|
666
|
+
event = "keydown",
|
|
667
|
+
preventDefault = false,
|
|
668
|
+
enabled = true
|
|
669
|
+
} = options;
|
|
670
|
+
const [isPressed, setIsPressed] = useState12(false);
|
|
671
|
+
const callbackRef = useCallback13(
|
|
672
|
+
(e) => {
|
|
673
|
+
callback?.(e);
|
|
674
|
+
},
|
|
675
|
+
[callback]
|
|
676
|
+
);
|
|
677
|
+
useEffect17(() => {
|
|
678
|
+
if (isServer || !enabled) return;
|
|
679
|
+
const targetElement = target === "document" ? document : window;
|
|
680
|
+
const handleKeyDown = (e) => {
|
|
681
|
+
const event2 = e;
|
|
682
|
+
if (event2.key === targetKey) {
|
|
683
|
+
if (preventDefault) {
|
|
684
|
+
event2.preventDefault();
|
|
685
|
+
}
|
|
686
|
+
setIsPressed(true);
|
|
687
|
+
callbackRef(event2);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
const handleKeyUp = (e) => {
|
|
691
|
+
const event2 = e;
|
|
692
|
+
if (event2.key === targetKey) {
|
|
693
|
+
setIsPressed(false);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
targetElement.addEventListener(event, handleKeyDown);
|
|
697
|
+
if (event === "keydown") {
|
|
698
|
+
targetElement.addEventListener("keyup", handleKeyUp);
|
|
699
|
+
}
|
|
700
|
+
return () => {
|
|
701
|
+
targetElement.removeEventListener(event, handleKeyDown);
|
|
702
|
+
if (event === "keydown") {
|
|
703
|
+
targetElement.removeEventListener("keyup", handleKeyUp);
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
}, [targetKey, target, event, preventDefault, enabled, callbackRef]);
|
|
707
|
+
return isPressed;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// src/hooks/useScroll/useScroll.ts
|
|
711
|
+
import { useEffect as useEffect18, useRef as useRef13, useState as useState13 } from "react";
|
|
712
|
+
function useScroll(ref, options = {}) {
|
|
713
|
+
const { throttle = 0 } = options;
|
|
714
|
+
const [position, setPosition] = useState13({ x: 0, y: 0 });
|
|
715
|
+
const [direction, setDirection] = useState13(null);
|
|
716
|
+
const [boundaries, setBoundaries] = useState13({
|
|
717
|
+
isAtTop: true,
|
|
718
|
+
isAtBottom: false,
|
|
719
|
+
isAtLeft: true,
|
|
720
|
+
isAtRight: false
|
|
721
|
+
});
|
|
722
|
+
const lastPositionRef = useRef13({ x: 0, y: 0 });
|
|
723
|
+
const throttleTimerRef = useRef13(null);
|
|
724
|
+
useEffect18(() => {
|
|
725
|
+
if (isServer) return;
|
|
726
|
+
const target = ref?.current ?? window;
|
|
727
|
+
const isWindow = target === window;
|
|
728
|
+
const getScrollPosition = () => {
|
|
729
|
+
if (isWindow) {
|
|
730
|
+
return {
|
|
731
|
+
x: window.scrollX ?? window.pageXOffset,
|
|
732
|
+
y: window.scrollY ?? window.pageYOffset
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
const element = target;
|
|
736
|
+
return {
|
|
737
|
+
x: element.scrollLeft,
|
|
738
|
+
y: element.scrollTop
|
|
739
|
+
};
|
|
740
|
+
};
|
|
741
|
+
const getBoundaries = () => {
|
|
742
|
+
if (isWindow) {
|
|
743
|
+
const scrollHeight = document.documentElement.scrollHeight;
|
|
744
|
+
const scrollWidth = document.documentElement.scrollWidth;
|
|
745
|
+
const clientHeight = window.innerHeight;
|
|
746
|
+
const clientWidth = window.innerWidth;
|
|
747
|
+
const { x, y } = getScrollPosition();
|
|
748
|
+
return {
|
|
749
|
+
isAtTop: y <= 0,
|
|
750
|
+
isAtBottom: y + clientHeight >= scrollHeight - 1,
|
|
751
|
+
isAtLeft: x <= 0,
|
|
752
|
+
isAtRight: x + clientWidth >= scrollWidth - 1
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
const element = target;
|
|
756
|
+
return {
|
|
757
|
+
isAtTop: element.scrollTop <= 0,
|
|
758
|
+
isAtBottom: element.scrollTop + element.clientHeight >= element.scrollHeight - 1,
|
|
759
|
+
isAtLeft: element.scrollLeft <= 0,
|
|
760
|
+
isAtRight: element.scrollLeft + element.clientWidth >= element.scrollWidth - 1
|
|
761
|
+
};
|
|
762
|
+
};
|
|
763
|
+
const handleScroll = () => {
|
|
764
|
+
const execute = () => {
|
|
765
|
+
const newPosition = getScrollPosition();
|
|
766
|
+
const lastPosition = lastPositionRef.current;
|
|
767
|
+
if (newPosition.y !== lastPosition.y) {
|
|
768
|
+
setDirection(newPosition.y > lastPosition.y ? "down" : "up");
|
|
769
|
+
} else if (newPosition.x !== lastPosition.x) {
|
|
770
|
+
setDirection(newPosition.x > lastPosition.x ? "right" : "left");
|
|
771
|
+
}
|
|
772
|
+
lastPositionRef.current = newPosition;
|
|
773
|
+
setPosition(newPosition);
|
|
774
|
+
setBoundaries(getBoundaries());
|
|
775
|
+
};
|
|
776
|
+
if (throttle > 0) {
|
|
777
|
+
if (!throttleTimerRef.current) {
|
|
778
|
+
throttleTimerRef.current = setTimeout(() => {
|
|
779
|
+
execute();
|
|
780
|
+
throttleTimerRef.current = null;
|
|
781
|
+
}, throttle);
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
execute();
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
setPosition(getScrollPosition());
|
|
788
|
+
setBoundaries(getBoundaries());
|
|
789
|
+
target.addEventListener("scroll", handleScroll, { passive: true });
|
|
790
|
+
return () => {
|
|
791
|
+
target.removeEventListener("scroll", handleScroll);
|
|
792
|
+
if (throttleTimerRef.current) {
|
|
793
|
+
clearTimeout(throttleTimerRef.current);
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
}, [ref, throttle]);
|
|
797
|
+
return {
|
|
798
|
+
...position,
|
|
799
|
+
...boundaries,
|
|
800
|
+
direction
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// src/hooks/useIntersection/useIntersection.ts
|
|
805
|
+
import { useEffect as useEffect19, useRef as useRef14, useState as useState14 } from "react";
|
|
806
|
+
function useIntersection(options = {}) {
|
|
807
|
+
const {
|
|
808
|
+
root = null,
|
|
809
|
+
rootMargin = "0px",
|
|
810
|
+
threshold = 0,
|
|
811
|
+
triggerOnce = false,
|
|
812
|
+
enabled = true
|
|
813
|
+
} = options;
|
|
814
|
+
const [entry, setEntry] = useState14(null);
|
|
815
|
+
const [isIntersecting, setIsIntersecting] = useState14(false);
|
|
816
|
+
const ref = useRef14(null);
|
|
817
|
+
const hasTriggeredRef = useRef14(false);
|
|
818
|
+
useEffect19(() => {
|
|
819
|
+
if (isServer || !enabled) return;
|
|
820
|
+
const element = ref.current;
|
|
821
|
+
if (!element) return;
|
|
822
|
+
if (triggerOnce && hasTriggeredRef.current) return;
|
|
823
|
+
const observer = new IntersectionObserver(
|
|
824
|
+
([observerEntry]) => {
|
|
825
|
+
setEntry(observerEntry);
|
|
826
|
+
setIsIntersecting(observerEntry.isIntersecting);
|
|
827
|
+
if (observerEntry.isIntersecting && triggerOnce) {
|
|
828
|
+
hasTriggeredRef.current = true;
|
|
829
|
+
observer.unobserve(element);
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
root,
|
|
834
|
+
rootMargin,
|
|
835
|
+
threshold
|
|
836
|
+
}
|
|
837
|
+
);
|
|
838
|
+
observer.observe(element);
|
|
839
|
+
return () => {
|
|
840
|
+
observer.disconnect();
|
|
841
|
+
};
|
|
842
|
+
}, [root, rootMargin, threshold, triggerOnce, enabled]);
|
|
843
|
+
return {
|
|
844
|
+
isIntersecting,
|
|
845
|
+
entry,
|
|
846
|
+
ref
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// src/hooks/useCopyToClipboard/useCopyToClipboard.ts
|
|
851
|
+
import { useCallback as useCallback14, useState as useState15 } from "react";
|
|
852
|
+
function useCopyToClipboard() {
|
|
853
|
+
const [copiedValue, setCopiedValue] = useState15(null);
|
|
854
|
+
const [isSuccess, setIsSuccess] = useState15(false);
|
|
855
|
+
const [error, setError] = useState15(null);
|
|
856
|
+
const copy = useCallback14(async (text) => {
|
|
857
|
+
if (isServer) {
|
|
858
|
+
console.warn("Clipboard is not available on the server");
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
if (navigator?.clipboard?.writeText) {
|
|
862
|
+
try {
|
|
863
|
+
await navigator.clipboard.writeText(text);
|
|
864
|
+
setCopiedValue(text);
|
|
865
|
+
setIsSuccess(true);
|
|
866
|
+
setError(null);
|
|
867
|
+
return true;
|
|
868
|
+
} catch (err) {
|
|
869
|
+
setError(err instanceof Error ? err : new Error("Failed to copy"));
|
|
870
|
+
setIsSuccess(false);
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
try {
|
|
875
|
+
const textArea = document.createElement("textarea");
|
|
876
|
+
textArea.value = text;
|
|
877
|
+
textArea.style.position = "fixed";
|
|
878
|
+
textArea.style.left = "-9999px";
|
|
879
|
+
textArea.style.top = "-9999px";
|
|
880
|
+
document.body.appendChild(textArea);
|
|
881
|
+
textArea.focus();
|
|
882
|
+
textArea.select();
|
|
883
|
+
const successful = document.execCommand("copy");
|
|
884
|
+
document.body.removeChild(textArea);
|
|
885
|
+
if (successful) {
|
|
886
|
+
setCopiedValue(text);
|
|
887
|
+
setIsSuccess(true);
|
|
888
|
+
setError(null);
|
|
889
|
+
return true;
|
|
890
|
+
} else {
|
|
891
|
+
throw new Error('execCommand("copy") failed');
|
|
892
|
+
}
|
|
893
|
+
} catch (err) {
|
|
894
|
+
setError(err instanceof Error ? err : new Error("Failed to copy"));
|
|
895
|
+
setIsSuccess(false);
|
|
896
|
+
return false;
|
|
897
|
+
}
|
|
898
|
+
}, []);
|
|
899
|
+
const reset = useCallback14(() => {
|
|
900
|
+
setCopiedValue(null);
|
|
901
|
+
setIsSuccess(false);
|
|
902
|
+
setError(null);
|
|
903
|
+
}, []);
|
|
904
|
+
return {
|
|
905
|
+
copiedValue,
|
|
906
|
+
isSuccess,
|
|
907
|
+
error,
|
|
908
|
+
copy,
|
|
909
|
+
reset
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// src/hooks/useDocumentTitle/useDocumentTitle.ts
|
|
914
|
+
import { useEffect as useEffect20, useRef as useRef15 } from "react";
|
|
915
|
+
function useDocumentTitle(title, options = {}) {
|
|
916
|
+
const { restoreOnUnmount = true } = options;
|
|
917
|
+
const previousTitleRef = useRef15(null);
|
|
918
|
+
useEffect20(() => {
|
|
919
|
+
if (isServer) return;
|
|
920
|
+
if (previousTitleRef.current === null) {
|
|
921
|
+
previousTitleRef.current = document.title;
|
|
922
|
+
}
|
|
923
|
+
document.title = title;
|
|
924
|
+
return () => {
|
|
925
|
+
if (restoreOnUnmount && previousTitleRef.current !== null) {
|
|
926
|
+
document.title = previousTitleRef.current;
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
}, [title, restoreOnUnmount]);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// src/hooks/useLockBodyScroll/useLockBodyScroll.ts
|
|
933
|
+
import { useEffect as useEffect21 } from "react";
|
|
934
|
+
function useLockBodyScroll(lock = true) {
|
|
935
|
+
useEffect21(() => {
|
|
936
|
+
if (isServer || !lock) return;
|
|
937
|
+
const originalOverflow = document.body.style.overflow;
|
|
938
|
+
const originalPaddingRight = document.body.style.paddingRight;
|
|
939
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
940
|
+
document.body.style.overflow = "hidden";
|
|
941
|
+
if (scrollbarWidth > 0) {
|
|
942
|
+
document.body.style.paddingRight = `${scrollbarWidth}px`;
|
|
943
|
+
}
|
|
944
|
+
return () => {
|
|
945
|
+
document.body.style.overflow = originalOverflow;
|
|
946
|
+
document.body.style.paddingRight = originalPaddingRight;
|
|
947
|
+
};
|
|
948
|
+
}, [lock]);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/hooks/useTheme/useTheme.ts
|
|
952
|
+
import { useCallback as useCallback15, useMemo } from "react";
|
|
953
|
+
function useTheme(options = {}) {
|
|
954
|
+
const {
|
|
955
|
+
storageKey = "theme",
|
|
956
|
+
attribute = "data-theme",
|
|
957
|
+
defaultTheme = "system",
|
|
958
|
+
disableTransitionOnChange = true
|
|
959
|
+
} = options;
|
|
960
|
+
const { value: theme, setValue: setTheme } = useLocalStorage(
|
|
961
|
+
storageKey,
|
|
962
|
+
defaultTheme
|
|
963
|
+
);
|
|
964
|
+
const systemPrefersDark = useMediaQuery("(prefers-color-scheme: dark)");
|
|
965
|
+
const resolvedTheme = useMemo(() => {
|
|
966
|
+
if (theme === "system") {
|
|
967
|
+
return systemPrefersDark ? "dark" : "light";
|
|
968
|
+
}
|
|
969
|
+
return theme === "dark" ? "dark" : "light";
|
|
970
|
+
}, [theme, systemPrefersDark]);
|
|
971
|
+
const applyTheme = useCallback15(() => {
|
|
972
|
+
if (isServer) return;
|
|
973
|
+
const root = document.documentElement;
|
|
974
|
+
if (disableTransitionOnChange) {
|
|
975
|
+
const css = document.createElement("style");
|
|
976
|
+
css.appendChild(
|
|
977
|
+
document.createTextNode(
|
|
978
|
+
`* {
|
|
979
|
+
-webkit-transition: none !important;
|
|
980
|
+
-moz-transition: none !important;
|
|
981
|
+
-o-transition: none !important;
|
|
982
|
+
-ms-transition: none !important;
|
|
983
|
+
transition: none !important;
|
|
984
|
+
}`
|
|
985
|
+
)
|
|
986
|
+
);
|
|
987
|
+
document.head.appendChild(css);
|
|
988
|
+
setTimeout(() => {
|
|
989
|
+
(() => window.getComputedStyle(document.body))();
|
|
990
|
+
setTimeout(() => {
|
|
991
|
+
document.head.removeChild(css);
|
|
992
|
+
}, 1);
|
|
993
|
+
}, 1);
|
|
994
|
+
}
|
|
995
|
+
if (attribute === "class") {
|
|
996
|
+
root.classList.remove("light", "dark");
|
|
997
|
+
root.classList.add(resolvedTheme);
|
|
998
|
+
} else {
|
|
999
|
+
root.setAttribute(attribute, resolvedTheme);
|
|
1000
|
+
}
|
|
1001
|
+
}, [resolvedTheme, attribute, disableTransitionOnChange]);
|
|
1002
|
+
useIsomorphicLayoutEffect(() => {
|
|
1003
|
+
applyTheme();
|
|
1004
|
+
}, [applyTheme]);
|
|
1005
|
+
const toggleTheme = useCallback15(() => {
|
|
1006
|
+
setTheme((prev) => {
|
|
1007
|
+
if (prev === "system") return resolvedTheme === "dark" ? "light" : "dark";
|
|
1008
|
+
return prev === "dark" ? "light" : "dark";
|
|
1009
|
+
});
|
|
1010
|
+
}, [setTheme, resolvedTheme]);
|
|
1011
|
+
return {
|
|
1012
|
+
theme,
|
|
1013
|
+
resolvedTheme,
|
|
1014
|
+
setTheme,
|
|
1015
|
+
toggleTheme
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// src/hooks/useEvent/useEvent.ts
|
|
1020
|
+
import { useEffect as useEffect22, useRef as useRef16 } from "react";
|
|
1021
|
+
function useEvent(name, handler, target = isServer ? null : window, options = {}) {
|
|
1022
|
+
const handlerRef = useRef16(handler);
|
|
1023
|
+
handlerRef.current = handler;
|
|
1024
|
+
useEffect22(() => {
|
|
1025
|
+
if (!target) return;
|
|
1026
|
+
const eventListener = (event) => {
|
|
1027
|
+
if (typeof handlerRef.current === "function") {
|
|
1028
|
+
handlerRef.current(event);
|
|
1029
|
+
} else {
|
|
1030
|
+
handlerRef.current.handleEvent(event);
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
target.addEventListener(name, eventListener, options);
|
|
1034
|
+
return () => {
|
|
1035
|
+
target.removeEventListener(name, eventListener, options);
|
|
1036
|
+
};
|
|
1037
|
+
}, [name, target, options?.capture, options?.passive, options?.once]);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// src/hooks/useLongPress/useLongPress.ts
|
|
1041
|
+
import { useCallback as useCallback16, useRef as useRef17 } from "react";
|
|
1042
|
+
function useLongPress(onLongPress, options = {}) {
|
|
1043
|
+
const {
|
|
1044
|
+
threshold = 500,
|
|
1045
|
+
cancelOnMove = true,
|
|
1046
|
+
onStart,
|
|
1047
|
+
onFinish,
|
|
1048
|
+
onCancel
|
|
1049
|
+
} = options;
|
|
1050
|
+
const callbackRef = useRef17(onLongPress);
|
|
1051
|
+
const onStartRef = useRef17(onStart);
|
|
1052
|
+
const onFinishRef = useRef17(onFinish);
|
|
1053
|
+
const onCancelRef = useRef17(onCancel);
|
|
1054
|
+
callbackRef.current = onLongPress;
|
|
1055
|
+
onStartRef.current = onStart;
|
|
1056
|
+
onFinishRef.current = onFinish;
|
|
1057
|
+
onCancelRef.current = onCancel;
|
|
1058
|
+
const timerRef = useRef17(null);
|
|
1059
|
+
const isLongPressActive = useRef17(false);
|
|
1060
|
+
const start = useCallback16(
|
|
1061
|
+
(event) => {
|
|
1062
|
+
onStartRef.current?.(event);
|
|
1063
|
+
isLongPressActive.current = false;
|
|
1064
|
+
timerRef.current = setTimeout(() => {
|
|
1065
|
+
isLongPressActive.current = true;
|
|
1066
|
+
callbackRef.current(event);
|
|
1067
|
+
onFinishRef.current?.(event);
|
|
1068
|
+
}, threshold);
|
|
1069
|
+
},
|
|
1070
|
+
[threshold]
|
|
1071
|
+
);
|
|
1072
|
+
const cancel = useCallback16(
|
|
1073
|
+
(event) => {
|
|
1074
|
+
if (timerRef.current) {
|
|
1075
|
+
clearTimeout(timerRef.current);
|
|
1076
|
+
timerRef.current = null;
|
|
1077
|
+
if (!isLongPressActive.current) {
|
|
1078
|
+
onCancelRef.current?.(event);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
isLongPressActive.current = false;
|
|
1082
|
+
},
|
|
1083
|
+
[]
|
|
1084
|
+
);
|
|
1085
|
+
return {
|
|
1086
|
+
onMouseDown: (e) => start(e),
|
|
1087
|
+
onTouchStart: (e) => start(e),
|
|
1088
|
+
onMouseUp: (e) => cancel(e),
|
|
1089
|
+
onMouseLeave: (e) => cancel(e),
|
|
1090
|
+
onTouchEnd: (e) => cancel(e)
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// src/hooks/useWindowFocus/useWindowFocus.ts
|
|
1095
|
+
import { useEffect as useEffect23, useState as useState16 } from "react";
|
|
1096
|
+
function useWindowFocus() {
|
|
1097
|
+
const [isFocused, setIsFocused] = useState16(
|
|
1098
|
+
() => !isServer && typeof document !== "undefined" ? document.hasFocus() : true
|
|
1099
|
+
);
|
|
1100
|
+
useEffect23(() => {
|
|
1101
|
+
if (isServer) return;
|
|
1102
|
+
const onFocus = () => setIsFocused(true);
|
|
1103
|
+
const onBlur = () => setIsFocused(false);
|
|
1104
|
+
window.addEventListener("focus", onFocus);
|
|
1105
|
+
window.addEventListener("blur", onBlur);
|
|
1106
|
+
setIsFocused(document.hasFocus());
|
|
1107
|
+
return () => {
|
|
1108
|
+
window.removeEventListener("focus", onFocus);
|
|
1109
|
+
window.removeEventListener("blur", onBlur);
|
|
1110
|
+
};
|
|
1111
|
+
}, []);
|
|
1112
|
+
return isFocused;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// src/hooks/useResizeObserver/useResizeObserver.ts
|
|
1116
|
+
import { useEffect as useEffect24, useRef as useRef18, useState as useState17 } from "react";
|
|
1117
|
+
function useResizeObserver(ref, options = {}) {
|
|
1118
|
+
const { throttle = 0 } = options;
|
|
1119
|
+
const [contentRect, setContentRect] = useState17();
|
|
1120
|
+
const throttleTimerRef = useRef18(null);
|
|
1121
|
+
useEffect24(() => {
|
|
1122
|
+
if (isServer) return;
|
|
1123
|
+
const element = ref.current;
|
|
1124
|
+
if (!element) return;
|
|
1125
|
+
if (!window.ResizeObserver) {
|
|
1126
|
+
console.warn("ResizeObserver is not supported in this browser");
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
const observer = new ResizeObserver((entries) => {
|
|
1130
|
+
const entry = entries[0];
|
|
1131
|
+
if (!entry) return;
|
|
1132
|
+
const update = () => {
|
|
1133
|
+
setContentRect(entry.contentRect);
|
|
1134
|
+
};
|
|
1135
|
+
if (throttle > 0) {
|
|
1136
|
+
if (!throttleTimerRef.current) {
|
|
1137
|
+
throttleTimerRef.current = setTimeout(() => {
|
|
1138
|
+
update();
|
|
1139
|
+
throttleTimerRef.current = null;
|
|
1140
|
+
}, throttle);
|
|
1141
|
+
}
|
|
1142
|
+
} else {
|
|
1143
|
+
update();
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
observer.observe(element);
|
|
1147
|
+
return () => {
|
|
1148
|
+
observer.disconnect();
|
|
1149
|
+
if (throttleTimerRef.current) {
|
|
1150
|
+
clearTimeout(throttleTimerRef.current);
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
}, [ref, throttle]);
|
|
1154
|
+
return contentRect;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// src/hooks/useMutationObserver/useMutationObserver.ts
|
|
1158
|
+
import { useEffect as useEffect25, useRef as useRef19 } from "react";
|
|
1159
|
+
function useMutationObserver(ref, callback, options = {
|
|
1160
|
+
attributes: true,
|
|
1161
|
+
characterData: true,
|
|
1162
|
+
childList: true,
|
|
1163
|
+
subtree: true
|
|
1164
|
+
}) {
|
|
1165
|
+
const callbackRef = useRef19(callback);
|
|
1166
|
+
callbackRef.current = callback;
|
|
1167
|
+
const optionsRef = useRef19(options);
|
|
1168
|
+
optionsRef.current = options;
|
|
1169
|
+
useEffect25(() => {
|
|
1170
|
+
if (isServer) return;
|
|
1171
|
+
const element = ref.current;
|
|
1172
|
+
if (!element) return;
|
|
1173
|
+
if (!window.MutationObserver) {
|
|
1174
|
+
console.warn("MutationObserver is not supported");
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const observer = new MutationObserver((mutations, obs) => {
|
|
1178
|
+
callbackRef.current(mutations, obs);
|
|
1179
|
+
});
|
|
1180
|
+
observer.observe(element, optionsRef.current);
|
|
1181
|
+
return () => {
|
|
1182
|
+
observer.disconnect();
|
|
1183
|
+
};
|
|
1184
|
+
}, [ref]);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// src/hooks/usePageLeave/usePageLeave.ts
|
|
1188
|
+
import { useEffect as useEffect26 } from "react";
|
|
1189
|
+
function usePageLeave(onPageLeave) {
|
|
1190
|
+
useEffect26(() => {
|
|
1191
|
+
if (isServer) return;
|
|
1192
|
+
const handler = (event) => {
|
|
1193
|
+
event = event || window.event;
|
|
1194
|
+
const from = event.relatedTarget || event.toElement;
|
|
1195
|
+
if (!from || from.nodeName === "HTML") {
|
|
1196
|
+
if (event.clientY <= 0) {
|
|
1197
|
+
onPageLeave();
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
const simpleHandler = (event) => {
|
|
1202
|
+
if (event.clientY <= 0) {
|
|
1203
|
+
onPageLeave();
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
document.addEventListener("mouseleave", simpleHandler);
|
|
1207
|
+
return () => {
|
|
1208
|
+
document.removeEventListener("mouseleave", simpleHandler);
|
|
1209
|
+
};
|
|
1210
|
+
}, [onPageLeave]);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// src/hooks/useEyeDropper/useEyeDropper.ts
|
|
1214
|
+
import { useCallback as useCallback17 } from "react";
|
|
1215
|
+
function useEyeDropper() {
|
|
1216
|
+
const isSupported = !isServer && "EyeDropper" in window;
|
|
1217
|
+
const open = useCallback17(async (options) => {
|
|
1218
|
+
if (!isSupported) {
|
|
1219
|
+
throw new Error("EyeDropper is not supported");
|
|
1220
|
+
}
|
|
1221
|
+
const eyeDropper = new window.EyeDropper();
|
|
1222
|
+
return eyeDropper.open(options);
|
|
1223
|
+
}, [isSupported]);
|
|
1224
|
+
return {
|
|
1225
|
+
isSupported,
|
|
1226
|
+
open
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// src/hooks/useOnline/useOnline.ts
|
|
1231
|
+
import { useCallback as useCallback18, useEffect as useEffect27, useState as useState18 } from "react";
|
|
1232
|
+
function useOnline() {
|
|
1233
|
+
const [isOnline, setIsOnline] = useState18(() => {
|
|
1234
|
+
if (isServer) return true;
|
|
1235
|
+
return navigator.onLine;
|
|
1236
|
+
});
|
|
1237
|
+
const [since, setSince] = useState18(null);
|
|
1238
|
+
const handleOnline = useCallback18(() => {
|
|
1239
|
+
setIsOnline(true);
|
|
1240
|
+
setSince(Date.now());
|
|
1241
|
+
}, []);
|
|
1242
|
+
const handleOffline = useCallback18(() => {
|
|
1243
|
+
setIsOnline(false);
|
|
1244
|
+
setSince(Date.now());
|
|
1245
|
+
}, []);
|
|
1246
|
+
useEffect27(() => {
|
|
1247
|
+
if (isServer) return;
|
|
1248
|
+
setIsOnline(navigator.onLine);
|
|
1249
|
+
window.addEventListener("online", handleOnline);
|
|
1250
|
+
window.addEventListener("offline", handleOffline);
|
|
1251
|
+
return () => {
|
|
1252
|
+
window.removeEventListener("online", handleOnline);
|
|
1253
|
+
window.removeEventListener("offline", handleOffline);
|
|
1254
|
+
};
|
|
1255
|
+
}, [handleOnline, handleOffline]);
|
|
1256
|
+
return {
|
|
1257
|
+
isOnline,
|
|
1258
|
+
isOffline: !isOnline,
|
|
1259
|
+
since
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/hooks/useNetworkState/useNetworkState.ts
|
|
1264
|
+
import { useEffect as useEffect28, useState as useState19 } from "react";
|
|
1265
|
+
function useNetworkState() {
|
|
1266
|
+
const [state, setState] = useState19({
|
|
1267
|
+
online: true
|
|
1268
|
+
});
|
|
1269
|
+
useEffect28(() => {
|
|
1270
|
+
if (isServer) return;
|
|
1271
|
+
const nav = navigator;
|
|
1272
|
+
const connection = nav.connection || nav.mozConnection || nav.webkitConnection;
|
|
1273
|
+
const updateState = () => {
|
|
1274
|
+
setState({
|
|
1275
|
+
online: navigator.onLine,
|
|
1276
|
+
downlink: connection?.downlink,
|
|
1277
|
+
downlinkMax: connection?.downlinkMax,
|
|
1278
|
+
effectiveType: connection?.effectiveType,
|
|
1279
|
+
rtt: connection?.rtt,
|
|
1280
|
+
saveData: connection?.saveData,
|
|
1281
|
+
type: connection?.type
|
|
1282
|
+
});
|
|
1283
|
+
};
|
|
1284
|
+
updateState();
|
|
1285
|
+
window.addEventListener("online", updateState);
|
|
1286
|
+
window.addEventListener("offline", updateState);
|
|
1287
|
+
if (connection) {
|
|
1288
|
+
connection.addEventListener("change", updateState);
|
|
1289
|
+
}
|
|
1290
|
+
return () => {
|
|
1291
|
+
window.removeEventListener("online", updateState);
|
|
1292
|
+
window.removeEventListener("offline", updateState);
|
|
1293
|
+
if (connection) {
|
|
1294
|
+
connection.removeEventListener("change", updateState);
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
}, []);
|
|
1298
|
+
return state;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// src/hooks/useFullscreen/useFullscreen.ts
|
|
1302
|
+
import { useCallback as useCallback19, useEffect as useEffect29, useState as useState20 } from "react";
|
|
1303
|
+
function useFullscreen(ref) {
|
|
1304
|
+
const [isFullscreen, setIsFullscreen] = useState20(false);
|
|
1305
|
+
const isSupported = !isServer && !!document.fullscreenEnabled;
|
|
1306
|
+
const getElement = useCallback19(() => {
|
|
1307
|
+
return ref?.current ?? document.documentElement;
|
|
1308
|
+
}, [ref]);
|
|
1309
|
+
const enter = useCallback19(async () => {
|
|
1310
|
+
if (isServer || !isSupported) return;
|
|
1311
|
+
const element = getElement();
|
|
1312
|
+
if (!element) return;
|
|
1313
|
+
try {
|
|
1314
|
+
await element.requestFullscreen();
|
|
1315
|
+
} catch (error) {
|
|
1316
|
+
console.error("Failed to enter fullscreen:", error);
|
|
1317
|
+
}
|
|
1318
|
+
}, [isSupported, getElement]);
|
|
1319
|
+
const exit = useCallback19(async () => {
|
|
1320
|
+
if (isServer || !isSupported) return;
|
|
1321
|
+
try {
|
|
1322
|
+
await document.exitFullscreen();
|
|
1323
|
+
} catch (error) {
|
|
1324
|
+
console.error("Failed to exit fullscreen:", error);
|
|
1325
|
+
}
|
|
1326
|
+
}, [isSupported]);
|
|
1327
|
+
const toggle = useCallback19(async () => {
|
|
1328
|
+
if (isFullscreen) {
|
|
1329
|
+
await exit();
|
|
1330
|
+
} else {
|
|
1331
|
+
await enter();
|
|
1332
|
+
}
|
|
1333
|
+
}, [isFullscreen, enter, exit]);
|
|
1334
|
+
useEffect29(() => {
|
|
1335
|
+
if (isServer) return;
|
|
1336
|
+
const handleChange = () => {
|
|
1337
|
+
setIsFullscreen(!!document.fullscreenElement);
|
|
1338
|
+
};
|
|
1339
|
+
document.addEventListener("fullscreenchange", handleChange);
|
|
1340
|
+
return () => {
|
|
1341
|
+
document.removeEventListener("fullscreenchange", handleChange);
|
|
1342
|
+
};
|
|
1343
|
+
}, []);
|
|
1344
|
+
return {
|
|
1345
|
+
isFullscreen,
|
|
1346
|
+
isSupported,
|
|
1347
|
+
enter,
|
|
1348
|
+
exit,
|
|
1349
|
+
toggle
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// src/hooks/useShare/useShare.ts
|
|
1354
|
+
import { useCallback as useCallback20, useState as useState21 } from "react";
|
|
1355
|
+
function useShare() {
|
|
1356
|
+
const [isSharing, setIsSharing] = useState21(false);
|
|
1357
|
+
const [isSuccess, setIsSuccess] = useState21(false);
|
|
1358
|
+
const [error, setError] = useState21(null);
|
|
1359
|
+
const isSupported = !isServer && !!navigator.share;
|
|
1360
|
+
const canShare = useCallback20(
|
|
1361
|
+
(data) => {
|
|
1362
|
+
if (isServer || !navigator.canShare) return isSupported;
|
|
1363
|
+
if (!data) return isSupported;
|
|
1364
|
+
return navigator.canShare(data);
|
|
1365
|
+
},
|
|
1366
|
+
[isSupported]
|
|
1367
|
+
);
|
|
1368
|
+
const share = useCallback20(
|
|
1369
|
+
async (data) => {
|
|
1370
|
+
if (isServer || !isSupported) {
|
|
1371
|
+
setError(new Error("Web Share API is not supported"));
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
setIsSharing(true);
|
|
1375
|
+
setIsSuccess(false);
|
|
1376
|
+
setError(null);
|
|
1377
|
+
try {
|
|
1378
|
+
await navigator.share(data);
|
|
1379
|
+
setIsSuccess(true);
|
|
1380
|
+
return true;
|
|
1381
|
+
} catch (err) {
|
|
1382
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1383
|
+
setIsSuccess(false);
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
1386
|
+
const error2 = err instanceof Error ? err : new Error("Share failed");
|
|
1387
|
+
setError(error2);
|
|
1388
|
+
return false;
|
|
1389
|
+
} finally {
|
|
1390
|
+
setIsSharing(false);
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
[isSupported]
|
|
1394
|
+
);
|
|
1395
|
+
return {
|
|
1396
|
+
isSupported,
|
|
1397
|
+
isSharing,
|
|
1398
|
+
isSuccess,
|
|
1399
|
+
error,
|
|
1400
|
+
share,
|
|
1401
|
+
canShare
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/hooks/usePermissions/usePermissions.ts
|
|
1406
|
+
import { useEffect as useEffect30, useState as useState22 } from "react";
|
|
1407
|
+
function usePermissions(name) {
|
|
1408
|
+
const [state, setState] = useState22("unknown");
|
|
1409
|
+
useEffect30(() => {
|
|
1410
|
+
if (isServer || !navigator.permissions) return;
|
|
1411
|
+
let mounted = true;
|
|
1412
|
+
let permissionStatus = null;
|
|
1413
|
+
const handleChange = () => {
|
|
1414
|
+
if (mounted && permissionStatus) {
|
|
1415
|
+
setState(permissionStatus.state);
|
|
1416
|
+
}
|
|
1417
|
+
};
|
|
1418
|
+
navigator.permissions.query({ name }).then((status) => {
|
|
1419
|
+
if (!mounted) return;
|
|
1420
|
+
permissionStatus = status;
|
|
1421
|
+
setState(permissionStatus.state);
|
|
1422
|
+
status.addEventListener("change", handleChange);
|
|
1423
|
+
}).catch(() => {
|
|
1424
|
+
if (mounted) setState("unknown");
|
|
1425
|
+
});
|
|
1426
|
+
return () => {
|
|
1427
|
+
mounted = false;
|
|
1428
|
+
if (permissionStatus) {
|
|
1429
|
+
permissionStatus.removeEventListener("change", handleChange);
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
}, [name]);
|
|
1433
|
+
return state;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// src/hooks/useWakeLock/useWakeLock.ts
|
|
1437
|
+
import { useCallback as useCallback21, useEffect as useEffect31, useRef as useRef20, useState as useState23 } from "react";
|
|
1438
|
+
function useWakeLock({
|
|
1439
|
+
onRequest,
|
|
1440
|
+
onRelease,
|
|
1441
|
+
onError
|
|
1442
|
+
} = {}) {
|
|
1443
|
+
const [released, setReleased] = useState23();
|
|
1444
|
+
const wakeLockRef = useRef20(null);
|
|
1445
|
+
const isSupported = !isServer && "wakeLock" in navigator;
|
|
1446
|
+
const release = useCallback21(async () => {
|
|
1447
|
+
if (!wakeLockRef.current) return;
|
|
1448
|
+
try {
|
|
1449
|
+
await wakeLockRef.current.release();
|
|
1450
|
+
wakeLockRef.current = null;
|
|
1451
|
+
} catch (err) {
|
|
1452
|
+
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
1453
|
+
}
|
|
1454
|
+
}, [onError]);
|
|
1455
|
+
const request = useCallback21(async () => {
|
|
1456
|
+
if (!isSupported) return;
|
|
1457
|
+
try {
|
|
1458
|
+
const wakeLock = await navigator.wakeLock.request("screen");
|
|
1459
|
+
wakeLock.addEventListener("release", () => {
|
|
1460
|
+
setReleased(true);
|
|
1461
|
+
onRelease?.();
|
|
1462
|
+
wakeLockRef.current = null;
|
|
1463
|
+
});
|
|
1464
|
+
wakeLockRef.current = wakeLock;
|
|
1465
|
+
setReleased(false);
|
|
1466
|
+
onRequest?.();
|
|
1467
|
+
} catch (err) {
|
|
1468
|
+
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
1469
|
+
}
|
|
1470
|
+
}, [isSupported, onRequest, onRelease, onError]);
|
|
1471
|
+
useEffect31(() => {
|
|
1472
|
+
return () => {
|
|
1473
|
+
release();
|
|
1474
|
+
};
|
|
1475
|
+
}, [release]);
|
|
1476
|
+
useEffect31(() => {
|
|
1477
|
+
const handleVisibilityChange = async () => {
|
|
1478
|
+
if (wakeLockRef.current !== null && document.visibilityState === "visible") {
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1482
|
+
return () => {
|
|
1483
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1484
|
+
};
|
|
1485
|
+
}, []);
|
|
1486
|
+
return {
|
|
1487
|
+
isSupported,
|
|
1488
|
+
released: released ?? true,
|
|
1489
|
+
// Default to true (released) initially
|
|
1490
|
+
request,
|
|
1491
|
+
release
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// src/hooks/useMediaDevices/useMediaDevices.ts
|
|
1496
|
+
import { useEffect as useEffect32, useState as useState24 } from "react";
|
|
1497
|
+
function useMediaDevices() {
|
|
1498
|
+
const [devices, setDevices] = useState24([]);
|
|
1499
|
+
const [isLoading, setIsLoading] = useState24(true);
|
|
1500
|
+
const [error, setError] = useState24(null);
|
|
1501
|
+
const isSupported = !isServer && !!navigator.mediaDevices?.enumerateDevices;
|
|
1502
|
+
const getDevices = useStableCallback_default(async () => {
|
|
1503
|
+
if (!isSupported) {
|
|
1504
|
+
setIsLoading(false);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
try {
|
|
1508
|
+
const allDevices = await navigator.mediaDevices.enumerateDevices();
|
|
1509
|
+
setDevices(allDevices);
|
|
1510
|
+
setError(null);
|
|
1511
|
+
} catch (err) {
|
|
1512
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1513
|
+
} finally {
|
|
1514
|
+
setIsLoading(false);
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1517
|
+
useEffect32(() => {
|
|
1518
|
+
getDevices();
|
|
1519
|
+
if (isSupported) {
|
|
1520
|
+
navigator.mediaDevices.addEventListener("devicechange", getDevices);
|
|
1521
|
+
return () => {
|
|
1522
|
+
navigator.mediaDevices.removeEventListener("devicechange", getDevices);
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
}, [getDevices, isSupported]);
|
|
1526
|
+
return {
|
|
1527
|
+
devices,
|
|
1528
|
+
isLoading,
|
|
1529
|
+
error,
|
|
1530
|
+
isSupported
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// src/hooks/useMediaRecorder/useMediaRecorder.ts
|
|
1535
|
+
import { useCallback as useCallback22, useRef as useRef21, useState as useState25 } from "react";
|
|
1536
|
+
function useMediaRecorder(stream, options) {
|
|
1537
|
+
const [status, setStatus] = useState25("idle");
|
|
1538
|
+
const [mediaBlob, setMediaBlob] = useState25();
|
|
1539
|
+
const [mediaUrl, setMediaUrl] = useState25();
|
|
1540
|
+
const [error, setError] = useState25(null);
|
|
1541
|
+
const mediaRecorderRef = useRef21(null);
|
|
1542
|
+
const chunksRef = useRef21([]);
|
|
1543
|
+
const start = useCallback22(
|
|
1544
|
+
(timeSlice) => {
|
|
1545
|
+
if (!stream) return;
|
|
1546
|
+
try {
|
|
1547
|
+
if (!mediaRecorderRef.current) {
|
|
1548
|
+
mediaRecorderRef.current = new MediaRecorder(stream, options);
|
|
1549
|
+
mediaRecorderRef.current.ondataavailable = (event) => {
|
|
1550
|
+
if (event.data && event.data.size > 0) {
|
|
1551
|
+
chunksRef.current.push(event.data);
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
mediaRecorderRef.current.onerror = (event) => {
|
|
1555
|
+
setError(event.error);
|
|
1556
|
+
setStatus("idle");
|
|
1557
|
+
};
|
|
1558
|
+
mediaRecorderRef.current.onstop = () => {
|
|
1559
|
+
const blob = new Blob(chunksRef.current, { type: options?.mimeType || "video/webm" });
|
|
1560
|
+
setMediaBlob(blob);
|
|
1561
|
+
const url = URL.createObjectURL(blob);
|
|
1562
|
+
setMediaUrl(url);
|
|
1563
|
+
chunksRef.current = [];
|
|
1564
|
+
setStatus("stopped");
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
if (mediaRecorderRef.current.state !== "recording") {
|
|
1568
|
+
mediaRecorderRef.current.start(timeSlice);
|
|
1569
|
+
setStatus("recording");
|
|
1570
|
+
}
|
|
1571
|
+
} catch (err) {
|
|
1572
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1573
|
+
}
|
|
1574
|
+
},
|
|
1575
|
+
[stream, options]
|
|
1576
|
+
);
|
|
1577
|
+
const stop = useCallback22(() => {
|
|
1578
|
+
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
|
|
1579
|
+
mediaRecorderRef.current.stop();
|
|
1580
|
+
}
|
|
1581
|
+
}, []);
|
|
1582
|
+
const pause = useCallback22(() => {
|
|
1583
|
+
if (mediaRecorderRef.current && mediaRecorderRef.current.state === "recording") {
|
|
1584
|
+
mediaRecorderRef.current.pause();
|
|
1585
|
+
setStatus("paused");
|
|
1586
|
+
}
|
|
1587
|
+
}, []);
|
|
1588
|
+
const resume = useCallback22(() => {
|
|
1589
|
+
if (mediaRecorderRef.current && mediaRecorderRef.current.state === "paused") {
|
|
1590
|
+
mediaRecorderRef.current.resume();
|
|
1591
|
+
setStatus("recording");
|
|
1592
|
+
}
|
|
1593
|
+
}, []);
|
|
1594
|
+
const clear = useCallback22(() => {
|
|
1595
|
+
if (mediaUrl) {
|
|
1596
|
+
URL.revokeObjectURL(mediaUrl);
|
|
1597
|
+
}
|
|
1598
|
+
setMediaBlob(void 0);
|
|
1599
|
+
setMediaUrl(void 0);
|
|
1600
|
+
setError(null);
|
|
1601
|
+
setStatus("idle");
|
|
1602
|
+
}, [mediaUrl]);
|
|
1603
|
+
return {
|
|
1604
|
+
status,
|
|
1605
|
+
start,
|
|
1606
|
+
stop,
|
|
1607
|
+
pause,
|
|
1608
|
+
resume,
|
|
1609
|
+
isRecording: status === "recording",
|
|
1610
|
+
mediaBlob,
|
|
1611
|
+
mediaUrl,
|
|
1612
|
+
clear,
|
|
1613
|
+
error
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
// src/hooks/useBattery/useBattery.ts
|
|
1618
|
+
import { useEffect as useEffect33, useState as useState26 } from "react";
|
|
1619
|
+
function useBattery() {
|
|
1620
|
+
const [state, setState] = useState26({
|
|
1621
|
+
supported: false,
|
|
1622
|
+
loading: true,
|
|
1623
|
+
level: 1,
|
|
1624
|
+
charging: false,
|
|
1625
|
+
chargingTime: 0,
|
|
1626
|
+
dischargingTime: Infinity
|
|
1627
|
+
});
|
|
1628
|
+
useEffect33(() => {
|
|
1629
|
+
if (isServer || !("getBattery" in navigator)) {
|
|
1630
|
+
setState((s) => ({ ...s, loading: false, supported: false }));
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
let mounted = true;
|
|
1634
|
+
let battery = null;
|
|
1635
|
+
const handleChange = () => {
|
|
1636
|
+
if (!mounted || !battery) return;
|
|
1637
|
+
setState({
|
|
1638
|
+
supported: true,
|
|
1639
|
+
loading: false,
|
|
1640
|
+
level: battery.level,
|
|
1641
|
+
charging: battery.charging,
|
|
1642
|
+
chargingTime: battery.chargingTime,
|
|
1643
|
+
dischargingTime: battery.dischargingTime
|
|
1644
|
+
});
|
|
1645
|
+
};
|
|
1646
|
+
navigator.getBattery().then((bat) => {
|
|
1647
|
+
if (!mounted) return;
|
|
1648
|
+
battery = bat;
|
|
1649
|
+
handleChange();
|
|
1650
|
+
battery.addEventListener("levelchange", handleChange);
|
|
1651
|
+
battery.addEventListener("chargingchange", handleChange);
|
|
1652
|
+
battery.addEventListener("chargingtimechange", handleChange);
|
|
1653
|
+
battery.addEventListener("dischargingtimechange", handleChange);
|
|
1654
|
+
});
|
|
1655
|
+
return () => {
|
|
1656
|
+
mounted = false;
|
|
1657
|
+
if (battery) {
|
|
1658
|
+
battery.removeEventListener("levelchange", handleChange);
|
|
1659
|
+
battery.removeEventListener("chargingchange", handleChange);
|
|
1660
|
+
battery.removeEventListener("chargingtimechange", handleChange);
|
|
1661
|
+
battery.removeEventListener("dischargingtimechange", handleChange);
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1664
|
+
}, []);
|
|
1665
|
+
return state;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// src/hooks/useBluetooth/useBluetooth.ts
|
|
1669
|
+
import { useCallback as useCallback23, useState as useState27 } from "react";
|
|
1670
|
+
function useBluetooth() {
|
|
1671
|
+
const [device, setDevice] = useState27(null);
|
|
1672
|
+
const [server, setServer] = useState27(null);
|
|
1673
|
+
const [error, setError] = useState27(null);
|
|
1674
|
+
const [isConnecting, setIsConnecting] = useState27(false);
|
|
1675
|
+
const isSupported = !isServer && "bluetooth" in navigator;
|
|
1676
|
+
const disconnect = useCallback23(() => {
|
|
1677
|
+
if (device && device.gatt?.connected) {
|
|
1678
|
+
device.gatt.disconnect();
|
|
1679
|
+
}
|
|
1680
|
+
setDevice(null);
|
|
1681
|
+
setServer(null);
|
|
1682
|
+
setIsConnecting(false);
|
|
1683
|
+
}, [device]);
|
|
1684
|
+
const requestDevice = useCallback23(
|
|
1685
|
+
async (options) => {
|
|
1686
|
+
if (!isSupported) {
|
|
1687
|
+
setError(new Error("Bluetooth API not supported"));
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
setIsConnecting(true);
|
|
1691
|
+
setError(null);
|
|
1692
|
+
try {
|
|
1693
|
+
const device2 = await navigator.bluetooth.requestDevice(options || { acceptAllDevices: true });
|
|
1694
|
+
setDevice(device2);
|
|
1695
|
+
device2.addEventListener("gattserverdisconnected", () => {
|
|
1696
|
+
setServer(null);
|
|
1697
|
+
});
|
|
1698
|
+
if (device2.gatt) {
|
|
1699
|
+
const server2 = await device2.gatt.connect();
|
|
1700
|
+
setServer(server2);
|
|
1701
|
+
}
|
|
1702
|
+
} catch (err) {
|
|
1703
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1704
|
+
} finally {
|
|
1705
|
+
setIsConnecting(false);
|
|
1706
|
+
}
|
|
1707
|
+
},
|
|
1708
|
+
[isSupported]
|
|
1709
|
+
);
|
|
1710
|
+
return {
|
|
1711
|
+
isSupported,
|
|
1712
|
+
isConnected: !!server?.connected,
|
|
1713
|
+
isConnecting,
|
|
1714
|
+
device,
|
|
1715
|
+
server,
|
|
1716
|
+
error,
|
|
1717
|
+
requestDevice,
|
|
1718
|
+
disconnect
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// src/hooks/useGamepad/useGamepad.ts
|
|
1723
|
+
import { useEffect as useEffect34, useRef as useRef22, useState as useState28 } from "react";
|
|
1724
|
+
function useGamepad(options = {}) {
|
|
1725
|
+
const { enabled = true } = options;
|
|
1726
|
+
const [gamepads, setGamepads] = useState28([]);
|
|
1727
|
+
const requestRef = useRef22(null);
|
|
1728
|
+
const scanGamepads = () => {
|
|
1729
|
+
if (isServer) return;
|
|
1730
|
+
const detectedGamepads = navigator.getGamepads ? navigator.getGamepads() : [];
|
|
1731
|
+
const gamepadArray = Array.from(detectedGamepads);
|
|
1732
|
+
setGamepads(gamepadArray);
|
|
1733
|
+
};
|
|
1734
|
+
useEffect34(() => {
|
|
1735
|
+
if (isServer) return;
|
|
1736
|
+
window.addEventListener("gamepadconnected", scanGamepads);
|
|
1737
|
+
window.addEventListener("gamepaddisconnected", scanGamepads);
|
|
1738
|
+
return () => {
|
|
1739
|
+
window.removeEventListener("gamepadconnected", scanGamepads);
|
|
1740
|
+
window.removeEventListener("gamepaddisconnected", scanGamepads);
|
|
1741
|
+
};
|
|
1742
|
+
}, []);
|
|
1743
|
+
useEffect34(() => {
|
|
1744
|
+
if (isServer || !enabled) return;
|
|
1745
|
+
const tick = () => {
|
|
1746
|
+
scanGamepads();
|
|
1747
|
+
requestRef.current = requestAnimationFrame(tick);
|
|
1748
|
+
};
|
|
1749
|
+
requestRef.current = requestAnimationFrame(tick);
|
|
1750
|
+
return () => {
|
|
1751
|
+
if (requestRef.current) {
|
|
1752
|
+
cancelAnimationFrame(requestRef.current);
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
}, [enabled]);
|
|
1756
|
+
return { gamepads };
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// src/hooks/useFileSystem/useFileSystem.ts
|
|
1760
|
+
import { useCallback as useCallback24, useState as useState29 } from "react";
|
|
1761
|
+
function useFileSystem() {
|
|
1762
|
+
const [file, setFile] = useState29(null);
|
|
1763
|
+
const [fileHandle, setFileHandle] = useState29(null);
|
|
1764
|
+
const [error, setError] = useState29(null);
|
|
1765
|
+
const isSupported = !isServer && "showOpenFilePicker" in window;
|
|
1766
|
+
const openFile = useCallback24(async (options) => {
|
|
1767
|
+
if (!isSupported) {
|
|
1768
|
+
setError(new Error("File System Access API not supported"));
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
try {
|
|
1772
|
+
const handles = await window.showOpenFilePicker(options);
|
|
1773
|
+
const handle = handles[0];
|
|
1774
|
+
const file2 = await handle.getFile();
|
|
1775
|
+
setFileHandle(handle);
|
|
1776
|
+
setFile(file2);
|
|
1777
|
+
setError(null);
|
|
1778
|
+
} catch (err) {
|
|
1779
|
+
if (err.name !== "AbortError") {
|
|
1780
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
}, [isSupported]);
|
|
1784
|
+
const saveFile = useCallback24(async (data, options) => {
|
|
1785
|
+
if (!isSupported) {
|
|
1786
|
+
setError(new Error("File System Access API not supported"));
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
try {
|
|
1790
|
+
const handle = await window.showSaveFilePicker(options);
|
|
1791
|
+
const writable = await handle.createWritable();
|
|
1792
|
+
await writable.write(data);
|
|
1793
|
+
await writable.close();
|
|
1794
|
+
setError(null);
|
|
1795
|
+
} catch (err) {
|
|
1796
|
+
if (err.name !== "AbortError") {
|
|
1797
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}, [isSupported]);
|
|
1801
|
+
return {
|
|
1802
|
+
isSupported,
|
|
1803
|
+
file,
|
|
1804
|
+
fileHandle,
|
|
1805
|
+
openFile,
|
|
1806
|
+
saveFile,
|
|
1807
|
+
error
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// src/hooks/useStorageEstimate/useStorageEstimate.ts
|
|
1812
|
+
import { useEffect as useEffect35, useState as useState30 } from "react";
|
|
1813
|
+
function useStorageEstimate() {
|
|
1814
|
+
const [estimate, setEstimate] = useState30({
|
|
1815
|
+
quota: void 0,
|
|
1816
|
+
usage: void 0,
|
|
1817
|
+
supported: false,
|
|
1818
|
+
loading: true
|
|
1819
|
+
});
|
|
1820
|
+
useEffect35(() => {
|
|
1821
|
+
if (isServer || !navigator.storage?.estimate) {
|
|
1822
|
+
setEstimate((s) => ({ ...s, loading: false, supported: false }));
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
navigator.storage.estimate().then(({ quota, usage }) => {
|
|
1826
|
+
setEstimate({
|
|
1827
|
+
quota,
|
|
1828
|
+
usage,
|
|
1829
|
+
supported: true,
|
|
1830
|
+
loading: false
|
|
1831
|
+
});
|
|
1832
|
+
}).catch(() => {
|
|
1833
|
+
setEstimate((s) => ({ ...s, loading: false }));
|
|
1834
|
+
});
|
|
1835
|
+
}, []);
|
|
1836
|
+
return estimate;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// src/hooks/useAsync/useAsync.ts
|
|
1840
|
+
import { useCallback as useCallback25, useEffect as useEffect36, useRef as useRef23, useState as useState31 } from "react";
|
|
1841
|
+
function useAsync(asyncFunction, options = {}) {
|
|
1842
|
+
const { immediate = false, initialData, onSuccess, onError } = options;
|
|
1843
|
+
const [status, setStatus] = useState31("idle");
|
|
1844
|
+
const [data, setData] = useState31(initialData);
|
|
1845
|
+
const [error, setError] = useState31(null);
|
|
1846
|
+
const isMountedRef = useRef23(true);
|
|
1847
|
+
const asyncFunctionRef = useRef23(asyncFunction);
|
|
1848
|
+
asyncFunctionRef.current = asyncFunction;
|
|
1849
|
+
const execute = useCallback25(
|
|
1850
|
+
async (...args) => {
|
|
1851
|
+
setStatus("pending");
|
|
1852
|
+
setError(null);
|
|
1853
|
+
try {
|
|
1854
|
+
const result = await asyncFunctionRef.current(...args);
|
|
1855
|
+
if (isMountedRef.current) {
|
|
1856
|
+
setData(result);
|
|
1857
|
+
setStatus("success");
|
|
1858
|
+
onSuccess?.(result);
|
|
1859
|
+
}
|
|
1860
|
+
return result;
|
|
1861
|
+
} catch (err) {
|
|
1862
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1863
|
+
if (isMountedRef.current) {
|
|
1864
|
+
setError(error2);
|
|
1865
|
+
setStatus("error");
|
|
1866
|
+
onError?.(error2);
|
|
1867
|
+
}
|
|
1868
|
+
return void 0;
|
|
1869
|
+
}
|
|
1870
|
+
},
|
|
1871
|
+
[onSuccess, onError]
|
|
1872
|
+
);
|
|
1873
|
+
const reset = useCallback25(() => {
|
|
1874
|
+
setStatus("idle");
|
|
1875
|
+
setData(initialData);
|
|
1876
|
+
setError(null);
|
|
1877
|
+
}, [initialData]);
|
|
1878
|
+
useEffect36(() => {
|
|
1879
|
+
if (immediate) {
|
|
1880
|
+
execute(...[]);
|
|
1881
|
+
}
|
|
1882
|
+
}, []);
|
|
1883
|
+
useEffect36(() => {
|
|
1884
|
+
isMountedRef.current = true;
|
|
1885
|
+
return () => {
|
|
1886
|
+
isMountedRef.current = false;
|
|
1887
|
+
};
|
|
1888
|
+
}, []);
|
|
1889
|
+
return {
|
|
1890
|
+
data,
|
|
1891
|
+
error,
|
|
1892
|
+
status,
|
|
1893
|
+
isLoading: status === "pending",
|
|
1894
|
+
isSuccess: status === "success",
|
|
1895
|
+
isError: status === "error",
|
|
1896
|
+
execute,
|
|
1897
|
+
reset
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
// src/hooks/useFetch/useFetch.ts
|
|
1902
|
+
import { useCallback as useCallback26, useEffect as useEffect37, useRef as useRef24, useState as useState32 } from "react";
|
|
1903
|
+
function useFetch(url, options = {}) {
|
|
1904
|
+
const {
|
|
1905
|
+
immediate = true,
|
|
1906
|
+
initialData,
|
|
1907
|
+
transform,
|
|
1908
|
+
retries = 0,
|
|
1909
|
+
retryDelay = 1e3,
|
|
1910
|
+
onSuccess,
|
|
1911
|
+
onError,
|
|
1912
|
+
abortPrevious = true,
|
|
1913
|
+
...fetchOptions
|
|
1914
|
+
} = options;
|
|
1915
|
+
const [data, setData] = useState32(initialData);
|
|
1916
|
+
const [error, setError] = useState32(null);
|
|
1917
|
+
const [status, setStatus] = useState32("idle");
|
|
1918
|
+
const abortControllerRef = useRef24(null);
|
|
1919
|
+
const isMountedRef = useRef24(true);
|
|
1920
|
+
const retryCountRef = useRef24(0);
|
|
1921
|
+
const abort = useCallback26(() => {
|
|
1922
|
+
abortControllerRef.current?.abort();
|
|
1923
|
+
}, []);
|
|
1924
|
+
const optionsRef = useRef24(fetchOptions);
|
|
1925
|
+
optionsRef.current = fetchOptions;
|
|
1926
|
+
const onSuccessRef = useRef24(onSuccess);
|
|
1927
|
+
onSuccessRef.current = onSuccess;
|
|
1928
|
+
const onErrorRef = useRef24(onError);
|
|
1929
|
+
onErrorRef.current = onError;
|
|
1930
|
+
const transformRef = useRef24(transform);
|
|
1931
|
+
transformRef.current = transform;
|
|
1932
|
+
const fetchData = useCallback26(async () => {
|
|
1933
|
+
if (!url) return;
|
|
1934
|
+
if (abortPrevious) {
|
|
1935
|
+
abort();
|
|
1936
|
+
}
|
|
1937
|
+
const controller = new AbortController();
|
|
1938
|
+
abortControllerRef.current = controller;
|
|
1939
|
+
setStatus("loading");
|
|
1940
|
+
setError(null);
|
|
1941
|
+
const attemptFetch = async (attempt) => {
|
|
1942
|
+
try {
|
|
1943
|
+
const response = await fetch(url, {
|
|
1944
|
+
...optionsRef.current,
|
|
1945
|
+
signal: controller.signal
|
|
1946
|
+
});
|
|
1947
|
+
if (!response.ok) {
|
|
1948
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1949
|
+
}
|
|
1950
|
+
const result = transformRef.current ? await transformRef.current(response) : await response.json();
|
|
1951
|
+
if (isMountedRef.current && !controller.signal.aborted) {
|
|
1952
|
+
setData(result);
|
|
1953
|
+
setStatus("success");
|
|
1954
|
+
setError(null);
|
|
1955
|
+
retryCountRef.current = 0;
|
|
1956
|
+
onSuccessRef.current?.(result);
|
|
1957
|
+
}
|
|
1958
|
+
} catch (err) {
|
|
1959
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1963
|
+
if (attempt < retries && isMountedRef.current) {
|
|
1964
|
+
retryCountRef.current = attempt + 1;
|
|
1965
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
1966
|
+
if (!controller.signal.aborted) {
|
|
1967
|
+
return attemptFetch(attempt + 1);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
if (isMountedRef.current && !controller.signal.aborted) {
|
|
1971
|
+
setError(error2);
|
|
1972
|
+
setStatus("error");
|
|
1973
|
+
retryCountRef.current = 0;
|
|
1974
|
+
onErrorRef.current?.(error2);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1978
|
+
await attemptFetch(0);
|
|
1979
|
+
}, [url, retries, retryDelay, abort, abortPrevious]);
|
|
1980
|
+
useEffect37(() => {
|
|
1981
|
+
if (immediate && url) {
|
|
1982
|
+
fetchData();
|
|
1983
|
+
}
|
|
1984
|
+
return () => {
|
|
1985
|
+
abort();
|
|
1986
|
+
};
|
|
1987
|
+
}, [url, immediate]);
|
|
1988
|
+
useEffect37(() => {
|
|
1989
|
+
isMountedRef.current = true;
|
|
1990
|
+
return () => {
|
|
1991
|
+
isMountedRef.current = false;
|
|
1992
|
+
};
|
|
1993
|
+
}, []);
|
|
1994
|
+
return {
|
|
1995
|
+
data,
|
|
1996
|
+
error,
|
|
1997
|
+
status,
|
|
1998
|
+
isLoading: status === "loading",
|
|
1999
|
+
isSuccess: status === "success",
|
|
2000
|
+
isError: status === "error",
|
|
2001
|
+
refetch: fetchData,
|
|
2002
|
+
abort
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
// src/hooks/useScript/useScript.ts
|
|
2007
|
+
import { useEffect as useEffect38, useRef as useRef25, useState as useState33 } from "react";
|
|
2008
|
+
var scriptCache = /* @__PURE__ */ new Map();
|
|
2009
|
+
function useScript(src, options = {}) {
|
|
2010
|
+
const { immediate = true, removeOnUnmount = false, attributes = {} } = options;
|
|
2011
|
+
const [status, setStatus] = useState33(() => {
|
|
2012
|
+
if (isServer) return "idle";
|
|
2013
|
+
const cached = scriptCache.get(src);
|
|
2014
|
+
if (cached) return cached;
|
|
2015
|
+
const existingScript = document.querySelector(`script[src="${src}"]`);
|
|
2016
|
+
if (existingScript) {
|
|
2017
|
+
return "ready";
|
|
2018
|
+
}
|
|
2019
|
+
return "idle";
|
|
2020
|
+
});
|
|
2021
|
+
const hasLoaded = useRef25(false);
|
|
2022
|
+
const load = () => {
|
|
2023
|
+
if (isServer || hasLoaded.current) return;
|
|
2024
|
+
const cached = scriptCache.get(src);
|
|
2025
|
+
if (cached === "ready") {
|
|
2026
|
+
setStatus("ready");
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
const existingScript = document.querySelector(`script[src="${src}"]`);
|
|
2030
|
+
if (existingScript) {
|
|
2031
|
+
setStatus("ready");
|
|
2032
|
+
scriptCache.set(src, "ready");
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
hasLoaded.current = true;
|
|
2036
|
+
setStatus("loading");
|
|
2037
|
+
scriptCache.set(src, "loading");
|
|
2038
|
+
const script = document.createElement("script");
|
|
2039
|
+
script.src = src;
|
|
2040
|
+
script.async = true;
|
|
2041
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
2042
|
+
script.setAttribute(key, value);
|
|
2043
|
+
});
|
|
2044
|
+
const handleLoad = () => {
|
|
2045
|
+
setStatus("ready");
|
|
2046
|
+
scriptCache.set(src, "ready");
|
|
2047
|
+
};
|
|
2048
|
+
const handleError = () => {
|
|
2049
|
+
setStatus("error");
|
|
2050
|
+
scriptCache.set(src, "error");
|
|
2051
|
+
script.remove();
|
|
2052
|
+
};
|
|
2053
|
+
script.addEventListener("load", handleLoad);
|
|
2054
|
+
script.addEventListener("error", handleError);
|
|
2055
|
+
document.body.appendChild(script);
|
|
2056
|
+
};
|
|
2057
|
+
useEffect38(() => {
|
|
2058
|
+
if (immediate && status === "idle") {
|
|
2059
|
+
load();
|
|
2060
|
+
}
|
|
2061
|
+
return () => {
|
|
2062
|
+
if (removeOnUnmount && !isServer) {
|
|
2063
|
+
const script = document.querySelector(`script[src="${src}"]`);
|
|
2064
|
+
if (script) {
|
|
2065
|
+
script.remove();
|
|
2066
|
+
scriptCache.delete(src);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
}, [src, immediate]);
|
|
2071
|
+
return {
|
|
2072
|
+
status,
|
|
2073
|
+
isLoading: status === "loading",
|
|
2074
|
+
isReady: status === "ready",
|
|
2075
|
+
isError: status === "error",
|
|
2076
|
+
load
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
// src/hooks/useWorker/useWorker.ts
|
|
2081
|
+
import { useCallback as useCallback27, useEffect as useEffect39, useRef as useRef26, useState as useState34 } from "react";
|
|
2082
|
+
function useWorker(workerScript) {
|
|
2083
|
+
const [result, setResult] = useState34();
|
|
2084
|
+
const [error, setError] = useState34(null);
|
|
2085
|
+
const [status, setStatus] = useState34("idle");
|
|
2086
|
+
const workerRef = useRef26(null);
|
|
2087
|
+
const workerUrlRef = useRef26(null);
|
|
2088
|
+
useEffect39(() => {
|
|
2089
|
+
if (isServer) return;
|
|
2090
|
+
const createWorker = () => {
|
|
2091
|
+
try {
|
|
2092
|
+
let worker;
|
|
2093
|
+
if (typeof workerScript === "string") {
|
|
2094
|
+
worker = new Worker(workerScript);
|
|
2095
|
+
} else {
|
|
2096
|
+
const code = `
|
|
2097
|
+
self.onmessage = function(e) {
|
|
2098
|
+
const result = (${workerScript.toString()})(e.data);
|
|
2099
|
+
self.postMessage(result);
|
|
2100
|
+
}
|
|
2101
|
+
`;
|
|
2102
|
+
const blob = new Blob([code], { type: "application/javascript" });
|
|
2103
|
+
const url = URL.createObjectURL(blob);
|
|
2104
|
+
workerUrlRef.current = url;
|
|
2105
|
+
worker = new Worker(url);
|
|
2106
|
+
}
|
|
2107
|
+
worker.onmessage = (e) => {
|
|
2108
|
+
setResult(e.data);
|
|
2109
|
+
setStatus("success");
|
|
2110
|
+
};
|
|
2111
|
+
worker.onerror = (e) => {
|
|
2112
|
+
setError(new Error(e.message));
|
|
2113
|
+
setStatus("error");
|
|
2114
|
+
};
|
|
2115
|
+
workerRef.current = worker;
|
|
2116
|
+
} catch (err) {
|
|
2117
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2118
|
+
setStatus("error");
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
createWorker();
|
|
2122
|
+
return () => {
|
|
2123
|
+
workerRef.current?.terminate();
|
|
2124
|
+
if (workerUrlRef.current) {
|
|
2125
|
+
URL.revokeObjectURL(workerUrlRef.current);
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
}, [workerScript]);
|
|
2129
|
+
const run = useCallback27((input) => {
|
|
2130
|
+
if (workerRef.current) {
|
|
2131
|
+
setStatus("running");
|
|
2132
|
+
setError(null);
|
|
2133
|
+
workerRef.current.postMessage(input);
|
|
2134
|
+
}
|
|
2135
|
+
}, []);
|
|
2136
|
+
const terminate = useCallback27(() => {
|
|
2137
|
+
workerRef.current?.terminate();
|
|
2138
|
+
setStatus("idle");
|
|
2139
|
+
}, []);
|
|
2140
|
+
return {
|
|
2141
|
+
result,
|
|
2142
|
+
error,
|
|
2143
|
+
status,
|
|
2144
|
+
run,
|
|
2145
|
+
terminate
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// src/hooks/useIndexedDB/useIndexedDB.ts
|
|
2150
|
+
import { useCallback as useCallback28, useEffect as useEffect40, useState as useState35 } from "react";
|
|
2151
|
+
function useIndexedDB(dbName, storeName, key) {
|
|
2152
|
+
const [value, setValue] = useState35();
|
|
2153
|
+
const [error, setError] = useState35(null);
|
|
2154
|
+
const [isLoading, setIsLoading] = useState35(true);
|
|
2155
|
+
const openDB = useCallback28(() => {
|
|
2156
|
+
return new Promise((resolve, reject) => {
|
|
2157
|
+
const request = indexedDB.open(dbName, 1);
|
|
2158
|
+
request.onupgradeneeded = (event) => {
|
|
2159
|
+
const db = event.target.result;
|
|
2160
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
2161
|
+
db.createObjectStore(storeName);
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
request.onsuccess = () => resolve(request.result);
|
|
2165
|
+
request.onerror = () => reject(request.error);
|
|
2166
|
+
});
|
|
2167
|
+
}, [dbName, storeName]);
|
|
2168
|
+
const load = useCallback28(async () => {
|
|
2169
|
+
if (isServer) {
|
|
2170
|
+
setIsLoading(false);
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
try {
|
|
2174
|
+
const db = await openDB();
|
|
2175
|
+
const tx = db.transaction(storeName, "readonly");
|
|
2176
|
+
const store = tx.objectStore(storeName);
|
|
2177
|
+
const request = store.get(key);
|
|
2178
|
+
request.onsuccess = () => {
|
|
2179
|
+
setValue(request.result);
|
|
2180
|
+
setIsLoading(false);
|
|
2181
|
+
};
|
|
2182
|
+
request.onerror = () => {
|
|
2183
|
+
throw request.error;
|
|
2184
|
+
};
|
|
2185
|
+
} catch (err) {
|
|
2186
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2187
|
+
setIsLoading(false);
|
|
2188
|
+
}
|
|
2189
|
+
}, [key, storeName, openDB]);
|
|
2190
|
+
useEffect40(() => {
|
|
2191
|
+
load();
|
|
2192
|
+
}, [load]);
|
|
2193
|
+
const set = useCallback28(async (newValue) => {
|
|
2194
|
+
try {
|
|
2195
|
+
const db = await openDB();
|
|
2196
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
2197
|
+
const store = tx.objectStore(storeName);
|
|
2198
|
+
return new Promise((resolve, reject) => {
|
|
2199
|
+
const req = store.put(newValue, key);
|
|
2200
|
+
req.onsuccess = () => {
|
|
2201
|
+
setValue(newValue);
|
|
2202
|
+
resolve();
|
|
2203
|
+
};
|
|
2204
|
+
req.onerror = () => reject(req.error);
|
|
2205
|
+
});
|
|
2206
|
+
} catch (err) {
|
|
2207
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2208
|
+
throw err;
|
|
2209
|
+
}
|
|
2210
|
+
}, [openDB, storeName, key]);
|
|
2211
|
+
const remove = useCallback28(async () => {
|
|
2212
|
+
try {
|
|
2213
|
+
const db = await openDB();
|
|
2214
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
2215
|
+
const store = tx.objectStore(storeName);
|
|
2216
|
+
return new Promise((resolve, reject) => {
|
|
2217
|
+
const req = store.delete(key);
|
|
2218
|
+
req.onsuccess = () => {
|
|
2219
|
+
setValue(void 0);
|
|
2220
|
+
resolve();
|
|
2221
|
+
};
|
|
2222
|
+
req.onerror = () => reject(req.error);
|
|
2223
|
+
});
|
|
2224
|
+
} catch (err) {
|
|
2225
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2226
|
+
throw err;
|
|
2227
|
+
}
|
|
2228
|
+
}, [openDB, storeName, key]);
|
|
2229
|
+
return {
|
|
2230
|
+
value,
|
|
2231
|
+
set,
|
|
2232
|
+
remove,
|
|
2233
|
+
error,
|
|
2234
|
+
isLoading
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
// src/hooks/useHistory/useHistory.ts
|
|
2239
|
+
import { useCallback as useCallback29, useState as useState36 } from "react";
|
|
2240
|
+
function useHistory(initialValue, options = {}) {
|
|
2241
|
+
const { maxSize = 100 } = options;
|
|
2242
|
+
const [state, setState] = useState36({
|
|
2243
|
+
history: [initialValue],
|
|
2244
|
+
position: 0
|
|
2245
|
+
});
|
|
2246
|
+
const { history, position } = state;
|
|
2247
|
+
const value = history[position];
|
|
2248
|
+
const set = useCallback29(
|
|
2249
|
+
(newValue) => {
|
|
2250
|
+
setState((currentState) => {
|
|
2251
|
+
const { history: currentHistory, position: currentPosition } = currentState;
|
|
2252
|
+
const resolvedValue = typeof newValue === "function" ? newValue(currentHistory[currentPosition]) : newValue;
|
|
2253
|
+
const slicedHistory = currentHistory.slice(0, currentPosition + 1);
|
|
2254
|
+
const newHistory = [...slicedHistory, resolvedValue];
|
|
2255
|
+
if (newHistory.length > maxSize) {
|
|
2256
|
+
const trimmedHistory = newHistory.slice(newHistory.length - maxSize);
|
|
2257
|
+
return {
|
|
2258
|
+
history: trimmedHistory,
|
|
2259
|
+
position: trimmedHistory.length - 1
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
return {
|
|
2263
|
+
history: newHistory,
|
|
2264
|
+
position: newHistory.length - 1
|
|
2265
|
+
};
|
|
2266
|
+
});
|
|
2267
|
+
},
|
|
2268
|
+
[maxSize]
|
|
2269
|
+
);
|
|
2270
|
+
const undo = useCallback29(() => {
|
|
2271
|
+
setState((prev) => ({
|
|
2272
|
+
...prev,
|
|
2273
|
+
position: Math.max(0, prev.position - 1)
|
|
2274
|
+
}));
|
|
2275
|
+
}, []);
|
|
2276
|
+
const redo = useCallback29(() => {
|
|
2277
|
+
setState((prev) => ({
|
|
2278
|
+
...prev,
|
|
2279
|
+
position: Math.min(prev.history.length - 1, prev.position + 1)
|
|
2280
|
+
}));
|
|
2281
|
+
}, []);
|
|
2282
|
+
const reset = useCallback29((value2) => {
|
|
2283
|
+
setState({
|
|
2284
|
+
history: [value2],
|
|
2285
|
+
position: 0
|
|
2286
|
+
});
|
|
2287
|
+
}, []);
|
|
2288
|
+
const go = useCallback29((newPosition) => {
|
|
2289
|
+
setState((prev) => {
|
|
2290
|
+
const maxPosition = prev.history.length - 1;
|
|
2291
|
+
return {
|
|
2292
|
+
...prev,
|
|
2293
|
+
position: Math.max(0, Math.min(maxPosition, newPosition))
|
|
2294
|
+
};
|
|
2295
|
+
});
|
|
2296
|
+
}, []);
|
|
2297
|
+
const canUndo = position > 0;
|
|
2298
|
+
const canRedo = position < history.length - 1;
|
|
2299
|
+
return {
|
|
2300
|
+
value,
|
|
2301
|
+
set,
|
|
2302
|
+
undo,
|
|
2303
|
+
redo,
|
|
2304
|
+
reset,
|
|
2305
|
+
canUndo,
|
|
2306
|
+
canRedo,
|
|
2307
|
+
history,
|
|
2308
|
+
position,
|
|
2309
|
+
go
|
|
2310
|
+
};
|
|
4
2311
|
}
|
|
5
|
-
|
|
6
|
-
|
|
2312
|
+
|
|
2313
|
+
// src/hooks/useStep/useStep.ts
|
|
2314
|
+
import { useCallback as useCallback30, useMemo as useMemo2, useState as useState37 } from "react";
|
|
2315
|
+
function useStep(options) {
|
|
2316
|
+
const { initialStep = 0, maxStep } = options;
|
|
2317
|
+
const [currentStep, setCurrentStep] = useState37(
|
|
2318
|
+
() => Math.max(0, Math.min(initialStep, maxStep - 1))
|
|
2319
|
+
);
|
|
2320
|
+
const next = useCallback30(() => {
|
|
2321
|
+
setCurrentStep((prev2) => Math.min(prev2 + 1, maxStep - 1));
|
|
2322
|
+
}, [maxStep]);
|
|
2323
|
+
const prev = useCallback30(() => {
|
|
2324
|
+
setCurrentStep((prev2) => Math.max(prev2 - 1, 0));
|
|
2325
|
+
}, []);
|
|
2326
|
+
const goTo = useCallback30(
|
|
2327
|
+
(step) => {
|
|
2328
|
+
setCurrentStep(Math.max(0, Math.min(step, maxStep - 1)));
|
|
2329
|
+
},
|
|
2330
|
+
[maxStep]
|
|
2331
|
+
);
|
|
2332
|
+
const reset = useCallback30(() => {
|
|
2333
|
+
setCurrentStep(Math.max(0, Math.min(initialStep, maxStep - 1)));
|
|
2334
|
+
}, [initialStep, maxStep]);
|
|
2335
|
+
const isStepComplete = useCallback30(
|
|
2336
|
+
(step) => {
|
|
2337
|
+
return step < currentStep;
|
|
2338
|
+
},
|
|
2339
|
+
[currentStep]
|
|
2340
|
+
);
|
|
2341
|
+
const isFirst = currentStep === 0;
|
|
2342
|
+
const isLast = currentStep === maxStep - 1;
|
|
2343
|
+
const progress = useMemo2(
|
|
2344
|
+
() => (currentStep + 1) / maxStep * 100,
|
|
2345
|
+
[currentStep, maxStep]
|
|
2346
|
+
);
|
|
2347
|
+
return {
|
|
2348
|
+
currentStep,
|
|
2349
|
+
step: currentStep + 1,
|
|
2350
|
+
next,
|
|
2351
|
+
prev,
|
|
2352
|
+
goTo,
|
|
2353
|
+
reset,
|
|
2354
|
+
isFirst,
|
|
2355
|
+
isLast,
|
|
2356
|
+
progress,
|
|
2357
|
+
totalSteps: maxStep,
|
|
2358
|
+
isStepComplete
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
// src/hooks/usePagination/usePagination.ts
|
|
2363
|
+
import { useCallback as useCallback31, useMemo as useMemo3, useState as useState38 } from "react";
|
|
2364
|
+
function usePagination(options) {
|
|
2365
|
+
const {
|
|
2366
|
+
totalItems,
|
|
2367
|
+
pageSize: initialPageSize = 10,
|
|
2368
|
+
initialPage = 1,
|
|
2369
|
+
siblings = 1
|
|
2370
|
+
} = options;
|
|
2371
|
+
const [page, setPage] = useState38(() => Math.max(1, initialPage));
|
|
2372
|
+
const [pageSize, setPageSizeState] = useState38(initialPageSize);
|
|
2373
|
+
const totalPages = useMemo3(
|
|
2374
|
+
() => Math.max(1, Math.ceil(totalItems / pageSize)),
|
|
2375
|
+
[totalItems, pageSize]
|
|
2376
|
+
);
|
|
2377
|
+
const boundedPage = useMemo3(
|
|
2378
|
+
() => Math.min(page, totalPages),
|
|
2379
|
+
[page, totalPages]
|
|
2380
|
+
);
|
|
2381
|
+
const next = useCallback31(() => {
|
|
2382
|
+
setPage((prev2) => Math.min(prev2 + 1, totalPages));
|
|
2383
|
+
}, [totalPages]);
|
|
2384
|
+
const prev = useCallback31(() => {
|
|
2385
|
+
setPage((prev2) => Math.max(prev2 - 1, 1));
|
|
2386
|
+
}, []);
|
|
2387
|
+
const goTo = useCallback31(
|
|
2388
|
+
(newPage) => {
|
|
2389
|
+
setPage(Math.max(1, Math.min(newPage, totalPages)));
|
|
2390
|
+
},
|
|
2391
|
+
[totalPages]
|
|
2392
|
+
);
|
|
2393
|
+
const first = useCallback31(() => {
|
|
2394
|
+
setPage(1);
|
|
2395
|
+
}, []);
|
|
2396
|
+
const last = useCallback31(() => {
|
|
2397
|
+
setPage(totalPages);
|
|
2398
|
+
}, [totalPages]);
|
|
2399
|
+
const setPageSize = useCallback31(
|
|
2400
|
+
(size) => {
|
|
2401
|
+
const newTotalPages = Math.max(1, Math.ceil(totalItems / size));
|
|
2402
|
+
setPageSizeState(size);
|
|
2403
|
+
setPage((prev2) => Math.min(prev2, newTotalPages));
|
|
2404
|
+
},
|
|
2405
|
+
[totalItems]
|
|
2406
|
+
);
|
|
2407
|
+
const startIndex = (boundedPage - 1) * pageSize;
|
|
2408
|
+
const endIndex = Math.min(startIndex + pageSize, totalItems);
|
|
2409
|
+
const range = useMemo3(() => {
|
|
2410
|
+
const totalPageNumbers = siblings * 2 + 5;
|
|
2411
|
+
if (totalPageNumbers >= totalPages) {
|
|
2412
|
+
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
2413
|
+
}
|
|
2414
|
+
const leftSiblingIndex = Math.max(boundedPage - siblings, 1);
|
|
2415
|
+
const rightSiblingIndex = Math.min(boundedPage + siblings, totalPages);
|
|
2416
|
+
const showLeftDots = leftSiblingIndex > 2;
|
|
2417
|
+
const showRightDots = rightSiblingIndex < totalPages - 1;
|
|
2418
|
+
if (!showLeftDots && showRightDots) {
|
|
2419
|
+
const leftItemCount = 3 + 2 * siblings;
|
|
2420
|
+
const leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
|
|
2421
|
+
return [...leftRange, "dots", totalPages];
|
|
2422
|
+
}
|
|
2423
|
+
if (showLeftDots && !showRightDots) {
|
|
2424
|
+
const rightItemCount = 3 + 2 * siblings;
|
|
2425
|
+
const rightRange = Array.from(
|
|
2426
|
+
{ length: rightItemCount },
|
|
2427
|
+
(_, i) => totalPages - rightItemCount + i + 1
|
|
2428
|
+
);
|
|
2429
|
+
return [1, "dots", ...rightRange];
|
|
2430
|
+
}
|
|
2431
|
+
const middleRange = Array.from(
|
|
2432
|
+
{ length: rightSiblingIndex - leftSiblingIndex + 1 },
|
|
2433
|
+
(_, i) => leftSiblingIndex + i
|
|
2434
|
+
);
|
|
2435
|
+
return [1, "dots", ...middleRange, "dots", totalPages];
|
|
2436
|
+
}, [boundedPage, totalPages, siblings]);
|
|
2437
|
+
return {
|
|
2438
|
+
page: boundedPage,
|
|
2439
|
+
totalPages,
|
|
2440
|
+
pageSize,
|
|
2441
|
+
next,
|
|
2442
|
+
prev,
|
|
2443
|
+
goTo,
|
|
2444
|
+
first,
|
|
2445
|
+
last,
|
|
2446
|
+
isFirst: boundedPage === 1,
|
|
2447
|
+
isLast: boundedPage === totalPages,
|
|
2448
|
+
startIndex,
|
|
2449
|
+
endIndex,
|
|
2450
|
+
range,
|
|
2451
|
+
setPageSize
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
// src/hooks/useMachine/useMachine.ts
|
|
2456
|
+
import { useReducer } from "react";
|
|
2457
|
+
function useMachine(config) {
|
|
2458
|
+
const [state, dispatch] = useReducer(
|
|
2459
|
+
(currentState, event) => {
|
|
2460
|
+
const stateConfig = config.states[currentState];
|
|
2461
|
+
const transition = stateConfig?.on?.[event];
|
|
2462
|
+
if (!transition) {
|
|
2463
|
+
return currentState;
|
|
2464
|
+
}
|
|
2465
|
+
if (typeof transition === "string") {
|
|
2466
|
+
return transition;
|
|
2467
|
+
}
|
|
2468
|
+
if (transition.action) {
|
|
2469
|
+
transition.action();
|
|
2470
|
+
}
|
|
2471
|
+
return transition.target;
|
|
2472
|
+
},
|
|
2473
|
+
config.initial
|
|
2474
|
+
);
|
|
2475
|
+
return [state, dispatch];
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// src/hooks/useVirtualList/useVirtualList.ts
|
|
2479
|
+
import { useCallback as useCallback32, useMemo as useMemo4, useState as useState39 } from "react";
|
|
2480
|
+
function useVirtualList(options) {
|
|
2481
|
+
const { itemHeight, itemCount, containerHeight, overscan = 3 } = options;
|
|
2482
|
+
const [scrollTop, setScrollTop] = useState39(0);
|
|
2483
|
+
const totalHeight = itemCount * itemHeight;
|
|
2484
|
+
const onScroll = useCallback32((event) => {
|
|
2485
|
+
setScrollTop(event.currentTarget.scrollTop);
|
|
2486
|
+
}, []);
|
|
2487
|
+
const items = useMemo4(() => {
|
|
2488
|
+
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
|
|
2489
|
+
const endIndex = Math.min(
|
|
2490
|
+
itemCount - 1,
|
|
2491
|
+
Math.floor((scrollTop + containerHeight) / itemHeight) + overscan
|
|
2492
|
+
);
|
|
2493
|
+
const virtualItems = [];
|
|
2494
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
2495
|
+
virtualItems.push({
|
|
2496
|
+
index: i,
|
|
2497
|
+
style: {
|
|
2498
|
+
position: "absolute",
|
|
2499
|
+
top: i * itemHeight,
|
|
2500
|
+
height: itemHeight,
|
|
2501
|
+
width: "100%"
|
|
2502
|
+
}
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
return virtualItems;
|
|
2506
|
+
}, [scrollTop, itemHeight, itemCount, containerHeight, overscan]);
|
|
2507
|
+
return {
|
|
2508
|
+
items,
|
|
2509
|
+
totalHeight,
|
|
2510
|
+
onScroll
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
// src/hooks/useInfiniteScroll/useInfiniteScroll.ts
|
|
2515
|
+
import { useEffect as useEffect41, useRef as useRef27 } from "react";
|
|
2516
|
+
function useInfiniteScroll(onLoadMore, options = {}) {
|
|
2517
|
+
const {
|
|
2518
|
+
threshold = 0,
|
|
2519
|
+
rootMargin = "0px",
|
|
2520
|
+
isLoading = false,
|
|
2521
|
+
hasMore = true,
|
|
2522
|
+
disabled = false
|
|
2523
|
+
} = options;
|
|
2524
|
+
const targetRef = useRef27(null);
|
|
2525
|
+
const onLoadMoreStable = useStableCallback_default(onLoadMore);
|
|
2526
|
+
useEffect41(() => {
|
|
2527
|
+
if (disabled || isLoading || !hasMore || !targetRef.current) return;
|
|
2528
|
+
const observer = new IntersectionObserver(
|
|
2529
|
+
(entries) => {
|
|
2530
|
+
const entry = entries[0];
|
|
2531
|
+
if (entry.isIntersecting) {
|
|
2532
|
+
onLoadMoreStable();
|
|
2533
|
+
}
|
|
2534
|
+
},
|
|
2535
|
+
{ threshold, rootMargin }
|
|
2536
|
+
);
|
|
2537
|
+
observer.observe(targetRef.current);
|
|
2538
|
+
return () => {
|
|
2539
|
+
observer.disconnect();
|
|
2540
|
+
};
|
|
2541
|
+
}, [disabled, isLoading, hasMore, threshold, rootMargin, onLoadMoreStable]);
|
|
2542
|
+
return targetRef;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
// src/hooks/useAnthropic/useAnthropic.ts
|
|
2546
|
+
import { useCallback as useCallback33, useState as useState40 } from "react";
|
|
2547
|
+
function useAnthropic(options = {}) {
|
|
2548
|
+
const {
|
|
2549
|
+
apiKey,
|
|
2550
|
+
model = "claude-3-haiku-20240307",
|
|
2551
|
+
maxTokens = 1024,
|
|
2552
|
+
temperature = 0.7
|
|
2553
|
+
} = options;
|
|
2554
|
+
const [loading, setLoading] = useState40(false);
|
|
2555
|
+
const [response, setResponse] = useState40(null);
|
|
2556
|
+
const [error, setError] = useState40(null);
|
|
2557
|
+
const chat = useCallback33(async (messages) => {
|
|
2558
|
+
setLoading(true);
|
|
2559
|
+
setResponse(null);
|
|
2560
|
+
setError(null);
|
|
2561
|
+
try {
|
|
2562
|
+
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
2563
|
+
method: "POST",
|
|
2564
|
+
headers: {
|
|
2565
|
+
"x-api-key": apiKey || "",
|
|
2566
|
+
"anthropic-version": "2023-06-01",
|
|
2567
|
+
"content-type": "application/json"
|
|
2568
|
+
},
|
|
2569
|
+
body: JSON.stringify({
|
|
2570
|
+
model,
|
|
2571
|
+
messages,
|
|
2572
|
+
max_tokens: maxTokens,
|
|
2573
|
+
temperature
|
|
2574
|
+
})
|
|
2575
|
+
});
|
|
2576
|
+
if (!res.ok) {
|
|
2577
|
+
const err = await res.json();
|
|
2578
|
+
throw new Error(err.error?.message || "Anthropic API Error");
|
|
2579
|
+
}
|
|
2580
|
+
const data = await res.json();
|
|
2581
|
+
const content = data.content?.[0]?.text || "";
|
|
2582
|
+
setResponse(content);
|
|
2583
|
+
return content;
|
|
2584
|
+
} catch (err) {
|
|
2585
|
+
setError(err);
|
|
2586
|
+
throw err;
|
|
2587
|
+
} finally {
|
|
2588
|
+
setLoading(false);
|
|
2589
|
+
}
|
|
2590
|
+
}, [apiKey, model, maxTokens, temperature]);
|
|
2591
|
+
return { chat, response, loading, error };
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
// src/hooks/useEmbeddings/useEmbeddings.ts
|
|
2595
|
+
import { useCallback as useCallback34, useState as useState41 } from "react";
|
|
2596
|
+
function useEmbeddings(fetcher) {
|
|
2597
|
+
const [embeddings, setEmbeddings] = useState41(null);
|
|
2598
|
+
const [loading, setLoading] = useState41(false);
|
|
2599
|
+
const [error, setError] = useState41(null);
|
|
2600
|
+
const generate = useCallback34(async (texts) => {
|
|
2601
|
+
setLoading(true);
|
|
2602
|
+
setError(null);
|
|
2603
|
+
try {
|
|
2604
|
+
const result = await fetcher(texts);
|
|
2605
|
+
setEmbeddings(result);
|
|
2606
|
+
return result;
|
|
2607
|
+
} catch (err) {
|
|
2608
|
+
setError(err);
|
|
2609
|
+
throw err;
|
|
2610
|
+
} finally {
|
|
2611
|
+
setLoading(false);
|
|
2612
|
+
}
|
|
2613
|
+
}, [fetcher]);
|
|
2614
|
+
return { embeddings, loading, error, generate };
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// src/hooks/useGemini/useGemini.ts
|
|
2618
|
+
import { useCallback as useCallback35, useState as useState42 } from "react";
|
|
2619
|
+
function useGemini(options = {}) {
|
|
2620
|
+
const {
|
|
2621
|
+
apiKey,
|
|
2622
|
+
model = "gemini-pro"
|
|
2623
|
+
} = options;
|
|
2624
|
+
const [loading, setLoading] = useState42(false);
|
|
2625
|
+
const [response, setResponse] = useState42(null);
|
|
2626
|
+
const [error, setError] = useState42(null);
|
|
2627
|
+
const chat = useCallback35(async (contents) => {
|
|
2628
|
+
setLoading(true);
|
|
2629
|
+
setResponse(null);
|
|
2630
|
+
setError(null);
|
|
2631
|
+
try {
|
|
2632
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
|
|
2633
|
+
const res = await fetch(url, {
|
|
2634
|
+
method: "POST",
|
|
2635
|
+
headers: {
|
|
2636
|
+
"Content-Type": "application/json"
|
|
2637
|
+
},
|
|
2638
|
+
body: JSON.stringify({ contents })
|
|
2639
|
+
});
|
|
2640
|
+
if (!res.ok) {
|
|
2641
|
+
const err = await res.json();
|
|
2642
|
+
throw new Error(err.error?.message || "Gemini API Error");
|
|
2643
|
+
}
|
|
2644
|
+
const data = await res.json();
|
|
2645
|
+
const text = data.candidates?.[0]?.content?.parts?.[0]?.text || "";
|
|
2646
|
+
setResponse(text);
|
|
2647
|
+
return text;
|
|
2648
|
+
} catch (err) {
|
|
2649
|
+
setError(err);
|
|
2650
|
+
throw err;
|
|
2651
|
+
} finally {
|
|
2652
|
+
setLoading(false);
|
|
2653
|
+
}
|
|
2654
|
+
}, [apiKey, model]);
|
|
2655
|
+
return { chat, response, loading, error };
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
// src/hooks/useLLMStream/useLLMStream.ts
|
|
2659
|
+
import { useCallback as useCallback36, useRef as useRef28, useState as useState43 } from "react";
|
|
2660
|
+
function useLLMStream(options = {}) {
|
|
2661
|
+
const [data, setData] = useState43("");
|
|
2662
|
+
const [loading, setLoading] = useState43(false);
|
|
2663
|
+
const [error, setError] = useState43(null);
|
|
2664
|
+
const abortControllerRef = useRef28(null);
|
|
2665
|
+
const stream = useCallback36(async (url, fetchOptions = {}) => {
|
|
2666
|
+
try {
|
|
2667
|
+
if (abortControllerRef.current) {
|
|
2668
|
+
abortControllerRef.current.abort();
|
|
2669
|
+
}
|
|
2670
|
+
const controller = new AbortController();
|
|
2671
|
+
abortControllerRef.current = controller;
|
|
2672
|
+
setLoading(true);
|
|
2673
|
+
setError(null);
|
|
2674
|
+
setData("");
|
|
2675
|
+
const response = await fetch(url, {
|
|
2676
|
+
...fetchOptions,
|
|
2677
|
+
signal: controller.signal
|
|
2678
|
+
});
|
|
2679
|
+
if (!response.ok) {
|
|
2680
|
+
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
|
|
2681
|
+
}
|
|
2682
|
+
const reader = response.body?.getReader();
|
|
2683
|
+
const decoder = new TextDecoder();
|
|
2684
|
+
if (!reader) {
|
|
2685
|
+
throw new Error("Response body is not readable");
|
|
2686
|
+
}
|
|
2687
|
+
let accumulatedText = "";
|
|
2688
|
+
while (true) {
|
|
2689
|
+
const { done, value } = await reader.read();
|
|
2690
|
+
if (done) break;
|
|
2691
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
2692
|
+
accumulatedText += chunk;
|
|
2693
|
+
setData((prev) => prev + chunk);
|
|
2694
|
+
options.onToken?.(chunk, accumulatedText);
|
|
2695
|
+
}
|
|
2696
|
+
options.onComplete?.(accumulatedText);
|
|
2697
|
+
} catch (err) {
|
|
2698
|
+
if (err.name === "AbortError") return;
|
|
2699
|
+
setError(err);
|
|
2700
|
+
options.onError?.(err);
|
|
2701
|
+
} finally {
|
|
2702
|
+
setLoading(false);
|
|
2703
|
+
abortControllerRef.current = null;
|
|
2704
|
+
}
|
|
2705
|
+
}, [options]);
|
|
2706
|
+
const cancel = useCallback36(() => {
|
|
2707
|
+
if (abortControllerRef.current) {
|
|
2708
|
+
abortControllerRef.current.abort();
|
|
2709
|
+
abortControllerRef.current = null;
|
|
2710
|
+
setLoading(false);
|
|
2711
|
+
}
|
|
2712
|
+
}, []);
|
|
2713
|
+
return {
|
|
2714
|
+
stream,
|
|
2715
|
+
cancel,
|
|
2716
|
+
data,
|
|
2717
|
+
loading,
|
|
2718
|
+
error
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// src/hooks/useOpenAI/useOpenAI.ts
|
|
2723
|
+
import { useCallback as useCallback37, useState as useState44 } from "react";
|
|
2724
|
+
function useOpenAI(options = {}) {
|
|
2725
|
+
const {
|
|
2726
|
+
apiKey,
|
|
2727
|
+
apiBase = "https://api.openai.com/v1",
|
|
2728
|
+
model = "gpt-3.5-turbo",
|
|
2729
|
+
temperature = 0.7,
|
|
2730
|
+
stream = false
|
|
2731
|
+
} = options;
|
|
2732
|
+
const [loading, setLoading] = useState44(false);
|
|
2733
|
+
const [response, setResponse] = useState44(null);
|
|
2734
|
+
const [error, setError] = useState44(null);
|
|
2735
|
+
const chat = useCallback37(async (messages) => {
|
|
2736
|
+
setLoading(true);
|
|
2737
|
+
setResponse(null);
|
|
2738
|
+
setError(null);
|
|
2739
|
+
try {
|
|
2740
|
+
const res = await fetch(`${apiBase}/chat/completions`, {
|
|
2741
|
+
method: "POST",
|
|
2742
|
+
headers: {
|
|
2743
|
+
"Content-Type": "application/json",
|
|
2744
|
+
"Authorization": `Bearer ${apiKey}`
|
|
2745
|
+
},
|
|
2746
|
+
body: JSON.stringify({
|
|
2747
|
+
model,
|
|
2748
|
+
messages,
|
|
2749
|
+
temperature,
|
|
2750
|
+
stream: false
|
|
2751
|
+
// Streaming strictly not supported in this basic version, useLLMStream for that
|
|
2752
|
+
})
|
|
2753
|
+
});
|
|
2754
|
+
if (!res.ok) {
|
|
2755
|
+
const err = await res.json();
|
|
2756
|
+
throw new Error(err.error?.message || "OpenAI API Error");
|
|
2757
|
+
}
|
|
2758
|
+
const data = await res.json();
|
|
2759
|
+
const content = data.choices?.[0]?.message?.content || "";
|
|
2760
|
+
setResponse(content);
|
|
2761
|
+
return content;
|
|
2762
|
+
} catch (err) {
|
|
2763
|
+
setError(err);
|
|
2764
|
+
throw err;
|
|
2765
|
+
} finally {
|
|
2766
|
+
setLoading(false);
|
|
2767
|
+
}
|
|
2768
|
+
}, [apiKey, apiBase, model, temperature]);
|
|
2769
|
+
return {
|
|
2770
|
+
chat,
|
|
2771
|
+
response,
|
|
2772
|
+
loading,
|
|
2773
|
+
error
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
// src/hooks/useRAG/useRAG.ts
|
|
2778
|
+
import { useCallback as useCallback38, useState as useState45 } from "react";
|
|
2779
|
+
function useRAG(options) {
|
|
2780
|
+
const [loading, setLoading] = useState45(false);
|
|
2781
|
+
const [answer, setAnswer] = useState45(null);
|
|
2782
|
+
const [retrievedDocs, setRetrievedDocs] = useState45([]);
|
|
2783
|
+
const [error, setError] = useState45(null);
|
|
2784
|
+
const ask = useCallback38(async (query) => {
|
|
2785
|
+
setLoading(true);
|
|
2786
|
+
setError(null);
|
|
2787
|
+
setAnswer(null);
|
|
2788
|
+
setRetrievedDocs([]);
|
|
2789
|
+
try {
|
|
2790
|
+
const docs = await options.retrieve(query);
|
|
2791
|
+
setRetrievedDocs(docs);
|
|
2792
|
+
const contextPrompt = `
|
|
2793
|
+
Context documents:
|
|
2794
|
+
${docs.map((d) => `- ${d.content}`).join("\n")}
|
|
2795
|
+
|
|
2796
|
+
Query: ${query}
|
|
2797
|
+
`;
|
|
2798
|
+
const result = await options.generate(contextPrompt, docs);
|
|
2799
|
+
setAnswer(result);
|
|
2800
|
+
return result;
|
|
2801
|
+
} catch (err) {
|
|
2802
|
+
setError(err);
|
|
2803
|
+
throw err;
|
|
2804
|
+
} finally {
|
|
2805
|
+
setLoading(false);
|
|
2806
|
+
}
|
|
2807
|
+
}, [options]);
|
|
2808
|
+
return { ask, answer, retrievedDocs, loading, error };
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2811
|
+
// src/hooks/useSearchHighlight/useSearchHighlight.ts
|
|
2812
|
+
import { useMemo as useMemo5 } from "react";
|
|
2813
|
+
function useSearchHighlight(text, query, options = {}) {
|
|
2814
|
+
const { caseSensitive = false } = options;
|
|
2815
|
+
return useMemo5(() => {
|
|
2816
|
+
if (!query) {
|
|
2817
|
+
return [{ text, isMatch: false }];
|
|
2818
|
+
}
|
|
2819
|
+
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2820
|
+
const flags = caseSensitive ? "g" : "gi";
|
|
2821
|
+
const regex = new RegExp(`(${escapedQuery})`, flags);
|
|
2822
|
+
const parts = text.split(regex);
|
|
2823
|
+
return parts.filter((part) => part).map((part) => {
|
|
2824
|
+
const isMatch = caseSensitive ? part === query : part.toLowerCase() === query.toLowerCase();
|
|
2825
|
+
return {
|
|
2826
|
+
text: part,
|
|
2827
|
+
isMatch
|
|
2828
|
+
};
|
|
2829
|
+
});
|
|
2830
|
+
}, [text, query, caseSensitive]);
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
// src/hooks/useSemanticSearch/useSemanticSearch.ts
|
|
2834
|
+
import { useMemo as useMemo6 } from "react";
|
|
2835
|
+
function useSemanticSearch(queryEmbedding, items, getItemEmbedding, topK = 5) {
|
|
2836
|
+
return useMemo6(() => {
|
|
2837
|
+
if (!queryEmbedding || items.length === 0) return [];
|
|
2838
|
+
const results = items.map((item) => {
|
|
2839
|
+
const itemEmbedding = getItemEmbedding(item);
|
|
2840
|
+
const score = cosineSimilarity(queryEmbedding, itemEmbedding);
|
|
2841
|
+
return { item, score };
|
|
2842
|
+
});
|
|
2843
|
+
return results.sort((a, b) => b.score - a.score).slice(0, topK);
|
|
2844
|
+
}, [queryEmbedding, items, getItemEmbedding, topK]);
|
|
2845
|
+
}
|
|
2846
|
+
function cosineSimilarity(a, b) {
|
|
2847
|
+
if (a.length !== b.length) return 0;
|
|
2848
|
+
let dotProduct = 0;
|
|
2849
|
+
let magnitudeA = 0;
|
|
2850
|
+
let magnitudeB = 0;
|
|
2851
|
+
for (let i = 0; i < a.length; i++) {
|
|
2852
|
+
dotProduct += a[i] * b[i];
|
|
2853
|
+
magnitudeA += a[i] * a[i];
|
|
2854
|
+
magnitudeB += b[i] * b[i];
|
|
2855
|
+
}
|
|
2856
|
+
magnitudeA = Math.sqrt(magnitudeA);
|
|
2857
|
+
magnitudeB = Math.sqrt(magnitudeB);
|
|
2858
|
+
if (magnitudeA === 0 || magnitudeB === 0) return 0;
|
|
2859
|
+
return dotProduct / (magnitudeA * magnitudeB);
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
// src/hooks/useSTT/useSTT.ts
|
|
2863
|
+
import { useCallback as useCallback39, useEffect as useEffect42, useRef as useRef29, useState as useState46 } from "react";
|
|
2864
|
+
function useSTT(options = {}) {
|
|
2865
|
+
const [listening, setListening] = useState46(false);
|
|
2866
|
+
const [transcript, setTranscript] = useState46("");
|
|
2867
|
+
const [supported, setSupported] = useState46(false);
|
|
2868
|
+
const recognitionRef = useRef29(null);
|
|
2869
|
+
const optionsRef = useRef29(options);
|
|
2870
|
+
optionsRef.current = options;
|
|
2871
|
+
useEffect42(() => {
|
|
2872
|
+
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
2873
|
+
if (SpeechRecognition) {
|
|
2874
|
+
setSupported(true);
|
|
2875
|
+
recognitionRef.current = new SpeechRecognition();
|
|
2876
|
+
}
|
|
2877
|
+
}, []);
|
|
2878
|
+
const listen = useCallback39(() => {
|
|
2879
|
+
if (!recognitionRef.current) return;
|
|
2880
|
+
const recognition = recognitionRef.current;
|
|
2881
|
+
const opts = optionsRef.current;
|
|
2882
|
+
recognition.continuous = opts.continuous ?? false;
|
|
2883
|
+
recognition.interimResults = opts.interimResults ?? false;
|
|
2884
|
+
recognition.lang = opts.lang || "en-US";
|
|
2885
|
+
recognition.onstart = () => setListening(true);
|
|
2886
|
+
recognition.onresult = (event) => {
|
|
2887
|
+
let finalTranscript = "";
|
|
2888
|
+
for (let i = event.resultIndex; i < event.results.length; ++i) {
|
|
2889
|
+
if (event.results[i].isFinal) {
|
|
2890
|
+
finalTranscript += event.results[i][0].transcript;
|
|
2891
|
+
} else if (opts.interimResults) {
|
|
2892
|
+
finalTranscript += event.results[i][0].transcript;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
setTranscript(finalTranscript);
|
|
2896
|
+
opts.onResult?.(finalTranscript);
|
|
2897
|
+
};
|
|
2898
|
+
recognition.onerror = (event) => {
|
|
2899
|
+
setListening(false);
|
|
2900
|
+
opts.onError?.(event.error);
|
|
2901
|
+
};
|
|
2902
|
+
recognition.onend = () => {
|
|
2903
|
+
setListening(false);
|
|
2904
|
+
opts.onEnd?.();
|
|
2905
|
+
};
|
|
2906
|
+
try {
|
|
2907
|
+
recognition.start();
|
|
2908
|
+
} catch (e) {
|
|
2909
|
+
}
|
|
2910
|
+
}, []);
|
|
2911
|
+
const stop = useCallback39(() => {
|
|
2912
|
+
recognitionRef.current?.stop();
|
|
2913
|
+
setListening(false);
|
|
2914
|
+
}, []);
|
|
2915
|
+
const abort = useCallback39(() => {
|
|
2916
|
+
recognitionRef.current?.abort();
|
|
2917
|
+
setListening(false);
|
|
2918
|
+
}, []);
|
|
2919
|
+
return {
|
|
2920
|
+
listen,
|
|
2921
|
+
stop,
|
|
2922
|
+
abort,
|
|
2923
|
+
listening,
|
|
2924
|
+
transcript,
|
|
2925
|
+
supported
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
// src/hooks/useTTS/useTTS.ts
|
|
2930
|
+
import { useCallback as useCallback40, useEffect as useEffect43, useRef as useRef30, useState as useState47 } from "react";
|
|
2931
|
+
function useTTS(options = {}) {
|
|
2932
|
+
const [speaking, setSpeaking] = useState47(false);
|
|
2933
|
+
const [supported, setSupported] = useState47(false);
|
|
2934
|
+
const [voices, setVoices] = useState47([]);
|
|
2935
|
+
const optionsRef = useRef30(options);
|
|
2936
|
+
optionsRef.current = options;
|
|
2937
|
+
useEffect43(() => {
|
|
2938
|
+
if (typeof window !== "undefined" && "speechSynthesis" in window) {
|
|
2939
|
+
setSupported(true);
|
|
2940
|
+
const updateVoices = () => {
|
|
2941
|
+
setVoices(window.speechSynthesis.getVoices());
|
|
2942
|
+
};
|
|
2943
|
+
updateVoices();
|
|
2944
|
+
window.speechSynthesis.onvoiceschanged = updateVoices;
|
|
2945
|
+
}
|
|
2946
|
+
}, []);
|
|
2947
|
+
const cancel = useCallback40(() => {
|
|
2948
|
+
if (!supported) return;
|
|
2949
|
+
window.speechSynthesis.cancel();
|
|
2950
|
+
setSpeaking(false);
|
|
2951
|
+
}, [supported]);
|
|
2952
|
+
const speak = useCallback40((text, overrideOptions = {}) => {
|
|
2953
|
+
if (!supported) return;
|
|
2954
|
+
cancel();
|
|
2955
|
+
const opts = { ...optionsRef.current, ...overrideOptions };
|
|
2956
|
+
const utterance = new SpeechSynthesisUtterance(text);
|
|
2957
|
+
if (opts.volume !== void 0) utterance.volume = opts.volume;
|
|
2958
|
+
if (opts.rate !== void 0) utterance.rate = opts.rate;
|
|
2959
|
+
if (opts.pitch !== void 0) utterance.pitch = opts.pitch;
|
|
2960
|
+
if (opts.voiceURI) {
|
|
2961
|
+
const voice = voices.find((v) => v.voiceURI === opts.voiceURI);
|
|
2962
|
+
if (voice) utterance.voice = voice;
|
|
2963
|
+
}
|
|
2964
|
+
utterance.onstart = () => {
|
|
2965
|
+
setSpeaking(true);
|
|
2966
|
+
opts.onStart?.();
|
|
2967
|
+
};
|
|
2968
|
+
utterance.onend = () => {
|
|
2969
|
+
setSpeaking(false);
|
|
2970
|
+
opts.onEnd?.();
|
|
2971
|
+
};
|
|
2972
|
+
utterance.onerror = (event) => {
|
|
2973
|
+
setSpeaking(false);
|
|
2974
|
+
opts.onError?.(event);
|
|
2975
|
+
};
|
|
2976
|
+
window.speechSynthesis.speak(utterance);
|
|
2977
|
+
}, [supported, voices, cancel]);
|
|
2978
|
+
useEffect43(() => {
|
|
2979
|
+
return () => {
|
|
2980
|
+
if (supported) {
|
|
2981
|
+
window.speechSynthesis.cancel();
|
|
2982
|
+
}
|
|
2983
|
+
};
|
|
2984
|
+
}, [supported]);
|
|
2985
|
+
return {
|
|
2986
|
+
speak,
|
|
2987
|
+
cancel,
|
|
2988
|
+
speaking,
|
|
2989
|
+
supported,
|
|
2990
|
+
voices
|
|
2991
|
+
};
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
// src/hooks/useAnimate/useAnimate.ts
|
|
2995
|
+
import { useCallback as useCallback41, useEffect as useEffect44, useRef as useRef31 } from "react";
|
|
2996
|
+
function useAnimate(callback, running = true) {
|
|
2997
|
+
const requestRef = useRef31();
|
|
2998
|
+
const previousTimeRef = useRef31();
|
|
2999
|
+
const savedCallback = useStableCallback(callback);
|
|
3000
|
+
const animate = useCallback41((time) => {
|
|
3001
|
+
if (previousTimeRef.current !== void 0) {
|
|
3002
|
+
const deltaTime = time - previousTimeRef.current;
|
|
3003
|
+
savedCallback(time, deltaTime);
|
|
3004
|
+
}
|
|
3005
|
+
previousTimeRef.current = time;
|
|
3006
|
+
requestRef.current = requestAnimationFrame(animate);
|
|
3007
|
+
}, [savedCallback]);
|
|
3008
|
+
useEffect44(() => {
|
|
3009
|
+
if (running) {
|
|
3010
|
+
requestRef.current = requestAnimationFrame(animate);
|
|
3011
|
+
} else {
|
|
3012
|
+
previousTimeRef.current = void 0;
|
|
3013
|
+
if (requestRef.current) {
|
|
3014
|
+
cancelAnimationFrame(requestRef.current);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
return () => {
|
|
3018
|
+
if (requestRef.current) {
|
|
3019
|
+
cancelAnimationFrame(requestRef.current);
|
|
3020
|
+
}
|
|
3021
|
+
};
|
|
3022
|
+
}, [running, animate]);
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
// src/hooks/useVideoRef/useVideoRef.ts
|
|
3026
|
+
import { useCallback as useCallback42, useEffect as useEffect45, useRef as useRef32, useState as useState48 } from "react";
|
|
3027
|
+
function useMediaRef() {
|
|
3028
|
+
const ref = useRef32(null);
|
|
3029
|
+
const [state, setState] = useState48({
|
|
3030
|
+
playing: false,
|
|
3031
|
+
paused: true,
|
|
3032
|
+
muted: false,
|
|
3033
|
+
volume: 1,
|
|
3034
|
+
time: 0,
|
|
3035
|
+
duration: 0,
|
|
3036
|
+
buffered: 0,
|
|
3037
|
+
waiting: false,
|
|
3038
|
+
ended: false
|
|
3039
|
+
});
|
|
3040
|
+
const updateState = useCallback42(() => {
|
|
3041
|
+
const el = ref.current;
|
|
3042
|
+
if (!el) return;
|
|
3043
|
+
setState({
|
|
3044
|
+
playing: !el.paused && !el.ended && el.readyState > 2,
|
|
3045
|
+
paused: el.paused,
|
|
3046
|
+
muted: el.muted,
|
|
3047
|
+
volume: el.volume,
|
|
3048
|
+
time: el.currentTime,
|
|
3049
|
+
duration: el.duration || 0,
|
|
3050
|
+
buffered: el.buffered.length > 0 ? el.buffered.end(el.buffered.length - 1) : 0,
|
|
3051
|
+
waiting: el.readyState < 3,
|
|
3052
|
+
// HAVE_FUTURE_DATA
|
|
3053
|
+
ended: el.ended
|
|
3054
|
+
});
|
|
3055
|
+
}, []);
|
|
3056
|
+
useEffect45(() => {
|
|
3057
|
+
const el = ref.current;
|
|
3058
|
+
if (!el) return;
|
|
3059
|
+
const events = [
|
|
3060
|
+
"loadstart",
|
|
3061
|
+
"loadedmetadata",
|
|
3062
|
+
"canplay",
|
|
3063
|
+
"play",
|
|
3064
|
+
"playing",
|
|
3065
|
+
"pause",
|
|
3066
|
+
"waiting",
|
|
3067
|
+
"seeking",
|
|
3068
|
+
"seeked",
|
|
3069
|
+
"ended",
|
|
3070
|
+
"durationchange",
|
|
3071
|
+
"timeupdate",
|
|
3072
|
+
"progress",
|
|
3073
|
+
"volumechange"
|
|
3074
|
+
];
|
|
3075
|
+
events.forEach((ev) => el.addEventListener(ev, updateState));
|
|
3076
|
+
updateState();
|
|
3077
|
+
return () => {
|
|
3078
|
+
events.forEach((ev) => el.removeEventListener(ev, updateState));
|
|
3079
|
+
};
|
|
3080
|
+
}, [updateState]);
|
|
3081
|
+
const controls = {
|
|
3082
|
+
play: async () => {
|
|
3083
|
+
await ref.current?.play();
|
|
3084
|
+
},
|
|
3085
|
+
pause: () => {
|
|
3086
|
+
ref.current?.pause();
|
|
3087
|
+
},
|
|
3088
|
+
toggle: () => {
|
|
3089
|
+
const el = ref.current;
|
|
3090
|
+
if (el) el.paused ? el.play() : el.pause();
|
|
3091
|
+
},
|
|
3092
|
+
seek: (time) => {
|
|
3093
|
+
if (ref.current) ref.current.currentTime = time;
|
|
3094
|
+
},
|
|
3095
|
+
mute: () => {
|
|
3096
|
+
if (ref.current) ref.current.muted = true;
|
|
3097
|
+
},
|
|
3098
|
+
unmute: () => {
|
|
3099
|
+
if (ref.current) ref.current.muted = false;
|
|
3100
|
+
},
|
|
3101
|
+
setVolume: (val) => {
|
|
3102
|
+
if (ref.current) ref.current.volume = Math.max(0, Math.min(1, val));
|
|
3103
|
+
}
|
|
3104
|
+
};
|
|
3105
|
+
return { ref, controls, state };
|
|
3106
|
+
}
|
|
3107
|
+
function useVideoRef() {
|
|
3108
|
+
return useMediaRef();
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
// src/hooks/useAudioRef/useAudioRef.ts
|
|
3112
|
+
function useAudioRef() {
|
|
3113
|
+
return useMediaRef();
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
// src/hooks/useCanvas/useCanvas.ts
|
|
3117
|
+
import { useEffect as useEffect46, useRef as useRef33 } from "react";
|
|
3118
|
+
function useCanvas(options = {}) {
|
|
3119
|
+
const canvasRef = useRef33(null);
|
|
3120
|
+
const requestRef = useRef33();
|
|
3121
|
+
const frameCountRef = useRef33(0);
|
|
3122
|
+
useEffect46(() => {
|
|
3123
|
+
const canvas = canvasRef.current;
|
|
3124
|
+
if (!canvas) return;
|
|
3125
|
+
const context = canvas.getContext("2d");
|
|
3126
|
+
if (!context) return;
|
|
3127
|
+
const dpr = window.devicePixelRatio || 1;
|
|
3128
|
+
const rect = canvas.getBoundingClientRect();
|
|
3129
|
+
const width = options.width ?? rect.width;
|
|
3130
|
+
const height = options.height ?? rect.height;
|
|
3131
|
+
canvas.width = width * dpr;
|
|
3132
|
+
canvas.height = height * dpr;
|
|
3133
|
+
context.scale(dpr, dpr);
|
|
3134
|
+
canvas.style.width = `${width}px`;
|
|
3135
|
+
canvas.style.height = `${height}px`;
|
|
3136
|
+
const render = () => {
|
|
3137
|
+
frameCountRef.current++;
|
|
3138
|
+
if (options.animate) {
|
|
3139
|
+
options.animate(context, frameCountRef.current);
|
|
3140
|
+
}
|
|
3141
|
+
requestRef.current = requestAnimationFrame(render);
|
|
3142
|
+
};
|
|
3143
|
+
if (options.animate) {
|
|
3144
|
+
render();
|
|
3145
|
+
}
|
|
3146
|
+
return () => {
|
|
3147
|
+
if (requestRef.current) cancelAnimationFrame(requestRef.current);
|
|
3148
|
+
};
|
|
3149
|
+
}, [options.width, options.height, options.animate]);
|
|
3150
|
+
return canvasRef;
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
// src/hooks/useFrameRate/useFrameRate.ts
|
|
3154
|
+
import { useEffect as useEffect47, useRef as useRef34, useState as useState49 } from "react";
|
|
3155
|
+
function useFrameRate(running = true) {
|
|
3156
|
+
const [fps, setFps] = useState49(0);
|
|
3157
|
+
const frameCountRef = useRef34(0);
|
|
3158
|
+
const lastTimeRef = useRef34(performance.now());
|
|
3159
|
+
const requestRef = useRef34();
|
|
3160
|
+
useEffect47(() => {
|
|
3161
|
+
if (!running) return;
|
|
3162
|
+
const loop = (time) => {
|
|
3163
|
+
frameCountRef.current++;
|
|
3164
|
+
if (time - lastTimeRef.current >= 1e3) {
|
|
3165
|
+
setFps(frameCountRef.current);
|
|
3166
|
+
frameCountRef.current = 0;
|
|
3167
|
+
lastTimeRef.current = time;
|
|
3168
|
+
}
|
|
3169
|
+
requestRef.current = requestAnimationFrame(loop);
|
|
3170
|
+
};
|
|
3171
|
+
requestRef.current = requestAnimationFrame(loop);
|
|
3172
|
+
return () => {
|
|
3173
|
+
if (requestRef.current) cancelAnimationFrame(requestRef.current);
|
|
3174
|
+
};
|
|
3175
|
+
}, [running]);
|
|
3176
|
+
return fps;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
// src/hooks/useLottie/useLottie.ts
|
|
3180
|
+
import lottie from "lottie-web";
|
|
3181
|
+
import { useEffect as useEffect48, useRef as useRef35 } from "react";
|
|
3182
|
+
function useLottie(containerRef, options) {
|
|
3183
|
+
const animationRef = useRef35();
|
|
3184
|
+
useEffect48(() => {
|
|
3185
|
+
if (!containerRef.current) return;
|
|
3186
|
+
const anim = lottie.loadAnimation({
|
|
3187
|
+
container: containerRef.current,
|
|
3188
|
+
renderer: "svg",
|
|
3189
|
+
loop: options.loop ?? true,
|
|
3190
|
+
autoplay: options.autoplay ?? true,
|
|
3191
|
+
animationData: options.animationData,
|
|
3192
|
+
path: options.path
|
|
3193
|
+
});
|
|
3194
|
+
animationRef.current = anim;
|
|
3195
|
+
if (options.speed) anim.setSpeed(options.speed);
|
|
3196
|
+
if (options.direction) anim.setDirection(options.direction);
|
|
3197
|
+
return () => {
|
|
3198
|
+
anim.destroy();
|
|
3199
|
+
};
|
|
3200
|
+
}, [options.path, options.animationData]);
|
|
3201
|
+
useEffect48(() => {
|
|
3202
|
+
if (!animationRef.current) return;
|
|
3203
|
+
if (options.speed !== void 0) animationRef.current.setSpeed(options.speed);
|
|
3204
|
+
if (options.direction !== void 0) animationRef.current.setDirection(options.direction);
|
|
3205
|
+
if (options.isPlaying !== void 0) {
|
|
3206
|
+
options.isPlaying ? animationRef.current.play() : animationRef.current.pause();
|
|
3207
|
+
}
|
|
3208
|
+
}, [options.speed, options.direction, options.isPlaying]);
|
|
3209
|
+
return animationRef.current;
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
// src/hooks/useParallax/useParallax.ts
|
|
3213
|
+
import { useEffect as useEffect49, useRef as useRef36, useState as useState50 } from "react";
|
|
3214
|
+
function useParallax(options = {}) {
|
|
3215
|
+
const { speed = 0.5, axis = "y" } = options;
|
|
3216
|
+
const [offset, setOffset] = useState50(0);
|
|
3217
|
+
const ref = useRef36(null);
|
|
3218
|
+
useEffect49(() => {
|
|
3219
|
+
const handleScroll = () => {
|
|
3220
|
+
if (!ref.current) return;
|
|
3221
|
+
const rect = ref.current.getBoundingClientRect();
|
|
3222
|
+
const scrollY = window.scrollY || window.pageYOffset;
|
|
3223
|
+
const scrollX = window.scrollX || window.pageXOffset;
|
|
3224
|
+
const val = axis === "y" ? scrollY : scrollX;
|
|
3225
|
+
setOffset(val * speed);
|
|
3226
|
+
};
|
|
3227
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
3228
|
+
handleScroll();
|
|
3229
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
3230
|
+
}, [speed, axis]);
|
|
3231
|
+
return {
|
|
3232
|
+
ref,
|
|
3233
|
+
style: {
|
|
3234
|
+
transform: axis === "y" ? `translateY(${offset}px)` : `translateX(${offset}px)`,
|
|
3235
|
+
willChange: "transform"
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
// src/hooks/useSpringCore/useSpringCore.ts
|
|
3241
|
+
import { useEffect as useEffect50, useRef as useRef37, useState as useState51 } from "react";
|
|
3242
|
+
var DEFAULT_CONFIG = {
|
|
3243
|
+
stiffness: 170,
|
|
3244
|
+
damping: 26,
|
|
3245
|
+
mass: 1
|
|
7
3246
|
};
|
|
3247
|
+
function useSpringCore(targetValue, options = {}) {
|
|
3248
|
+
const [value, setValue] = useState51(options.initialValue ?? targetValue);
|
|
3249
|
+
const velocityRef = useRef37(0);
|
|
3250
|
+
const valueRef = useRef37(options.initialValue ?? targetValue);
|
|
3251
|
+
const lastTimeRef = useRef37();
|
|
3252
|
+
const rafRef = useRef37();
|
|
3253
|
+
const config = { ...DEFAULT_CONFIG, ...options };
|
|
3254
|
+
useEffect50(() => {
|
|
3255
|
+
const animate = (time) => {
|
|
3256
|
+
if (lastTimeRef.current === void 0) {
|
|
3257
|
+
lastTimeRef.current = time;
|
|
3258
|
+
rafRef.current = requestAnimationFrame(animate);
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
const deltaTime = (time - lastTimeRef.current) / 1e3;
|
|
3262
|
+
lastTimeRef.current = time;
|
|
3263
|
+
const dt = Math.min(deltaTime, 0.1);
|
|
3264
|
+
const force = -config.stiffness * (valueRef.current - targetValue);
|
|
3265
|
+
const dampingForce = -config.damping * velocityRef.current;
|
|
3266
|
+
const acceleration = (force + dampingForce) / config.mass;
|
|
3267
|
+
velocityRef.current += acceleration * dt;
|
|
3268
|
+
valueRef.current += velocityRef.current * dt;
|
|
3269
|
+
setValue(valueRef.current);
|
|
3270
|
+
if (Math.abs(velocityRef.current) < 0.01 && Math.abs(valueRef.current - targetValue) < 0.01) {
|
|
3271
|
+
setValue(targetValue);
|
|
3272
|
+
valueRef.current = targetValue;
|
|
3273
|
+
velocityRef.current = 0;
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
rafRef.current = requestAnimationFrame(animate);
|
|
3277
|
+
};
|
|
3278
|
+
if (valueRef.current !== targetValue) {
|
|
3279
|
+
lastTimeRef.current = void 0;
|
|
3280
|
+
rafRef.current = requestAnimationFrame(animate);
|
|
3281
|
+
}
|
|
3282
|
+
return () => {
|
|
3283
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
3284
|
+
};
|
|
3285
|
+
}, [targetValue, config.stiffness, config.damping, config.mass]);
|
|
3286
|
+
return value;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
// src/hooks/useSVGAnimation/useSVGAnimation.ts
|
|
3290
|
+
import { useEffect as useEffect51, useState as useState52 } from "react";
|
|
3291
|
+
function useSVGAnimation(length, options = {}) {
|
|
3292
|
+
const [offset, setOffset] = useState52(length);
|
|
3293
|
+
const { duration = 1e3, delay = 0, easing = (t) => t } = options;
|
|
3294
|
+
useEffect51(() => {
|
|
3295
|
+
let start = null;
|
|
3296
|
+
let rafId;
|
|
3297
|
+
const animate = (time) => {
|
|
3298
|
+
if (!start) start = time;
|
|
3299
|
+
const elapsed = time - start;
|
|
3300
|
+
const progress = Math.min(Math.max((elapsed - delay) / duration, 0), 1);
|
|
3301
|
+
const easedProgress = easing(progress);
|
|
3302
|
+
setOffset(length * (1 - easedProgress));
|
|
3303
|
+
if (progress < 1) {
|
|
3304
|
+
rafId = requestAnimationFrame(animate);
|
|
3305
|
+
}
|
|
3306
|
+
};
|
|
3307
|
+
rafId = requestAnimationFrame(animate);
|
|
3308
|
+
return () => {
|
|
3309
|
+
cancelAnimationFrame(rafId);
|
|
3310
|
+
};
|
|
3311
|
+
}, [length, duration, delay]);
|
|
3312
|
+
return {
|
|
3313
|
+
strokeDasharray: length,
|
|
3314
|
+
strokeDashoffset: offset
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3318
|
+
// src/hooks/useThreeJS/useThreeJS.ts
|
|
3319
|
+
import { useEffect as useEffect52, useRef as useRef38 } from "react";
|
|
3320
|
+
import * as THREE from "three";
|
|
3321
|
+
function useThreeJS(containerRef, options = {}) {
|
|
3322
|
+
const { init, animate, resize = true } = options;
|
|
3323
|
+
const rendererRef = useRef38();
|
|
3324
|
+
const sceneRef = useRef38();
|
|
3325
|
+
const cameraRef = useRef38();
|
|
3326
|
+
const requestRef = useRef38();
|
|
3327
|
+
useEffect52(() => {
|
|
3328
|
+
if (!containerRef.current) return;
|
|
3329
|
+
const width = containerRef.current.clientWidth;
|
|
3330
|
+
const height = containerRef.current.clientHeight;
|
|
3331
|
+
const scene = new THREE.Scene();
|
|
3332
|
+
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1e3);
|
|
3333
|
+
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
|
|
3334
|
+
renderer.setSize(width, height);
|
|
3335
|
+
containerRef.current.appendChild(renderer.domElement);
|
|
3336
|
+
sceneRef.current = scene;
|
|
3337
|
+
cameraRef.current = camera;
|
|
3338
|
+
rendererRef.current = renderer;
|
|
3339
|
+
if (init) {
|
|
3340
|
+
init(scene, camera, renderer);
|
|
3341
|
+
}
|
|
3342
|
+
const loop = (time) => {
|
|
3343
|
+
if (animate) {
|
|
3344
|
+
animate(time, scene, camera);
|
|
3345
|
+
}
|
|
3346
|
+
renderer.render(scene, camera);
|
|
3347
|
+
requestRef.current = requestAnimationFrame(loop);
|
|
3348
|
+
};
|
|
3349
|
+
loop(0);
|
|
3350
|
+
const handleResize = () => {
|
|
3351
|
+
if (!containerRef.current) return;
|
|
3352
|
+
const w = containerRef.current.clientWidth;
|
|
3353
|
+
const h = containerRef.current.clientHeight;
|
|
3354
|
+
camera.aspect = w / h;
|
|
3355
|
+
camera.updateProjectionMatrix();
|
|
3356
|
+
renderer.setSize(w, h);
|
|
3357
|
+
};
|
|
3358
|
+
if (resize) {
|
|
3359
|
+
window.addEventListener("resize", handleResize);
|
|
3360
|
+
}
|
|
3361
|
+
return () => {
|
|
3362
|
+
if (requestRef.current) cancelAnimationFrame(requestRef.current);
|
|
3363
|
+
if (resize) window.removeEventListener("resize", handleResize);
|
|
3364
|
+
renderer.dispose();
|
|
3365
|
+
if (containerRef.current) {
|
|
3366
|
+
containerRef.current.removeChild(renderer.domElement);
|
|
3367
|
+
}
|
|
3368
|
+
};
|
|
3369
|
+
}, [init, animate, resize]);
|
|
3370
|
+
return { scene: sceneRef.current, camera: cameraRef.current, renderer: rendererRef.current };
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
// src/hooks/useBroadcastChannel/useBroadcastChannel.ts
|
|
3374
|
+
import { useCallback as useCallback43, useEffect as useEffect53, useRef as useRef39, useState as useState53 } from "react";
|
|
3375
|
+
function useBroadcastChannel(channelName) {
|
|
3376
|
+
const [lastMessage, setLastMessage] = useState53(null);
|
|
3377
|
+
const channelRef = useRef39(null);
|
|
3378
|
+
useEffect53(() => {
|
|
3379
|
+
const channel = new BroadcastChannel(channelName);
|
|
3380
|
+
channelRef.current = channel;
|
|
3381
|
+
channel.onmessage = (event) => {
|
|
3382
|
+
setLastMessage(event.data);
|
|
3383
|
+
};
|
|
3384
|
+
return () => {
|
|
3385
|
+
channel.close();
|
|
3386
|
+
};
|
|
3387
|
+
}, [channelName]);
|
|
3388
|
+
const postMessage = useCallback43((message) => {
|
|
3389
|
+
channelRef.current?.postMessage(message);
|
|
3390
|
+
}, []);
|
|
3391
|
+
return { postMessage, lastMessage };
|
|
3392
|
+
}
|
|
3393
|
+
|
|
3394
|
+
// src/hooks/useCalendar/useCalendar.ts
|
|
3395
|
+
import { useMemo as useMemo7, useState as useState54 } from "react";
|
|
3396
|
+
function useCalendar(options = {}) {
|
|
3397
|
+
const [date, setDate] = useState54(options.initialDate || /* @__PURE__ */ new Date());
|
|
3398
|
+
const daysInMonth = useMemo7(() => {
|
|
3399
|
+
const year = date.getFullYear();
|
|
3400
|
+
const month = date.getMonth();
|
|
3401
|
+
return new Date(year, month + 1, 0).getDate();
|
|
3402
|
+
}, [date]);
|
|
3403
|
+
const firstDayOfMonth = useMemo7(() => {
|
|
3404
|
+
const year = date.getFullYear();
|
|
3405
|
+
const month = date.getMonth();
|
|
3406
|
+
return new Date(year, month, 1).getDay();
|
|
3407
|
+
}, [date]);
|
|
3408
|
+
const nextMonth = () => {
|
|
3409
|
+
setDate((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1));
|
|
3410
|
+
};
|
|
3411
|
+
const prevMonth = () => {
|
|
3412
|
+
setDate((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1));
|
|
3413
|
+
};
|
|
3414
|
+
return {
|
|
3415
|
+
date,
|
|
3416
|
+
daysInMonth,
|
|
3417
|
+
firstDayOfMonth,
|
|
3418
|
+
nextMonth,
|
|
3419
|
+
prevMonth,
|
|
3420
|
+
setHeader: setDate
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
// src/hooks/useDragAndDrop/useDragAndDrop.ts
|
|
3425
|
+
import { useCallback as useCallback44, useEffect as useEffect54, useRef as useRef40, useState as useState55 } from "react";
|
|
3426
|
+
function useDragAndDrop(options = {}) {
|
|
3427
|
+
const [isDragging, setIsDragging] = useState55(false);
|
|
3428
|
+
const ref = useRef40(null);
|
|
3429
|
+
const handleDragEnter = useCallback44((e) => {
|
|
3430
|
+
e.preventDefault();
|
|
3431
|
+
e.stopPropagation();
|
|
3432
|
+
setIsDragging(true);
|
|
3433
|
+
}, []);
|
|
3434
|
+
const handleDragLeave = useCallback44((e) => {
|
|
3435
|
+
e.preventDefault();
|
|
3436
|
+
e.stopPropagation();
|
|
3437
|
+
setIsDragging(false);
|
|
3438
|
+
}, []);
|
|
3439
|
+
const handleDragOver = useCallback44((e) => {
|
|
3440
|
+
e.preventDefault();
|
|
3441
|
+
e.stopPropagation();
|
|
3442
|
+
}, []);
|
|
3443
|
+
const handleDrop = useCallback44((e) => {
|
|
3444
|
+
e.preventDefault();
|
|
3445
|
+
e.stopPropagation();
|
|
3446
|
+
setIsDragging(false);
|
|
3447
|
+
if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
|
3448
|
+
const files = Array.from(e.dataTransfer.files);
|
|
3449
|
+
options.onDrop?.(files);
|
|
3450
|
+
e.dataTransfer.clearData();
|
|
3451
|
+
}
|
|
3452
|
+
}, [options]);
|
|
3453
|
+
useEffect54(() => {
|
|
3454
|
+
const el = ref.current;
|
|
3455
|
+
if (!el) return;
|
|
3456
|
+
el.addEventListener("dragenter", handleDragEnter);
|
|
3457
|
+
el.addEventListener("dragleave", handleDragLeave);
|
|
3458
|
+
el.addEventListener("dragover", handleDragOver);
|
|
3459
|
+
el.addEventListener("drop", handleDrop);
|
|
3460
|
+
return () => {
|
|
3461
|
+
el.removeEventListener("dragenter", handleDragEnter);
|
|
3462
|
+
el.removeEventListener("dragleave", handleDragLeave);
|
|
3463
|
+
el.removeEventListener("dragover", handleDragOver);
|
|
3464
|
+
el.removeEventListener("drop", handleDrop);
|
|
3465
|
+
};
|
|
3466
|
+
}, [handleDragEnter, handleDragLeave, handleDragOver, handleDrop]);
|
|
3467
|
+
return { ref, isDragging };
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
// src/hooks/useFileProcessing/useFileProcessing.ts
|
|
3471
|
+
import { useCallback as useCallback45, useState as useState56 } from "react";
|
|
3472
|
+
function useFileProcessing(options = {}) {
|
|
3473
|
+
const [processing, setProcessing] = useState56(false);
|
|
3474
|
+
const [error, setError] = useState56(null);
|
|
3475
|
+
const process = useCallback45(async (file) => {
|
|
3476
|
+
setProcessing(true);
|
|
3477
|
+
setError(null);
|
|
3478
|
+
try {
|
|
3479
|
+
if (options.limit && file.size > options.limit) {
|
|
3480
|
+
throw new Error("File too large");
|
|
3481
|
+
}
|
|
3482
|
+
const text = await file.text();
|
|
3483
|
+
return text;
|
|
3484
|
+
} catch (err) {
|
|
3485
|
+
setError(err.message);
|
|
3486
|
+
throw err;
|
|
3487
|
+
} finally {
|
|
3488
|
+
setProcessing(false);
|
|
3489
|
+
}
|
|
3490
|
+
}, [options.limit]);
|
|
3491
|
+
return { process, processing, error };
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
// src/hooks/useForm/useForm.ts
|
|
3495
|
+
import { useCallback as useCallback46, useState as useState57 } from "react";
|
|
3496
|
+
function useForm(options) {
|
|
3497
|
+
const [values, setValues] = useState57(options.initialValues);
|
|
3498
|
+
const [errors, setErrors] = useState57({});
|
|
3499
|
+
const [isSubmitting, setIsSubmitting] = useState57(false);
|
|
3500
|
+
const [touched, setTouched] = useState57({});
|
|
3501
|
+
const handleChange = useCallback46((e) => {
|
|
3502
|
+
const { name, value, type } = e.target;
|
|
3503
|
+
const val = type === "checkbox" ? e.target.checked : value;
|
|
3504
|
+
setValues((prev) => ({ ...prev, [name]: val }));
|
|
3505
|
+
}, []);
|
|
3506
|
+
const setFieldValue = useCallback46((name, value) => {
|
|
3507
|
+
setValues((prev) => ({ ...prev, [name]: value }));
|
|
3508
|
+
}, []);
|
|
3509
|
+
const handleBlur = useCallback46((e) => {
|
|
3510
|
+
const { name } = e.target;
|
|
3511
|
+
setTouched((prev) => ({ ...prev, [name]: true }));
|
|
3512
|
+
}, []);
|
|
3513
|
+
const handleSubmit = useCallback46(async (e) => {
|
|
3514
|
+
e?.preventDefault();
|
|
3515
|
+
setIsSubmitting(true);
|
|
3516
|
+
setErrors({});
|
|
3517
|
+
if (options.validate) {
|
|
3518
|
+
const validationErrors = await options.validate(values);
|
|
3519
|
+
if (Object.keys(validationErrors).length > 0) {
|
|
3520
|
+
setErrors(validationErrors);
|
|
3521
|
+
setIsSubmitting(false);
|
|
3522
|
+
return;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
try {
|
|
3526
|
+
await options.onSubmit(values);
|
|
3527
|
+
} catch (err) {
|
|
3528
|
+
} finally {
|
|
3529
|
+
setIsSubmitting(false);
|
|
3530
|
+
}
|
|
3531
|
+
}, [values, options]);
|
|
3532
|
+
return {
|
|
3533
|
+
values,
|
|
3534
|
+
errors,
|
|
3535
|
+
touched,
|
|
3536
|
+
isSubmitting,
|
|
3537
|
+
handleChange,
|
|
3538
|
+
handleBlur,
|
|
3539
|
+
handleSubmit,
|
|
3540
|
+
setFieldValue,
|
|
3541
|
+
setValues
|
|
3542
|
+
// expose for resets
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
// src/hooks/useMarkdown/useMarkdown.ts
|
|
3547
|
+
function useMarkdown() {
|
|
3548
|
+
const compile = (markdown) => {
|
|
3549
|
+
let html = markdown.replace(/^# (.*)/gm, "<h1>$1</h1>").replace(/^## (.*)/gm, "<h2>$1</h2>").replace(/\*\*(.*)\*\*/g, "<b>$1</b>");
|
|
3550
|
+
return html;
|
|
3551
|
+
};
|
|
3552
|
+
return { compile };
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
// src/hooks/usePDF/usePDF.ts
|
|
3556
|
+
import { useCallback as useCallback47, useState as useState58 } from "react";
|
|
3557
|
+
function usePDF() {
|
|
3558
|
+
const [generating, setGenerating] = useState58(false);
|
|
3559
|
+
const generate = useCallback47(async (content, filename = "document.pdf") => {
|
|
3560
|
+
setGenerating(true);
|
|
3561
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
3562
|
+
console.log("Generating PDF for:", filename);
|
|
3563
|
+
setGenerating(false);
|
|
3564
|
+
return true;
|
|
3565
|
+
}, []);
|
|
3566
|
+
return { generate, generating };
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
// src/hooks/usePresence/usePresence.ts
|
|
3570
|
+
import { useEffect as useEffect55, useState as useState59 } from "react";
|
|
3571
|
+
function usePresence(heartbeatInterval = 5e3) {
|
|
3572
|
+
const [lastActive, setLastActive] = useState59(Date.now());
|
|
3573
|
+
const [isIdle, setIsIdle] = useState59(false);
|
|
3574
|
+
useEffect55(() => {
|
|
3575
|
+
const handleActivity = () => {
|
|
3576
|
+
setLastActive(Date.now());
|
|
3577
|
+
if (isIdle) setIsIdle(false);
|
|
3578
|
+
};
|
|
3579
|
+
window.addEventListener("mousemove", handleActivity);
|
|
3580
|
+
window.addEventListener("keydown", handleActivity);
|
|
3581
|
+
const interval = setInterval(() => {
|
|
3582
|
+
if (Date.now() - lastActive > heartbeatInterval * 2) {
|
|
3583
|
+
setIsIdle(true);
|
|
3584
|
+
}
|
|
3585
|
+
}, heartbeatInterval);
|
|
3586
|
+
return () => {
|
|
3587
|
+
window.removeEventListener("mousemove", handleActivity);
|
|
3588
|
+
window.removeEventListener("keydown", handleActivity);
|
|
3589
|
+
clearInterval(interval);
|
|
3590
|
+
};
|
|
3591
|
+
}, [heartbeatInterval, lastActive, isIdle]);
|
|
3592
|
+
return { isIdle, lastActive };
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
// src/hooks/useRealtimeCollab/useRealtimeCollab.ts
|
|
3596
|
+
import { useCallback as useCallback48, useState as useState60 } from "react";
|
|
3597
|
+
function useRealtimeCollab(userId) {
|
|
3598
|
+
const [events, setEvents] = useState60([]);
|
|
3599
|
+
const emit = useCallback48((type, payload) => {
|
|
3600
|
+
const event = {
|
|
3601
|
+
type,
|
|
3602
|
+
payload,
|
|
3603
|
+
timestamp: Date.now(),
|
|
3604
|
+
userId
|
|
3605
|
+
};
|
|
3606
|
+
setEvents((prev) => [...prev, event]);
|
|
3607
|
+
}, [userId]);
|
|
3608
|
+
const applyRemote = useCallback48((event) => {
|
|
3609
|
+
setEvents((prev) => [...prev, event]);
|
|
3610
|
+
}, []);
|
|
3611
|
+
return { events, emit, applyRemote };
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
// src/hooks/useShortcuts/useShortcuts.ts
|
|
3615
|
+
import { useEffect as useEffect56 } from "react";
|
|
3616
|
+
function useShortcuts(shortcuts) {
|
|
3617
|
+
useEffect56(() => {
|
|
3618
|
+
const handleKeyDown = (event) => {
|
|
3619
|
+
const parts = [];
|
|
3620
|
+
if (event.ctrlKey) parts.push("Control");
|
|
3621
|
+
if (event.metaKey) parts.push("Meta");
|
|
3622
|
+
if (event.altKey) parts.push("Alt");
|
|
3623
|
+
if (event.shiftKey) parts.push("Shift");
|
|
3624
|
+
if (["Control", "Meta", "Alt", "Shift"].includes(event.key)) {
|
|
3625
|
+
} else {
|
|
3626
|
+
parts.push(event.key);
|
|
3627
|
+
}
|
|
3628
|
+
const combo = parts.join("+");
|
|
3629
|
+
if (shortcuts[combo]) {
|
|
3630
|
+
shortcuts[combo](event);
|
|
3631
|
+
} else if (shortcuts[event.key]) {
|
|
3632
|
+
shortcuts[event.key](event);
|
|
3633
|
+
}
|
|
3634
|
+
};
|
|
3635
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
3636
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
3637
|
+
}, [shortcuts]);
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3640
|
+
// src/hooks/useSortable/useSortable.ts
|
|
3641
|
+
import { useCallback as useCallback49, useState as useState61 } from "react";
|
|
3642
|
+
function useSortable(options) {
|
|
3643
|
+
const [draggedIndex, setDraggedIndex] = useState61(null);
|
|
3644
|
+
const move = useCallback49((fromIndex, toIndex) => {
|
|
3645
|
+
if (fromIndex === toIndex) return;
|
|
3646
|
+
const newItems = [...options.items];
|
|
3647
|
+
const [removed] = newItems.splice(fromIndex, 1);
|
|
3648
|
+
newItems.splice(toIndex, 0, removed);
|
|
3649
|
+
options.onReorder(newItems);
|
|
3650
|
+
}, [options]);
|
|
3651
|
+
return {
|
|
3652
|
+
draggedIndex,
|
|
3653
|
+
setDraggedIndex,
|
|
3654
|
+
move
|
|
3655
|
+
};
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
// src/hooks/useSpreadsheet/useSpreadsheet.ts
|
|
3659
|
+
import { useState as useState62 } from "react";
|
|
3660
|
+
function useSpreadsheet(options) {
|
|
3661
|
+
const { rows, cols, initialData } = options;
|
|
3662
|
+
const createGrid = () => {
|
|
3663
|
+
if (initialData) return initialData;
|
|
3664
|
+
return Array.from({ length: rows }, () => Array(cols).fill(null));
|
|
3665
|
+
};
|
|
3666
|
+
const [data, setData] = useState62(createGrid);
|
|
3667
|
+
const [selectedCell, setSelectedCell] = useState62(null);
|
|
3668
|
+
const setCell = (r, c, value) => {
|
|
3669
|
+
setData((prev) => {
|
|
3670
|
+
const next = [...prev];
|
|
3671
|
+
next[r] = [...next[r]];
|
|
3672
|
+
next[r][c] = value;
|
|
3673
|
+
return next;
|
|
3674
|
+
});
|
|
3675
|
+
};
|
|
3676
|
+
return {
|
|
3677
|
+
data,
|
|
3678
|
+
setCell,
|
|
3679
|
+
selectedCell,
|
|
3680
|
+
setSelectedCell
|
|
3681
|
+
};
|
|
3682
|
+
}
|
|
3683
|
+
|
|
3684
|
+
// src/hooks/useTable/useTable.ts
|
|
3685
|
+
import { useMemo as useMemo8, useState as useState63 } from "react";
|
|
3686
|
+
function useTable(options) {
|
|
3687
|
+
const [sortConfig, setSortConfig] = useState63(
|
|
3688
|
+
options.initialSort || null
|
|
3689
|
+
);
|
|
3690
|
+
const requestSort = (key) => {
|
|
3691
|
+
let direction = "asc";
|
|
3692
|
+
if (sortConfig && sortConfig.key === key && sortConfig.direction === "asc") {
|
|
3693
|
+
direction = "desc";
|
|
3694
|
+
}
|
|
3695
|
+
setSortConfig({ key, direction });
|
|
3696
|
+
};
|
|
3697
|
+
const sortedData = useMemo8(() => {
|
|
3698
|
+
const sortableItems = [...options.data];
|
|
3699
|
+
if (sortConfig !== null) {
|
|
3700
|
+
sortableItems.sort((a, b) => {
|
|
3701
|
+
const valA = a[sortConfig.key];
|
|
3702
|
+
const valB = b[sortConfig.key];
|
|
3703
|
+
if (valA < valB) return sortConfig.direction === "asc" ? -1 : 1;
|
|
3704
|
+
if (valA > valB) return sortConfig.direction === "asc" ? 1 : -1;
|
|
3705
|
+
return 0;
|
|
3706
|
+
});
|
|
3707
|
+
}
|
|
3708
|
+
return sortableItems;
|
|
3709
|
+
}, [options.data, sortConfig]);
|
|
3710
|
+
return {
|
|
3711
|
+
items: sortedData,
|
|
3712
|
+
requestSort,
|
|
3713
|
+
sortConfig,
|
|
3714
|
+
columns: options.columns
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3718
|
+
// src/hooks/useWebRTC/useWebRTC.ts
|
|
3719
|
+
import { useEffect as useEffect57, useRef as useRef41, useState as useState64 } from "react";
|
|
3720
|
+
function useWebRTC(options = {}) {
|
|
3721
|
+
const [connectionState, setConnectionState] = useState64("new");
|
|
3722
|
+
const [signalingState, setSignalingState] = useState64("stable");
|
|
3723
|
+
const pcRef = useRef41(null);
|
|
3724
|
+
useEffect57(() => {
|
|
3725
|
+
const pc = new RTCPeerConnection({
|
|
3726
|
+
iceServers: options.iceServers || [{ urls: "stun:stun.l.google.com:19302" }]
|
|
3727
|
+
});
|
|
3728
|
+
pcRef.current = pc;
|
|
3729
|
+
pc.onconnectionstatechange = () => setConnectionState(pc.connectionState);
|
|
3730
|
+
pc.onsignalingstatechange = () => setSignalingState(pc.signalingState);
|
|
3731
|
+
return () => {
|
|
3732
|
+
pc.close();
|
|
3733
|
+
};
|
|
3734
|
+
}, [options.iceServers]);
|
|
3735
|
+
return {
|
|
3736
|
+
pc: pcRef.current,
|
|
3737
|
+
connectionState,
|
|
3738
|
+
signalingState
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
// src/hooks/useWebSocket/useWebSocket.ts
|
|
3743
|
+
import { useCallback as useCallback50, useEffect as useEffect58, useRef as useRef42, useState as useState65 } from "react";
|
|
3744
|
+
function useWebSocket(url, options = {}) {
|
|
3745
|
+
const {
|
|
3746
|
+
reconnect = true,
|
|
3747
|
+
reconnectAttempts = 5,
|
|
3748
|
+
reconnectInterval = 3e3
|
|
3749
|
+
} = options;
|
|
3750
|
+
const [isConnected, setIsConnected] = useState65(false);
|
|
3751
|
+
const [lastMessage, setLastMessage] = useState65(null);
|
|
3752
|
+
const wsRef = useRef42(null);
|
|
3753
|
+
const reconnectCountRef = useRef42(0);
|
|
3754
|
+
const reconnectTimerRef = useRef42();
|
|
3755
|
+
const connect = useCallback50(() => {
|
|
3756
|
+
if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
|
|
3757
|
+
const ws = new WebSocket(url);
|
|
3758
|
+
wsRef.current = ws;
|
|
3759
|
+
ws.onopen = (e) => {
|
|
3760
|
+
setIsConnected(true);
|
|
3761
|
+
reconnectCountRef.current = 0;
|
|
3762
|
+
options.onOpen?.(e);
|
|
3763
|
+
};
|
|
3764
|
+
ws.onclose = (e) => {
|
|
3765
|
+
setIsConnected(false);
|
|
3766
|
+
options.onClose?.(e);
|
|
3767
|
+
if (reconnect && reconnectCountRef.current < reconnectAttempts) {
|
|
3768
|
+
reconnectTimerRef.current = setTimeout(() => {
|
|
3769
|
+
reconnectCountRef.current++;
|
|
3770
|
+
connect();
|
|
3771
|
+
}, reconnectInterval);
|
|
3772
|
+
}
|
|
3773
|
+
};
|
|
3774
|
+
ws.onmessage = (e) => {
|
|
3775
|
+
setLastMessage(e);
|
|
3776
|
+
options.onMessage?.(e);
|
|
3777
|
+
};
|
|
3778
|
+
ws.onerror = (e) => {
|
|
3779
|
+
options.onError?.(e);
|
|
3780
|
+
};
|
|
3781
|
+
}, [url, reconnect, reconnectAttempts, reconnectInterval, options]);
|
|
3782
|
+
useEffect58(() => {
|
|
3783
|
+
connect();
|
|
3784
|
+
return () => {
|
|
3785
|
+
if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
|
|
3786
|
+
wsRef.current?.close();
|
|
3787
|
+
};
|
|
3788
|
+
}, [connect]);
|
|
3789
|
+
const send = useCallback50((data) => {
|
|
3790
|
+
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
3791
|
+
wsRef.current.send(data);
|
|
3792
|
+
}
|
|
3793
|
+
}, []);
|
|
3794
|
+
return {
|
|
3795
|
+
send,
|
|
3796
|
+
lastMessage,
|
|
3797
|
+
isConnected,
|
|
3798
|
+
ws: wsRef.current
|
|
3799
|
+
};
|
|
3800
|
+
}
|
|
3801
|
+
|
|
3802
|
+
// src/hooks/useClickAnywhere/useClickAnywhere.ts
|
|
3803
|
+
function useClickAnywhere(handler) {
|
|
3804
|
+
useEvent("click", (event) => {
|
|
3805
|
+
handler(event);
|
|
3806
|
+
});
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
// src/hooks/useCountdown/useCountdown.ts
|
|
3810
|
+
import { useCallback as useCallback51, useEffect as useEffect59, useRef as useRef43, useState as useState66 } from "react";
|
|
3811
|
+
function useCountdown({
|
|
3812
|
+
countStart,
|
|
3813
|
+
intervalMs = 1e3,
|
|
3814
|
+
isIncrement = false,
|
|
3815
|
+
countStop = 0
|
|
3816
|
+
}) {
|
|
3817
|
+
const [count, setCount] = useState66(countStart);
|
|
3818
|
+
const [isRunning, setIsRunning] = useState66(false);
|
|
3819
|
+
const intervalRef = useRef43(null);
|
|
3820
|
+
const startCountdown = useCallback51(() => setIsRunning(true), []);
|
|
3821
|
+
const stopCountdown = useCallback51(() => setIsRunning(false), []);
|
|
3822
|
+
const resetCountdown = useCallback51(() => {
|
|
3823
|
+
setIsRunning(false);
|
|
3824
|
+
setCount(countStart);
|
|
3825
|
+
}, [countStart]);
|
|
3826
|
+
useEffect59(() => {
|
|
3827
|
+
if (!isRunning) {
|
|
3828
|
+
if (intervalRef.current) {
|
|
3829
|
+
clearInterval(intervalRef.current);
|
|
3830
|
+
intervalRef.current = null;
|
|
3831
|
+
}
|
|
3832
|
+
return;
|
|
3833
|
+
}
|
|
3834
|
+
intervalRef.current = setInterval(() => {
|
|
3835
|
+
setCount((prev) => {
|
|
3836
|
+
if (isIncrement) {
|
|
3837
|
+
if (prev < countStop) {
|
|
3838
|
+
return prev + 1;
|
|
3839
|
+
} else {
|
|
3840
|
+
setIsRunning(false);
|
|
3841
|
+
return prev;
|
|
3842
|
+
}
|
|
3843
|
+
} else {
|
|
3844
|
+
if (prev > countStop) {
|
|
3845
|
+
return prev - 1;
|
|
3846
|
+
} else {
|
|
3847
|
+
setIsRunning(false);
|
|
3848
|
+
return prev;
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
});
|
|
3852
|
+
}, intervalMs);
|
|
3853
|
+
return () => {
|
|
3854
|
+
if (intervalRef.current) {
|
|
3855
|
+
clearInterval(intervalRef.current);
|
|
3856
|
+
}
|
|
3857
|
+
};
|
|
3858
|
+
}, [isRunning, intervalMs, isIncrement, countStop]);
|
|
3859
|
+
return { count, startCountdown, stopCountdown, resetCountdown };
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
// src/hooks/useDebounceCallback/useDebounceCallback.ts
|
|
3863
|
+
import { useEffect as useEffect60, useMemo as useMemo9, useRef as useRef44 } from "react";
|
|
3864
|
+
function useDebounceCallback(func, wait = 0, options = {}) {
|
|
3865
|
+
const funcRef = useRef44(func);
|
|
3866
|
+
const timeoutRef = useRef44();
|
|
3867
|
+
useEffect60(() => {
|
|
3868
|
+
funcRef.current = func;
|
|
3869
|
+
}, [func]);
|
|
3870
|
+
const debounced = useMemo9(() => {
|
|
3871
|
+
const debouncedFunc = (...args) => {
|
|
3872
|
+
if (timeoutRef.current) {
|
|
3873
|
+
clearTimeout(timeoutRef.current);
|
|
3874
|
+
}
|
|
3875
|
+
timeoutRef.current = setTimeout(() => {
|
|
3876
|
+
funcRef.current(...args);
|
|
3877
|
+
}, wait);
|
|
3878
|
+
};
|
|
3879
|
+
const cancel = () => {
|
|
3880
|
+
if (timeoutRef.current) {
|
|
3881
|
+
clearTimeout(timeoutRef.current);
|
|
3882
|
+
}
|
|
3883
|
+
};
|
|
3884
|
+
const flush = () => {
|
|
3885
|
+
if (timeoutRef.current) {
|
|
3886
|
+
clearTimeout(timeoutRef.current);
|
|
3887
|
+
}
|
|
3888
|
+
};
|
|
3889
|
+
debouncedFunc.cancel = cancel;
|
|
3890
|
+
debouncedFunc.flush = flush;
|
|
3891
|
+
return debouncedFunc;
|
|
3892
|
+
}, [wait]);
|
|
3893
|
+
return debounced;
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3896
|
+
// src/hooks/useIsClient/useIsClient.ts
|
|
3897
|
+
import { useEffect as useEffect61, useState as useState67 } from "react";
|
|
3898
|
+
function useIsClient() {
|
|
3899
|
+
const [isClient, setIsClient] = useState67(false);
|
|
3900
|
+
useEffect61(() => {
|
|
3901
|
+
setIsClient(true);
|
|
3902
|
+
}, []);
|
|
3903
|
+
return isClient;
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
// src/hooks/useList/useList.ts
|
|
3907
|
+
import { useCallback as useCallback52, useState as useState68 } from "react";
|
|
3908
|
+
function useList(initialList = []) {
|
|
3909
|
+
const [list, setList] = useState68(initialList);
|
|
3910
|
+
const actions = {
|
|
3911
|
+
set: useCallback52((l) => setList(l), []),
|
|
3912
|
+
push: useCallback52((element) => setList((l) => [...l, element]), []),
|
|
3913
|
+
updateAt: useCallback52(
|
|
3914
|
+
(index, element) => setList((l) => {
|
|
3915
|
+
const m = [...l];
|
|
3916
|
+
m[index] = element;
|
|
3917
|
+
return m;
|
|
3918
|
+
}),
|
|
3919
|
+
[]
|
|
3920
|
+
),
|
|
3921
|
+
insertAt: useCallback52(
|
|
3922
|
+
(index, element) => setList((l) => {
|
|
3923
|
+
const m = [...l];
|
|
3924
|
+
m.splice(index, 0, element);
|
|
3925
|
+
return m;
|
|
3926
|
+
}),
|
|
3927
|
+
[]
|
|
3928
|
+
),
|
|
3929
|
+
update: useCallback52(
|
|
3930
|
+
(predicate, newElement) => setList((l) => l.map((item) => predicate(item, newElement) ? newElement : item)),
|
|
3931
|
+
[]
|
|
3932
|
+
),
|
|
3933
|
+
removeAt: useCallback52(
|
|
3934
|
+
(index) => setList((l) => {
|
|
3935
|
+
const m = [...l];
|
|
3936
|
+
m.splice(index, 1);
|
|
3937
|
+
return m;
|
|
3938
|
+
}),
|
|
3939
|
+
[]
|
|
3940
|
+
),
|
|
3941
|
+
clear: useCallback52(() => setList([]), []),
|
|
3942
|
+
reset: useCallback52(() => setList(initialList), [initialList])
|
|
3943
|
+
};
|
|
3944
|
+
return [list, actions];
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
// src/hooks/useMap/useMap.ts
|
|
3948
|
+
import { useCallback as useCallback53, useState as useState69 } from "react";
|
|
3949
|
+
function useMap(initialState = /* @__PURE__ */ new Map()) {
|
|
3950
|
+
const [map, setMap] = useState69(new Map(initialState));
|
|
3951
|
+
const actions = {
|
|
3952
|
+
set: useCallback53((key, value) => {
|
|
3953
|
+
setMap((prev) => {
|
|
3954
|
+
const copy = new Map(prev);
|
|
3955
|
+
copy.set(key, value);
|
|
3956
|
+
return copy;
|
|
3957
|
+
});
|
|
3958
|
+
}, []),
|
|
3959
|
+
setAll: useCallback53((entries) => {
|
|
3960
|
+
setMap(new Map(entries));
|
|
3961
|
+
}, []),
|
|
3962
|
+
remove: useCallback53((key) => {
|
|
3963
|
+
setMap((prev) => {
|
|
3964
|
+
const copy = new Map(prev);
|
|
3965
|
+
copy.delete(key);
|
|
3966
|
+
return copy;
|
|
3967
|
+
});
|
|
3968
|
+
}, []),
|
|
3969
|
+
reset: useCallback53(() => {
|
|
3970
|
+
setMap(new Map(initialState));
|
|
3971
|
+
}, [initialState])
|
|
3972
|
+
};
|
|
3973
|
+
return [map, actions];
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
// src/hooks/useQueue/useQueue.ts
|
|
3977
|
+
import { useCallback as useCallback54, useState as useState70 } from "react";
|
|
3978
|
+
function useQueue(initialValue = []) {
|
|
3979
|
+
const [queue, setQueue] = useState70(initialValue);
|
|
3980
|
+
const add = useCallback54((element) => {
|
|
3981
|
+
setQueue((q) => [...q, element]);
|
|
3982
|
+
}, []);
|
|
3983
|
+
const remove = useCallback54(() => {
|
|
3984
|
+
let removedElement;
|
|
3985
|
+
setQueue((q) => {
|
|
3986
|
+
const [first, ...rest] = q;
|
|
3987
|
+
removedElement = first;
|
|
3988
|
+
return rest;
|
|
3989
|
+
});
|
|
3990
|
+
return removedElement;
|
|
3991
|
+
}, []);
|
|
3992
|
+
const clear = useCallback54(() => {
|
|
3993
|
+
setQueue([]);
|
|
3994
|
+
}, []);
|
|
3995
|
+
return {
|
|
3996
|
+
queue,
|
|
3997
|
+
add,
|
|
3998
|
+
remove,
|
|
3999
|
+
clear,
|
|
4000
|
+
first: queue[0],
|
|
4001
|
+
last: queue[queue.length - 1],
|
|
4002
|
+
size: queue.length
|
|
4003
|
+
};
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
// src/hooks/useScreen/useScreen.ts
|
|
4007
|
+
import { useEffect as useEffect62, useState as useState71 } from "react";
|
|
4008
|
+
function useScreen() {
|
|
4009
|
+
const getScreen = () => {
|
|
4010
|
+
if (typeof window !== "undefined" && window.screen) {
|
|
4011
|
+
return window.screen;
|
|
4012
|
+
}
|
|
4013
|
+
return void 0;
|
|
4014
|
+
};
|
|
4015
|
+
const [screen, setScreen] = useState71(getScreen());
|
|
4016
|
+
const handleResize = () => {
|
|
4017
|
+
setScreen(getScreen());
|
|
4018
|
+
};
|
|
4019
|
+
useEvent("resize", handleResize);
|
|
4020
|
+
useEffect62(() => {
|
|
4021
|
+
setScreen(getScreen());
|
|
4022
|
+
}, []);
|
|
4023
|
+
return screen;
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
// src/hooks/useSet/useSet.ts
|
|
4027
|
+
import { useCallback as useCallback55, useState as useState72 } from "react";
|
|
4028
|
+
function useSet(initialSet = /* @__PURE__ */ new Set()) {
|
|
4029
|
+
const [set, setSet] = useState72(initialSet);
|
|
4030
|
+
const actions = {
|
|
4031
|
+
add: useCallback55((key) => {
|
|
4032
|
+
setSet((prev) => {
|
|
4033
|
+
const copy = new Set(prev);
|
|
4034
|
+
copy.add(key);
|
|
4035
|
+
return copy;
|
|
4036
|
+
});
|
|
4037
|
+
}, []),
|
|
4038
|
+
remove: useCallback55((key) => {
|
|
4039
|
+
setSet((prev) => {
|
|
4040
|
+
const copy = new Set(prev);
|
|
4041
|
+
copy.delete(key);
|
|
4042
|
+
return copy;
|
|
4043
|
+
});
|
|
4044
|
+
}, []),
|
|
4045
|
+
toggle: useCallback55((key) => {
|
|
4046
|
+
setSet((prev) => {
|
|
4047
|
+
const copy = new Set(prev);
|
|
4048
|
+
if (copy.has(key)) {
|
|
4049
|
+
copy.delete(key);
|
|
4050
|
+
} else {
|
|
4051
|
+
copy.add(key);
|
|
4052
|
+
}
|
|
4053
|
+
return copy;
|
|
4054
|
+
});
|
|
4055
|
+
}, []),
|
|
4056
|
+
reset: useCallback55(() => {
|
|
4057
|
+
setSet(initialSet);
|
|
4058
|
+
}, [initialSet]),
|
|
4059
|
+
clear: useCallback55(() => {
|
|
4060
|
+
setSet(/* @__PURE__ */ new Set());
|
|
4061
|
+
}, [])
|
|
4062
|
+
};
|
|
4063
|
+
return [set, actions];
|
|
4064
|
+
}
|
|
8
4065
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
4066
|
+
isBrowser,
|
|
4067
|
+
isServer,
|
|
4068
|
+
noop,
|
|
4069
|
+
useAnimate,
|
|
4070
|
+
useAnthropic,
|
|
4071
|
+
useAsync,
|
|
4072
|
+
useAudioRef,
|
|
4073
|
+
useBattery,
|
|
4074
|
+
useBluetooth,
|
|
4075
|
+
useBroadcastChannel,
|
|
4076
|
+
useCalendar,
|
|
4077
|
+
useCanvas,
|
|
4078
|
+
useClickAnywhere,
|
|
4079
|
+
useClickOutside,
|
|
4080
|
+
useCopyToClipboard,
|
|
4081
|
+
useCountdown,
|
|
4082
|
+
useCounter,
|
|
4083
|
+
useDebounce,
|
|
4084
|
+
useDebounceCallback,
|
|
4085
|
+
useDocumentTitle,
|
|
4086
|
+
useDragAndDrop,
|
|
4087
|
+
useEmbeddings,
|
|
4088
|
+
useEvent,
|
|
4089
|
+
useEyeDropper,
|
|
4090
|
+
useFetch,
|
|
4091
|
+
useFileProcessing,
|
|
4092
|
+
useFileSystem,
|
|
4093
|
+
useForm,
|
|
4094
|
+
useFrameRate,
|
|
4095
|
+
useFullscreen,
|
|
4096
|
+
useGamepad,
|
|
4097
|
+
useGemini,
|
|
4098
|
+
useHistory,
|
|
4099
|
+
useHover,
|
|
4100
|
+
useIndexedDB,
|
|
4101
|
+
useInfiniteScroll,
|
|
4102
|
+
useIntersection,
|
|
4103
|
+
useInterval,
|
|
4104
|
+
useIsClient,
|
|
4105
|
+
useIsMounted,
|
|
4106
|
+
useIsomorphicLayoutEffect,
|
|
4107
|
+
useKeyPress,
|
|
4108
|
+
useLLMStream,
|
|
4109
|
+
useList,
|
|
4110
|
+
useLocalStorage,
|
|
4111
|
+
useLockBodyScroll,
|
|
4112
|
+
useLongPress,
|
|
4113
|
+
useLottie,
|
|
4114
|
+
useMachine,
|
|
4115
|
+
useMap,
|
|
4116
|
+
useMarkdown,
|
|
4117
|
+
useMediaDevices,
|
|
4118
|
+
useMediaQuery,
|
|
4119
|
+
useMediaRecorder,
|
|
4120
|
+
useMount,
|
|
4121
|
+
useMutationObserver,
|
|
4122
|
+
useNetworkState,
|
|
4123
|
+
useOnline,
|
|
4124
|
+
useOpenAI,
|
|
4125
|
+
usePDF,
|
|
4126
|
+
usePageLeave,
|
|
4127
|
+
usePagination,
|
|
4128
|
+
useParallax,
|
|
4129
|
+
usePermissions,
|
|
4130
|
+
usePresence,
|
|
4131
|
+
usePrevious,
|
|
4132
|
+
useQueue,
|
|
4133
|
+
useRAG,
|
|
4134
|
+
useRealtimeCollab,
|
|
4135
|
+
useResizeObserver,
|
|
4136
|
+
useSTT,
|
|
4137
|
+
useSVGAnimation,
|
|
4138
|
+
useScreen,
|
|
4139
|
+
useScript,
|
|
4140
|
+
useScroll,
|
|
4141
|
+
useSearchHighlight,
|
|
4142
|
+
useSemanticSearch,
|
|
4143
|
+
useSessionStorage,
|
|
4144
|
+
useSet,
|
|
4145
|
+
useShare,
|
|
4146
|
+
useShortcuts,
|
|
4147
|
+
useSortable,
|
|
4148
|
+
useSpreadsheet,
|
|
4149
|
+
useSpringCore,
|
|
4150
|
+
useStableCallback,
|
|
4151
|
+
useStep,
|
|
4152
|
+
useStorageEstimate,
|
|
4153
|
+
useTTS,
|
|
4154
|
+
useTable,
|
|
4155
|
+
useTheme,
|
|
4156
|
+
useThreeJS,
|
|
4157
|
+
useThrottle,
|
|
4158
|
+
useTimeout,
|
|
4159
|
+
useToggle,
|
|
4160
|
+
useUnmount,
|
|
4161
|
+
useUpdateEffect,
|
|
4162
|
+
useVideoRef,
|
|
4163
|
+
useVirtualList,
|
|
4164
|
+
useWakeLock,
|
|
4165
|
+
useWebRTC,
|
|
4166
|
+
useWebSocket,
|
|
4167
|
+
useWindowFocus,
|
|
4168
|
+
useWindowSize,
|
|
4169
|
+
useWorker
|
|
11
4170
|
};
|
|
4171
|
+
/**
|
|
4172
|
+
* Hookery - A collection of high-quality React hooks
|
|
4173
|
+
* @version 0.0.1
|
|
4174
|
+
* @license MIT
|
|
4175
|
+
*/
|
|
4176
|
+
//# sourceMappingURL=index.mjs.map
|