@roy-ui/ui 0.0.11 → 0.0.13

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.
@@ -0,0 +1,448 @@
1
+ .royui-upload {
2
+ /* Dark by default — opt into light with theme="light", or theme="auto"
3
+ to follow the OS. Every surface is a variable. */
4
+ --royui-upload-bg: #1a1a1c;
5
+ --royui-upload-row: rgba(255, 255, 255, 0.03);
6
+ --royui-upload-row-line: rgba(255, 255, 255, 0.07);
7
+ --royui-upload-fg: #f5f5f7;
8
+ --royui-upload-muted: rgba(255, 255, 255, 0.52);
9
+ --royui-upload-faint: rgba(255, 255, 255, 0.38);
10
+ --royui-upload-line: rgba(255, 255, 255, 0.12);
11
+ --royui-upload-dash: rgba(255, 255, 255, 0.16);
12
+ --royui-upload-drop-bg: rgba(255, 255, 255, 0.015);
13
+ --royui-upload-tile: #202023;
14
+ --royui-upload-track: rgba(255, 255, 255, 0.14);
15
+ --royui-upload-hover: rgba(255, 255, 255, 0.06);
16
+
17
+ --royui-upload-accent: #ff7a45;
18
+ --royui-upload-success: #4ade80;
19
+ --royui-upload-danger: #f87171;
20
+
21
+ --royui-upload-radius: 18px;
22
+ --royui-upload-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
23
+
24
+ box-sizing: border-box;
25
+ width: 100%;
26
+ max-width: 520px;
27
+ display: flex;
28
+ flex-direction: column;
29
+ padding: 18px;
30
+ background: var(--royui-upload-bg);
31
+ color: var(--royui-upload-fg);
32
+ border-radius: var(--royui-upload-radius);
33
+ font-family: inherit;
34
+ box-shadow:
35
+ 0 0 0 1px var(--royui-upload-line),
36
+ 0 24px 60px -24px rgba(0, 0, 0, 0.6);
37
+ }
38
+ .royui-upload *,
39
+ .royui-upload *::before,
40
+ .royui-upload *::after {
41
+ box-sizing: border-box;
42
+ }
43
+
44
+ /* ── Header ─────────────────────────────────────────────────────────────── */
45
+ .royui-upload__header {
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: space-between;
49
+ padding: 2px 4px 16px;
50
+ }
51
+ .royui-upload__title {
52
+ margin: 0;
53
+ font-size: 16px;
54
+ font-weight: 550;
55
+ letter-spacing: -0.01em;
56
+ color: var(--royui-upload-fg);
57
+ }
58
+ .royui-upload__close {
59
+ display: inline-flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ width: 28px;
63
+ height: 28px;
64
+ padding: 0;
65
+ border: none;
66
+ border-radius: 8px;
67
+ background: transparent;
68
+ color: var(--royui-upload-muted);
69
+ cursor: pointer;
70
+ transition:
71
+ background 160ms var(--royui-upload-ease),
72
+ color 160ms var(--royui-upload-ease);
73
+ }
74
+ .royui-upload__close:hover {
75
+ background: var(--royui-upload-hover);
76
+ color: var(--royui-upload-fg);
77
+ }
78
+
79
+ /* ── Dropzone ───────────────────────────────────────────────────────────── */
80
+ .royui-upload__dropzone {
81
+ display: flex;
82
+ flex-direction: column;
83
+ align-items: center;
84
+ text-align: center;
85
+ gap: 14px;
86
+ padding: 40px 24px;
87
+ border: 1.5px dashed var(--royui-upload-dash);
88
+ border-radius: 14px;
89
+ background: var(--royui-upload-drop-bg);
90
+ transition:
91
+ border-color 180ms var(--royui-upload-ease),
92
+ background 180ms var(--royui-upload-ease);
93
+ }
94
+ .royui-upload__dropzone.is-dragging {
95
+ border-color: var(--royui-upload-accent);
96
+ background: color-mix(in srgb, var(--royui-upload-accent) 8%, transparent);
97
+ }
98
+ .royui-upload__drop-icon {
99
+ display: inline-flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ width: 48px;
103
+ height: 48px;
104
+ border-radius: 12px;
105
+ background: var(--royui-upload-tile);
106
+ box-shadow: inset 0 0 0 1px var(--royui-upload-line);
107
+ color: var(--royui-upload-accent);
108
+ }
109
+ .royui-upload__drop-text {
110
+ margin: 0;
111
+ font-size: 14.5px;
112
+ color: var(--royui-upload-fg);
113
+ }
114
+ .royui-upload__browse {
115
+ padding: 0;
116
+ border: none;
117
+ background: none;
118
+ font: inherit;
119
+ color: var(--royui-upload-accent);
120
+ cursor: pointer;
121
+ text-decoration: underline;
122
+ text-underline-offset: 2px;
123
+ text-decoration-thickness: 1px;
124
+ transition: opacity 140ms var(--royui-upload-ease);
125
+ }
126
+ .royui-upload__browse:hover {
127
+ opacity: 0.82;
128
+ }
129
+ .royui-upload__drop-hint {
130
+ margin: 0;
131
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
132
+ font-size: 11px;
133
+ letter-spacing: 0.08em;
134
+ text-transform: uppercase;
135
+ color: var(--royui-upload-faint);
136
+ }
137
+ .royui-upload__input {
138
+ display: none;
139
+ }
140
+
141
+ /* ── File list ──────────────────────────────────────────────────────────── */
142
+ .royui-upload__list {
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: 10px;
146
+ margin-top: 12px;
147
+ }
148
+ .royui-upload__row {
149
+ display: flex;
150
+ align-items: flex-start;
151
+ gap: 14px;
152
+ padding: 14px;
153
+ border-radius: 12px;
154
+ background: var(--royui-upload-row);
155
+ box-shadow: inset 0 0 0 1px var(--royui-upload-row-line);
156
+ animation: royui-upload-row-in 280ms var(--royui-upload-ease) both;
157
+ }
158
+ @keyframes royui-upload-row-in {
159
+ from {
160
+ opacity: 0;
161
+ transform: translateY(6px);
162
+ }
163
+ to {
164
+ opacity: 1;
165
+ transform: none;
166
+ }
167
+ }
168
+
169
+ .royui-upload__badge {
170
+ flex: none;
171
+ display: inline-flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ width: 40px;
175
+ height: 40px;
176
+ border-radius: 11px;
177
+ background: var(--royui-upload-badge, #8b8b94);
178
+ background-image: linear-gradient(
179
+ 150deg,
180
+ rgba(255, 255, 255, 0.28),
181
+ rgba(255, 255, 255, 0) 55%
182
+ );
183
+ box-shadow:
184
+ inset 0 1px 0 rgba(255, 255, 255, 0.22),
185
+ 0 2px 6px -2px rgba(0, 0, 0, 0.45);
186
+ }
187
+ .royui-upload__badge-label {
188
+ font-size: 9px;
189
+ font-weight: 700;
190
+ letter-spacing: 0.04em;
191
+ color: #fff;
192
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.28);
193
+ }
194
+
195
+ .royui-upload__row-main {
196
+ flex: 1;
197
+ min-width: 0;
198
+ }
199
+ .royui-upload__row-top {
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: space-between;
203
+ gap: 12px;
204
+ }
205
+ .royui-upload__name {
206
+ font-size: 14px;
207
+ font-weight: 500;
208
+ color: var(--royui-upload-fg);
209
+ white-space: nowrap;
210
+ overflow: hidden;
211
+ text-overflow: ellipsis;
212
+ }
213
+ .royui-upload__row-action {
214
+ flex: none;
215
+ display: inline-flex;
216
+ align-items: center;
217
+ justify-content: center;
218
+ width: 26px;
219
+ height: 26px;
220
+ padding: 0;
221
+ border: none;
222
+ border-radius: 7px;
223
+ background: transparent;
224
+ color: var(--royui-upload-faint);
225
+ cursor: pointer;
226
+ transition:
227
+ background 140ms var(--royui-upload-ease),
228
+ color 140ms var(--royui-upload-ease);
229
+ }
230
+ .royui-upload__row-action:hover {
231
+ background: var(--royui-upload-hover);
232
+ color: var(--royui-upload-fg);
233
+ }
234
+
235
+ /* ── Meta line ──────────────────────────────────────────────────────────── */
236
+ .royui-upload__meta {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: 7px;
240
+ margin-top: 5px;
241
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
242
+ font-size: 11px;
243
+ letter-spacing: 0.04em;
244
+ text-transform: uppercase;
245
+ color: var(--royui-upload-muted);
246
+ }
247
+ .royui-upload__meta-sep {
248
+ color: var(--royui-upload-faint);
249
+ }
250
+ .royui-upload__complete {
251
+ display: inline-flex;
252
+ align-items: center;
253
+ gap: 5px;
254
+ color: var(--royui-upload-success);
255
+ }
256
+ .royui-upload__error {
257
+ color: var(--royui-upload-danger);
258
+ }
259
+ .royui-upload__status {
260
+ color: var(--royui-upload-muted);
261
+ }
262
+
263
+ /* Claude-Code-style shimmer sweep across the status word. */
264
+ .royui-upload__shimmer {
265
+ background: linear-gradient(
266
+ 90deg,
267
+ var(--royui-upload-faint) 0%,
268
+ var(--royui-upload-fg) 18%,
269
+ var(--royui-upload-faint) 40%
270
+ );
271
+ background-size: 220% 100%;
272
+ -webkit-background-clip: text;
273
+ background-clip: text;
274
+ -webkit-text-fill-color: transparent;
275
+ color: transparent;
276
+ animation: royui-upload-shimmer 1.6s linear infinite;
277
+ }
278
+ @keyframes royui-upload-shimmer {
279
+ to {
280
+ background-position: -220% 0;
281
+ }
282
+ }
283
+
284
+ /* ── Progress bar (segmented "ticks") ───────────────────────────────────── */
285
+ .royui-upload__progress {
286
+ display: flex;
287
+ align-items: center;
288
+ gap: 12px;
289
+ margin-top: 11px;
290
+ }
291
+ .royui-upload__bar {
292
+ position: relative;
293
+ flex: 1;
294
+ height: 14px;
295
+ border-radius: 2px;
296
+ overflow: hidden;
297
+ background-image: repeating-linear-gradient(
298
+ 90deg,
299
+ var(--royui-upload-track) 0 2px,
300
+ transparent 2px 5px
301
+ );
302
+ }
303
+ .royui-upload__bar-fill {
304
+ position: absolute;
305
+ inset: 0 auto 0 0;
306
+ height: 100%;
307
+ background-image: repeating-linear-gradient(
308
+ 90deg,
309
+ var(--royui-upload-accent) 0 2px,
310
+ transparent 2px 5px
311
+ );
312
+ transition: width 260ms var(--royui-upload-ease);
313
+ }
314
+ .royui-upload__pct {
315
+ flex: none;
316
+ min-width: 34px;
317
+ text-align: right;
318
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
319
+ font-size: 12px;
320
+ font-variant-numeric: tabular-nums;
321
+ color: var(--royui-upload-muted);
322
+ }
323
+
324
+ /* ── Footer ─────────────────────────────────────────────────────────────── */
325
+ .royui-upload__footer {
326
+ display: flex;
327
+ align-items: center;
328
+ gap: 14px;
329
+ margin-top: 16px;
330
+ padding: 14px 4px 2px;
331
+ border-top: 1px solid var(--royui-upload-line);
332
+ }
333
+ .royui-upload__icon-btn {
334
+ display: inline-flex;
335
+ align-items: center;
336
+ justify-content: center;
337
+ width: 26px;
338
+ height: 26px;
339
+ padding: 0;
340
+ border: none;
341
+ border-radius: 7px;
342
+ background: transparent;
343
+ color: var(--royui-upload-faint);
344
+ cursor: pointer;
345
+ transition:
346
+ background 140ms var(--royui-upload-ease),
347
+ color 140ms var(--royui-upload-ease);
348
+ }
349
+ .royui-upload__icon-btn:hover {
350
+ background: var(--royui-upload-hover);
351
+ color: var(--royui-upload-fg);
352
+ }
353
+ .royui-upload__remove-all {
354
+ display: inline-flex;
355
+ align-items: center;
356
+ gap: 7px;
357
+ padding: 0;
358
+ border: none;
359
+ background: none;
360
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
361
+ font-size: 11px;
362
+ letter-spacing: 0.07em;
363
+ text-transform: uppercase;
364
+ color: var(--royui-upload-muted);
365
+ cursor: pointer;
366
+ transition: color 140ms var(--royui-upload-ease);
367
+ }
368
+ .royui-upload__remove-all:hover {
369
+ color: var(--royui-upload-fg);
370
+ }
371
+ .royui-upload__footer-spacer {
372
+ flex: 1;
373
+ }
374
+
375
+ /* ── Light theme ────────────────────────────────────────────────────────── */
376
+ .royui-upload--light {
377
+ --royui-upload-bg: #ffffff;
378
+ --royui-upload-row: #ffffff;
379
+ --royui-upload-row-line: rgba(0, 0, 0, 0.08);
380
+ --royui-upload-fg: #1a1a1c;
381
+ --royui-upload-muted: rgba(0, 0, 0, 0.55);
382
+ --royui-upload-faint: rgba(0, 0, 0, 0.4);
383
+ --royui-upload-line: rgba(0, 0, 0, 0.09);
384
+ --royui-upload-dash: rgba(0, 0, 0, 0.16);
385
+ --royui-upload-drop-bg: rgba(0, 0, 0, 0.012);
386
+ --royui-upload-tile: #ffffff;
387
+ --royui-upload-track: rgba(0, 0, 0, 0.12);
388
+ --royui-upload-hover: rgba(0, 0, 0, 0.05);
389
+ box-shadow:
390
+ 0 0 0 1px rgba(0, 0, 0, 0.07),
391
+ 0 24px 60px -28px rgba(0, 0, 0, 0.3);
392
+ }
393
+ .royui-upload--light .royui-upload__row {
394
+ box-shadow:
395
+ inset 0 0 0 1px var(--royui-upload-row-line),
396
+ 0 1px 2px rgba(0, 0, 0, 0.04);
397
+ }
398
+ .royui-upload--light .royui-upload__drop-icon {
399
+ box-shadow:
400
+ inset 0 0 0 1px var(--royui-upload-line),
401
+ 0 1px 2px rgba(0, 0, 0, 0.05);
402
+ }
403
+
404
+ @media (prefers-color-scheme: light) {
405
+ .royui-upload--auto {
406
+ --royui-upload-bg: #ffffff;
407
+ --royui-upload-row: #ffffff;
408
+ --royui-upload-row-line: rgba(0, 0, 0, 0.08);
409
+ --royui-upload-fg: #1a1a1c;
410
+ --royui-upload-muted: rgba(0, 0, 0, 0.55);
411
+ --royui-upload-faint: rgba(0, 0, 0, 0.4);
412
+ --royui-upload-line: rgba(0, 0, 0, 0.09);
413
+ --royui-upload-dash: rgba(0, 0, 0, 0.16);
414
+ --royui-upload-drop-bg: rgba(0, 0, 0, 0.012);
415
+ --royui-upload-tile: #ffffff;
416
+ --royui-upload-track: rgba(0, 0, 0, 0.12);
417
+ --royui-upload-hover: rgba(0, 0, 0, 0.05);
418
+ box-shadow:
419
+ 0 0 0 1px rgba(0, 0, 0, 0.07),
420
+ 0 24px 60px -28px rgba(0, 0, 0, 0.3);
421
+ }
422
+ .royui-upload--auto .royui-upload__row {
423
+ box-shadow:
424
+ inset 0 0 0 1px var(--royui-upload-row-line),
425
+ 0 1px 2px rgba(0, 0, 0, 0.04);
426
+ }
427
+ .royui-upload--auto .royui-upload__drop-icon {
428
+ box-shadow:
429
+ inset 0 0 0 1px var(--royui-upload-line),
430
+ 0 1px 2px rgba(0, 0, 0, 0.05);
431
+ }
432
+ }
433
+
434
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
435
+ @media (prefers-reduced-motion: reduce) {
436
+ .royui-upload__row {
437
+ animation: none;
438
+ }
439
+ .royui-upload__bar-fill {
440
+ transition: none;
441
+ }
442
+ .royui-upload__shimmer {
443
+ animation: none;
444
+ background: none;
445
+ -webkit-text-fill-color: currentColor;
446
+ color: var(--royui-upload-muted);
447
+ }
448
+ }
@@ -157,5 +157,5 @@ var Button = forwardRef(
157
157
  Button.displayName = "Button";
158
158
 
159
159
  export { Button };
160
- //# sourceMappingURL=chunk-4SGMAZBG.js.map
161
- //# sourceMappingURL=chunk-4SGMAZBG.js.map
160
+ //# sourceMappingURL=chunk-B6LXWGX5.js.map
161
+ //# sourceMappingURL=chunk-B6LXWGX5.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/button/Button.tsx"],"names":[],"mappings":";;;;;AAmDA,SAAS,WAAW,KAAA,EAA2B;AAC7C,EAAA,MAAM,CAAA,GAAI,MAAM,IAAA,EAAK;AACrB,EAAA,MAAM,GAAA,GAAM,CAAA,CAAE,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AAC9B,EAAA,IAAI,kBAAA,CAAmB,IAAA,CAAK,GAAG,CAAA,EAAG;AAChC,IAAA,OAAO;AAAA,MACL,CAAA,EAAG,SAAS,GAAA,CAAI,CAAC,IAAK,GAAA,CAAI,CAAC,GAAI,EAAE,CAAA;AAAA,MACjC,CAAA,EAAG,SAAS,GAAA,CAAI,CAAC,IAAK,GAAA,CAAI,CAAC,GAAI,EAAE,CAAA;AAAA,MACjC,CAAA,EAAG,SAAS,GAAA,CAAI,CAAC,IAAK,GAAA,CAAI,CAAC,GAAI,EAAE;AAAA,KACnC;AAAA,EACF;AACA,EAAA,IAAI,iBAAiB,IAAA,CAAK,GAAG,KAAK,gBAAA,CAAiB,IAAA,CAAK,GAAG,CAAA,EAAG;AAC5D,IAAA,OAAO;AAAA,MACL,GAAG,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AAAA,MAC/B,GAAG,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AAAA,MAC/B,GAAG,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAG,CAAC,GAAG,EAAE;AAAA,KACjC;AAAA,EACF;AACA,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,qBAAqB,CAAA;AACvC,EAAA,IAAI,CAAA,EAAG;AACL,IAAA,MAAM,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,CAAG,KAAA,CAAM,SAAS,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA;AAC3D,IAAA,IAAI,EAAE,MAAA,IAAU,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,GAAG,CAAC,CAAA,CAAE,KAAA,CAAM,CAAC,MAAM,CAAC,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,EAAG;AACjE,MAAA,OAAO,EAAE,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA,EAAI,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA,EAAI,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA,EAAG;AAAA,IACxC;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AAEA,IAAM,KAAA,GAAQ,CAAC,CAAA,KAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAC,CAAA;AAErE,IAAM,GAAA,GAAM,CAAC,CAAA,EAAQ,MAAA,EAAgB,GAAA,MAAsB;AAAA,EACzD,GAAG,KAAA,CAAM,CAAA,CAAE,KAAK,MAAA,GAAS,CAAA,CAAE,KAAK,GAAG,CAAA;AAAA,EACnC,GAAG,KAAA,CAAM,CAAA,CAAE,KAAK,MAAA,GAAS,CAAA,CAAE,KAAK,GAAG,CAAA;AAAA,EACnC,GAAG,KAAA,CAAM,CAAA,CAAE,KAAK,MAAA,GAAS,CAAA,CAAE,KAAK,GAAG;AACrC,CAAA,CAAA;AACA,IAAM,GAAA,GAAM,CAAC,CAAA,KAAW,CAAA,IAAA,EAAO,CAAA,CAAE,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,CAAC,CAAA,CAAA,CAAA;AAGlD,SAAS,SAAS,KAAA,EAA8B;AAC9C,EAAA,MAAM,IAAA,GAAO,WAAW,KAAK,CAAA;AAG7B,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO;AAAA,MACL,CAAC,iBAAiB,GAAG,KAAA;AAAA,MACrB,CAAC,oBAAoB,GAAG;AAAA,KAC1B;AAAA,EACF;AACA,EAAA,MAAM,GAAA,GAAA,CAAO,SAAS,IAAA,CAAK,CAAA,GAAI,SAAS,IAAA,CAAK,CAAA,GAAI,MAAA,GAAS,IAAA,CAAK,CAAA,IAAK,GAAA;AACpE,EAAA,MAAM,QAAQ,GAAA,GAAM,IAAA;AACpB,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,GAAG,CAAA;AAC9B,EAAA,OAAO;AAAA,IACL,CAAC,iBAAiB,GAAG,GAAA,CAAI,IAAI,IAAA,EAAM,GAAA,EAAK,GAAG,CAAC,CAAA;AAAA,IAC5C,CAAC,oBAAoB,GAAG,GAAA,CAAI,IAAI,IAAA,EAAM,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA;AAAA;AAAA,IAG7C,CAAC,kBAAkB,GAAG,GAAA,CAAI,QAAQ,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,IAAI,CAAA,GAAI,GAAA,CAAI,IAAA,EAAM,GAAA,EAAK,IAAI,CAAC,CAAA;AAAA,IAC3E,CAAC,gBAAgB,GAAG,KAAA,GAAQ,SAAA,GAAY,SAAA;AAAA;AAAA;AAAA,IAGxC,CAAC,uBAAuB,GAAG,KAAA,GACvB,0BAAA,GACA,2BAAA;AAAA,IACJ,CAAC,mBAAmB,GAAG,CAAA,KAAA,EAAQ,KAAA,CAAM,CAAC,CAAA,EAAA,EAAK,KAAA,CAAM,CAAC,CAAA,EAAA,EAAK,KAAA,CAAM,CAAC,CAAA,MAAA,CAAA;AAAA,IAC9D,CAAC,mBAAmB,GAAG,KAAA,GAAQ,qBAAA,GAAwB,0BAAA;AAAA;AAAA,IAEvD,CAAC,2BAA2B,GAAG,KAAA,GAAQ,MAAA,GAAS;AAAA,GAClD;AACF;AAGA,SAAS,aAAgB,IAAA,EAA8B;AACrD,EAAA,OAAO,CAAC,IAAA,KAAY;AAClB,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,GAAA,CAAI,IAAI,CAAA;AAAA,WAAA,IAC9B,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,EAAU;AACvC,QAAC,IAA8B,OAAA,GAAU,IAAA;AAAA,MAC3C;AAAA,IACF;AAAA,EACF,CAAA;AACF;AAEA,IAAM,iBAAiB,sBACrB,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,sBAAqB,aAAA,EAAY,MAAA,EAC/C,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAQ,WAAA,EAAY,KAAA,EAAM,MAAK,MAAA,EAAO,IAAA,EAAK,MAAK,MAAA,EACnD,QAAA,EAAA;AAAA,kBAAA,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,EAAA,EAAG,IAAA;AAAA,MACH,EAAA,EAAG,IAAA;AAAA,MACH,CAAA,EAAE,GAAA;AAAA,MACF,MAAA,EAAO,cAAA;AAAA,MACP,aAAA,EAAc,KAAA;AAAA,MACd,WAAA,EAAY;AAAA;AAAA,GACd;AAAA,kBACA,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,CAAA,EAAE,sBAAA;AAAA,MACF,MAAA,EAAO,cAAA;AAAA,MACP,WAAA,EAAY,KAAA;AAAA,MACZ,aAAA,EAAc;AAAA;AAAA;AAChB,CAAA,EACF,CAAA,EACF,CAAA;AAGK,IAAM,MAAA,GAAS,UAAA;AAAA,EACpB,CACE;AAAA,IACE,IAAA,GAAO,IAAA;AAAA,IACP,OAAA,GAAU,SAAA;AAAA,IACV,OAAA,GAAU,KAAA;AAAA,IACV,KAAA;AAAA,IACA,SAAA,GAAY,KAAA;AAAA,IACZ,OAAA,GAAU,KAAA;AAAA,IACV,YAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA,GAAY,EAAA;AAAA,IACZ,KAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA,GAAO,QAAA;AAAA,IACP,GAAG;AAAA,KAEL,GAAA,KACG;AACH,IAAA,MAAM,OAAA,GAAU;AAAA,MACd,WAAA;AAAA,MACA,cAAc,IAAI,CAAA,CAAA;AAAA,MAClB,cAAc,OAAO,CAAA,CAAA;AAAA,MACrB,YAAY,iBAAA,GAAoB,EAAA;AAAA,MAChC,UAAU,oBAAA,GAAuB,EAAA;AAAA,MACjC;AAAA,KACF,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAGX,IAAA,MAAM,WAAA,GAAc,QAAQ,EAAE,GAAG,SAAS,KAAK,CAAA,EAAG,GAAG,KAAA,EAAM,GAAI,KAAA;AAC/D,IAAA,MAAM,OAAA,GAAU,YAAA,oBAAgB,GAAA,CAAC,cAAA,EAAA,EAAe,CAAA;AAChD,IAAA,MAAM,aAAa,QAAA,IAAY,OAAA;AAI/B,IAAA,IAAI,OAAA,IAAW,cAAA,CAAe,QAAQ,CAAA,EAAG;AACvC,MAAA,MAAM,KAAA,GAAQ,QAAA;AAKd,MAAA,MAAM,WAAY,KAAA,CAA4C,GAAA;AAC9D,MAAA,OAAO,YAAA;AAAA,QACL,KAAA;AAAA,QACA;AAAA,UACE,GAAG,IAAA;AAAA,UACH,SAAA,EAAW,CAAC,OAAA,EAAS,KAAA,CAAM,KAAA,CAAM,SAAS,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAAA,UACpE,OAAO,EAAE,GAAG,aAAa,GAAG,KAAA,CAAM,MAAM,KAAA,EAAM;AAAA,UAC9C,aAAa,OAAA,IAAW,MAAA;AAAA,UACxB,iBAAiB,UAAA,IAAc,MAAA;AAAA,UAC/B,GAAA,EAAK,SAAA,CAAU,GAAA,EAAqB,QAAQ;AAAA,SAC9C;AAAA,QACA,OAAA,GAAU,OAAA,GAAU,KAAA,CAAM,KAAA,CAAM;AAAA,OAClC;AAAA,IACF;AAEA,IAAA,uBACE,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA,EAAU,UAAA;AAAA,QACV,SAAA,EAAW,OAAA;AAAA,QACX,KAAA,EAAO,WAAA;AAAA,QACP,aAAW,OAAA,IAAW,MAAA;AAAA,QACrB,GAAG,IAAA;AAAA,QAEH,oBAAU,OAAA,GAAU;AAAA;AAAA,KACvB;AAAA,EAEJ;AACF;AAEA,MAAA,CAAO,WAAA,GAAc,QAAA","file":"chunk-4SGMAZBG.js","sourcesContent":["'use client';\n\nimport {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n type ButtonHTMLAttributes,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\nimport './Button.css';\n\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual scale. Defaults to \"md\". */\n size?: ButtonSize;\n /** Visual weight. \"primary\" is the solid depth button; \"secondary\" is a\n * quieter raised surface; \"ghost\" is text-only until hovered. Defaults to \"primary\". */\n variant?: ButtonVariant;\n /**\n * Render the single child element instead of a <button>, merging the\n * button's classes, styles, and props onto it. Use this for a\n * button-styled link with real anchor semantics:\n * `<Button asChild><Link href=\"/x\">Go</Link></Button>`.\n */\n asChild?: boolean;\n /**\n * Base color (hex or rgb()). The whole depth treatment derives from it —\n * a lighter top and darker base for the gradient, a hairline ring that\n * adapts to the tone, and a readable label color picked by luminance.\n * Defaults to the near-black surface. Override individual CSS variables\n * (--royui-btn-top etc.) for finer control.\n */\n color?: string;\n /** Stretch the button to fill its container. Defaults to false. */\n fullWidth?: boolean;\n /** Replaces children with a spinner and disables the button. */\n loading?: boolean;\n /** Optional override for the loading visual. Defaults to a spinner. */\n loadingLabel?: ReactNode;\n children: ReactNode;\n}\n\ntype RGB = { r: number; g: number; b: number };\n\n/** Parse hex (#rgb/#rrggbb/#rrggbbaa) or rgb()/rgba(). Returns null otherwise. */\nfunction parseColor(input: string): RGB | null {\n const s = input.trim();\n const hex = s.replace(/^#/, '');\n if (/^[0-9a-f]{3,4}$/i.test(hex)) {\n return {\n r: parseInt(hex[0]! + hex[0]!, 16),\n g: parseInt(hex[1]! + hex[1]!, 16),\n b: parseInt(hex[2]! + hex[2]!, 16),\n };\n }\n if (/^[0-9a-f]{6}$/i.test(hex) || /^[0-9a-f]{8}$/i.test(hex)) {\n return {\n r: parseInt(hex.slice(0, 2), 16),\n g: parseInt(hex.slice(2, 4), 16),\n b: parseInt(hex.slice(4, 6), 16),\n };\n }\n const m = s.match(/^rgba?\\(([^)]+)\\)$/i);\n if (m) {\n const p = m[1]!.split(/[,\\s/]+/).filter(Boolean).map(Number);\n if (p.length >= 3 && p.slice(0, 3).every((n) => !Number.isNaN(n))) {\n return { r: p[0]!, g: p[1]!, b: p[2]! };\n }\n }\n return null;\n}\n\nconst clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)));\n/** Mix a channel toward a target (255 = white, 0 = black) by amount 0..1. */\nconst mix = (c: RGB, target: number, amt: number): RGB => ({\n r: clamp(c.r + (target - c.r) * amt),\n g: clamp(c.g + (target - c.g) * amt),\n b: clamp(c.b + (target - c.b) * amt),\n});\nconst rgb = (c: RGB) => `rgb(${c.r}, ${c.g}, ${c.b})`;\n\n/** Derive the full set of depth variables from one base color. */\nfunction tintVars(color: string): CSSProperties {\n const base = parseColor(color);\n // Unparseable (named color, hsl, etc.) — let the raw value drive the\n // surface and keep the default overlays. Depth still reads via the shade.\n if (!base) {\n return {\n ['--royui-btn-top']: color,\n ['--royui-btn-bottom']: color,\n } as CSSProperties;\n }\n const lum = (0.2126 * base.r + 0.7152 * base.g + 0.0722 * base.b) / 255;\n const light = lum > 0.62;\n const shade = mix(base, 0, 0.6);\n return {\n ['--royui-btn-top']: rgb(mix(base, 255, 0.1)),\n ['--royui-btn-bottom']: rgb(mix(base, 0, 0.1)),\n // Light buttons need a darker edge to show on light backgrounds; dark\n // buttons need a lighter one. Either way the ring stays a hairline.\n ['--royui-btn-ring']: rgb(light ? mix(base, 0, 0.16) : mix(base, 255, 0.22)),\n ['--royui-btn-fg']: light ? '#0b0b0d' : '#ffffff',\n // Stronger top sheen on light surfaces (where a faint white is invisible),\n // an in-hue shade at the base so it never looks like grime.\n ['--royui-btn-highlight']: light\n ? 'rgba(255, 255, 255, 0.5)'\n : 'rgba(255, 255, 255, 0.15)',\n ['--royui-btn-shade']: `rgba(${shade.r}, ${shade.g}, ${shade.b}, 0.5)`,\n ['--royui-btn-focus']: light ? 'rgba(0, 0, 0, 0.55)' : 'rgba(255, 255, 255, 0.7)',\n // Press darkens less on light buttons so the dip doesn't look like a smudge.\n ['--royui-btn-active-bright']: light ? '0.93' : '0.86',\n } as CSSProperties;\n}\n\n/** Combine the forwarded ref with the child's own ref (asChild path). */\nfunction mergeRefs<T>(...refs: (Ref<T> | undefined)[]) {\n return (node: T) => {\n for (const ref of refs) {\n if (typeof ref === 'function') ref(node);\n else if (ref && typeof ref === 'object') {\n (ref as { current: T | null }).current = node;\n }\n }\n };\n}\n\nconst DefaultSpinner = () => (\n <span className=\"royui-btn__spinner\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"9\"\n stroke=\"currentColor\"\n strokeOpacity=\"0.3\"\n strokeWidth=\"2.5\"\n />\n <path\n d=\"M21 12a9 9 0 0 0-9-9\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n);\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n size = 'md',\n variant = 'primary',\n asChild = false,\n color,\n fullWidth = false,\n loading = false,\n loadingLabel,\n disabled,\n className = '',\n style,\n children,\n type = 'button',\n ...rest\n },\n ref,\n ) => {\n const classes = [\n 'royui-btn',\n `royui-btn--${size}`,\n `royui-btn--${variant}`,\n fullWidth ? 'royui-btn--full' : '',\n loading ? 'royui-btn--loading' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // Derived vars first so an explicit `style` (or CSS-var override) still wins.\n const mergedStyle = color ? { ...tintVars(color), ...style } : style;\n const spinner = loadingLabel ?? <DefaultSpinner />;\n const isDisabled = disabled || loading;\n\n // Polymorphic path: paint the styles onto the child element (e.g. a link)\n // instead of a <button>, so native anchor semantics survive.\n if (asChild && isValidElement(children)) {\n const child = children as ReactElement<{\n className?: string;\n style?: CSSProperties;\n children?: ReactNode;\n }>;\n const childRef = (child as unknown as { ref?: Ref<unknown> }).ref;\n return cloneElement(\n child,\n {\n ...rest,\n className: [classes, child.props.className].filter(Boolean).join(' '),\n style: { ...mergedStyle, ...child.props.style },\n 'aria-busy': loading || undefined,\n 'aria-disabled': isDisabled || undefined,\n ref: mergeRefs(ref as Ref<unknown>, childRef),\n } as Record<string, unknown>,\n loading ? spinner : child.props.children,\n );\n }\n\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled}\n className={classes}\n style={mergedStyle}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? spinner : children}\n </button>\n );\n },\n);\n\nButton.displayName = 'Button';\n"]}
1
+ {"version":3,"sources":["../src/components/button/Button.tsx"],"names":[],"mappings":"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","file":"chunk-B6LXWGX5.js","sourcesContent":["'use client';\n\nimport {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n type ButtonHTMLAttributes,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\nimport './Button.css';\n\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual scale. Defaults to \"md\". */\n size?: ButtonSize;\n /** Visual weight. \"primary\" is the solid depth button; \"secondary\" is a\n * quieter raised surface; \"ghost\" is text-only until hovered. Defaults to \"primary\". */\n variant?: ButtonVariant;\n /**\n * Render the single child element instead of a <button>, merging the\n * button's classes, styles, and props onto it. Use this for a\n * button-styled link with real anchor semantics:\n * `<Button asChild><Link href=\"/x\">Go</Link></Button>`.\n */\n asChild?: boolean;\n /**\n * Base color (hex or rgb()). The whole depth treatment derives from it —\n * a lighter top and darker base for the gradient, a hairline ring that\n * adapts to the tone, and a readable label color picked by luminance.\n * Defaults to the near-black surface. Override individual CSS variables\n * (--royui-btn-top etc.) for finer control.\n */\n color?: string;\n /** Stretch the button to fill its container. Defaults to false. */\n fullWidth?: boolean;\n /** Replaces children with a spinner and disables the button. */\n loading?: boolean;\n /** Optional override for the loading visual. Defaults to a spinner. */\n loadingLabel?: ReactNode;\n children: ReactNode;\n}\n\ntype RGB = { r: number; g: number; b: number };\n\n/** Parse hex (#rgb/#rrggbb/#rrggbbaa) or rgb()/rgba(). Returns null otherwise. */\nfunction parseColor(input: string): RGB | null {\n const s = input.trim();\n const hex = s.replace(/^#/, '');\n if (/^[0-9a-f]{3,4}$/i.test(hex)) {\n return {\n r: parseInt(hex[0]! + hex[0]!, 16),\n g: parseInt(hex[1]! + hex[1]!, 16),\n b: parseInt(hex[2]! + hex[2]!, 16),\n };\n }\n if (/^[0-9a-f]{6}$/i.test(hex) || /^[0-9a-f]{8}$/i.test(hex)) {\n return {\n r: parseInt(hex.slice(0, 2), 16),\n g: parseInt(hex.slice(2, 4), 16),\n b: parseInt(hex.slice(4, 6), 16),\n };\n }\n const m = s.match(/^rgba?\\(([^)]+)\\)$/i);\n if (m) {\n const p = m[1]!.split(/[,\\s/]+/).filter(Boolean).map(Number);\n if (p.length >= 3 && p.slice(0, 3).every((n) => !Number.isNaN(n))) {\n return { r: p[0]!, g: p[1]!, b: p[2]! };\n }\n }\n return null;\n}\n\nconst clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)));\n/** Mix a channel toward a target (255 = white, 0 = black) by amount 0..1. */\nconst mix = (c: RGB, target: number, amt: number): RGB => ({\n r: clamp(c.r + (target - c.r) * amt),\n g: clamp(c.g + (target - c.g) * amt),\n b: clamp(c.b + (target - c.b) * amt),\n});\nconst rgb = (c: RGB) => `rgb(${c.r}, ${c.g}, ${c.b})`;\n\n/** Derive the full set of depth variables from one base color. */\nfunction tintVars(color: string): CSSProperties {\n const base = parseColor(color);\n // Unparseable (named color, hsl, etc.) — let the raw value drive the\n // surface and keep the default overlays. Depth still reads via the shade.\n if (!base) {\n return {\n ['--royui-btn-top']: color,\n ['--royui-btn-bottom']: color,\n } as CSSProperties;\n }\n const lum = (0.2126 * base.r + 0.7152 * base.g + 0.0722 * base.b) / 255;\n const light = lum > 0.62;\n const shade = mix(base, 0, 0.6);\n return {\n ['--royui-btn-top']: rgb(mix(base, 255, 0.1)),\n ['--royui-btn-bottom']: rgb(mix(base, 0, 0.1)),\n // Light buttons need a darker edge to show on light backgrounds; dark\n // buttons need a lighter one. Either way the ring stays a hairline.\n ['--royui-btn-ring']: rgb(light ? mix(base, 0, 0.16) : mix(base, 255, 0.22)),\n ['--royui-btn-fg']: light ? '#0b0b0d' : '#ffffff',\n // Stronger top sheen on light surfaces (where a faint white is invisible),\n // an in-hue shade at the base so it never looks like grime.\n ['--royui-btn-highlight']: light\n ? 'rgba(255, 255, 255, 0.5)'\n : 'rgba(255, 255, 255, 0.15)',\n ['--royui-btn-shade']: `rgba(${shade.r}, ${shade.g}, ${shade.b}, 0.5)`,\n ['--royui-btn-focus']: light ? 'rgba(0, 0, 0, 0.55)' : 'rgba(255, 255, 255, 0.7)',\n // Press darkens less on light buttons so the dip doesn't look like a smudge.\n ['--royui-btn-active-bright']: light ? '0.93' : '0.86',\n } as CSSProperties;\n}\n\n/** Combine the forwarded ref with the child's own ref (asChild path). */\nfunction mergeRefs<T>(...refs: (Ref<T> | undefined)[]) {\n return (node: T) => {\n for (const ref of refs) {\n if (typeof ref === 'function') ref(node);\n else if (ref && typeof ref === 'object') {\n (ref as { current: T | null }).current = node;\n }\n }\n };\n}\n\nconst DefaultSpinner = () => (\n <span className=\"royui-btn__spinner\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"9\"\n stroke=\"currentColor\"\n strokeOpacity=\"0.3\"\n strokeWidth=\"2.5\"\n />\n <path\n d=\"M21 12a9 9 0 0 0-9-9\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n);\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n size = 'md',\n variant = 'primary',\n asChild = false,\n color,\n fullWidth = false,\n loading = false,\n loadingLabel,\n disabled,\n className = '',\n style,\n children,\n type = 'button',\n ...rest\n },\n ref,\n ) => {\n const classes = [\n 'royui-btn',\n `royui-btn--${size}`,\n `royui-btn--${variant}`,\n fullWidth ? 'royui-btn--full' : '',\n loading ? 'royui-btn--loading' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // Derived vars first so an explicit `style` (or CSS-var override) still wins.\n const mergedStyle = color ? { ...tintVars(color), ...style } : style;\n const spinner = loadingLabel ?? <DefaultSpinner />;\n const isDisabled = disabled || loading;\n\n // Polymorphic path: paint the styles onto the child element (e.g. a link)\n // instead of a <button>, so native anchor semantics survive.\n if (asChild && isValidElement(children)) {\n const child = children as ReactElement<{\n className?: string;\n style?: CSSProperties;\n children?: ReactNode;\n }>;\n const childRef = (child as unknown as { ref?: Ref<unknown> }).ref;\n return cloneElement(\n child,\n {\n ...rest,\n className: [classes, child.props.className].filter(Boolean).join(' '),\n style: { ...mergedStyle, ...child.props.style },\n 'aria-busy': loading || undefined,\n 'aria-disabled': isDisabled || undefined,\n ref: mergeRefs(ref as Ref<unknown>, childRef),\n } as Record<string, unknown>,\n loading ? spinner : child.props.children,\n );\n }\n\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled}\n className={classes}\n style={mergedStyle}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? spinner : children}\n </button>\n );\n },\n);\n\nButton.displayName = 'Button';\n"]}
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ //# sourceMappingURL=chunk-DAA5LXWQ.js.map
4
+ //# sourceMappingURL=chunk-DAA5LXWQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-DAA5LXWQ.js"}
@@ -103,5 +103,5 @@ var TextMorph = forwardRef(
103
103
  );
104
104
 
105
105
  export { TextMorph };
106
- //# sourceMappingURL=chunk-PGV55XSZ.js.map
107
- //# sourceMappingURL=chunk-PGV55XSZ.js.map
106
+ //# sourceMappingURL=chunk-MO7UPMW7.js.map
107
+ //# sourceMappingURL=chunk-MO7UPMW7.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/text-morph/TextMorph.tsx"],"names":["TextMorph"],"mappings":"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","file":"chunk-PGV55XSZ.js","sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport './TextMorph.css';\n\nexport interface TextMorphProps\n extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {\n /** The current text. When this prop changes, the component diff-types\n from the previously displayed text to the new value. */\n value: string;\n /** Optional renderer for the current intermediate text — handy for\n syntax highlighting, gradient spans, or wrapping each word. Receives\n the partial string at every keystroke during the animation. */\n renderText?: (current: string) => ReactNode;\n /** Per-character typing delay range in ms. Default [30, 60]. */\n typeDelay?: [min: number, max: number];\n /** Per-character backspace delay range in ms. Default [18, 30]. */\n backspaceDelay?: [min: number, max: number];\n /** Characters that get an additional delay (harder to type on a real\n keyboard — punctuation, brackets, symbols). Default /[\\/{}\\-_@]/. */\n hardChars?: RegExp;\n /** Extra delay range for hard chars in ms. Default [30, 65]. */\n hardCharExtraDelay?: [min: number, max: number];\n /** Pause between backspace phase and typing phase, in ms. Default 70. */\n pauseMs?: number;\n /** Skip animation entirely and just swap text. */\n disabled?: boolean;\n}\n\nfunction sleep(ms: number) {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\nfunction findDiff(from: string, to: string) {\n let p = 0;\n const maxP = Math.min(from.length, to.length);\n while (p < maxP && from[p] === to[p]) p++;\n let s = 0;\n while (\n s < from.length - p &&\n s < to.length - p &&\n from[from.length - 1 - s] === to[to.length - 1 - s]\n ) {\n s++;\n }\n return {\n prefix: from.slice(0, p),\n suffix: from.slice(from.length - s),\n oldMid: from.slice(p, from.length - s),\n newMid: to.slice(p, to.length - s),\n };\n}\n\nfunction rand(min: number, max: number) {\n return min + Math.random() * (max - min);\n}\n\nexport const TextMorph = forwardRef<HTMLSpanElement, TextMorphProps>(\n function TextMorph(\n {\n value,\n renderText,\n typeDelay = [30, 60],\n backspaceDelay = [18, 30],\n hardChars = /[\\/{}\\-_@]/,\n hardCharExtraDelay = [30, 65],\n pauseMs = 70,\n disabled = false,\n className = '',\n ...rest\n },\n ref,\n ) {\n const [displayed, setDisplayed] = useState(value);\n const tokenRef = useRef(0);\n const reducedRef = useRef(false);\n const prevValueRef = useRef(value);\n const displayedRef = useRef(value);\n\n useEffect(() => {\n if (typeof window !== 'undefined') {\n reducedRef.current = window.matchMedia(\n '(prefers-reduced-motion: reduce)',\n ).matches;\n }\n }, []);\n\n // Keep a ref of the currently shown text so the animation always\n // starts from the latest frame (even if interrupted mid-typing).\n useEffect(() => {\n displayedRef.current = displayed;\n }, [displayed]);\n\n useEffect(() => {\n if (value === prevValueRef.current) return;\n const source = displayedRef.current;\n prevValueRef.current = value;\n\n if (disabled || reducedRef.current) {\n setDisplayed(value);\n return;\n }\n\n const myToken = ++tokenRef.current;\n\n (async () => {\n const { prefix, suffix, oldMid, newMid } = findDiff(source, value);\n\n for (let i = oldMid.length - 1; i >= 0; i--) {\n if (myToken !== tokenRef.current) return;\n setDisplayed(prefix + oldMid.slice(0, i) + suffix);\n await sleep(rand(backspaceDelay[0], backspaceDelay[1]));\n }\n\n if (myToken !== tokenRef.current) return;\n await sleep(pauseMs);\n\n for (let i = 1; i <= newMid.length; i++) {\n if (myToken !== tokenRef.current) return;\n setDisplayed(prefix + newMid.slice(0, i) + suffix);\n const ch = newMid.charAt(i - 1);\n const base = rand(typeDelay[0], typeDelay[1]);\n const extra = hardChars.test(ch)\n ? rand(hardCharExtraDelay[0], hardCharExtraDelay[1])\n : 0;\n await sleep(base + extra);\n }\n })();\n }, [\n value,\n disabled,\n typeDelay,\n backspaceDelay,\n hardChars,\n hardCharExtraDelay,\n pauseMs,\n ]);\n\n return (\n <span\n ref={ref}\n className={`royui-textmorph ${className}`.trim()}\n aria-live=\"polite\"\n {...rest}\n >\n {renderText ? renderText(displayed) : displayed}\n </span>\n );\n },\n);\n"]}
1
+ {"version":3,"sources":["../src/components/text-morph/TextMorph.tsx"],"names":["TextMorph"],"mappings":"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","file":"chunk-MO7UPMW7.js","sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport './TextMorph.css';\n\nexport interface TextMorphProps\n extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {\n /** The current text. When this prop changes, the component diff-types\n from the previously displayed text to the new value. */\n value: string;\n /** Optional renderer for the current intermediate text — handy for\n syntax highlighting, gradient spans, or wrapping each word. Receives\n the partial string at every keystroke during the animation. */\n renderText?: (current: string) => ReactNode;\n /** Per-character typing delay range in ms. Default [30, 60]. */\n typeDelay?: [min: number, max: number];\n /** Per-character backspace delay range in ms. Default [18, 30]. */\n backspaceDelay?: [min: number, max: number];\n /** Characters that get an additional delay (harder to type on a real\n keyboard — punctuation, brackets, symbols). Default /[\\/{}\\-_@]/. */\n hardChars?: RegExp;\n /** Extra delay range for hard chars in ms. Default [30, 65]. */\n hardCharExtraDelay?: [min: number, max: number];\n /** Pause between backspace phase and typing phase, in ms. Default 70. */\n pauseMs?: number;\n /** Skip animation entirely and just swap text. */\n disabled?: boolean;\n}\n\nfunction sleep(ms: number) {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\nfunction findDiff(from: string, to: string) {\n let p = 0;\n const maxP = Math.min(from.length, to.length);\n while (p < maxP && from[p] === to[p]) p++;\n let s = 0;\n while (\n s < from.length - p &&\n s < to.length - p &&\n from[from.length - 1 - s] === to[to.length - 1 - s]\n ) {\n s++;\n }\n return {\n prefix: from.slice(0, p),\n suffix: from.slice(from.length - s),\n oldMid: from.slice(p, from.length - s),\n newMid: to.slice(p, to.length - s),\n };\n}\n\nfunction rand(min: number, max: number) {\n return min + Math.random() * (max - min);\n}\n\nexport const TextMorph = forwardRef<HTMLSpanElement, TextMorphProps>(\n function TextMorph(\n {\n value,\n renderText,\n typeDelay = [30, 60],\n backspaceDelay = [18, 30],\n hardChars = /[\\/{}\\-_@]/,\n hardCharExtraDelay = [30, 65],\n pauseMs = 70,\n disabled = false,\n className = '',\n ...rest\n },\n ref,\n ) {\n const [displayed, setDisplayed] = useState(value);\n const tokenRef = useRef(0);\n const reducedRef = useRef(false);\n const prevValueRef = useRef(value);\n const displayedRef = useRef(value);\n\n useEffect(() => {\n if (typeof window !== 'undefined') {\n reducedRef.current = window.matchMedia(\n '(prefers-reduced-motion: reduce)',\n ).matches;\n }\n }, []);\n\n // Keep a ref of the currently shown text so the animation always\n // starts from the latest frame (even if interrupted mid-typing).\n useEffect(() => {\n displayedRef.current = displayed;\n }, [displayed]);\n\n useEffect(() => {\n if (value === prevValueRef.current) return;\n const source = displayedRef.current;\n prevValueRef.current = value;\n\n if (disabled || reducedRef.current) {\n setDisplayed(value);\n return;\n }\n\n const myToken = ++tokenRef.current;\n\n (async () => {\n const { prefix, suffix, oldMid, newMid } = findDiff(source, value);\n\n for (let i = oldMid.length - 1; i >= 0; i--) {\n if (myToken !== tokenRef.current) return;\n setDisplayed(prefix + oldMid.slice(0, i) + suffix);\n await sleep(rand(backspaceDelay[0], backspaceDelay[1]));\n }\n\n if (myToken !== tokenRef.current) return;\n await sleep(pauseMs);\n\n for (let i = 1; i <= newMid.length; i++) {\n if (myToken !== tokenRef.current) return;\n setDisplayed(prefix + newMid.slice(0, i) + suffix);\n const ch = newMid.charAt(i - 1);\n const base = rand(typeDelay[0], typeDelay[1]);\n const extra = hardChars.test(ch)\n ? rand(hardCharExtraDelay[0], hardCharExtraDelay[1])\n : 0;\n await sleep(base + extra);\n }\n })();\n }, [\n value,\n disabled,\n typeDelay,\n backspaceDelay,\n hardChars,\n hardCharExtraDelay,\n pauseMs,\n ]);\n\n return (\n <span\n ref={ref}\n className={`royui-textmorph ${className}`.trim()}\n aria-live=\"polite\"\n {...rest}\n >\n {renderText ? renderText(displayed) : displayed}\n </span>\n );\n },\n);\n"]}
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ //# sourceMappingURL=chunk-ORTCK34Y.js.map
4
+ //# sourceMappingURL=chunk-ORTCK34Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-ORTCK34Y.js"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { Button } from './chunk-4SGMAZBG.js';
2
+ import { Button } from './chunk-B6LXWGX5.js';
3
3
  import { forwardRef, useId, useState, useCallback, useRef, useEffect, Fragment as Fragment$1 } from 'react';
4
4
  import './ImageCarousel-4L4MNUP2.css';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -303,5 +303,5 @@ var Card = forwardRef(
303
303
  Card.displayName = "Card";
304
304
 
305
305
  export { Card, ImageCarousel };
306
- //# sourceMappingURL=chunk-B7QN2JTN.js.map
307
- //# sourceMappingURL=chunk-B7QN2JTN.js.map
306
+ //# sourceMappingURL=chunk-TBBQRMCG.js.map
307
+ //# sourceMappingURL=chunk-TBBQRMCG.js.map