not-a-spinner 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,551 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ NotASpinner: () => NotASpinner,
34
+ cn: () => cn,
35
+ createAnthropicFetcher: () => createAnthropicFetcher,
36
+ createOpenAIFetcher: () => createOpenAIFetcher,
37
+ getPhrasesForConfig: () => getPhrasesForConfig,
38
+ notASpinnerVariants: () => notASpinnerVariants,
39
+ phrases: () => phrases,
40
+ shufflePhrases: () => shufflePhrases,
41
+ useTextRotation: () => useTextRotation
42
+ });
43
+ module.exports = __toCommonJS(src_exports);
44
+
45
+ // src/utils.ts
46
+ var import_clsx = require("clsx");
47
+ var import_tailwind_merge = require("tailwind-merge");
48
+ function cn(...inputs) {
49
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
50
+ }
51
+
52
+ // src/not-a-spinner.tsx
53
+ var React = __toESM(require("react"), 1);
54
+ var import_class_variance_authority = require("class-variance-authority");
55
+
56
+ // src/use-text-rotation.ts
57
+ var import_react = require("react");
58
+
59
+ // src/phrases.ts
60
+ var phrases = {
61
+ en: [
62
+ "Contemplating the meaning of null",
63
+ "Asking the rubber duck",
64
+ "Reticulating splines",
65
+ "Consulting the ancient scrolls",
66
+ "Negotiating with the API gods",
67
+ "Untangling spaghetti code",
68
+ "Feeding the hamsters that power the server",
69
+ "Converting caffeine to code",
70
+ "Aligning the cosmic bits",
71
+ "Performing mass speculation",
72
+ "Warming up the neural pathways",
73
+ "Bribing the cache fairy",
74
+ "Dividing by almost zero",
75
+ "Searching for the missing semicolon",
76
+ "Teaching electrons to think",
77
+ "Compiling excuses",
78
+ "Reversing entropy locally",
79
+ "Summoning the async wizard",
80
+ "Polishing the pixels",
81
+ "Calculating the meaning of life",
82
+ "Downloading more RAM",
83
+ "Defragmenting the thought process",
84
+ "Poking the language model",
85
+ "Waiting for the stars to align",
86
+ "Generating plausible nonsense",
87
+ "Consulting the magic 8-ball API",
88
+ "Herding digital cats",
89
+ "Overthinking at machine speed",
90
+ "Pretending to be busy",
91
+ "Running on vibes and vectors"
92
+ ],
93
+ zh: [
94
+ "\u8BA9AI\u518D\u60F3\u60F3",
95
+ "\u6B63\u5728\u6478\u9C7C\u5F0F\u601D\u8003",
96
+ "\u6B63\u5728\u7FFB\u9605\u7956\u4F20\u4EE3\u7801",
97
+ "\u6B63\u5728\u548Cbug\u8C08\u5224",
98
+ "\u75AF\u72C2\u6697\u793A\u670D\u52A1\u5668\u52A0\u901F",
99
+ "\u6B63\u5728\u8585\u7F8A\u6BDB\u5F0F\u8BA1\u7B97",
100
+ "\u5047\u88C5\u5F88\u5FD9\u7684\u6837\u5B50",
101
+ "\u6B63\u5728\u91CF\u5B50\u7EA0\u7F20\u4E2D",
102
+ "\u8BA9\u5B50\u5F39\u98DE\u4E00\u4F1A\u513F",
103
+ "\u6B63\u5728\u52A0\u8F7D\u4EBA\u751F\u7684\u610F\u4E49",
104
+ "\u6B63\u5728\u6478\u7D22\u5B87\u5B99\u7684\u89C4\u5F8B",
105
+ "\u8DDF\u7F13\u5B58\u7CBE\u7075\u8BA8\u4EF7\u8FD8\u4EF7",
106
+ "\u6B63\u5728\u70BC\u4E39",
107
+ "\u8BF7\u6C42\u795E\u7ECF\u7F51\u7EDC\u5F00\u4E2A\u540E\u95E8",
108
+ "\u6B63\u5728\u6D88\u5316\u4F60\u7684\u8BF7\u6C42",
109
+ "\u6316\u77FF\u4E2D\u8BF7\u7A0D\u5019",
110
+ "\u6B63\u5728\u7FFB\u8BD1\u673A\u5668\u8BED\u8A00",
111
+ "\u5927\u8111\u6B63\u5728\u8D85\u9891\u8FD0\u884C",
112
+ "\u6B63\u5728\u627E\u7075\u611F",
113
+ "\u5411\u670D\u52A1\u5668\u53D1\u9001\u8D3F\u8D42",
114
+ "\u6B63\u5728\u8FDB\u884C\u610F\u5FF5\u52A0\u901F",
115
+ "\u8BA9\u4EE3\u7801\u5148\u8DD1\u4E00\u4F1A\u513F",
116
+ "\u6B63\u5728\u65BD\u5C55\u6570\u636E\u9B54\u6CD5",
117
+ "\u5077\u5077\u67E5\u770B\u7B54\u6848\u4E2D",
118
+ "\u6B63\u5728\u89E3\u5F00\u859B\u5B9A\u8C14\u7684Bug",
119
+ "\u62FC\u547D\u601D\u8003\u4E0D\u8981\u50AC",
120
+ "\u6B63\u5728\u548CAI\u540C\u4E8B\u5F00\u4F1A",
121
+ "\u7A7F\u8D8A\u6570\u636E\u7684\u661F\u8FB0\u5927\u6D77",
122
+ "\u6B63\u5728\u8FDB\u884C\u7075\u9B42\u62F7\u95EE",
123
+ "\u7B49\u7B49\u9A6C\u4E0A\u5C31\u597D"
124
+ ],
125
+ ja: [
126
+ "AI\u304C\u60A9\u3093\u3067\u3044\u307E\u3059",
127
+ "\u30B9\u30D1\u30B2\u30C3\u30C6\u30A3\u30B3\u30FC\u30C9\u3092\u89E3\u8AAD\u4E2D",
128
+ "\u30B4\u30E0\u306E\u30A2\u30D2\u30EB\u306B\u76F8\u8AC7\u4E2D",
129
+ "\u30B5\u30FC\u30D0\u30FC\u306E\u30CF\u30E0\u30B9\u30BF\u30FC\u3092\u8D77\u52D5\u4E2D",
130
+ "\u30AB\u30D5\u30A7\u30A4\u30F3\u3092\u30B3\u30FC\u30C9\u306B\u5909\u63DB\u4E2D",
131
+ "\u5B87\u5B99\u306E\u30D3\u30C3\u30C8\u3092\u6574\u5217\u4E2D",
132
+ "\u30BB\u30DF\u30B3\u30ED\u30F3\u3092\u63A2\u7D22\u4E2D",
133
+ "\u96FB\u5B50\u306B\u8003\u3048\u308B\u3053\u3068\u3092\u6559\u80B2\u4E2D",
134
+ "\u8A00\u3044\u8A33\u3092\u30B3\u30F3\u30D1\u30A4\u30EB\u4E2D",
135
+ "\u975E\u540C\u671F\u306E\u9B54\u6CD5\u4F7F\u3044\u3092\u53EC\u559A\u4E2D",
136
+ "\u30D4\u30AF\u30BB\u30EB\u3092\u78E8\u304D\u4E2D",
137
+ "\u4EBA\u751F\u306E\u610F\u5473\u3092\u8A08\u7B97\u4E2D",
138
+ "RAM\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u4E2D",
139
+ "\u601D\u8003\u30D7\u30ED\u30BB\u30B9\u3092\u30C7\u30D5\u30E9\u30B0\u4E2D",
140
+ "\u30DE\u30B7\u30F3\u901F\u5EA6\u3067\u8003\u3048\u3059\u304E\u4E2D",
141
+ "\u91CF\u5B50\u306E\u3082\u3064\u308C\u3092\u89E3\u6D88\u4E2D",
142
+ "API\u306E\u795E\u69D8\u3068\u4EA4\u6E09\u4E2D",
143
+ "\u30C7\u30B8\u30BF\u30EB\u732B\u3092\u8FFD\u3044\u304B\u3051\u4E2D",
144
+ "\u30CB\u30E5\u30FC\u30E9\u30EB\u30D1\u30B9\u3092\u6696\u3081\u4E2D",
145
+ "\u30C7\u30FC\u30BF\u306E\u6D77\u3092\u6CF3\u3044\u3067\u3044\u307E\u3059",
146
+ "\u30D0\u30B0\u3068\u548C\u89E3\u4EA4\u6E09\u4E2D",
147
+ "\u5FD9\u3057\u3044\u3075\u308A\u3092\u3057\u3066\u3044\u307E\u3059",
148
+ "\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u304C\u7791\u60F3\u4E2D",
149
+ "\u9B54\u6CD5\u306E8\u30DC\u30FC\u30EB\u306B\u805E\u3044\u3066\u307E\u3059",
150
+ "\u3082\u3046\u3061\u3087\u3063\u3068\u5F85\u3063\u3066\u306D",
151
+ "\u5168\u529B\u3067\u51E6\u7406\u3057\u3066\u3044\u307E\u3059",
152
+ "\u7B54\u3048\u306E\u8FD1\u304F\u306B\u3044\u307E\u3059",
153
+ "\u5929\u624D\u7684\u306A\u3072\u3089\u3081\u304D\u3092\u5F85\u6A5F\u4E2D",
154
+ "\u30B3\u30FC\u30D2\u30FC\u30D6\u30EC\u30A4\u30AF\u4E2D\u3067\u3059",
155
+ "\u9811\u5F35\u3063\u3066\u8003\u3048\u4E2D"
156
+ ],
157
+ es: [
158
+ "Consultando al or\xE1culo digital",
159
+ "Pensando intensamente",
160
+ "Negociando con los dioses del API",
161
+ "Desenredando c\xF3digo espagueti",
162
+ "Alimentando los h\xE1msters del servidor",
163
+ "Convirtiendo cafe\xEDna en c\xF3digo",
164
+ "Alineando los bits c\xF3smicos",
165
+ "Buscando el punto y coma perdido",
166
+ "Ense\xF1ando a pensar a los electrones",
167
+ "Compilando excusas creativas",
168
+ "Invocando al mago as\xEDncrono",
169
+ "Puliendo los p\xEDxeles",
170
+ "Calculando el sentido de la vida",
171
+ "Descargando m\xE1s RAM",
172
+ "Preguntando al pato de goma",
173
+ "Pastoreando gatos digitales",
174
+ "Sobrepensando a velocidad m\xE1quina",
175
+ "Fingiendo estar ocupado",
176
+ "Sobornando al hada del cach\xE9",
177
+ "Dividiendo por casi cero",
178
+ "Generando tonter\xEDas plausibles",
179
+ "Calentando las rutas neuronales",
180
+ "Esperando que se alineen las estrellas",
181
+ "Haciendo magia con los datos",
182
+ "Meditando algor\xEDtmicamente",
183
+ "Revolviendo la sopa cu\xE1ntica",
184
+ "Susurrando al servidor",
185
+ "Recalibrando la realidad",
186
+ "Casi casi ya casi",
187
+ "Un momento de genialidad en curso"
188
+ ],
189
+ fr: [
190
+ "Consultation de l'oracle num\xE9rique",
191
+ "R\xE9flexion intense en cours",
192
+ "N\xE9gociation avec les dieux de l'API",
193
+ "D\xE9m\xEAlage de code spaghetti",
194
+ "Conversion de caf\xE9ine en code",
195
+ "Alignement des bits cosmiques",
196
+ "Recherche du point-virgule perdu",
197
+ "Compilation d'excuses cr\xE9atives",
198
+ "Invocation du sorcier asynchrone",
199
+ "Polissage des pixels",
200
+ "Calcul du sens de la vie",
201
+ "T\xE9l\xE9chargement de RAM suppl\xE9mentaire",
202
+ "Consultation du canard en caoutchouc",
203
+ "Rassemblement de chats num\xE9riques",
204
+ "Surr\xE9flexion \xE0 vitesse machine",
205
+ "Simulation d'activit\xE9 intense",
206
+ "Corruption de la f\xE9e du cache",
207
+ "R\xE9chauffement des voies neuronales",
208
+ "G\xE9n\xE9ration de b\xEAtises plausibles",
209
+ "M\xE9ditation algorithmique",
210
+ "Persuasion quantique en cours",
211
+ "Interrogation de la boule magique",
212
+ "D\xE9fragmentation de la pens\xE9e",
213
+ "Dressage des \xE9lectrons pensants",
214
+ "Attente de l'alignement stellaire",
215
+ "Magie des donn\xE9es en cours",
216
+ "Murmures au serveur",
217
+ "Recalibrage de la r\xE9alit\xE9",
218
+ "Presque presque bient\xF4t",
219
+ "Un \xE9clair de g\xE9nie arrive"
220
+ ],
221
+ de: [
222
+ "Algorithmus gr\xFCbelt nach",
223
+ "Daten werden meditiert",
224
+ "Verhandlung mit den API-G\xF6ttern",
225
+ "Spaghetti-Code wird entwirrt",
226
+ "Server-Hamster werden gef\xFCttert",
227
+ "Koffein wird in Code umgewandelt",
228
+ "Kosmische Bits werden ausgerichtet",
229
+ "Das fehlende Semikolon wird gesucht",
230
+ "Elektronen lernen das Denken",
231
+ "Ausreden werden kompiliert",
232
+ "Der asynchrone Zauberer wird beschworen",
233
+ "Pixel werden poliert",
234
+ "Der Sinn des Lebens wird berechnet",
235
+ "Mehr RAM wird heruntergeladen",
236
+ "Die Gummiente wird befragt",
237
+ "Digitale Katzen werden geh\xFCtet",
238
+ "Maschinengeschwindigkeit-\xDCberdenken",
239
+ "Besch\xE4ftigt-Sein wird simuliert",
240
+ "Die Cache-Fee wird bestochen",
241
+ "Beinahe durch Null geteilt",
242
+ "Plausibeler Unsinn wird generiert",
243
+ "Neuronale Pfade werden aufgew\xE4rmt",
244
+ "Quantensuppe wird umger\xFChrt",
245
+ "Sternenausrichtung wird abgewartet",
246
+ "Datenmagie wird ausgef\xFChrt",
247
+ "Dem Server wird zugefl\xFCstert",
248
+ "Realit\xE4t wird neu kalibriert",
249
+ "Gleich gleich fast fertig",
250
+ "Ein Geistesblitz ist unterwegs",
251
+ "Intensives Nachdenken l\xE4uft"
252
+ ],
253
+ ko: [
254
+ "AI\uAC00 \uACE0\uBBFC \uC911",
255
+ "\uB370\uC774\uD130\uB97C \uBA85\uC0C1 \uC911",
256
+ "\uACE0\uBB34 \uC624\uB9AC\uC5D0\uAC8C \uC0C1\uB2F4 \uC911",
257
+ "\uC11C\uBC84 \uD584\uC2A4\uD130\uC5D0\uAC8C \uBC25 \uC8FC\uB294 \uC911",
258
+ "\uCE74\uD398\uC778\uC744 \uCF54\uB4DC\uB85C \uBCC0\uD658 \uC911",
259
+ "\uC6B0\uC8FC \uBE44\uD2B8\uB97C \uC815\uB82C \uC911",
260
+ "\uC783\uC5B4\uBC84\uB9B0 \uC138\uBBF8\uCF5C\uB860 \uCC3E\uB294 \uC911",
261
+ "\uC804\uC790\uC5D0\uAC8C \uC0DD\uAC01\uD558\uB294 \uBC95 \uAC00\uB974\uCE58\uB294 \uC911",
262
+ "\uBCC0\uBA85\uC744 \uCEF4\uD30C\uC77C \uC911",
263
+ "\uBE44\uB3D9\uAE30 \uB9C8\uBC95\uC0AC \uC18C\uD658 \uC911",
264
+ "\uD53D\uC140\uC744 \uB2E6\uB294 \uC911",
265
+ "\uC778\uC0DD\uC758 \uC758\uBBF8 \uACC4\uC0B0 \uC911",
266
+ "RAM \uB2E4\uC6B4\uB85C\uB4DC \uC911",
267
+ "\uB514\uC9C0\uD138 \uACE0\uC591\uC774 \uBAB0\uC774 \uC911",
268
+ "\uBA38\uC2E0 \uC18D\uB3C4\uB85C \uACFC\uB3C4\uD558\uAC8C \uC0DD\uAC01 \uC911",
269
+ "\uBC14\uC05C \uCC99\uD558\uB294 \uC911",
270
+ "\uCE90\uC2DC \uC694\uC815\uC5D0\uAC8C \uB1CC\uBB3C \uC8FC\uB294 \uC911",
271
+ "\uAC70\uC758 0\uC73C\uB85C \uB098\uB204\uB294 \uC911",
272
+ "\uADF8\uB7F4\uB4EF\uD55C \uD5DB\uC18C\uB9AC \uC0DD\uC131 \uC911",
273
+ "\uC2E0\uACBD \uACBD\uB85C \uC6CC\uBC0D\uC5C5 \uC911",
274
+ "\uC591\uC790 \uC218\uD504 \uC813\uB294 \uC911",
275
+ "\uBCC4\uC774 \uC815\uB82C\uB418\uAE38 \uAE30\uB2E4\uB9AC\uB294 \uC911",
276
+ "\uB370\uC774\uD130 \uB9C8\uBC95 \uC2DC\uC804 \uC911",
277
+ "\uC11C\uBC84\uC5D0\uAC8C \uC18D\uC0AD\uC774\uB294 \uC911",
278
+ "\uD604\uC2E4 \uC7AC\uBCF4\uC815 \uC911",
279
+ "\uAC70\uC758 \uAC70\uC758 \uB2E4 \uB410\uC5B4\uC694",
280
+ "\uCC9C\uC7AC\uC801 \uC601\uAC10 \uB300\uAE30 \uC911",
281
+ "\uC2A4\uD30C\uAC8C\uD2F0 \uCF54\uB4DC \uD574\uB3C5 \uC911",
282
+ "API \uC2E0\uACFC \uD611\uC0C1 \uC911",
283
+ "\uC5F4\uC2EC\uD788 \uC0DD\uAC01\uD558\uB294 \uC911"
284
+ ]
285
+ };
286
+ function shufflePhrases(input) {
287
+ const arr = [...input];
288
+ for (let i = arr.length - 1; i > 0; i--) {
289
+ const j = Math.floor(Math.random() * (i + 1));
290
+ [arr[i], arr[j]] = [arr[j], arr[i]];
291
+ }
292
+ return arr;
293
+ }
294
+ function getPhrasesForConfig(messages, locale) {
295
+ if (messages && messages.length > 0) return messages;
296
+ if (locale && phrases[locale]) return phrases[locale];
297
+ return phrases.en;
298
+ }
299
+
300
+ // src/use-text-rotation.ts
301
+ function useTextRotation({
302
+ phrases: inputPhrases,
303
+ interval = 3e3,
304
+ animationDuration = 400
305
+ }) {
306
+ const [currentIndex, setCurrentIndex] = (0, import_react.useState)(0);
307
+ const [isExiting, setIsExiting] = (0, import_react.useState)(false);
308
+ const [isEntering, setIsEntering] = (0, import_react.useState)(false);
309
+ const poolRef = (0, import_react.useRef)(inputPhrases);
310
+ const initializedRef = (0, import_react.useRef)(false);
311
+ const timerRef = (0, import_react.useRef)(null);
312
+ const mountedRef = (0, import_react.useRef)(true);
313
+ (0, import_react.useEffect)(() => {
314
+ if (!initializedRef.current) {
315
+ poolRef.current = shufflePhrases(inputPhrases);
316
+ initializedRef.current = true;
317
+ setCurrentIndex(0);
318
+ }
319
+ }, []);
320
+ (0, import_react.useEffect)(() => {
321
+ poolRef.current = shufflePhrases(inputPhrases);
322
+ setCurrentIndex(0);
323
+ }, [inputPhrases]);
324
+ const appendPhrases = (0, import_react.useCallback)((newPhrases) => {
325
+ const current = poolRef.current;
326
+ const combined = [...current, ...newPhrases];
327
+ if (combined.length > 20) {
328
+ poolRef.current = combined.slice(combined.length - 20);
329
+ } else {
330
+ poolRef.current = combined;
331
+ }
332
+ }, []);
333
+ (0, import_react.useEffect)(() => {
334
+ mountedRef.current = true;
335
+ if (poolRef.current.length <= 1) return;
336
+ timerRef.current = setInterval(() => {
337
+ if (!mountedRef.current) return;
338
+ setIsExiting(true);
339
+ setTimeout(() => {
340
+ if (!mountedRef.current) return;
341
+ setIsExiting(false);
342
+ setCurrentIndex((prev) => {
343
+ const next = prev + 1;
344
+ if (next >= poolRef.current.length) {
345
+ poolRef.current = shufflePhrases(poolRef.current);
346
+ return 0;
347
+ }
348
+ return next;
349
+ });
350
+ setIsEntering(true);
351
+ setTimeout(() => {
352
+ if (!mountedRef.current) return;
353
+ setIsEntering(false);
354
+ }, animationDuration);
355
+ }, animationDuration);
356
+ }, interval);
357
+ return () => {
358
+ mountedRef.current = false;
359
+ if (timerRef.current) {
360
+ clearInterval(timerRef.current);
361
+ timerRef.current = null;
362
+ }
363
+ };
364
+ }, [interval, animationDuration]);
365
+ const pool = poolRef.current;
366
+ const phrase = pool.length > 0 ? pool[currentIndex % pool.length] : "";
367
+ return {
368
+ currentPhrase: phrase,
369
+ isExiting,
370
+ isEntering,
371
+ appendPhrases
372
+ };
373
+ }
374
+
375
+ // src/not-a-spinner.tsx
376
+ var import_jsx_runtime = require("react/jsx-runtime");
377
+ var notASpinnerVariants = (0, import_class_variance_authority.cva)("nas-base", {
378
+ variants: {
379
+ size: {
380
+ sm: "nas-sm",
381
+ default: "nas-default",
382
+ lg: "nas-lg"
383
+ },
384
+ animation: {
385
+ fade: "nas-animation-fade",
386
+ typewriter: "nas-animation-typewriter",
387
+ slide: "nas-animation-slide",
388
+ blur: "nas-animation-blur"
389
+ }
390
+ },
391
+ defaultVariants: {
392
+ size: "default",
393
+ animation: "fade"
394
+ }
395
+ });
396
+ var NotASpinner = React.forwardRef(
397
+ ({
398
+ className,
399
+ size,
400
+ animation = "fade",
401
+ messages,
402
+ locale,
403
+ fetchPhrase,
404
+ interval = 3e3,
405
+ dots = true,
406
+ ...props
407
+ }, ref) => {
408
+ const resolvedPhrases = React.useMemo(
409
+ () => getPhrasesForConfig(messages, locale),
410
+ [messages, locale]
411
+ );
412
+ const { currentPhrase, isExiting, isEntering, appendPhrases } = useTextRotation({
413
+ phrases: resolvedPhrases,
414
+ interval
415
+ });
416
+ React.useEffect(() => {
417
+ if (!fetchPhrase) return;
418
+ let cancelled = false;
419
+ const fetchInterval = setInterval(async () => {
420
+ if (cancelled) return;
421
+ try {
422
+ const phrase = await fetchPhrase();
423
+ if (!cancelled && phrase) {
424
+ appendPhrases([phrase]);
425
+ }
426
+ } catch {
427
+ }
428
+ }, interval * 2);
429
+ return () => {
430
+ cancelled = true;
431
+ clearInterval(fetchInterval);
432
+ };
433
+ }, [fetchPhrase, interval, appendPhrases]);
434
+ const getAnimationClass = () => {
435
+ if (animation === "typewriter") return "";
436
+ const prefix = animation === "blur" ? "nas-blur" : animation === "slide" ? "nas-slide" : "nas-fade";
437
+ if (isExiting) return `${prefix}-exit`;
438
+ if (isEntering) return `${prefix}-enter`;
439
+ return "";
440
+ };
441
+ if (animation === "typewriter") {
442
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
443
+ TypewriterRenderer,
444
+ {
445
+ ref,
446
+ className: cn(notASpinnerVariants({ size, animation }), className),
447
+ currentPhrase,
448
+ interval,
449
+ dots,
450
+ ...props
451
+ }
452
+ );
453
+ }
454
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
455
+ "div",
456
+ {
457
+ ref,
458
+ className: cn(notASpinnerVariants({ size, animation }), className),
459
+ ...props,
460
+ children: [
461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "nas-text-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: getAnimationClass(), children: currentPhrase }) }),
462
+ dots && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "nas-dots", children: "..." })
463
+ ]
464
+ }
465
+ );
466
+ }
467
+ );
468
+ NotASpinner.displayName = "NotASpinner";
469
+ var TypewriterRenderer = React.forwardRef(({ className, currentPhrase, interval, dots, ...props }, ref) => {
470
+ const [displayedChars, setDisplayedChars] = React.useState(0);
471
+ const [phase, setPhase] = React.useState(
472
+ "typing"
473
+ );
474
+ const phraseRef = React.useRef(currentPhrase);
475
+ React.useEffect(() => {
476
+ phraseRef.current = currentPhrase;
477
+ setDisplayedChars(0);
478
+ setPhase("typing");
479
+ }, [currentPhrase]);
480
+ React.useEffect(() => {
481
+ const totalChars = phraseRef.current.length;
482
+ if (totalChars === 0) return;
483
+ const typingSpeed = 50;
484
+ const erasingSpeed = 30;
485
+ const typingDuration = totalChars * typingSpeed;
486
+ const erasingDuration = totalChars * erasingSpeed;
487
+ const holdDuration = Math.max(interval - typingDuration - erasingDuration, 500);
488
+ let timer;
489
+ if (phase === "typing") {
490
+ if (displayedChars < totalChars) {
491
+ timer = setTimeout(() => setDisplayedChars((c) => c + 1), typingSpeed);
492
+ } else {
493
+ timer = setTimeout(() => setPhase("holding"), 0);
494
+ }
495
+ } else if (phase === "holding") {
496
+ timer = setTimeout(() => setPhase("erasing"), holdDuration);
497
+ } else if (phase === "erasing") {
498
+ if (displayedChars > 0) {
499
+ timer = setTimeout(() => setDisplayedChars((c) => c - 1), erasingSpeed);
500
+ }
501
+ }
502
+ return () => clearTimeout(timer);
503
+ }, [phase, displayedChars, interval]);
504
+ const displayedText = phraseRef.current.slice(0, displayedChars);
505
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref, className, ...props, children: [
506
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "nas-text-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
507
+ displayedText,
508
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "nas-cursor" })
509
+ ] }) }),
510
+ dots && displayedChars > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "nas-dots", children: "..." })
511
+ ] });
512
+ });
513
+ TypewriterRenderer.displayName = "TypewriterRenderer";
514
+
515
+ // src/providers.ts
516
+ function createAnthropicFetcher(endpoint, locale = "en") {
517
+ return async () => {
518
+ const res = await fetch(endpoint, {
519
+ method: "POST",
520
+ headers: { "Content-Type": "application/json" },
521
+ body: JSON.stringify({ provider: "anthropic", locale })
522
+ });
523
+ if (!res.ok) throw new Error(`Failed to fetch phrase: ${res.status}`);
524
+ const data = await res.json();
525
+ return data.phrase;
526
+ };
527
+ }
528
+ function createOpenAIFetcher(endpoint, locale = "en") {
529
+ return async () => {
530
+ const res = await fetch(endpoint, {
531
+ method: "POST",
532
+ headers: { "Content-Type": "application/json" },
533
+ body: JSON.stringify({ provider: "openai", locale })
534
+ });
535
+ if (!res.ok) throw new Error(`Failed to fetch phrase: ${res.status}`);
536
+ const data = await res.json();
537
+ return data.phrase;
538
+ };
539
+ }
540
+ // Annotate the CommonJS export names for ESM import in node:
541
+ 0 && (module.exports = {
542
+ NotASpinner,
543
+ cn,
544
+ createAnthropicFetcher,
545
+ createOpenAIFetcher,
546
+ getPhrasesForConfig,
547
+ notASpinnerVariants,
548
+ phrases,
549
+ shufflePhrases,
550
+ useTextRotation
551
+ });
@@ -0,0 +1,39 @@
1
+ import { ClassValue } from 'clsx';
2
+ import * as class_variance_authority_types from 'class-variance-authority/types';
3
+ import * as React from 'react';
4
+ import { VariantProps } from 'class-variance-authority';
5
+ import { L as Locale } from './phrases-Dvrk5dm5.cjs';
6
+ export { g as getPhrasesForConfig, p as phrases, s as shufflePhrases } from './phrases-Dvrk5dm5.cjs';
7
+
8
+ declare function cn(...inputs: ClassValue[]): string;
9
+
10
+ declare const notASpinnerVariants: (props?: ({
11
+ size?: "sm" | "default" | "lg" | null | undefined;
12
+ animation?: "fade" | "typewriter" | "slide" | "blur" | null | undefined;
13
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
14
+ interface NotASpinnerProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof notASpinnerVariants> {
15
+ messages?: string[];
16
+ locale?: Locale;
17
+ fetchPhrase?: () => Promise<string>;
18
+ interval?: number;
19
+ dots?: boolean;
20
+ }
21
+ declare const NotASpinner: React.ForwardRefExoticComponent<NotASpinnerProps & React.RefAttributes<HTMLDivElement>>;
22
+
23
+ interface UseTextRotationOptions {
24
+ phrases: string[];
25
+ interval?: number;
26
+ animationDuration?: number;
27
+ }
28
+ interface UseTextRotationReturn {
29
+ currentPhrase: string;
30
+ isExiting: boolean;
31
+ isEntering: boolean;
32
+ appendPhrases: (newPhrases: string[]) => void;
33
+ }
34
+ declare function useTextRotation({ phrases: inputPhrases, interval, animationDuration, }: UseTextRotationOptions): UseTextRotationReturn;
35
+
36
+ declare function createAnthropicFetcher(endpoint: string, locale?: string): () => Promise<string>;
37
+ declare function createOpenAIFetcher(endpoint: string, locale?: string): () => Promise<string>;
38
+
39
+ export { Locale, NotASpinner, type NotASpinnerProps, cn, createAnthropicFetcher, createOpenAIFetcher, notASpinnerVariants, useTextRotation };
@@ -0,0 +1,39 @@
1
+ import { ClassValue } from 'clsx';
2
+ import * as class_variance_authority_types from 'class-variance-authority/types';
3
+ import * as React from 'react';
4
+ import { VariantProps } from 'class-variance-authority';
5
+ import { L as Locale } from './phrases-Dvrk5dm5.js';
6
+ export { g as getPhrasesForConfig, p as phrases, s as shufflePhrases } from './phrases-Dvrk5dm5.js';
7
+
8
+ declare function cn(...inputs: ClassValue[]): string;
9
+
10
+ declare const notASpinnerVariants: (props?: ({
11
+ size?: "sm" | "default" | "lg" | null | undefined;
12
+ animation?: "fade" | "typewriter" | "slide" | "blur" | null | undefined;
13
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
14
+ interface NotASpinnerProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof notASpinnerVariants> {
15
+ messages?: string[];
16
+ locale?: Locale;
17
+ fetchPhrase?: () => Promise<string>;
18
+ interval?: number;
19
+ dots?: boolean;
20
+ }
21
+ declare const NotASpinner: React.ForwardRefExoticComponent<NotASpinnerProps & React.RefAttributes<HTMLDivElement>>;
22
+
23
+ interface UseTextRotationOptions {
24
+ phrases: string[];
25
+ interval?: number;
26
+ animationDuration?: number;
27
+ }
28
+ interface UseTextRotationReturn {
29
+ currentPhrase: string;
30
+ isExiting: boolean;
31
+ isEntering: boolean;
32
+ appendPhrases: (newPhrases: string[]) => void;
33
+ }
34
+ declare function useTextRotation({ phrases: inputPhrases, interval, animationDuration, }: UseTextRotationOptions): UseTextRotationReturn;
35
+
36
+ declare function createAnthropicFetcher(endpoint: string, locale?: string): () => Promise<string>;
37
+ declare function createOpenAIFetcher(endpoint: string, locale?: string): () => Promise<string>;
38
+
39
+ export { Locale, NotASpinner, type NotASpinnerProps, cn, createAnthropicFetcher, createOpenAIFetcher, notASpinnerVariants, useTextRotation };