@expo-forge/forge-ui 0.0.47

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,3137 @@
1
+ "use strict";
2
+
3
+ const SPACING = {
4
+ 0: 0,
5
+ 0.5: 2,
6
+ 1: 4,
7
+ 1.5: 6,
8
+ 2: 8,
9
+ 2.5: 10,
10
+ 3: 12,
11
+ 3.5: 14,
12
+ 4: 16,
13
+ 5: 20,
14
+ 6: 24,
15
+ 7: 28,
16
+ 8: 32,
17
+ 9: 36,
18
+ 10: 40,
19
+ 11: 44,
20
+ 12: 48,
21
+ 14: 56,
22
+ 16: 64,
23
+ 20: 80,
24
+ 24: 96,
25
+ 28: 112,
26
+ 32: 128,
27
+ 36: 144,
28
+ 40: 160,
29
+ 44: 176,
30
+ 48: 192,
31
+ 52: 208,
32
+ 56: 224,
33
+ 60: 240,
34
+ 64: 256,
35
+ 72: 288,
36
+ 80: 320,
37
+ 96: 384,
38
+ px: 1,
39
+ full: "100%",
40
+ auto: "auto",
41
+ "1/2": "50%",
42
+ "1/3": "33.33%",
43
+ "2/3": "66.67%",
44
+ "1/4": "25%",
45
+ "3/4": "75%",
46
+ };
47
+
48
+ function sp(step) {
49
+ return step in SPACING ? SPACING[step] : null;
50
+ }
51
+
52
+ const PALETTES = {
53
+ slate: [
54
+ "#f8fafc",
55
+ "#f1f5f9",
56
+ "#e2e8f0",
57
+ "#cbd5e1",
58
+ "#94a3b8",
59
+ "#64748b",
60
+ "#475569",
61
+ "#334155",
62
+ "#1e293b",
63
+ "#0f172a",
64
+ "#020617",
65
+ ],
66
+ gray: [
67
+ "#f9fafb",
68
+ "#f3f4f6",
69
+ "#e5e7eb",
70
+ "#d1d5db",
71
+ "#9ca3af",
72
+ "#6b7280",
73
+ "#4b5563",
74
+ "#374151",
75
+ "#1f2937",
76
+ "#111827",
77
+ "#030712",
78
+ ],
79
+ zinc: [
80
+ "#fafafa",
81
+ "#f4f4f5",
82
+ "#e4e4e7",
83
+ "#d4d4d8",
84
+ "#a1a1aa",
85
+ "#71717a",
86
+ "#52525b",
87
+ "#3f3f46",
88
+ "#27272a",
89
+ "#18181b",
90
+ "#09090b",
91
+ ],
92
+ neutral: [
93
+ "#fafafa",
94
+ "#f5f5f5",
95
+ "#e5e5e5",
96
+ "#d4d4d4",
97
+ "#a3a3a3",
98
+ "#737373",
99
+ "#525252",
100
+ "#404040",
101
+ "#262626",
102
+ "#171717",
103
+ "#0a0a0a",
104
+ ],
105
+ stone: [
106
+ "#fafaf9",
107
+ "#f5f5f4",
108
+ "#e7e5e4",
109
+ "#d6d3d1",
110
+ "#a8a29e",
111
+ "#78716c",
112
+ "#57534e",
113
+ "#44403c",
114
+ "#292524",
115
+ "#1c1917",
116
+ "#0c0a09",
117
+ ],
118
+ red: [
119
+ "#fef2f2",
120
+ "#fee2e2",
121
+ "#fecaca",
122
+ "#fca5a5",
123
+ "#f87171",
124
+ "#ef4444",
125
+ "#dc2626",
126
+ "#b91c1c",
127
+ "#991b1b",
128
+ "#7f1d1d",
129
+ "#450a0a",
130
+ ],
131
+ orange: [
132
+ "#fff7ed",
133
+ "#ffedd5",
134
+ "#fed7aa",
135
+ "#fdba74",
136
+ "#fb923c",
137
+ "#f97316",
138
+ "#ea580c",
139
+ "#c2410c",
140
+ "#9a3412",
141
+ "#7c2d12",
142
+ "#431407",
143
+ ],
144
+ amber: [
145
+ "#fffbeb",
146
+ "#fef3c7",
147
+ "#fde68a",
148
+ "#fcd34d",
149
+ "#fbbf24",
150
+ "#f59e0b",
151
+ "#d97706",
152
+ "#b45309",
153
+ "#92400e",
154
+ "#78350f",
155
+ "#451a03",
156
+ ],
157
+ yellow: [
158
+ "#fefce8",
159
+ "#fef9c3",
160
+ "#fef08a",
161
+ "#fde047",
162
+ "#facc15",
163
+ "#eab308",
164
+ "#ca8a04",
165
+ "#a16207",
166
+ "#854d0e",
167
+ "#713f12",
168
+ "#422006",
169
+ ],
170
+ lime: [
171
+ "#f7fee7",
172
+ "#ecfccb",
173
+ "#d9f99d",
174
+ "#bef264",
175
+ "#a3e635",
176
+ "#84cc16",
177
+ "#65a30d",
178
+ "#4d7c0f",
179
+ "#3f6212",
180
+ "#365314",
181
+ "#1a2e05",
182
+ ],
183
+ green: [
184
+ "#f0fdf4",
185
+ "#dcfce7",
186
+ "#bbf7d0",
187
+ "#86efac",
188
+ "#4ade80",
189
+ "#22c55e",
190
+ "#16a34a",
191
+ "#15803d",
192
+ "#166534",
193
+ "#14532d",
194
+ "#052e16",
195
+ ],
196
+ emerald: [
197
+ "#ecfdf5",
198
+ "#d1fae5",
199
+ "#a7f3d0",
200
+ "#6ee7b7",
201
+ "#34d399",
202
+ "#10b981",
203
+ "#059669",
204
+ "#047857",
205
+ "#065f46",
206
+ "#064e3b",
207
+ "#022c22",
208
+ ],
209
+ teal: [
210
+ "#f0fdfa",
211
+ "#ccfbf1",
212
+ "#99f6e4",
213
+ "#5eead4",
214
+ "#2dd4bf",
215
+ "#14b8a6",
216
+ "#0d9488",
217
+ "#0f766e",
218
+ "#115e59",
219
+ "#134e4a",
220
+ "#042f2e",
221
+ ],
222
+ cyan: [
223
+ "#ecfeff",
224
+ "#cffafe",
225
+ "#a5f3fc",
226
+ "#67e8f9",
227
+ "#22d3ee",
228
+ "#06b6d4",
229
+ "#0891b2",
230
+ "#0e7490",
231
+ "#155e75",
232
+ "#164e63",
233
+ "#083344",
234
+ ],
235
+ sky: [
236
+ "#f0f9ff",
237
+ "#e0f2fe",
238
+ "#bae6fd",
239
+ "#7dd3fc",
240
+ "#38bdf8",
241
+ "#0ea5e9",
242
+ "#0284c7",
243
+ "#0369a1",
244
+ "#075985",
245
+ "#0c4a6e",
246
+ "#082f49",
247
+ ],
248
+ blue: [
249
+ "#eff6ff",
250
+ "#dbeafe",
251
+ "#bfdbfe",
252
+ "#93c5fd",
253
+ "#60a5fa",
254
+ "#3b82f6",
255
+ "#2563eb",
256
+ "#1d4ed8",
257
+ "#1e40af",
258
+ "#1e3a8a",
259
+ "#172554",
260
+ ],
261
+ indigo: [
262
+ "#eef2ff",
263
+ "#e0e7ff",
264
+ "#c7d2fe",
265
+ "#a5b4fc",
266
+ "#818cf8",
267
+ "#6366f1",
268
+ "#4f46e5",
269
+ "#4338ca",
270
+ "#3730a3",
271
+ "#312e81",
272
+ "#1e1b4b",
273
+ ],
274
+ violet: [
275
+ "#f5f3ff",
276
+ "#ede9fe",
277
+ "#ddd6fe",
278
+ "#c4b5fd",
279
+ "#a78bfa",
280
+ "#8b5cf6",
281
+ "#7c3aed",
282
+ "#6d28d9",
283
+ "#5b21b6",
284
+ "#4c1d95",
285
+ "#2e1065",
286
+ ],
287
+ purple: [
288
+ "#faf5ff",
289
+ "#f3e8ff",
290
+ "#e9d5ff",
291
+ "#d8b4fe",
292
+ "#c084fc",
293
+ "#a855f7",
294
+ "#9333ea",
295
+ "#7e22ce",
296
+ "#6b21a8",
297
+ "#581c87",
298
+ "#3b0764",
299
+ ],
300
+ fuchsia: [
301
+ "#fdf4ff",
302
+ "#fae8ff",
303
+ "#f5d0fe",
304
+ "#f0abfc",
305
+ "#e879f9",
306
+ "#d946ef",
307
+ "#c026d3",
308
+ "#a21caf",
309
+ "#86198f",
310
+ "#701a75",
311
+ "#4a044e",
312
+ ],
313
+ pink: [
314
+ "#fdf2f8",
315
+ "#fce7f3",
316
+ "#fbcfe8",
317
+ "#f9a8d4",
318
+ "#f472b6",
319
+ "#ec4899",
320
+ "#db2777",
321
+ "#be185d",
322
+ "#9d174d",
323
+ "#831843",
324
+ "#500724",
325
+ ],
326
+ rose: [
327
+ "#fff1f2",
328
+ "#ffe4e6",
329
+ "#fecdd3",
330
+ "#fda4af",
331
+ "#fb7185",
332
+ "#f43f5e",
333
+ "#e11d48",
334
+ "#be123c",
335
+ "#9f1239",
336
+ "#881337",
337
+ "#4c0519",
338
+ ],
339
+ // --- Extended palettes ---
340
+ brown: [
341
+ "#fdf8f6",
342
+ "#f5e6e0",
343
+ "#e8c9be",
344
+ "#d9a897",
345
+ "#c47f6a",
346
+ "#a85c43",
347
+ "#8a4330",
348
+ "#6e3020",
349
+ "#502015",
350
+ "#35130d",
351
+ "#1a0906",
352
+ ],
353
+ tan: [
354
+ "#fdfaf4",
355
+ "#f7f0e0",
356
+ "#ede0c4",
357
+ "#dfcca4",
358
+ "#ccb47e",
359
+ "#b59a5e",
360
+ "#967d44",
361
+ "#756030",
362
+ "#554422",
363
+ "#382c15",
364
+ "#1c150a",
365
+ ],
366
+ sand: [
367
+ "#fdfcf5",
368
+ "#f9f5e4",
369
+ "#f0eaca",
370
+ "#e4d9a8",
371
+ "#d4c484",
372
+ "#bfaa65",
373
+ "#9e8c4a",
374
+ "#7c6c38",
375
+ "#5a4e28",
376
+ "#3c3419",
377
+ "#1e1a0c",
378
+ ],
379
+ chocolate: [
380
+ "#fdf5f0",
381
+ "#f8e4d8",
382
+ "#f0cab5",
383
+ "#e4aa8a",
384
+ "#d4845c",
385
+ "#bf5e3a",
386
+ "#9e4427",
387
+ "#7a3019",
388
+ "#582210",
389
+ "#3a160a",
390
+ "#1c0b05",
391
+ ],
392
+ sienna: [
393
+ "#fdf6f3",
394
+ "#f9e8e2",
395
+ "#f0d0c4",
396
+ "#e4b4a0",
397
+ "#d49278",
398
+ "#c07358",
399
+ "#9e5640",
400
+ "#7a3d2e",
401
+ "#58291e",
402
+ "#3a1a12",
403
+ "#1c0d09",
404
+ ],
405
+ mint: [
406
+ "#f2fdf8",
407
+ "#e0f9f0",
408
+ "#c2f2e2",
409
+ "#98e8cf",
410
+ "#66d9b5",
411
+ "#3dc79a",
412
+ "#27a87e",
413
+ "#1c846a",
414
+ "#135f50",
415
+ "#0d3f35",
416
+ "#07201b",
417
+ ],
418
+ sage: [
419
+ "#f5f9f5",
420
+ "#e8f2e8",
421
+ "#cfe4cf",
422
+ "#b0d1b0",
423
+ "#8eb88e",
424
+ "#6e9e6e",
425
+ "#538053",
426
+ "#3c603c",
427
+ "#2b452b",
428
+ "#1c2e1c",
429
+ "#0e170e",
430
+ ],
431
+ olive: [
432
+ "#f7f9f2",
433
+ "#edf2e0",
434
+ "#d8e4be",
435
+ "#bfd198",
436
+ "#a2b970",
437
+ "#849e4e",
438
+ "#688038",
439
+ "#4e6128",
440
+ "#38461c",
441
+ "#262f12",
442
+ "#13170a",
443
+ ],
444
+ moss: [
445
+ "#f4f7f0",
446
+ "#e5ecdc",
447
+ "#cbdcb8",
448
+ "#adc792",
449
+ "#88ad6a",
450
+ "#688e48",
451
+ "#4e6e32",
452
+ "#395122",
453
+ "#283918",
454
+ "#1a250f",
455
+ "#0c1207",
456
+ ],
457
+ forest: [
458
+ "#f0f7f1",
459
+ "#d5ebdb",
460
+ "#aed8bc",
461
+ "#80bf9a",
462
+ "#50a373",
463
+ "#318554",
464
+ "#22673e",
465
+ "#164d2d",
466
+ "#0f361e",
467
+ "#0a2314",
468
+ "#04120a",
469
+ ],
470
+ navy: [
471
+ "#f0f3f9",
472
+ "#d8e1f2",
473
+ "#b5c5e6",
474
+ "#8aa4d8",
475
+ "#5c7ec5",
476
+ "#3a5aae",
477
+ "#273f8f",
478
+ "#1c2c70",
479
+ "#131e51",
480
+ "#0d1337",
481
+ "#07091d",
482
+ ],
483
+ cobalt: [
484
+ "#f0f5fc",
485
+ "#d8e8f9",
486
+ "#b5d0f4",
487
+ "#8ab5ed",
488
+ "#5a95e3",
489
+ "#3777d5",
490
+ "#255abf",
491
+ "#1a40a4",
492
+ "#122c86",
493
+ "#0c1b63",
494
+ "#060e33",
495
+ ],
496
+ royal: [
497
+ "#f3f0fc",
498
+ "#e3d8f9",
499
+ "#c8b5f4",
500
+ "#a88ced",
501
+ "#845de3",
502
+ "#673bd5",
503
+ "#5128bf",
504
+ "#3c1ca4",
505
+ "#2a1386",
506
+ "#1c0d63",
507
+ "#0e0733",
508
+ ],
509
+ cerulean: [
510
+ "#f0f8fd",
511
+ "#d5edf9",
512
+ "#acdaf4",
513
+ "#78c3ec",
514
+ "#42ace0",
515
+ "#1c94cc",
516
+ "#1478ae",
517
+ "#0e5e8c",
518
+ "#09446a",
519
+ "#062c46",
520
+ "#031624",
521
+ ],
522
+ crimson: [
523
+ "#fdf0f3",
524
+ "#f9d8e0",
525
+ "#f3b5c4",
526
+ "#eb8aa3",
527
+ "#de5c7d",
528
+ "#cc3a5a",
529
+ "#aa2740",
530
+ "#881c2e",
531
+ "#651420",
532
+ "#440d16",
533
+ "#22060b",
534
+ ],
535
+ ruby: [
536
+ "#fdf2f4",
537
+ "#f9dce1",
538
+ "#f3bcc4",
539
+ "#eb96a4",
540
+ "#de6d7e",
541
+ "#cc4a5c",
542
+ "#aa333f",
543
+ "#882327",
544
+ "#651819",
545
+ "#440f10",
546
+ "#220708",
547
+ ],
548
+ wine: [
549
+ "#fdf0f3",
550
+ "#f5d3db",
551
+ "#ebaebc",
552
+ "#de8a9f",
553
+ "#cc6681",
554
+ "#b54465",
555
+ "#923350",
556
+ "#702439",
557
+ "#4f1828",
558
+ "#341019",
559
+ "#1a080d",
560
+ ],
561
+ burgundy: [
562
+ "#fdf0f2",
563
+ "#f4d0d7",
564
+ "#e8a8b4",
565
+ "#d97f90",
566
+ "#c65a6e",
567
+ "#ae3c52",
568
+ "#8c2c3e",
569
+ "#6b1e2c",
570
+ "#4c141f",
571
+ "#320d14",
572
+ "#19060a",
573
+ ],
574
+ maroon: [
575
+ "#fdf0f0",
576
+ "#f4cecd",
577
+ "#e8a4a2",
578
+ "#d87a78",
579
+ "#c55553",
580
+ "#ae3836",
581
+ "#8c2826",
582
+ "#6b1c1b",
583
+ "#4c1312",
584
+ "#32090b",
585
+ "#190506",
586
+ ],
587
+ gold: [
588
+ "#fdfbf0",
589
+ "#f9f3d8",
590
+ "#f3e7b5",
591
+ "#ebd88a",
592
+ "#dfc55c",
593
+ "#d0ae3a",
594
+ "#b09227",
595
+ "#8a721c",
596
+ "#655213",
597
+ "#43370d",
598
+ "#221c07",
599
+ ],
600
+ bronze: [
601
+ "#fdf7f0",
602
+ "#f9edd8",
603
+ "#f0dcb5",
604
+ "#e4c78a",
605
+ "#d4ad5c",
606
+ "#bf933a",
607
+ "#9e7827",
608
+ "#7a5c1c",
609
+ "#5a4213",
610
+ "#3c2c0d",
611
+ "#1e1607",
612
+ ],
613
+ copper: [
614
+ "#fdf5f0",
615
+ "#f9e8d8",
616
+ "#f0d4b5",
617
+ "#e4bc8a",
618
+ "#d49f5c",
619
+ "#bf833a",
620
+ "#9e6727",
621
+ "#7a4e1c",
622
+ "#5a3813",
623
+ "#3c240d",
624
+ "#1e1207",
625
+ ],
626
+ lavender: [
627
+ "#f8f5fd",
628
+ "#f0e8fb",
629
+ "#e2d0f8",
630
+ "#d0b5f2",
631
+ "#b894e6",
632
+ "#9e73d6",
633
+ "#8158c0",
634
+ "#63419a",
635
+ "#462c70",
636
+ "#2e1c48",
637
+ "#170e24",
638
+ ],
639
+ lilac: [
640
+ "#faf5fd",
641
+ "#f3e8fb",
642
+ "#e8d0f8",
643
+ "#d8b4f2",
644
+ "#c294e6",
645
+ "#a874d6",
646
+ "#8a58c0",
647
+ "#6b419a",
648
+ "#4c2c70",
649
+ "#321c48",
650
+ "#190e24",
651
+ ],
652
+ mauve: [
653
+ "#fdf5fc",
654
+ "#f7e5f5",
655
+ "#eedaec",
656
+ "#e0c4e0",
657
+ "#cca4cc",
658
+ "#b484b4",
659
+ "#946894",
660
+ "#724f72",
661
+ "#523852",
662
+ "#352435",
663
+ "#1a111a",
664
+ ],
665
+ orchid: [
666
+ "#fdf2fc",
667
+ "#f9d8f8",
668
+ "#f3b5f2",
669
+ "#eb8aec",
670
+ "#df5de2",
671
+ "#cc3acc",
672
+ "#aa28aa",
673
+ "#881b88",
674
+ "#651265",
675
+ "#440c44",
676
+ "#220622",
677
+ ],
678
+ coral: [
679
+ "#fdf3f0",
680
+ "#f9ddd8",
681
+ "#f3bdb5",
682
+ "#eb988a",
683
+ "#e06e5c",
684
+ "#cc4e3a",
685
+ "#aa3827",
686
+ "#88271c",
687
+ "#651b12",
688
+ "#44110d",
689
+ "#220807",
690
+ ],
691
+ salmon: [
692
+ "#fdf4f2",
693
+ "#f9e2dc",
694
+ "#f3c6bc",
695
+ "#eba498",
696
+ "#e07e6c",
697
+ "#cc5e4a",
698
+ "#aa4234",
699
+ "#882e24",
700
+ "#651f18",
701
+ "#44130f",
702
+ "#220908",
703
+ ],
704
+ peach: [
705
+ "#fdf7f3",
706
+ "#f9edd8",
707
+ "#f3dfc2",
708
+ "#ebcc9e",
709
+ "#e0b478",
710
+ "#cc9850",
711
+ "#aa7a38",
712
+ "#885e28",
713
+ "#65431c",
714
+ "#442c12",
715
+ "#221609",
716
+ ],
717
+ turquoise: [
718
+ "#f0fdfb",
719
+ "#d5f9f4",
720
+ "#aaf2ea",
721
+ "#72e8dc",
722
+ "#38d9ca",
723
+ "#14c4b4",
724
+ "#0da48e",
725
+ "#088070",
726
+ "#065c50",
727
+ "#043c34",
728
+ "#021e1b",
729
+ ],
730
+ aqua: [
731
+ "#f0fdfd",
732
+ "#d5f9f9",
733
+ "#aaf2f3",
734
+ "#72e8ea",
735
+ "#38d9de",
736
+ "#14c4ca",
737
+ "#0da4ab",
738
+ "#08808a",
739
+ "#065c64",
740
+ "#043c42",
741
+ "#021e22",
742
+ ],
743
+ };
744
+
745
+ function colorHex(name, scale) {
746
+ const p = PALETTES[name];
747
+ return p && scale >= 0 && scale <= 10 ? p[scale] : null;
748
+ }
749
+
750
+ function hexToRgba(hex, opacity) {
751
+ const h = hex.replace("#", "");
752
+ const r = parseInt(h.slice(0, 2), 16);
753
+ const g = parseInt(h.slice(2, 4), 16);
754
+ const b = parseInt(h.slice(4, 6), 16);
755
+ return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
756
+ }
757
+
758
+ // Parse "palette-scale" or "palette-scale/opacity" → color string
759
+ function parseColor(str) {
760
+ const named = str.match(/^(white|black|transparent)(?:\/(\d+))?$/);
761
+ if (named) {
762
+ const name = named[1];
763
+ const opacity = named[2] !== undefined ? parseInt(named[2], 10) : null;
764
+ if (name === "transparent") return "transparent";
765
+ const base = name === "white" ? "#ffffff" : "#000000";
766
+ return opacity !== null ? hexToRgba(base, opacity) : base;
767
+ }
768
+
769
+ const m = str.match(/^(.+)-(\d+)(?:\/(\d+))?$/);
770
+ if (!m) return null;
771
+ const hex = colorHex(m[1], parseInt(m[2]));
772
+ if (!hex) return null;
773
+ const opacity = m[3] !== undefined ? parseInt(m[3]) : null;
774
+ return opacity !== null ? hexToRgba(hex, opacity) : hex;
775
+ }
776
+
777
+ // ---------------------------------------------------------------------------
778
+ // Gradient system
779
+ // ---------------------------------------------------------------------------
780
+
781
+ // LinearGradient start/end for each direction
782
+ const GRADIENT_DIRS = {
783
+ "to-r": { start: { x: 0, y: 0.5 }, end: { x: 1, y: 0.5 } },
784
+ "to-l": { start: { x: 1, y: 0.5 }, end: { x: 0, y: 0.5 } },
785
+ "to-b": { start: { x: 0.5, y: 0 }, end: { x: 0.5, y: 1 } },
786
+ "to-t": { start: { x: 0.5, y: 1 }, end: { x: 0.5, y: 0 } },
787
+ "to-br": { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },
788
+ "to-bl": { start: { x: 1, y: 0 }, end: { x: 0, y: 1 } },
789
+ "to-tr": { start: { x: 0, y: 1 }, end: { x: 1, y: 0 } },
790
+ "to-tl": { start: { x: 1, y: 1 }, end: { x: 0, y: 0 } },
791
+ };
792
+
793
+ // 54 named gradient presets (use as: className="gradient-ocean")
794
+ const GRADIENT_PRESETS = {
795
+ // Blues
796
+ ocean: { colors: ["#0ea5e9", "#3b82f6", "#8b5cf6"], dir: "to-br" },
797
+ sky: { colors: ["#e0f2fe", "#7dd3fc", "#38bdf8"], dir: "to-b" },
798
+ "ocean-deep": { colors: ["#172554", "#1e3a8a", "#1d4ed8"], dir: "to-b" },
799
+ arctic: { colors: ["#ecfeff", "#cffafe", "#a5f3fc"], dir: "to-b" },
800
+ "cobalt-blue": { colors: ["#1e40af", "#2563eb", "#3b82f6"], dir: "to-tr" },
801
+ cyber: { colors: ["#0f172a", "#0e7490", "#22d3ee"], dir: "to-r" },
802
+ ice: { colors: ["#ecfeff", "#e0f2fe", "#eff6ff"], dir: "to-r" },
803
+ // Purples
804
+ cosmic: { colors: ["#7c3aed", "#6d28d9", "#4c1d95"], dir: "to-b" },
805
+ galaxy: { colors: ["#312e81", "#4f46e5", "#7c3aed", "#a855f7"], dir: "to-r" },
806
+ aurora: { colors: ["#4f46e5", "#7c3aed", "#ec4899"], dir: "to-r" },
807
+ dusk: { colors: ["#1e1b4b", "#4338ca", "#7c3aed"], dir: "to-tr" },
808
+ "lavender-mist": { colors: ["#ede9fe", "#c4b5fd", "#8b5cf6"], dir: "to-b" },
809
+ "royal-purple": { colors: ["#2e1065", "#4c1d95", "#6d28d9"], dir: "to-b" },
810
+ grape: { colors: ["#faf5ff", "#d8b4fe", "#7c3aed"], dir: "to-b" },
811
+ "violet-haze": {
812
+ colors: ["#f5f3ff", "#ede9fe", "#c4b5fd", "#8b5cf6"],
813
+ dir: "to-b",
814
+ },
815
+ // Pinks & Roses
816
+ sunset: { colors: ["#f97316", "#ec4899", "#8b5cf6"], dir: "to-r" },
817
+ "rose-glow": { colors: ["#fff1f2", "#fda4af", "#fb7185"], dir: "to-b" },
818
+ flamingo: { colors: ["#fce7f3", "#fbcfe8", "#f9a8d4"], dir: "to-b" },
819
+ candy: { colors: ["#ec4899", "#f43f5e", "#fb923c"], dir: "to-r" },
820
+ blossom: { colors: ["#fdf2f8", "#fbcfe8", "#f472b6"], dir: "to-b" },
821
+ "dusk-rose": { colors: ["#4c1d95", "#be185d", "#f97316"], dir: "to-r" },
822
+ "peach-sky": { colors: ["#fed7aa", "#fca5a5", "#c4b5fd"], dir: "to-r" },
823
+ // Reds & Oranges
824
+ fire: { colors: ["#ef4444", "#f97316", "#eab308"], dir: "to-r" },
825
+ volcano: { colors: ["#7f1d1d", "#991b1b", "#dc2626"], dir: "to-b" },
826
+ lava: { colors: ["#f97316", "#ef4444", "#dc2626"], dir: "to-b" },
827
+ autumn: { colors: ["#a16207", "#b45309", "#c2410c"], dir: "to-b" },
828
+ ember: { colors: ["#fef9c3", "#fde047", "#f97316"], dir: "to-b" },
829
+ heatwave: { colors: ["#fef9c3", "#f97316", "#dc2626"], dir: "to-b" },
830
+ "crimson-tide": { colors: ["#7f1d1d", "#be123c", "#e11d48"], dir: "to-tr" },
831
+ // Greens
832
+ "forest-deep": { colors: ["#14532d", "#166534", "#15803d"], dir: "to-b" },
833
+ "emerald-shine": { colors: ["#d1fae5", "#6ee7b7", "#10b981"], dir: "to-b" },
834
+ jungle: { colors: ["#052e16", "#14532d", "#166534"], dir: "to-b" },
835
+ spring: { colors: ["#ecfdf5", "#bbf7d0", "#4ade80"], dir: "to-b" },
836
+ minty: { colors: ["#ccfbf1", "#99f6e4", "#2dd4bf"], dir: "to-b" },
837
+ nature: { colors: ["#84cc16", "#22c55e", "#10b981"], dir: "to-r" },
838
+ meadow: { colors: ["#bbf7d0", "#6ee7b7", "#34d399"], dir: "to-br" },
839
+ // Yellows & Golds
840
+ "gold-shine": { colors: ["#fef9c3", "#fef08a", "#fde047"], dir: "to-b" },
841
+ sunshine: { colors: ["#fefce8", "#fef9c3", "#fef08a"], dir: "to-b" },
842
+ honey: { colors: ["#fcd34d", "#f59e0b", "#d97706"], dir: "to-b" },
843
+ "amber-glow": { colors: ["#fffbeb", "#fde68a", "#fbbf24"], dir: "to-b" },
844
+ sahara: { colors: ["#fef3c7", "#fbbf24", "#d97706"], dir: "to-b" },
845
+ // Neutrals & Dark
846
+ midnight: { colors: ["#020617", "#0f172a", "#1e293b"], dir: "to-b" },
847
+ charcoal: { colors: ["#111827", "#1f2937", "#374151"], dir: "to-b" },
848
+ silver: { colors: ["#f9fafb", "#e5e7eb", "#9ca3af"], dir: "to-b" },
849
+ "dark-night": { colors: ["#030712", "#111827", "#374151"], dir: "to-b" },
850
+ smoke: { colors: ["#f8fafc", "#e2e8f0", "#94a3b8"], dir: "to-b" },
851
+ storm: { colors: ["#e2e8f0", "#64748b", "#0f172a"], dir: "to-b" },
852
+ "deep-space": { colors: ["#020617", "#1e1b4b", "#4c1d95"], dir: "to-b" },
853
+ // Mixed / Multicolor
854
+ rainbow: {
855
+ colors: ["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6", "#8b5cf6"],
856
+ dir: "to-r",
857
+ },
858
+ neon: { colors: ["#22d3ee", "#34d399", "#a3e635"], dir: "to-r" },
859
+ tropical: { colors: ["#06b6d4", "#10b981", "#84cc16"], dir: "to-r" },
860
+ "ocean-sunset": { colors: ["#0ea5e9", "#8b5cf6", "#f43f5e"], dir: "to-r" },
861
+ "northern-lights": { colors: ["#22d3ee", "#4ade80", "#a78bfa"], dir: "to-r" },
862
+ beach: { colors: ["#fef9c3", "#fed7aa", "#5eead4"], dir: "to-r" },
863
+ };
864
+
865
+ // Detect if a class is gradient-related (including dark: variants)
866
+ function isGradientClass(c) {
867
+ const base = c.startsWith("dark:") ? c.slice(5) : c;
868
+ return (
869
+ base === "bg-gradient" ||
870
+ base.startsWith("bg-gradient-to-") ||
871
+ base.startsWith("from-") ||
872
+ base.startsWith("via-") ||
873
+ base.startsWith("to-") ||
874
+ base.startsWith("gradient-")
875
+ );
876
+ }
877
+
878
+ // Extract dark-variant gradient config from a list of classes
879
+ function extractDarkGradient(classes) {
880
+ const darkClasses = classes
881
+ .filter((c) => c.startsWith("dark:") && isGradientClass(c.slice(5)))
882
+ .map((c) => c.slice(5));
883
+ if (!darkClasses.length) return null;
884
+ return extractGradient(darkClasses);
885
+ }
886
+
887
+ // Resolve "blue-5", "blue-5/50", "white", "black", "transparent" to a color string
888
+ function resolveGradientColor(str) {
889
+ if (str === "white") return "#ffffff";
890
+ if (str === "black") return "#000000";
891
+ if (str === "transparent") return "transparent";
892
+ return parseColor(str);
893
+ }
894
+
895
+ // Extract gradient config from a list of classes, or null if none
896
+ function extractGradient(classes) {
897
+ // Named preset: gradient-ocean, gradient-sunset, etc.
898
+ const named = classes.find((c) => c.startsWith("gradient-"));
899
+ if (named) {
900
+ const preset = GRADIENT_PRESETS[named.slice(9)];
901
+ if (preset) {
902
+ const dir = GRADIENT_DIRS[preset.dir] || GRADIENT_DIRS["to-b"];
903
+ return { colors: preset.colors, ...dir };
904
+ }
905
+ }
906
+
907
+ // Utility: bg-gradient-to-r from-blue-5 via-green-3 to-purple-5
908
+ const dirClass = classes.find((c) => c.startsWith("bg-gradient-to-"));
909
+ const fromClass = classes.find((c) => c.startsWith("from-"));
910
+ const toClass = classes.find(
911
+ (c) => c.startsWith("to-") && !c.startsWith("top-"),
912
+ );
913
+ const viaClass = classes.find((c) => c.startsWith("via-"));
914
+
915
+ if (!fromClass && !toClass) return null;
916
+
917
+ const dirKey = dirClass ? dirClass.replace("bg-gradient-", "") : "to-b";
918
+ const dir = GRADIENT_DIRS[dirKey] || GRADIENT_DIRS["to-b"];
919
+
920
+ const colors = [];
921
+ if (fromClass) {
922
+ const c = resolveGradientColor(fromClass.slice(5));
923
+ if (c) colors.push(c);
924
+ }
925
+ if (viaClass) {
926
+ const c = resolveGradientColor(viaClass.slice(4));
927
+ if (c) colors.push(c);
928
+ }
929
+ if (toClass) {
930
+ const c = resolveGradientColor(toClass.slice(3));
931
+ if (c) colors.push(c);
932
+ }
933
+
934
+ if (colors.length < 2) return null;
935
+ return { colors, ...dir };
936
+ }
937
+
938
+ function cssToRn(css) {
939
+ if (!css) return null;
940
+ if (css.endsWith("rem")) return parseFloat(css) * 16;
941
+ if (css.endsWith("px")) return parseInt(css, 10);
942
+ return css;
943
+ }
944
+
945
+ const FONT_SIZES = {
946
+ xs: 12,
947
+ sm: 14,
948
+ base: 16,
949
+ lg: 18,
950
+ xl: 20,
951
+ "2xl": 24,
952
+ "3xl": 30,
953
+ "4xl": 36,
954
+ "5xl": 48,
955
+ "6xl": 60,
956
+ "7xl": 72,
957
+ "8xl": 96,
958
+ "9xl": 128,
959
+ };
960
+
961
+ const RADII = {
962
+ none: 0,
963
+ sm: 2,
964
+ "": 4,
965
+ md: 6,
966
+ lg: 8,
967
+ xl: 12,
968
+ "2xl": 16,
969
+ "3xl": 24,
970
+ full: 9999,
971
+ };
972
+
973
+ const JUSTIFY = {
974
+ start: "flex-start",
975
+ end: "flex-end",
976
+ center: "center",
977
+ between: "space-between",
978
+ around: "space-around",
979
+ evenly: "space-evenly",
980
+ };
981
+ const ALIGN = {
982
+ start: "flex-start",
983
+ end: "flex-end",
984
+ center: "center",
985
+ baseline: "baseline",
986
+ stretch: "stretch",
987
+ };
988
+
989
+ const TRACKING = {
990
+ tighter: -0.8,
991
+ tight: -0.4,
992
+ normal: 0,
993
+ wide: 0.4,
994
+ wider: 0.8,
995
+ widest: 1.6,
996
+ };
997
+
998
+ const LEADING = {
999
+ none: 16,
1000
+ tight: 20,
1001
+ snug: 22,
1002
+ normal: 24,
1003
+ relaxed: 26,
1004
+ loose: 32,
1005
+ };
1006
+
1007
+ // Responsive breakpoints (mobile-first, min-width in px)
1008
+ const BREAKPOINT_MIN = {
1009
+ xs: 480,
1010
+ sm: 640,
1011
+ md: 768,
1012
+ lg: 1024,
1013
+ xl: 1280,
1014
+ "2xl": 1536,
1015
+ };
1016
+
1017
+ const GRADIENT_HOST_TAGS = new Set([
1018
+ "View",
1019
+ "ImageBackground",
1020
+ "ScrollView",
1021
+ "Pressable",
1022
+ "TouchableOpacity",
1023
+ "TouchableHighlight",
1024
+ "Link",
1025
+ ]);
1026
+
1027
+ // Removed: SafeAreaView gradient wrapping logic
1028
+
1029
+ const CLASSNAME_HOST_TAGS = new Set([
1030
+ "View",
1031
+ "Text",
1032
+ "ScrollView",
1033
+ "SafeAreaView",
1034
+ "Image",
1035
+ "ImageBackground",
1036
+ "Pressable",
1037
+ "TouchableOpacity",
1038
+ "TouchableHighlight",
1039
+ "TouchableWithoutFeedback",
1040
+ "TextInput",
1041
+ "FlatList",
1042
+ "SectionList",
1043
+ "Link",
1044
+ ]);
1045
+
1046
+ function classToStyle(raw) {
1047
+ const neg = raw.startsWith("-");
1048
+ const c = neg ? raw.slice(1) : raw;
1049
+ let m;
1050
+ function signed(v) {
1051
+ return typeof v === "number" ? (neg ? -v : v) : v;
1052
+ }
1053
+ function spS(step) {
1054
+ const v = sp(step);
1055
+ return v === null ? null : signed(v);
1056
+ }
1057
+
1058
+ if ((m = c.match(/^p-(.+)$/))) {
1059
+ const v = spS(m[1]);
1060
+ if (v !== null) return { padding: v };
1061
+ }
1062
+ if ((m = c.match(/^pt-(.+)$/))) {
1063
+ const v = spS(m[1]);
1064
+ if (v !== null) return { paddingTop: v };
1065
+ }
1066
+ if ((m = c.match(/^pr-(.+)$/))) {
1067
+ const v = spS(m[1]);
1068
+ if (v !== null) return { paddingRight: v };
1069
+ }
1070
+ if ((m = c.match(/^pb-(.+)$/))) {
1071
+ const v = spS(m[1]);
1072
+ if (v !== null) return { paddingBottom: v };
1073
+ }
1074
+ if ((m = c.match(/^pl-(.+)$/))) {
1075
+ const v = spS(m[1]);
1076
+ if (v !== null) return { paddingLeft: v };
1077
+ }
1078
+ if ((m = c.match(/^px-(.+)$/))) {
1079
+ const v = spS(m[1]);
1080
+ if (v !== null) return { paddingHorizontal: v };
1081
+ }
1082
+ if ((m = c.match(/^py-(.+)$/))) {
1083
+ const v = spS(m[1]);
1084
+ if (v !== null) return { paddingVertical: v };
1085
+ }
1086
+
1087
+ if ((m = c.match(/^m-(.+)$/))) {
1088
+ const v = spS(m[1]);
1089
+ if (v !== null) return { margin: v };
1090
+ }
1091
+ if ((m = c.match(/^mt-(.+)$/))) {
1092
+ const v = spS(m[1]);
1093
+ if (v !== null) return { marginTop: v };
1094
+ }
1095
+ if ((m = c.match(/^mr-(.+)$/))) {
1096
+ const v = spS(m[1]);
1097
+ if (v !== null) return { marginRight: v };
1098
+ }
1099
+ if ((m = c.match(/^mb-(.+)$/))) {
1100
+ const v = spS(m[1]);
1101
+ if (v !== null) return { marginBottom: v };
1102
+ }
1103
+ if ((m = c.match(/^ml-(.+)$/))) {
1104
+ const v = spS(m[1]);
1105
+ if (v !== null) return { marginLeft: v };
1106
+ }
1107
+ if ((m = c.match(/^mx-(.+)$/))) {
1108
+ const v = spS(m[1]);
1109
+ if (v !== null) return { marginHorizontal: v };
1110
+ }
1111
+ if ((m = c.match(/^my-(.+)$/))) {
1112
+ const v = spS(m[1]);
1113
+ if (v !== null) return { marginVertical: v };
1114
+ }
1115
+
1116
+ if ((m = c.match(/^w-(.+)$/))) {
1117
+ const arb = m[1].match(/^\[(.+)\]$/);
1118
+ if (arb) {
1119
+ const v = cssToRn(arb[1]);
1120
+ if (v !== null) return { width: v };
1121
+ }
1122
+ const v = spS(m[1]);
1123
+ if (v !== null) return { width: v };
1124
+ }
1125
+ if ((m = c.match(/^h-(.+)$/))) {
1126
+ const arb = m[1].match(/^\[(.+)\]$/);
1127
+ if (arb) {
1128
+ const v = cssToRn(arb[1]);
1129
+ if (v !== null) return { height: v };
1130
+ }
1131
+ const v = spS(m[1]);
1132
+ if (v !== null) return { height: v };
1133
+ }
1134
+ if ((m = c.match(/^min-w-(.+)$/))) {
1135
+ const v = sp(m[1]);
1136
+ if (v !== null) return { minWidth: v };
1137
+ }
1138
+ if ((m = c.match(/^max-w-(.+)$/))) {
1139
+ const arb = m[1].match(/^\[(.+)\]$/);
1140
+ if (arb) {
1141
+ const v = cssToRn(arb[1]);
1142
+ if (v !== null) return { maxWidth: v };
1143
+ }
1144
+ const v = sp(m[1]);
1145
+ if (v !== null) return { maxWidth: v };
1146
+ }
1147
+ if ((m = c.match(/^min-h-(.+)$/))) {
1148
+ const v = sp(m[1]);
1149
+ if (v !== null) return { minHeight: v };
1150
+ }
1151
+ if ((m = c.match(/^max-h-(.+)$/))) {
1152
+ const arb = m[1].match(/^\[(.+)\]$/);
1153
+ if (arb) {
1154
+ const v = cssToRn(arb[1]);
1155
+ if (v !== null) return { maxHeight: v };
1156
+ }
1157
+ const v = sp(m[1]);
1158
+ if (v !== null) return { maxHeight: v };
1159
+ }
1160
+ if ((m = c.match(/^size-(.+)$/))) {
1161
+ const v = spS(m[1]);
1162
+ if (v !== null) return { width: v, height: v };
1163
+ }
1164
+
1165
+ if (c === "flex" || c === "inline-flex") return { flex: 1 };
1166
+ if ((m = c.match(/^flex-(\d+(?:\.\d+)?)$/)))
1167
+ return { flex: parseFloat(m[1]) };
1168
+ if (c === "flex-row") return { flexDirection: "row" };
1169
+ if (c === "flex-col") return { flexDirection: "column" };
1170
+ if (c === "flex-row-reverse") return { flexDirection: "row-reverse" };
1171
+ if (c === "flex-col-reverse") return { flexDirection: "column-reverse" };
1172
+ if (c === "flex-wrap") return { flexWrap: "wrap" };
1173
+ if (c === "flex-nowrap") return { flexWrap: "nowrap" };
1174
+ if (c === "flex-wrap-reverse") return { flexWrap: "wrap-reverse" };
1175
+ if (c === "flex-auto") return { flex: 1, flexBasis: "auto" };
1176
+ if (c === "flex-none") return { flex: 0 };
1177
+ if ((m = c.match(/^grow(?:-(\d+))?$/)))
1178
+ return { flexGrow: m[1] ? parseInt(m[1]) : 1 };
1179
+ if ((m = c.match(/^shrink(?:-(\d+))?$/)))
1180
+ return { flexShrink: m[1] ? parseInt(m[1]) : 1 };
1181
+
1182
+ if ((m = c.match(/^justify-(.+)$/)))
1183
+ return { justifyContent: JUSTIFY[m[1]] || m[1] };
1184
+ if ((m = c.match(/^items-(.+)$/))) return { alignItems: ALIGN[m[1]] || m[1] };
1185
+ if ((m = c.match(/^self-(.+)$/))) return { alignSelf: ALIGN[m[1]] || m[1] };
1186
+ if ((m = c.match(/^content-(.+)$/)))
1187
+ return { alignContent: ALIGN[m[1]] || m[1] };
1188
+
1189
+ if ((m = c.match(/^gap-x-(.+)$/))) {
1190
+ const v = sp(m[1]);
1191
+ if (v !== null) return { columnGap: v };
1192
+ }
1193
+ if ((m = c.match(/^gap-y-(.+)$/))) {
1194
+ const v = sp(m[1]);
1195
+ if (v !== null) return { rowGap: v };
1196
+ }
1197
+ if ((m = c.match(/^gap-(.+)$/))) {
1198
+ const v = sp(m[1]);
1199
+ if (v !== null) return { gap: v };
1200
+ }
1201
+
1202
+ // NativeWind-style custom font utility: font-ubuntu, font-ubuntu-bold, font-ubuntu-italic, etc.
1203
+ if ((m = c.match(/^font-([a-zA-Z0-9_-]+)$/))) {
1204
+ // Split by dash/underscore/camelCase
1205
+ let parts = m[1]
1206
+ .replace(/([a-z])([A-Z])/g, "$1-$2")
1207
+ .split(/[-_]/)
1208
+ .filter(Boolean);
1209
+ // Capitalize each part
1210
+ let fam = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
1211
+ return { fontFamily: fam };
1212
+ }
1213
+ if ((m = c.match(/^font-size-(.+)$/))) {
1214
+ const arb = m[1].match(/^\[(.+)\]$/);
1215
+ if (arb) {
1216
+ const v = cssToRn(arb[1]);
1217
+ if (v !== null) return { fontSize: v };
1218
+ }
1219
+ const v = FONT_SIZES[m[1]];
1220
+ if (v !== undefined) return { fontSize: v };
1221
+ }
1222
+ if ((m = c.match(/^font-weight-(\d+)$/))) return { fontWeight: m[1] };
1223
+ if (c === "font-bold") return { fontWeight: "700" };
1224
+ if (c === "font-semibold") return { fontWeight: "600" };
1225
+ if (c === "font-medium") return { fontWeight: "500" };
1226
+ if (c === "font-normal") return { fontWeight: "400" };
1227
+ if (c === "font-light") return { fontWeight: "300" };
1228
+ if (c === "font-italic" || c === "italic") return { fontStyle: "italic" };
1229
+ if (c === "not-italic") return { fontStyle: "normal" };
1230
+ if (c === "text-left") return { textAlign: "left" };
1231
+ if (c === "text-center") return { textAlign: "center" };
1232
+ if (c === "text-right") return { textAlign: "right" };
1233
+ if ((m = c.match(/^text-(.+)$/))) {
1234
+ const sz = FONT_SIZES[m[1]];
1235
+ if (sz !== undefined) return { fontSize: sz };
1236
+ if (m[1] === "white") return { color: "#ffffff" };
1237
+ if (m[1] === "black") return { color: "#000000" };
1238
+ const col = parseColor(m[1]);
1239
+ if (col) return { color: col };
1240
+ }
1241
+ if (c === "underline") return { textDecorationLine: "underline" };
1242
+ if (c === "line-through") return { textDecorationLine: "line-through" };
1243
+ if (c === "no-underline") return { textDecorationLine: "none" };
1244
+ if (c === "uppercase") return { textTransform: "uppercase" };
1245
+ if (c === "lowercase") return { textTransform: "lowercase" };
1246
+ if (c === "capitalize") return { textTransform: "capitalize" };
1247
+
1248
+ if (c === "bg-white") return { backgroundColor: "#ffffff" };
1249
+ if (c === "bg-black") return { backgroundColor: "#000000" };
1250
+ if (c === "bg-transparent") return { backgroundColor: "transparent" };
1251
+ if ((m = c.match(/^bg-(.+)$/))) {
1252
+ const col = parseColor(m[1]);
1253
+ if (col) return { backgroundColor: col };
1254
+ }
1255
+
1256
+ if ((m = c.match(/^border-(.+)$/))) {
1257
+ const col = parseColor(m[1]);
1258
+ if (col) return { borderColor: col };
1259
+ }
1260
+ if (c === "border" || c === "border-1") return { borderWidth: 1 };
1261
+ if ((m = c.match(/^border-(\d+)$/))) return { borderWidth: parseInt(m[1]) };
1262
+ // Only apply default border width for non-Text tags
1263
+ if ((m = c.match(/^border-t(?:-(\d+))?$/))) {
1264
+ if (this && this._tagName === "Text") {
1265
+ if (m[1]) return { borderTopWidth: parseInt(m[1]) };
1266
+ } else {
1267
+ return { borderTopWidth: m[1] ? parseInt(m[1]) : 1 };
1268
+ }
1269
+ }
1270
+ if ((m = c.match(/^border-b(?:-(\d+))?$/))) {
1271
+ if (this && this._tagName === "Text") {
1272
+ if (m[1]) return { borderBottomWidth: parseInt(m[1]) };
1273
+ } else {
1274
+ return { borderBottomWidth: m[1] ? parseInt(m[1]) : 1 };
1275
+ }
1276
+ }
1277
+ if ((m = c.match(/^border-l(?:-(\d+))?$/))) {
1278
+ if (this && this._tagName === "Text") {
1279
+ if (m[1]) return { borderLeftWidth: parseInt(m[1]) };
1280
+ } else {
1281
+ return { borderLeftWidth: m[1] ? parseInt(m[1]) : 1 };
1282
+ }
1283
+ }
1284
+ if ((m = c.match(/^border-r(?:-(\d+))?$/))) {
1285
+ if (this && this._tagName === "Text") {
1286
+ if (m[1]) return { borderRightWidth: parseInt(m[1]) };
1287
+ } else {
1288
+ return { borderRightWidth: m[1] ? parseInt(m[1]) : 1 };
1289
+ }
1290
+ }
1291
+ if (c === "border-solid") return { borderStyle: "solid" };
1292
+ if (c === "border-dashed") return { borderStyle: "dashed" };
1293
+ if (c === "border-dotted") return { borderStyle: "dotted" };
1294
+
1295
+ if ((m = c.match(/^rounded(?:-(.+))?$/))) {
1296
+ const key = m[1] || "";
1297
+ const arb = key.match(/^\[(.+)\]$/);
1298
+ if (arb) {
1299
+ const v = cssToRn(arb[1]);
1300
+ if (v !== null) return { borderRadius: v };
1301
+ }
1302
+ const v = RADII[key];
1303
+ if (v !== undefined) return { borderRadius: v };
1304
+ }
1305
+
1306
+ if (c === "relative") return { position: "relative" };
1307
+ if (c === "absolute") return { position: "absolute" };
1308
+ if ((m = c.match(/^top-(.+)$/))) {
1309
+ const v = spS(m[1]);
1310
+ if (v !== null) return { top: v };
1311
+ }
1312
+ if ((m = c.match(/^right-(.+)$/))) {
1313
+ const v = spS(m[1]);
1314
+ if (v !== null) return { right: v };
1315
+ }
1316
+ if ((m = c.match(/^bottom-(.+)$/))) {
1317
+ const v = spS(m[1]);
1318
+ if (v !== null) return { bottom: v };
1319
+ }
1320
+ if ((m = c.match(/^left-(.+)$/))) {
1321
+ const v = spS(m[1]);
1322
+ if (v !== null) return { left: v };
1323
+ }
1324
+ if ((m = c.match(/^inset-(.+)$/))) {
1325
+ const v = spS(m[1]);
1326
+ if (v !== null) return { top: v, right: v, bottom: v, left: v };
1327
+ }
1328
+ if ((m = c.match(/^z-(-?\d+)$/))) return { zIndex: parseInt(m[1]) };
1329
+
1330
+ if ((m = c.match(/^opacity-(\d+)$/)))
1331
+ return { opacity: parseInt(m[1]) / 100 };
1332
+
1333
+ if (c === "overflow-hidden") return { overflow: "hidden" };
1334
+ if (c === "overflow-visible") return { overflow: "visible" };
1335
+ if (c === "overflow-scroll") return { overflow: "scroll" };
1336
+
1337
+ if (c === "aspect-square") return { aspectRatio: 1 };
1338
+ if (c === "aspect-video") return { aspectRatio: 16 / 9 };
1339
+
1340
+ if (c === "shadow-none")
1341
+ return {
1342
+ shadowColor: "#000",
1343
+ shadowOffset: { width: 0, height: 0 },
1344
+ shadowOpacity: 0,
1345
+ shadowRadius: 0,
1346
+ elevation: 0,
1347
+ };
1348
+ if (c === "shadow-sm")
1349
+ return {
1350
+ shadowColor: "#000",
1351
+ shadowOffset: { width: 0, height: 1 },
1352
+ shadowOpacity: 0.05,
1353
+ shadowRadius: 2,
1354
+ elevation: 1,
1355
+ };
1356
+ if (c === "shadow")
1357
+ return {
1358
+ shadowColor: "#000",
1359
+ shadowOffset: { width: 0, height: 1 },
1360
+ shadowOpacity: 0.1,
1361
+ shadowRadius: 3,
1362
+ elevation: 2,
1363
+ };
1364
+ if (c === "shadow-md")
1365
+ return {
1366
+ shadowColor: "#000",
1367
+ shadowOffset: { width: 0, height: 4 },
1368
+ shadowOpacity: 0.15,
1369
+ shadowRadius: 6,
1370
+ elevation: 4,
1371
+ };
1372
+ if (c === "shadow-lg")
1373
+ return {
1374
+ shadowColor: "#000",
1375
+ shadowOffset: { width: 0, height: 10 },
1376
+ shadowOpacity: 0.15,
1377
+ shadowRadius: 15,
1378
+ elevation: 8,
1379
+ };
1380
+ if (c === "shadow-xl")
1381
+ return {
1382
+ shadowColor: "#000",
1383
+ shadowOffset: { width: 0, height: 20 },
1384
+ shadowOpacity: 0.25,
1385
+ shadowRadius: 25,
1386
+ elevation: 16,
1387
+ };
1388
+ if (c === "shadow-2xl")
1389
+ return {
1390
+ shadowColor: "#000",
1391
+ shadowOffset: { width: 0, height: 25 },
1392
+ shadowOpacity: 0.25,
1393
+ shadowRadius: 50,
1394
+ elevation: 24,
1395
+ };
1396
+
1397
+ // ── Transforms ──────────────────────────────────────────────────────────
1398
+ if ((m = c.match(/^rotate-(-?\d+(?:\.\d+)?)$/)))
1399
+ return { transform: [{ rotate: `${m[1]}deg` }] };
1400
+ if ((m = c.match(/^scale-(\d+(?:\.\d+)?)$/)))
1401
+ return { transform: [{ scale: parseInt(m[1]) / 100 }] };
1402
+ if ((m = c.match(/^scale-x-(\d+(?:\.\d+)?)$/)))
1403
+ return { transform: [{ scaleX: parseInt(m[1]) / 100 }] };
1404
+ if ((m = c.match(/^scale-y-(\d+(?:\.\d+)?)$/)))
1405
+ return { transform: [{ scaleY: parseInt(m[1]) / 100 }] };
1406
+ if ((m = c.match(/^translate-x-(.+)$/))) {
1407
+ const v = spS(m[1]);
1408
+ if (v !== null) return { transform: [{ translateX: v }] };
1409
+ }
1410
+ if ((m = c.match(/^translate-y-(.+)$/))) {
1411
+ const v = spS(m[1]);
1412
+ if (v !== null) return { transform: [{ translateY: v }] };
1413
+ }
1414
+ if ((m = c.match(/^skew-x-(-?\d+(?:\.\d+)?)$/)))
1415
+ return { transform: [{ skewX: `${m[1]}deg` }] };
1416
+ if ((m = c.match(/^skew-y-(-?\d+(?:\.\d+)?)$/)))
1417
+ return { transform: [{ skewY: `${m[1]}deg` }] };
1418
+
1419
+ // ── Border radius per side ───────────────────────────────────────────────
1420
+ if ((m = c.match(/^rounded-tl(?:-(.+))?$/))) {
1421
+ const v = RADII[m[1] || ""];
1422
+ if (v !== undefined) return { borderTopLeftRadius: v };
1423
+ }
1424
+ if ((m = c.match(/^rounded-tr(?:-(.+))?$/))) {
1425
+ const v = RADII[m[1] || ""];
1426
+ if (v !== undefined) return { borderTopRightRadius: v };
1427
+ }
1428
+ if ((m = c.match(/^rounded-bl(?:-(.+))?$/))) {
1429
+ const v = RADII[m[1] || ""];
1430
+ if (v !== undefined) return { borderBottomLeftRadius: v };
1431
+ }
1432
+ if ((m = c.match(/^rounded-br(?:-(.+))?$/))) {
1433
+ const v = RADII[m[1] || ""];
1434
+ if (v !== undefined) return { borderBottomRightRadius: v };
1435
+ }
1436
+ if ((m = c.match(/^rounded-t(?:-(.+))?$/))) {
1437
+ const v = RADII[m[1] || ""];
1438
+ if (v !== undefined)
1439
+ return { borderTopLeftRadius: v, borderTopRightRadius: v };
1440
+ }
1441
+ if ((m = c.match(/^rounded-b(?:-(.+))?$/))) {
1442
+ const v = RADII[m[1] || ""];
1443
+ if (v !== undefined)
1444
+ return { borderBottomLeftRadius: v, borderBottomRightRadius: v };
1445
+ }
1446
+ if ((m = c.match(/^rounded-l(?:-(.+))?$/))) {
1447
+ const v = RADII[m[1] || ""];
1448
+ if (v !== undefined)
1449
+ return { borderTopLeftRadius: v, borderBottomLeftRadius: v };
1450
+ }
1451
+ if ((m = c.match(/^rounded-r(?:-(.+))?$/))) {
1452
+ const v = RADII[m[1] || ""];
1453
+ if (v !== undefined)
1454
+ return { borderTopRightRadius: v, borderBottomRightRadius: v };
1455
+ }
1456
+
1457
+ // ── Typography extras ────────────────────────────────────────────────────
1458
+ if ((m = c.match(/^tracking-(.+)$/))) {
1459
+ const v = TRACKING[m[1]];
1460
+ if (v !== undefined) return { letterSpacing: v };
1461
+ }
1462
+ if ((m = c.match(/^leading-(.+)$/))) {
1463
+ const v = LEADING[m[1]];
1464
+ if (v !== undefined) return { lineHeight: v };
1465
+ const sv = sp(m[1]);
1466
+ if (sv !== null) return { lineHeight: sv };
1467
+ }
1468
+
1469
+ // ── Display ──────────────────────────────────────────────────────────────
1470
+ if (c === "hidden") return { display: "none" };
1471
+
1472
+ // ── Text decoration extras ───────────────────────────────────────────────
1473
+ if ((m = c.match(/^decoration-(.+)$/))) {
1474
+ const col = parseColor(m[1]);
1475
+ if (col) return { textDecorationColor: col };
1476
+ if (m[1] === "solid") return { textDecorationStyle: "solid" };
1477
+ if (m[1] === "double") return { textDecorationStyle: "double" };
1478
+ if (m[1] === "dotted") return { textDecorationStyle: "dotted" };
1479
+ if (m[1] === "dashed") return { textDecorationStyle: "dashed" };
1480
+ if (m[1] === "wavy") return { textDecorationStyle: "solid" }; // no wavy in RN
1481
+ }
1482
+
1483
+ return null;
1484
+ }
1485
+
1486
+ function classStringToStyle(str) {
1487
+ const merged = {};
1488
+ for (const cls of str.trim().split(/\s+/)) {
1489
+ if (!cls || cls.includes(":")) continue;
1490
+ const s = classToStyle(cls);
1491
+ if (s) Object.assign(merged, s);
1492
+ }
1493
+ return Object.keys(merged).length > 0 ? merged : null;
1494
+ }
1495
+
1496
+ // Returns {
1497
+ // base: {},
1498
+ // dark: {},
1499
+ // responsive: { sm:{}, md:{}, lg:{}, xl:{}, "2xl":{}, xs:{} },
1500
+ // states: { hover:{}, focus:{}, active:{}, disabled:{} },
1501
+ // compound: [{ dark, bp, state, style }]
1502
+ // }
1503
+ function classStringToStyleFull(str) {
1504
+ const base = {};
1505
+ const dark = {};
1506
+ const responsive = {};
1507
+ const states = { hover: {}, focus: {}, active: {}, disabled: {} };
1508
+ const compoundMap = new Map();
1509
+ const stateVariants = ["hover", "focus", "active", "disabled"];
1510
+
1511
+ function addCompound(darkOn, bp, state, styleObj) {
1512
+ const key = `${darkOn ? 1 : 0}|${bp || ""}|${state || ""}`;
1513
+ if (!compoundMap.has(key)) {
1514
+ compoundMap.set(key, {
1515
+ dark: !!darkOn,
1516
+ bp: bp || null,
1517
+ state: state || null,
1518
+ style: {},
1519
+ });
1520
+ }
1521
+ Object.assign(compoundMap.get(key).style, styleObj);
1522
+ }
1523
+
1524
+ for (const cls of str.trim().split(/\s+/)) {
1525
+ if (!cls) continue;
1526
+
1527
+ let rem = cls;
1528
+ let needDark = false;
1529
+ let needBp = null;
1530
+ let needState = null;
1531
+
1532
+ for (let guard = 0; guard < 6; guard++) {
1533
+ if (rem.startsWith("dark:")) {
1534
+ needDark = true;
1535
+ rem = rem.slice(5);
1536
+ continue;
1537
+ }
1538
+
1539
+ const bpM = rem.match(/^(xs|sm|md|lg|xl|2xl):/);
1540
+ if (bpM) {
1541
+ needBp = bpM[1];
1542
+ rem = rem.slice(bpM[0].length);
1543
+ continue;
1544
+ }
1545
+
1546
+ const sv = stateVariants.find((v) => rem.startsWith(`${v}:`));
1547
+ if (sv) {
1548
+ needState = sv;
1549
+ rem = rem.slice(sv.length + 1);
1550
+ continue;
1551
+ }
1552
+
1553
+ break;
1554
+ }
1555
+
1556
+ if (!rem || rem.includes(":")) continue;
1557
+ const s = classToStyle(rem);
1558
+ if (!s) continue;
1559
+
1560
+ if (!needDark && !needBp && !needState) {
1561
+ Object.assign(base, s);
1562
+ } else if (needDark && !needBp && !needState) {
1563
+ Object.assign(dark, s);
1564
+ } else if (!needDark && needBp && !needState) {
1565
+ if (!responsive[needBp]) responsive[needBp] = {};
1566
+ Object.assign(responsive[needBp], s);
1567
+ } else if (!needDark && !needBp && needState) {
1568
+ Object.assign(states[needState], s);
1569
+ } else {
1570
+ addCompound(needDark, needBp, needState, s);
1571
+ }
1572
+ }
1573
+
1574
+ const compound = Array.from(compoundMap.values()).filter(
1575
+ (e) => Object.keys(e.style).length > 0,
1576
+ );
1577
+
1578
+ return { base, dark, responsive, states, compound };
1579
+ }
1580
+
1581
+ function valueToAst(t, v) {
1582
+ if (typeof v === "number") return t.numericLiteral(v);
1583
+ if (typeof v === "object" && v !== null)
1584
+ return t.objectExpression(
1585
+ Object.entries(v).map(([k2, v2]) =>
1586
+ t.objectProperty(t.identifier(k2), valueToAst(t, v2)),
1587
+ ),
1588
+ );
1589
+ return t.stringLiteral(String(v));
1590
+ }
1591
+
1592
+ function styleToAst(t, style) {
1593
+ return t.objectExpression(
1594
+ Object.entries(style).map(([k, v]) =>
1595
+ t.objectProperty(t.identifier(k), valueToAst(t, v)),
1596
+ ),
1597
+ );
1598
+ }
1599
+
1600
+ module.exports = function forgeBabelPlugin(api) {
1601
+ const t = api.types;
1602
+ const tmpl = api.template;
1603
+ // Build AST for a {x, y} coordinate object
1604
+ function coordObj(x, y) {
1605
+ return t.objectExpression([
1606
+ t.objectProperty(t.identifier("x"), t.numericLiteral(x)),
1607
+ t.objectProperty(t.identifier("y"), t.numericLiteral(y)),
1608
+ ]);
1609
+ }
1610
+
1611
+ function runtimeArrayExpr(expr) {
1612
+ return t.conditionalExpression(
1613
+ t.callExpression(
1614
+ t.memberExpression(t.identifier("Array"), t.identifier("isArray")),
1615
+ [expr],
1616
+ ),
1617
+ expr,
1618
+ t.conditionalExpression(
1619
+ expr,
1620
+ t.arrayExpression([expr]),
1621
+ t.arrayExpression([]),
1622
+ ),
1623
+ );
1624
+ }
1625
+
1626
+ function isLikelyComponentFunction(fnPath) {
1627
+ const n = fnPath && fnPath.node;
1628
+ if (!n) return false;
1629
+
1630
+ if (t.isFunctionDeclaration(n) && n.id && /^[A-Z]/.test(n.id.name)) {
1631
+ return true;
1632
+ }
1633
+
1634
+ const parent = fnPath.parentPath;
1635
+ if (
1636
+ parent &&
1637
+ parent.isVariableDeclarator() &&
1638
+ t.isIdentifier(parent.node.id) &&
1639
+ /^[A-Z]/.test(parent.node.id.name)
1640
+ ) {
1641
+ return true;
1642
+ }
1643
+
1644
+ // default-exported component functions are common in RN apps
1645
+ if (parent && parent.isExportDefaultDeclaration()) {
1646
+ return true;
1647
+ }
1648
+
1649
+ return false;
1650
+ }
1651
+
1652
+ function findComponentFunctionPath(startPath) {
1653
+ let cursor = startPath;
1654
+ while (cursor) {
1655
+ const fn = cursor.findParent((p) => p.isFunction());
1656
+ if (!fn) return null;
1657
+ if (isLikelyComponentFunction(fn)) return fn;
1658
+ cursor = fn.parentPath;
1659
+ }
1660
+ return null;
1661
+ }
1662
+
1663
+ function widthExprFromId(widthId) {
1664
+ if (widthId) return t.cloneNode(widthId);
1665
+ return t.memberExpression(
1666
+ t.callExpression(
1667
+ t.memberExpression(
1668
+ t.memberExpression(
1669
+ t.callExpression(t.identifier("require"), [
1670
+ t.stringLiteral("react-native"),
1671
+ ]),
1672
+ t.identifier("Dimensions"),
1673
+ ),
1674
+ t.identifier("get"),
1675
+ ),
1676
+ [t.stringLiteral("window")],
1677
+ ),
1678
+ t.identifier("width"),
1679
+ );
1680
+ }
1681
+
1682
+ function extractClassExpressionParts(expr, clsxAliases) {
1683
+ function collectParts(node) {
1684
+ if (t.isStringLiteral(node)) {
1685
+ return { staticParts: [node.value], dynamicNodes: [] };
1686
+ }
1687
+
1688
+ if (t.isTemplateLiteral(node)) {
1689
+ const staticParts = node.quasis.map((q) => q.value.cooked || "");
1690
+ const dynamicNodes = node.expressions.map((e) => t.cloneNode(e, true));
1691
+ return { staticParts, dynamicNodes };
1692
+ }
1693
+
1694
+ // React compiler / Babel may rewrite template strings into string concatenation.
1695
+ if (t.isBinaryExpression(node, { operator: "+" })) {
1696
+ const left = collectParts(node.left);
1697
+ const right = collectParts(node.right);
1698
+ return {
1699
+ staticParts: [...left.staticParts, ...right.staticParts],
1700
+ dynamicNodes: [...left.dynamicNodes, ...right.dynamicNodes],
1701
+ };
1702
+ }
1703
+
1704
+ // Handles forms like "a ".concat(dynamic, " b")
1705
+ if (
1706
+ t.isCallExpression(node) &&
1707
+ t.isMemberExpression(node.callee) &&
1708
+ t.isIdentifier(node.callee.property, { name: "concat" })
1709
+ ) {
1710
+ const base = collectParts(node.callee.object);
1711
+ const args = Array.isArray(node.arguments) ? node.arguments : [];
1712
+ const next = args.map((arg) => collectParts(arg));
1713
+ return {
1714
+ staticParts: [
1715
+ ...base.staticParts,
1716
+ ...next.flatMap((x) => x.staticParts),
1717
+ ],
1718
+ dynamicNodes: [
1719
+ ...base.dynamicNodes,
1720
+ ...next.flatMap((x) => x.dynamicNodes),
1721
+ ],
1722
+ };
1723
+ }
1724
+
1725
+ return { staticParts: [], dynamicNodes: [t.cloneNode(node, true)] };
1726
+ }
1727
+
1728
+ if (
1729
+ t.isCallExpression(expr) &&
1730
+ t.isIdentifier(expr.callee) &&
1731
+ (clsxAliases
1732
+ ? clsxAliases.has(expr.callee.name)
1733
+ : /^(clsx|classnames|cn)$/.test(expr.callee.name))
1734
+ ) {
1735
+ const staticParts = [];
1736
+ const dynamicArgs = [];
1737
+
1738
+ const args = Array.isArray(expr.arguments) ? expr.arguments : [];
1739
+ for (const arg of args) {
1740
+ const parts = collectParts(arg);
1741
+ if (parts.staticParts.length) staticParts.push(...parts.staticParts);
1742
+ if (parts.dynamicNodes.length) dynamicArgs.push(...parts.dynamicNodes);
1743
+ }
1744
+
1745
+ return {
1746
+ staticClassStr: staticParts.join(" ").trim(),
1747
+ dynamicExpr:
1748
+ dynamicArgs.length === 0
1749
+ ? null
1750
+ : dynamicArgs.length === 1
1751
+ ? dynamicArgs[0]
1752
+ : t.arrayExpression(dynamicArgs),
1753
+ };
1754
+ }
1755
+
1756
+ const parts = collectParts(expr);
1757
+ return {
1758
+ staticClassStr: parts.staticParts.join(" ").trim(),
1759
+ dynamicExpr:
1760
+ parts.dynamicNodes.length === 0
1761
+ ? null
1762
+ : parts.dynamicNodes.length === 1
1763
+ ? parts.dynamicNodes[0]
1764
+ : t.arrayExpression(parts.dynamicNodes),
1765
+ };
1766
+ }
1767
+
1768
+ function buildRuntimeColorHelperNodes(ids) {
1769
+ const pn = ids.palettes.name;
1770
+ const hn = ids.hexToRgba.name;
1771
+ const cn = ids.parseColor.name;
1772
+ const tn = ids.tokenStyle.name;
1773
+ const sn = ids.styles.name;
1774
+ const gn = ids.gradient.name;
1775
+
1776
+ const SJ = JSON.stringify(SPACING);
1777
+ const FJ = JSON.stringify(FONT_SIZES);
1778
+ const RJ = JSON.stringify(RADII);
1779
+ const PJ = JSON.stringify(PALETTES);
1780
+ const GDJ = JSON.stringify(GRADIENT_DIRS);
1781
+ const GPJ = JSON.stringify(GRADIENT_PRESETS);
1782
+
1783
+ const src = [
1784
+ `var ${pn} = ${PJ};`,
1785
+
1786
+ `function ${hn}(hex, opacity) {`,
1787
+ ` var h = hex.replace("#", "");`,
1788
+ ` var r = parseInt(h.slice(0,2), 16);`,
1789
+ ` var g = parseInt(h.slice(2,4), 16);`,
1790
+ ` var b = parseInt(h.slice(4,6), 16);`,
1791
+ ` return "rgba(" + r + ", " + g + ", " + b + ", " + (opacity/100) + ")";`,
1792
+ `}`,
1793
+
1794
+ `function ${cn}(str) {`,
1795
+ ` if (typeof str !== "string") return null;`,
1796
+ ` var named = str.match(/^(white|black|transparent)(?:\\/(\\d+))?$/);`,
1797
+ ` if (named) {`,
1798
+ ` var n = named[1], op = named[2] !== undefined ? parseInt(named[2], 10) : null;`,
1799
+ ` if (n === "transparent") return "transparent";`,
1800
+ ` var base = n === "white" ? "#ffffff" : "#000000";`,
1801
+ ` return op !== null ? ${hn}(base, op) : base;`,
1802
+ ` }`,
1803
+ ` var m = str.match(/^(.+)-(\\d+)(?:\\/(\\d+))?$/);`,
1804
+ ` if (!m) return null;`,
1805
+ ` var pal = ${pn}[m[1]]; if (!pal) return null;`,
1806
+ ` var sc = parseInt(m[2], 10);`,
1807
+ ` var hex = (sc >= 0 && sc <= 10) ? pal[sc] : null; if (!hex) return null;`,
1808
+ ` var op2 = m[3] !== undefined ? parseInt(m[3], 10) : null;`,
1809
+ ` return op2 !== null ? ${hn}(hex, op2) : hex;`,
1810
+ `}`,
1811
+
1812
+ `function ${tn}(token) {`,
1813
+ ` if (typeof token !== "string") return null;`,
1814
+ ` var S = ${SJ};`,
1815
+ ` var F = ${FJ};`,
1816
+ ` var R = ${RJ};`,
1817
+ ` var m;`,
1818
+ ` function sp(k) { return k in S ? S[k] : null; }`,
1819
+
1820
+ // padding
1821
+ ` if ((m = token.match(/^p-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {padding:v}; }`,
1822
+ ` if ((m = token.match(/^pt-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {paddingTop:v}; }`,
1823
+ ` if ((m = token.match(/^pr-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {paddingRight:v}; }`,
1824
+ ` if ((m = token.match(/^pb-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {paddingBottom:v}; }`,
1825
+ ` if ((m = token.match(/^pl-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {paddingLeft:v}; }`,
1826
+ ` if ((m = token.match(/^px-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {paddingHorizontal:v}; }`,
1827
+ ` if ((m = token.match(/^py-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {paddingVertical:v}; }`,
1828
+
1829
+ // margin
1830
+ ` if ((m = token.match(/^m-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {margin:v}; }`,
1831
+ ` if ((m = token.match(/^mt-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {marginTop:v}; }`,
1832
+ ` if ((m = token.match(/^mr-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {marginRight:v}; }`,
1833
+ ` if ((m = token.match(/^mb-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {marginBottom:v}; }`,
1834
+ ` if ((m = token.match(/^ml-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {marginLeft:v}; }`,
1835
+ ` if ((m = token.match(/^mx-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {marginHorizontal:v}; }`,
1836
+ ` if ((m = token.match(/^my-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {marginVertical:v}; }`,
1837
+
1838
+ // sizing
1839
+ ` if ((m = token.match(/^w-(.+)$/))) {`,
1840
+ ` if(m[1]==="full"||m[1]==="screen") return {width:"100%"};`,
1841
+ ` var arb=m[1].match(/^\\[(.+)\\]$/); if(arb){var arv=parseFloat(arb[1]); return {width:isNaN(arv)?arb[1]:arv};}`,
1842
+ ` var v=sp(m[1]); if(v!==null) return {width:v};`,
1843
+ ` }`,
1844
+ ` if ((m = token.match(/^h-(.+)$/))) {`,
1845
+ ` if(m[1]==="full"||m[1]==="screen") return {height:"100%"};`,
1846
+ ` var arb=m[1].match(/^\\[(.+)\\]$/); if(arb){var arv=parseFloat(arb[1]); return {height:isNaN(arv)?arb[1]:arv};}`,
1847
+ ` var v=sp(m[1]); if(v!==null) return {height:v};`,
1848
+ ` }`,
1849
+ ` if ((m = token.match(/^size-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {width:v,height:v}; }`,
1850
+ ` if ((m = token.match(/^min-w-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {minWidth:v}; }`,
1851
+ ` if ((m = token.match(/^max-w-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {maxWidth:v}; }`,
1852
+ ` if ((m = token.match(/^min-h-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {minHeight:v}; }`,
1853
+ ` if ((m = token.match(/^max-h-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {maxHeight:v}; }`,
1854
+
1855
+ // flex
1856
+ ` if (token==="flex"||token==="inline-flex") return {flex:1};`,
1857
+ ` if ((m=token.match(/^flex-(\\d+(?:\\.\\d+)?)$/))) return {flex:parseFloat(m[1])};`,
1858
+ ` if (token==="flex-row") return {flexDirection:"row"};`,
1859
+ ` if (token==="flex-col") return {flexDirection:"column"};`,
1860
+ ` if (token==="flex-row-reverse") return {flexDirection:"row-reverse"};`,
1861
+ ` if (token==="flex-col-reverse") return {flexDirection:"column-reverse"};`,
1862
+ ` if (token==="flex-wrap") return {flexWrap:"wrap"};`,
1863
+ ` if (token==="flex-nowrap") return {flexWrap:"nowrap"};`,
1864
+ ` if (token==="flex-wrap-reverse") return {flexWrap:"wrap-reverse"};`,
1865
+ ` if (token==="flex-auto") return {flex:1,flexBasis:"auto"};`,
1866
+ ` if (token==="flex-none") return {flex:0};`,
1867
+ ` if ((m=token.match(/^grow(?:-(\\d+))?$/))) return {flexGrow:m[1]?parseInt(m[1]):1};`,
1868
+ ` if ((m=token.match(/^shrink(?:-(\\d+))?$/))) return {flexShrink:m[1]?parseInt(m[1]):1};`,
1869
+
1870
+ // justify / align
1871
+ ` if ((m=token.match(/^justify-(.+)$/))) { var jm={start:"flex-start",end:"flex-end",center:"center",between:"space-between",around:"space-around",evenly:"space-evenly"}; return {justifyContent:jm[m[1]]||m[1]}; }`,
1872
+ ` if ((m=token.match(/^items-(.+)$/))) { var am={start:"flex-start",end:"flex-end",center:"center",baseline:"baseline",stretch:"stretch"}; return {alignItems:am[m[1]]||m[1]}; }`,
1873
+ ` if ((m=token.match(/^self-(.+)$/))) { var am={start:"flex-start",end:"flex-end",center:"center",baseline:"baseline",stretch:"stretch"}; return {alignSelf:am[m[1]]||m[1]}; }`,
1874
+ ` if ((m=token.match(/^content-(.+)$/))) { var am={start:"flex-start",end:"flex-end",center:"center",baseline:"baseline",stretch:"stretch"}; return {alignContent:am[m[1]]||m[1]}; }`,
1875
+
1876
+ // gap
1877
+ ` if ((m=token.match(/^gap-x-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {columnGap:v}; }`,
1878
+ ` if ((m=token.match(/^gap-y-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {rowGap:v}; }`,
1879
+ ` if ((m=token.match(/^gap-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {gap:v}; }`,
1880
+
1881
+ // text / font
1882
+ ` if (token==="text-left") return {textAlign:"left"};`,
1883
+ ` if (token==="text-center") return {textAlign:"center"};`,
1884
+ ` if (token==="text-right") return {textAlign:"right"};`,
1885
+ ` if (token==="font-bold") return {fontWeight:"700"};`,
1886
+ ` if (token==="font-semibold") return {fontWeight:"600"};`,
1887
+ ` if (token==="font-medium") return {fontWeight:"500"};`,
1888
+ ` if (token==="font-normal") return {fontWeight:"400"};`,
1889
+ ` if (token==="font-light") return {fontWeight:"300"};`,
1890
+ ` if (token==="font-italic"||token==="italic") return {fontStyle:"italic"};`,
1891
+ ` if (token==="not-italic") return {fontStyle:"normal"};`,
1892
+ ` if (token==="underline") return {textDecorationLine:"underline"};`,
1893
+ ` if (token==="line-through") return {textDecorationLine:"line-through"};`,
1894
+ ` if (token==="no-underline") return {textDecorationLine:"none"};`,
1895
+ ` if (token==="uppercase") return {textTransform:"uppercase"};`,
1896
+ ` if (token==="lowercase") return {textTransform:"lowercase"};`,
1897
+ ` if (token==="capitalize") return {textTransform:"capitalize"};`,
1898
+ ` if ((m=token.match(/^font-size-(.+)$/))) { if(F[m[1]]!==undefined) return {fontSize:F[m[1]]}; }`,
1899
+ ` if ((m=token.match(/^font-weight-(\\d+)$/))) return {fontWeight:m[1]};`,
1900
+ ` if ((m=token.match(/^text-(.+)$/))) {`,
1901
+ ` if(F[m[1]]!==undefined) return {fontSize:F[m[1]]};`,
1902
+ ` if(m[1]==="white") return {color:"#ffffff"};`,
1903
+ ` if(m[1]==="black") return {color:"#000000"};`,
1904
+ ` var col=${cn}(m[1]); if(col) return {color:col};`,
1905
+ ` }`,
1906
+
1907
+ // bg / border colors
1908
+ ` if (token==="bg-white") return {backgroundColor:"#ffffff"};`,
1909
+ ` if (token==="bg-black") return {backgroundColor:"#000000"};`,
1910
+ ` if (token==="bg-transparent") return {backgroundColor:"transparent"};`,
1911
+ ` if ((m=token.match(/^bg-(.+)$/))) { var col=${cn}(m[1]); if(col) return {backgroundColor:col}; }`,
1912
+ ` if (token==="border"||token==="border-1") return {borderWidth:1};`,
1913
+ ` if ((m=token.match(/^border-(\\d+)$/))) return {borderWidth:parseInt(m[1])};`,
1914
+ ` if ((m=token.match(/^border-t-(\\d+)$/))) return {borderTopWidth:parseInt(m[1])};`,
1915
+ ` if ((m=token.match(/^border-b-(\\d+)$/))) return {borderBottomWidth:parseInt(m[1])};`,
1916
+ ` if ((m=token.match(/^border-l-(\\d+)$/))) return {borderLeftWidth:parseInt(m[1])};`,
1917
+ ` if ((m=token.match(/^border-r-(\\d+)$/))) return {borderRightWidth:parseInt(m[1])};`,
1918
+ ` if (token==="border-solid") return {borderStyle:"solid"};`,
1919
+ ` if (token==="border-dashed") return {borderStyle:"dashed"};`,
1920
+ ` if (token==="border-dotted") return {borderStyle:"dotted"};`,
1921
+ ` if ((m=token.match(/^border-(.+)$/))) { var col=${cn}(m[1]); if(col) return {borderColor:col}; }`,
1922
+
1923
+ // border radius
1924
+ ` if ((m=token.match(/^rounded(?:-(.+))?$/))) {`,
1925
+ ` var k=m[1]||"";`,
1926
+ ` var arb=k.match(/^\\[(.+)\\]$/); if(arb){var arv=parseFloat(arb[1]); if(!isNaN(arv)) return {borderRadius:arv};}`,
1927
+ ` if(k in R) return {borderRadius:R[k]};`,
1928
+ ` }`,
1929
+
1930
+ // position / inset
1931
+ ` if (token==="relative") return {position:"relative"};`,
1932
+ ` if (token==="absolute") return {position:"absolute"};`,
1933
+ ` if ((m=token.match(/^top-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {top:v}; }`,
1934
+ ` if ((m=token.match(/^right-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {right:v}; }`,
1935
+ ` if ((m=token.match(/^bottom-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {bottom:v}; }`,
1936
+ ` if ((m=token.match(/^left-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {left:v}; }`,
1937
+ ` if ((m=token.match(/^inset-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {top:v,right:v,bottom:v,left:v}; }`,
1938
+ ` if ((m=token.match(/^z-(-?\\d+)$/))) return {zIndex:parseInt(m[1])};`,
1939
+
1940
+ // opacity / overflow / aspect
1941
+ ` if ((m=token.match(/^opacity-(\\d+)$/))) return {opacity:parseInt(m[1])/100};`,
1942
+ ` if (token==="overflow-hidden") return {overflow:"hidden"};`,
1943
+ ` if (token==="overflow-visible") return {overflow:"visible"};`,
1944
+ ` if (token==="overflow-scroll") return {overflow:"scroll"};`,
1945
+ ` if (token==="aspect-square") return {aspectRatio:1};`,
1946
+ ` if (token==="aspect-video") return {aspectRatio:16/9};`,
1947
+
1948
+ // shadows
1949
+ ` if (token==="shadow-none") return {shadowColor:"#000",shadowOffset:{width:0,height:0},shadowOpacity:0,shadowRadius:0,elevation:0};`,
1950
+ ` if (token==="shadow-sm") return {shadowColor:"#000",shadowOffset:{width:0,height:1},shadowOpacity:0.05,shadowRadius:2,elevation:1};`,
1951
+ ` if (token==="shadow") return {shadowColor:"#000",shadowOffset:{width:0,height:1},shadowOpacity:0.1,shadowRadius:3,elevation:2};`,
1952
+ ` if (token==="shadow-md") return {shadowColor:"#000",shadowOffset:{width:0,height:4},shadowOpacity:0.15,shadowRadius:6,elevation:4};`,
1953
+ ` if (token==="shadow-lg") return {shadowColor:"#000",shadowOffset:{width:0,height:10},shadowOpacity:0.15,shadowRadius:15,elevation:8};`,
1954
+ ` if (token==="shadow-xl") return {shadowColor:"#000",shadowOffset:{width:0,height:20},shadowOpacity:0.25,shadowRadius:25,elevation:16};`,
1955
+ ` if (token==="shadow-2xl") return {shadowColor:"#000",shadowOffset:{width:0,height:25},shadowOpacity:0.25,shadowRadius:50,elevation:24};`,
1956
+
1957
+ // transforms
1958
+ ` if ((m=token.match(/^rotate-(-?\\d+(?:\\.\\d+)?)$/))) return {transform:[{rotate:m[1]+"deg"}]};`,
1959
+ ` if ((m=token.match(/^scale-(\\d+(?:\\.\\d+)?)$/))) return {transform:[{scale:parseInt(m[1])/100}]};`,
1960
+ ` if ((m=token.match(/^scale-x-(\\d+(?:\\.\\d+)?)$/))) return {transform:[{scaleX:parseInt(m[1])/100}]};`,
1961
+ ` if ((m=token.match(/^scale-y-(\\d+(?:\\.\\d+)?)$/))) return {transform:[{scaleY:parseInt(m[1])/100}]};`,
1962
+ ` if ((m=token.match(/^translate-x-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {transform:[{translateX:v}]}; }`,
1963
+ ` if ((m=token.match(/^translate-y-(.+)$/))) { var v=sp(m[1]); if(v!==null) return {transform:[{translateY:v}]}; }`,
1964
+ ` if ((m=token.match(/^skew-x-(-?\\d+(?:\\.\\d+)?)$/))) return {transform:[{skewX:m[1]+"deg"}]};`,
1965
+ ` if ((m=token.match(/^skew-y-(-?\\d+(?:\\.\\d+)?)$/))) return {transform:[{skewY:m[1]+"deg"}]};`,
1966
+
1967
+ // border radius per-side
1968
+ ` if ((m=token.match(/^rounded-tl(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderTopLeftRadius:rv}; }`,
1969
+ ` if ((m=token.match(/^rounded-tr(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderTopRightRadius:rv}; }`,
1970
+ ` if ((m=token.match(/^rounded-bl(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderBottomLeftRadius:rv}; }`,
1971
+ ` if ((m=token.match(/^rounded-br(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderBottomRightRadius:rv}; }`,
1972
+ ` if ((m=token.match(/^rounded-t(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderTopLeftRadius:rv,borderTopRightRadius:rv}; }`,
1973
+ ` if ((m=token.match(/^rounded-b(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderBottomLeftRadius:rv,borderBottomRightRadius:rv}; }`,
1974
+ ` if ((m=token.match(/^rounded-l(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderTopLeftRadius:rv,borderBottomLeftRadius:rv}; }`,
1975
+ ` if ((m=token.match(/^rounded-r(?:-(.+))?$/))) { var rv=R[m[1]||""]; if(rv!==undefined) return {borderTopRightRadius:rv,borderBottomRightRadius:rv}; }`,
1976
+
1977
+ // typography extras
1978
+ ` var TRK={tighter:-0.8,tight:-0.4,normal:0,wide:0.4,wider:0.8,widest:1.6};`,
1979
+ ` if ((m=token.match(/^tracking-(.+)$/))) { if(TRK[m[1]]!==undefined) return {letterSpacing:TRK[m[1]]}; }`,
1980
+ ` var LDG={none:16,tight:20,snug:22,normal:24,relaxed:26,loose:32};`,
1981
+ ` if ((m=token.match(/^leading-(.+)$/))) { if(LDG[m[1]]!==undefined) return {lineHeight:LDG[m[1]]}; var v=sp(m[1]); if(v!==null) return {lineHeight:v}; }`,
1982
+
1983
+ // display / decoration
1984
+ ` if (token==="hidden") return {display:"none"};`,
1985
+ ` if ((m=token.match(/^decoration-(.+)$/))) {`,
1986
+ ` var col=${cn}(m[1]); if(col) return {textDecorationColor:col};`,
1987
+ ` if(m[1]==="solid"||m[1]==="double"||m[1]==="dotted"||m[1]==="dashed") return {textDecorationStyle:m[1]};`,
1988
+ ` }`,
1989
+
1990
+ ` return null;`,
1991
+ `}`,
1992
+
1993
+ `function ${sn}(value, _st) {`,
1994
+ ` if (!value) return [];`,
1995
+ ` if (Array.isArray(value)) {`,
1996
+ ` var out=[];`,
1997
+ ` for (var item of value) { out.push.apply(out, ${sn}(item)); }`,
1998
+ ` return out;`,
1999
+ ` }`,
2000
+ ` if (typeof value==="object") {`,
2001
+ ` var out2=[];`,
2002
+ ` for (var key in value) { if(value[key]) out2.push.apply(out2, ${sn}(key)); }`,
2003
+ ` return out2;`,
2004
+ ` }`,
2005
+ ` if (typeof value!=="string") return [];`,
2006
+ ` var _isDark=require("react-native").Appearance.getColorScheme()==="dark";`,
2007
+ ` var _ww=require("react-native").Dimensions.get("window").width;`,
2008
+ ` var _bpMap={xs:480,sm:640,md:768,lg:1024,xl:1280,"2xl":1536};`,
2009
+ ` var out3=[];`,
2010
+ ` for (var tok of value.trim().split(/\\s+/)) {`,
2011
+ ` var rem = tok;`,
2012
+ ` var needDark = false;`,
2013
+ ` var needBp = null;`,
2014
+ ` var needState = null;`,
2015
+ ` for (var guard = 0; guard < 6; guard++) {`,
2016
+ ` var dm = rem.match(/^dark:/);`,
2017
+ ` if (dm) { needDark = true; rem = rem.slice(dm[0].length); continue; }`,
2018
+ ` var bm = rem.match(/^(xs|sm|md|lg|xl|2xl):/);`,
2019
+ ` if (bm) { needBp = bm[1]; rem = rem.slice(bm[0].length); continue; }`,
2020
+ ` var sm = rem.match(/^(hover|focus|active|disabled):/);`,
2021
+ ` if (sm) { needState = sm[1]; rem = rem.slice(sm[0].length); continue; }`,
2022
+ ` break;`,
2023
+ ` }`,
2024
+ ` if (needDark && !_isDark) continue;`,
2025
+ ` if (needBp && _ww < _bpMap[needBp]) continue;`,
2026
+ ` if (needState) {`,
2027
+ ` var ok = false;`,
2028
+ ` if (needState === "hover") ok = !!(_st && _st.hovered);`,
2029
+ ` else if (needState === "focus") ok = !!(_st && _st.focused);`,
2030
+ ` else if (needState === "active") ok = !!(_st && _st.pressed);`,
2031
+ ` else if (needState === "disabled") ok = !!(_st && _st.disabled);`,
2032
+ ` if (!ok) continue;`,
2033
+ ` }`,
2034
+ ` var st=${tn}(rem); if(st) out3.push(st);`,
2035
+ ` }`,
2036
+ ` return out3;`,
2037
+ `}`,
2038
+
2039
+ `function ${gn}(value) {`,
2040
+ ` if (!value) return null;`,
2041
+ ` var dirs = ${GDJ};`,
2042
+ ` var presets = ${GPJ};`,
2043
+ ` var tokens = [];`,
2044
+ ` function collect(v) {`,
2045
+ ` if (!v) return;`,
2046
+ ` if (Array.isArray(v)) { for (var i = 0; i < v.length; i++) collect(v[i]); return; }`,
2047
+ ` if (typeof v === "object") { for (var k in v) { if (v[k]) collect(k); } return; }`,
2048
+ ` if (typeof v !== "string") return;`,
2049
+ ` var parts = v.trim().split(/\\s+/);`,
2050
+ ` for (var j = 0; j < parts.length; j++) { if (parts[j]) tokens.push(parts[j]); }`,
2051
+ ` }`,
2052
+ ` collect(value);`,
2053
+ ` if (!tokens.length) return null;`,
2054
+
2055
+ ` var named = null;`,
2056
+ ` for (var n = 0; n < tokens.length; n++) {`,
2057
+ ` if (tokens[n].indexOf("gradient-") === 0) { named = tokens[n]; break; }`,
2058
+ ` }`,
2059
+ ` if (named) {`,
2060
+ ` var preset = presets[named.slice(9)];`,
2061
+ ` if (preset && Array.isArray(preset.colors) && preset.colors.length >= 2) {`,
2062
+ ` var pdir = dirs[preset.dir] || dirs["to-b"];`,
2063
+ ` return { colors: preset.colors, start: pdir.start, end: pdir.end, radii: {} };`,
2064
+ ` }`,
2065
+ ` }`,
2066
+
2067
+ ` var dirClass = null, fromClass = null, viaClass = null, toClass = null;`,
2068
+ ` for (var t2 = 0; t2 < tokens.length; t2++) {`,
2069
+ ` var tok = tokens[t2];`,
2070
+ ` if (!dirClass && tok.indexOf("bg-gradient-to-") === 0) dirClass = tok;`,
2071
+ ` else if (!fromClass && tok.indexOf("from-") === 0) fromClass = tok;`,
2072
+ ` else if (!viaClass && tok.indexOf("via-") === 0) viaClass = tok;`,
2073
+ ` else if (!toClass && tok.indexOf("to-") === 0 && tok.indexOf("top-") !== 0) toClass = tok;`,
2074
+ ` }`,
2075
+
2076
+ ` if (!fromClass && !toClass) return null;`,
2077
+ ` var dirKey = dirClass ? dirClass.replace("bg-gradient-", "") : "to-b";`,
2078
+ ` var dir = dirs[dirKey] || dirs["to-b"];`,
2079
+ ` var colors = [];`,
2080
+ ` if (fromClass) { var c1 = ${cn}(fromClass.slice(5)); if (c1) colors.push(c1); }`,
2081
+ ` if (viaClass) { var c2 = ${cn}(viaClass.slice(4)); if (c2) colors.push(c2); }`,
2082
+ ` if (toClass) { var c3 = ${cn}(toClass.slice(3)); if (c3) colors.push(c3); }`,
2083
+ ` if (colors.length < 2) return null;`,
2084
+ ` var radii = {};`,
2085
+ ` var R2 = ${RJ};`,
2086
+ ` for (var ri = 0; ri < tokens.length; ri++) {`,
2087
+ ` var rtok = tokens[ri]; var rm;`,
2088
+ ` if ((rm = rtok.match(/^rounded(?:-(.+))?$/))) {`,
2089
+ ` var rk = rm[1] || "";`,
2090
+ ` var arb2 = rk.match(/^\\[(.+)\\]$/); if (arb2) { var rv = parseFloat(arb2[1]); if (!isNaN(rv)) { radii.borderRadius = rv; } }`,
2091
+ ` else if (rk in R2) { radii.borderRadius = R2[rk]; }`,
2092
+ ` }`,
2093
+ ` }`,
2094
+ ` return { colors: colors, start: dir.start, end: dir.end, radii: radii };`,
2095
+ `}`,
2096
+ ].join("\n");
2097
+
2098
+ return tmpl.statements.ast(src);
2099
+ }
2100
+
2101
+ return {
2102
+ visitor: {
2103
+ // Track whether any gradient was emitted so we can inject the import
2104
+ Program: {
2105
+ enter(path, state) {
2106
+ state.needsGradientImport = false;
2107
+ state.needsRuntimeColorHelper = false;
2108
+ state.clsxAliases = new Set(["clsx", "classnames", "cn"]);
2109
+ state.classNameTransformAliases = new Set();
2110
+ // Per-function scheme identifiers for reactive useColorScheme() injection
2111
+ state.schemeIds = new Map(); // FunctionNode → Identifier
2112
+ state.functionsNeedingScheme = new Set(); // Set<FunctionNode>
2113
+ // Per-function width identifiers for reactive useWindowDimensions() injection
2114
+ state.dimensionIds = new Map(); // FunctionNode → Identifier
2115
+ state.functionsNeedingDimensions = new Set(); // Set<FunctionNode>
2116
+ state.runtimeColorIds = {
2117
+ palettes: path.scope.generateUidIdentifier("forgePalettes"),
2118
+ hexToRgba: path.scope.generateUidIdentifier("forgeHexToRgba"),
2119
+ parseColor: path.scope.generateUidIdentifier("forgeParseColor"),
2120
+ tokenStyle: path.scope.generateUidIdentifier(
2121
+ "forgeColorTokenStyle",
2122
+ ),
2123
+ styles: path.scope.generateUidIdentifier("forgeDynamicColorStyles"),
2124
+ gradient: path.scope.generateUidIdentifier("forgeDynamicGradient"),
2125
+ };
2126
+ },
2127
+ exit(programPath, state) {
2128
+ if (state.needsGradientImport) {
2129
+ let already = false;
2130
+ programPath.node.body.forEach((node) => {
2131
+ if (
2132
+ t.isImportDeclaration(node) &&
2133
+ node.source.value === "expo-linear-gradient"
2134
+ )
2135
+ already = true;
2136
+ });
2137
+ if (!already) {
2138
+ programPath.unshiftContainer(
2139
+ "body",
2140
+ t.importDeclaration(
2141
+ [
2142
+ t.importSpecifier(
2143
+ t.identifier("LinearGradient"),
2144
+ t.identifier("LinearGradient"),
2145
+ ),
2146
+ ],
2147
+ t.stringLiteral("expo-linear-gradient"),
2148
+ ),
2149
+ );
2150
+ }
2151
+ }
2152
+
2153
+ if (state.needsRuntimeColorHelper) {
2154
+ const helperNodes = buildRuntimeColorHelperNodes(
2155
+ state.runtimeColorIds,
2156
+ );
2157
+ const bodyPaths = programPath.get("body");
2158
+ let lastImportPath = null;
2159
+ for (const bodyPath of bodyPaths) {
2160
+ if (bodyPath.isImportDeclaration()) lastImportPath = bodyPath;
2161
+ }
2162
+ if (lastImportPath) {
2163
+ lastImportPath.insertAfter(helperNodes);
2164
+ } else {
2165
+ programPath.unshiftContainer("body", helperNodes);
2166
+ }
2167
+ }
2168
+ },
2169
+ },
2170
+
2171
+ ImportDeclaration(path, state) {
2172
+ if (/forge-styles/.test(path.node.source.value)) path.remove();
2173
+ if (/^(clsx|classnames)$/.test(path.node.source.value)) {
2174
+ for (const spec of path.node.specifiers) {
2175
+ if (
2176
+ t.isImportDefaultSpecifier(spec) ||
2177
+ t.isImportNamespaceSpecifier(spec) ||
2178
+ t.isImportSpecifier(spec)
2179
+ ) {
2180
+ state.clsxAliases.add(spec.local.name);
2181
+ }
2182
+ }
2183
+ }
2184
+
2185
+ // Keep className transform enabled for icon components imported from
2186
+ // vector icon libraries, even though they are custom PascalCase tags.
2187
+ if (
2188
+ /(vector-icons|lucide-react-native|phosphor-react-native)/.test(
2189
+ path.node.source.value,
2190
+ )
2191
+ ) {
2192
+ for (const spec of path.node.specifiers) {
2193
+ if (t.isImportDefaultSpecifier(spec) || t.isImportSpecifier(spec)) {
2194
+ state.classNameTransformAliases.add(spec.local.name);
2195
+ }
2196
+ }
2197
+ }
2198
+ },
2199
+
2200
+ JSXAttribute(path, state) {
2201
+ // EXPERIMENTAL: Auto-apply first: and last: classes in array maps (React Native)
2202
+ // Only runs if className contains first: or last:
2203
+ if (t.isJSXIdentifier(path.node.name, { name: "className" })) {
2204
+ const value = path.node.value;
2205
+ let staticClassStr = "";
2206
+ if (t.isStringLiteral(value)) {
2207
+ staticClassStr = value.value;
2208
+ } else if (
2209
+ t.isJSXExpressionContainer(value) &&
2210
+ value.expression &&
2211
+ t.isStringLiteral(value.expression)
2212
+ ) {
2213
+ staticClassStr = value.expression.value;
2214
+ }
2215
+ if (/\b(first:|last:)\w+/.test(staticClassStr)) {
2216
+ // Find parent JSXExpressionContainer > CallExpression (array map)
2217
+ let parent = path.parentPath;
2218
+ while (
2219
+ parent &&
2220
+ !parent.isJSXElement() &&
2221
+ !parent.isCallExpression()
2222
+ )
2223
+ parent = parent.parentPath;
2224
+ if (parent && parent.isCallExpression()) {
2225
+ const callNode = parent.node;
2226
+ // Check if .map is called
2227
+ if (
2228
+ t.isMemberExpression(callNode.callee) &&
2229
+ callNode.callee.property.name === "map"
2230
+ ) {
2231
+ // Find the function param (usually (item, i) => ...)
2232
+ const mapFn = callNode.arguments[0];
2233
+ if (
2234
+ t.isArrowFunctionExpression(mapFn) &&
2235
+ mapFn.params.length >= 2
2236
+ ) {
2237
+ const idxParam = mapFn.params[1];
2238
+ const arrParam = callNode.callee.object;
2239
+ // Remove first: and last: classes from staticClassStr
2240
+ const firstClasses =
2241
+ staticClassStr.match(/\bfirst:([\w-]+)/g) || [];
2242
+ const lastClasses =
2243
+ staticClassStr.match(/\blast:([\w-]+)/g) || [];
2244
+ let baseClass = staticClassStr
2245
+ .replace(/\bfirst:[\w-]+/g, "")
2246
+ .replace(/\blast:[\w-]+/g, "")
2247
+ .replace(/\s+/g, " ")
2248
+ .trim();
2249
+ // Build conditional className expression
2250
+ let expr = t.stringLiteral(baseClass);
2251
+ if (firstClasses.length > 0) {
2252
+ expr = t.conditionalExpression(
2253
+ t.binaryExpression("===", idxParam, t.numericLiteral(0)),
2254
+ t.stringLiteral(
2255
+ baseClass +
2256
+ " " +
2257
+ firstClasses
2258
+ .map((c) => c.replace("first:", ""))
2259
+ .join(" "),
2260
+ ),
2261
+ expr,
2262
+ );
2263
+ }
2264
+ if (lastClasses.length > 0) {
2265
+ expr = t.conditionalExpression(
2266
+ t.binaryExpression(
2267
+ "===",
2268
+ idxParam,
2269
+ t.binaryExpression(
2270
+ "-",
2271
+ t.memberExpression(arrParam, t.identifier("length")),
2272
+ t.numericLiteral(1),
2273
+ ),
2274
+ ),
2275
+ t.stringLiteral(
2276
+ baseClass +
2277
+ " " +
2278
+ lastClasses
2279
+ .map((c) => c.replace("last:", ""))
2280
+ .join(" "),
2281
+ ),
2282
+ expr,
2283
+ );
2284
+ }
2285
+ path.node.value = t.jsxExpressionContainer(expr);
2286
+ }
2287
+ }
2288
+ }
2289
+ }
2290
+ }
2291
+ if (!t.isJSXIdentifier(path.node.name, { name: "className" })) return;
2292
+ const openingEl = path.parentPath; // JSXOpeningElement
2293
+ const tagName =
2294
+ openingEl && t.isJSXIdentifier(openingEl.node.name)
2295
+ ? openingEl.node.name.name
2296
+ : null;
2297
+
2298
+ // Keep className on custom components (e.g. <Modal className="...">)
2299
+ // so component-level APIs can consume/forward it.
2300
+ if (
2301
+ tagName &&
2302
+ /^[A-Z]/.test(tagName) &&
2303
+ !CLASSNAME_HOST_TAGS.has(tagName) &&
2304
+ !(state.classNameTransformAliases || new Set()).has(tagName)
2305
+ ) {
2306
+ return;
2307
+ }
2308
+
2309
+ const value = path.node.value;
2310
+ let staticClassStr = "";
2311
+ let dynamicClassExpr = null;
2312
+ if (t.isStringLiteral(value)) {
2313
+ staticClassStr = value.value;
2314
+ } else if (t.isJSXExpressionContainer(value) && value.expression) {
2315
+ const parts = extractClassExpressionParts(
2316
+ value.expression,
2317
+ state.clsxAliases,
2318
+ );
2319
+ staticClassStr = parts.staticClassStr;
2320
+ dynamicClassExpr = parts.dynamicExpr;
2321
+ } else {
2322
+ path.remove();
2323
+ return;
2324
+ }
2325
+
2326
+ const classes = staticClassStr.trim().split(/\s+/).filter(Boolean);
2327
+ const gradient = extractGradient(classes);
2328
+ const darkGradient = gradient ? extractDarkGradient(classes) : null;
2329
+ const regularClasses = gradient
2330
+ ? classes.filter((c) => !isGradientClass(c)).join(" ")
2331
+ : staticClassStr;
2332
+
2333
+ const canApplyGradient = tagName && GRADIENT_HOST_TAGS.has(tagName);
2334
+ // Removed: SafeAreaView gradient wrapping
2335
+ const canWrapGradient = false;
2336
+ const shouldClipGradient =
2337
+ gradient && (canApplyGradient || canWrapGradient);
2338
+ const isInteractiveTag =
2339
+ tagName === "Pressable" ||
2340
+ tagName === "TouchableOpacity" ||
2341
+ tagName === "TouchableHighlight" ||
2342
+ tagName === "Link";
2343
+
2344
+ // Pre-compute style early so border radius is available for gradient injection
2345
+ const _preStyle = classStringToStyle(regularClasses) || {};
2346
+ const {
2347
+ dark: _darkStyleObj_raw,
2348
+ responsive: _responsiveStyles,
2349
+ states: _stateStyles,
2350
+ compound: _compoundStyles,
2351
+ } = classStringToStyleFull(staticClassStr);
2352
+ const _darkStyleObj =
2353
+ Object.keys(_darkStyleObj_raw).length > 0 ? _darkStyleObj_raw : null;
2354
+ const _hasResponsiveStyle = Object.values(_responsiveStyles).some(
2355
+ (o) => Object.keys(o).length > 0,
2356
+ );
2357
+ const _hasStateStyle = Object.values(_stateStyles).some(
2358
+ (o) => Object.keys(o).length > 0,
2359
+ );
2360
+ const _hasCompoundStyle = (_compoundStyles || []).some(
2361
+ (c) => Object.keys(c.style || {}).length > 0,
2362
+ );
2363
+ const _hasCompoundDark = _compoundStyles.some((c) => c.dark);
2364
+ const _hasCompoundResponsive = _compoundStyles.some((c) => !!c.bp);
2365
+ const _hasCompoundState = _compoundStyles.some((c) => !!c.state);
2366
+ const _supportsStateStyleFn = tagName === "Pressable";
2367
+ const _hasApplicableStateStyle =
2368
+ _supportsStateStyleFn && (_hasStateStyle || _hasCompoundState);
2369
+ const RADIUS_KEYS = [
2370
+ "borderRadius",
2371
+ "borderTopLeftRadius",
2372
+ "borderTopRightRadius",
2373
+ "borderBottomLeftRadius",
2374
+ "borderBottomRightRadius",
2375
+ "borderTopStartRadius",
2376
+ "borderTopEndRadius",
2377
+ "borderBottomStartRadius",
2378
+ "borderBottomEndRadius",
2379
+ ];
2380
+ const gradientRadiusProps = RADIUS_KEYS.filter(
2381
+ (k) => _preStyle[k] != null,
2382
+ ).map((k) =>
2383
+ t.objectProperty(t.identifier(k), t.numericLiteral(_preStyle[k])),
2384
+ );
2385
+
2386
+ // ── Gradient: transform non-interactive element to <LinearGradient>
2387
+ if (gradient && canApplyGradient && !isInteractiveTag) {
2388
+ const jsxElement = openingEl?.parentPath; // JSXElement
2389
+
2390
+ // Rename opening tag
2391
+ openingEl.node.name = t.jsxIdentifier("LinearGradient");
2392
+ // Rename closing tag
2393
+ if (jsxElement?.node?.closingElement) {
2394
+ jsxElement.node.closingElement.name =
2395
+ t.jsxIdentifier("LinearGradient");
2396
+ }
2397
+
2398
+ // Helper: build a conditional expression if darkGradient exists, else use static
2399
+ const colorsExpr = darkGradient
2400
+ ? t.conditionalExpression(
2401
+ t.binaryExpression(
2402
+ "===",
2403
+ t.identifier("_forgeScheme"),
2404
+ t.stringLiteral("dark"),
2405
+ ),
2406
+ t.arrayExpression(
2407
+ darkGradient.colors.map((c) => t.stringLiteral(c)),
2408
+ ),
2409
+ t.arrayExpression(
2410
+ gradient.colors.map((c) => t.stringLiteral(c)),
2411
+ ),
2412
+ )
2413
+ : t.arrayExpression(gradient.colors.map((c) => t.stringLiteral(c)));
2414
+
2415
+ const startExpr = darkGradient
2416
+ ? t.conditionalExpression(
2417
+ t.binaryExpression(
2418
+ "===",
2419
+ t.identifier("_forgeScheme"),
2420
+ t.stringLiteral("dark"),
2421
+ ),
2422
+ coordObj(darkGradient.start.x, darkGradient.start.y),
2423
+ coordObj(gradient.start.x, gradient.start.y),
2424
+ )
2425
+ : coordObj(gradient.start.x, gradient.start.y);
2426
+
2427
+ const endExpr = darkGradient
2428
+ ? t.conditionalExpression(
2429
+ t.binaryExpression(
2430
+ "===",
2431
+ t.identifier("_forgeScheme"),
2432
+ t.stringLiteral("dark"),
2433
+ ),
2434
+ coordObj(darkGradient.end.x, darkGradient.end.y),
2435
+ coordObj(gradient.end.x, gradient.end.y),
2436
+ )
2437
+ : coordObj(gradient.end.x, gradient.end.y);
2438
+
2439
+ // Add colors={[...]} or colors={_forgeScheme === "dark" ? [...] : [...]}
2440
+ openingEl.node.attributes.push(
2441
+ t.jsxAttribute(
2442
+ t.jsxIdentifier("colors"),
2443
+ t.jsxExpressionContainer(colorsExpr),
2444
+ ),
2445
+ );
2446
+ // Add start
2447
+ openingEl.node.attributes.push(
2448
+ t.jsxAttribute(
2449
+ t.jsxIdentifier("start"),
2450
+ t.jsxExpressionContainer(startExpr),
2451
+ ),
2452
+ );
2453
+ // Add end
2454
+ openingEl.node.attributes.push(
2455
+ t.jsxAttribute(
2456
+ t.jsxIdentifier("end"),
2457
+ t.jsxExpressionContainer(endExpr),
2458
+ ),
2459
+ );
2460
+
2461
+ if (darkGradient) {
2462
+ // Mark this function component as needing _forgeScheme
2463
+ const fn = path.getFunctionParent()?.node;
2464
+ if (fn) state.functionsNeedingScheme.add(fn);
2465
+ }
2466
+
2467
+ state.needsGradientImport = true;
2468
+ }
2469
+
2470
+ // ── Gradient on interactive element: inject background child ─────
2471
+ if (gradient && canApplyGradient && isInteractiveTag) {
2472
+ const jsxElement = openingEl?.parentPath;
2473
+ if (jsxElement && t.isJSXElement(jsxElement.node)) {
2474
+ const igColorsExpr = darkGradient
2475
+ ? t.conditionalExpression(
2476
+ t.binaryExpression(
2477
+ "===",
2478
+ t.identifier("_forgeScheme"),
2479
+ t.stringLiteral("dark"),
2480
+ ),
2481
+ t.arrayExpression(
2482
+ darkGradient.colors.map((c) => t.stringLiteral(c)),
2483
+ ),
2484
+ t.arrayExpression(
2485
+ gradient.colors.map((c) => t.stringLiteral(c)),
2486
+ ),
2487
+ )
2488
+ : t.arrayExpression(
2489
+ gradient.colors.map((c) => t.stringLiteral(c)),
2490
+ );
2491
+ const igStartExpr = darkGradient
2492
+ ? t.conditionalExpression(
2493
+ t.binaryExpression(
2494
+ "===",
2495
+ t.identifier("_forgeScheme"),
2496
+ t.stringLiteral("dark"),
2497
+ ),
2498
+ coordObj(darkGradient.start.x, darkGradient.start.y),
2499
+ coordObj(gradient.start.x, gradient.start.y),
2500
+ )
2501
+ : coordObj(gradient.start.x, gradient.start.y);
2502
+ const igEndExpr = darkGradient
2503
+ ? t.conditionalExpression(
2504
+ t.binaryExpression(
2505
+ "===",
2506
+ t.identifier("_forgeScheme"),
2507
+ t.stringLiteral("dark"),
2508
+ ),
2509
+ coordObj(darkGradient.end.x, darkGradient.end.y),
2510
+ coordObj(gradient.end.x, gradient.end.y),
2511
+ )
2512
+ : coordObj(gradient.end.x, gradient.end.y);
2513
+
2514
+ if (darkGradient) {
2515
+ const fn = path.getFunctionParent()?.node;
2516
+ if (fn) state.functionsNeedingScheme.add(fn);
2517
+ }
2518
+
2519
+ const gradientBg = t.jsxElement(
2520
+ t.jsxOpeningElement(
2521
+ t.jsxIdentifier("LinearGradient"),
2522
+ [
2523
+ t.jsxAttribute(
2524
+ t.jsxIdentifier("colors"),
2525
+ t.jsxExpressionContainer(igColorsExpr),
2526
+ ),
2527
+ t.jsxAttribute(
2528
+ t.jsxIdentifier("start"),
2529
+ t.jsxExpressionContainer(igStartExpr),
2530
+ ),
2531
+ t.jsxAttribute(
2532
+ t.jsxIdentifier("end"),
2533
+ t.jsxExpressionContainer(igEndExpr),
2534
+ ),
2535
+ t.jsxAttribute(
2536
+ t.jsxIdentifier("style"),
2537
+ t.jsxExpressionContainer(
2538
+ t.objectExpression([
2539
+ t.objectProperty(
2540
+ t.identifier("position"),
2541
+ t.stringLiteral("absolute"),
2542
+ ),
2543
+ t.objectProperty(
2544
+ t.identifier("top"),
2545
+ t.numericLiteral(0),
2546
+ ),
2547
+ t.objectProperty(
2548
+ t.identifier("right"),
2549
+ t.numericLiteral(0),
2550
+ ),
2551
+ t.objectProperty(
2552
+ t.identifier("bottom"),
2553
+ t.numericLiteral(0),
2554
+ ),
2555
+ t.objectProperty(
2556
+ t.identifier("left"),
2557
+ t.numericLiteral(0),
2558
+ ),
2559
+ t.objectProperty(
2560
+ t.identifier("pointerEvents"),
2561
+ t.stringLiteral("none"),
2562
+ ),
2563
+ ...gradientRadiusProps,
2564
+ ]),
2565
+ ),
2566
+ ),
2567
+ ],
2568
+ true,
2569
+ ),
2570
+ null,
2571
+ [],
2572
+ true,
2573
+ );
2574
+
2575
+ jsxElement.node.children.unshift(gradientBg);
2576
+ state.needsGradientImport = true;
2577
+ }
2578
+ }
2579
+
2580
+ // ── Regular style classes ────────────────────────────────────────
2581
+ const style = _preStyle;
2582
+ if (shouldClipGradient) {
2583
+ // Ensure rounded corners clip gradient fills on all host types.
2584
+ if (style.overflow == null) style.overflow = "hidden";
2585
+ }
2586
+ if (gradient && canApplyGradient && isInteractiveTag) {
2587
+ // Required so absolute gradient child paints behind content.
2588
+ if (style.position == null) style.position = "relative";
2589
+ }
2590
+ const styleAttrPath =
2591
+ openingEl &&
2592
+ openingEl
2593
+ .get("attributes")
2594
+ .find(
2595
+ (a) =>
2596
+ t.isJSXAttribute(a.node) &&
2597
+ t.isJSXIdentifier(a.node.name, { name: "style" }),
2598
+ );
2599
+
2600
+ const hasStaticStyle = Object.keys(style).length > 0;
2601
+ const hasDarkStyle = !!_darkStyleObj;
2602
+
2603
+ // Find the enclosing function component so we can inject useColorScheme() as a hook.
2604
+ // This makes dark: styles automatically reactive — no ThemeProvider needed.
2605
+ const _fnPath =
2606
+ hasDarkStyle || dynamicClassExpr || _hasCompoundDark
2607
+ ? findComponentFunctionPath(path)
2608
+ : null;
2609
+ if (_fnPath) {
2610
+ const _fnNode = _fnPath.node;
2611
+ state.functionsNeedingScheme.add(_fnNode);
2612
+ if (!state.schemeIds.has(_fnNode)) {
2613
+ state.schemeIds.set(
2614
+ _fnNode,
2615
+ _fnPath.scope.generateUidIdentifier("forgeScheme"),
2616
+ );
2617
+ }
2618
+ }
2619
+ const _schemeId = _fnPath ? state.schemeIds.get(_fnPath.node) : null;
2620
+
2621
+ // Find/register function for width tracking (responsive breakpoints)
2622
+ const _fnPathR =
2623
+ _hasResponsiveStyle || _hasCompoundResponsive
2624
+ ? findComponentFunctionPath(path)
2625
+ : null;
2626
+ if (_fnPathR) {
2627
+ const _fnNodeR = _fnPathR.node;
2628
+ state.functionsNeedingDimensions.add(_fnNodeR);
2629
+ if (!state.dimensionIds.has(_fnNodeR)) {
2630
+ state.dimensionIds.set(
2631
+ _fnNodeR,
2632
+ _fnPathR.scope.generateUidIdentifier("forgeWidth"),
2633
+ );
2634
+ }
2635
+ }
2636
+ const _widthId = _fnPathR
2637
+ ? state.dimensionIds.get(_fnPathR.node)
2638
+ : null;
2639
+
2640
+ // Build responsive style expressions: _forgeWidth >= N && { ...style }
2641
+ const responsiveExprs = _hasResponsiveStyle
2642
+ ? Object.entries(BREAKPOINT_MIN)
2643
+ .filter(
2644
+ ([bp]) =>
2645
+ _responsiveStyles[bp] &&
2646
+ Object.keys(_responsiveStyles[bp]).length > 0,
2647
+ )
2648
+ .map(([bp, minPx]) =>
2649
+ t.logicalExpression(
2650
+ "&&",
2651
+ t.binaryExpression(
2652
+ ">=",
2653
+ widthExprFromId(_widthId),
2654
+ t.numericLiteral(minPx),
2655
+ ),
2656
+ styleToAst(t, _responsiveStyles[bp]),
2657
+ ),
2658
+ )
2659
+ : [];
2660
+
2661
+ const _statePropMap = {
2662
+ hover: "hovered",
2663
+ focus: "focused",
2664
+ active: "pressed",
2665
+ disabled: "disabled",
2666
+ };
2667
+
2668
+ // Compound styles combine two or more variants (e.g. dark:hover:, md:dark:, md:active:)
2669
+ const compoundExprsNoState = (_compoundStyles || [])
2670
+ .filter((c) => !c.state)
2671
+ .map((c) => {
2672
+ const conds = [];
2673
+ if (c.dark) {
2674
+ conds.push(
2675
+ t.binaryExpression(
2676
+ "===",
2677
+ _schemeId
2678
+ ? t.cloneNode(_schemeId)
2679
+ : t.callExpression(
2680
+ t.memberExpression(
2681
+ t.memberExpression(
2682
+ t.callExpression(t.identifier("require"), [
2683
+ t.stringLiteral("react-native"),
2684
+ ]),
2685
+ t.identifier("Appearance"),
2686
+ ),
2687
+ t.identifier("getColorScheme"),
2688
+ ),
2689
+ [],
2690
+ ),
2691
+ t.stringLiteral("dark"),
2692
+ ),
2693
+ );
2694
+ }
2695
+ if (c.bp) {
2696
+ const minPx = BREAKPOINT_MIN[c.bp] || 0;
2697
+ conds.push(
2698
+ t.binaryExpression(
2699
+ ">=",
2700
+ widthExprFromId(_widthId),
2701
+ t.numericLiteral(minPx),
2702
+ ),
2703
+ );
2704
+ }
2705
+ const styleNode = styleToAst(t, c.style);
2706
+ if (conds.length === 0) return styleNode;
2707
+ const cond = conds.reduce(
2708
+ (acc, next) =>
2709
+ acc ? t.logicalExpression("&&", acc, next) : next,
2710
+ null,
2711
+ );
2712
+ return t.logicalExpression("&&", cond, styleNode);
2713
+ });
2714
+
2715
+ const compoundExprsWithState = (_compoundStyles || [])
2716
+ .map((c) => {
2717
+ const conds = [];
2718
+ if (c.dark) {
2719
+ conds.push(
2720
+ t.binaryExpression(
2721
+ "===",
2722
+ _schemeId
2723
+ ? t.cloneNode(_schemeId)
2724
+ : t.callExpression(
2725
+ t.memberExpression(
2726
+ t.memberExpression(
2727
+ t.callExpression(t.identifier("require"), [
2728
+ t.stringLiteral("react-native"),
2729
+ ]),
2730
+ t.identifier("Appearance"),
2731
+ ),
2732
+ t.identifier("getColorScheme"),
2733
+ ),
2734
+ [],
2735
+ ),
2736
+ t.stringLiteral("dark"),
2737
+ ),
2738
+ );
2739
+ }
2740
+ if (c.bp) {
2741
+ const minPx = BREAKPOINT_MIN[c.bp] || 0;
2742
+ conds.push(
2743
+ t.binaryExpression(
2744
+ ">=",
2745
+ widthExprFromId(_widthId),
2746
+ t.numericLiteral(minPx),
2747
+ ),
2748
+ );
2749
+ }
2750
+ if (c.state) {
2751
+ const prop = _statePropMap[c.state];
2752
+ if (!prop) return null;
2753
+ conds.push(
2754
+ t.memberExpression(
2755
+ t.identifier("_forgeState"),
2756
+ t.identifier(prop),
2757
+ ),
2758
+ );
2759
+ }
2760
+ const styleNode = styleToAst(t, c.style);
2761
+ if (conds.length === 0) return styleNode;
2762
+ const cond = conds.reduce(
2763
+ (acc, next) =>
2764
+ acc ? t.logicalExpression("&&", acc, next) : next,
2765
+ null,
2766
+ );
2767
+ return t.logicalExpression("&&", cond, styleNode);
2768
+ })
2769
+ .filter(Boolean);
2770
+
2771
+ const dynamicStyleExpr = dynamicClassExpr
2772
+ ? ((state.needsRuntimeColorHelper = true),
2773
+ t.callExpression(state.runtimeColorIds.styles, [dynamicClassExpr]))
2774
+ : null;
2775
+
2776
+ // Build dark-mode conditional expression:
2777
+ // _forgeScheme === 'dark' && { ...darkStyle }
2778
+ // _forgeScheme comes from useColorScheme() injected at the function component top.
2779
+ const darkStyleExpr = hasDarkStyle
2780
+ ? t.logicalExpression(
2781
+ "&&",
2782
+ t.binaryExpression(
2783
+ "===",
2784
+ _schemeId
2785
+ ? t.cloneNode(_schemeId)
2786
+ : t.callExpression(
2787
+ t.memberExpression(
2788
+ t.memberExpression(
2789
+ t.callExpression(t.identifier("require"), [
2790
+ t.stringLiteral("react-native"),
2791
+ ]),
2792
+ t.identifier("Appearance"),
2793
+ ),
2794
+ t.identifier("getColorScheme"),
2795
+ ),
2796
+ [],
2797
+ ),
2798
+ t.stringLiteral("dark"),
2799
+ ),
2800
+ styleToAst(t, _darkStyleObj),
2801
+ )
2802
+ : null;
2803
+
2804
+ const dynamicGradientExpr =
2805
+ dynamicClassExpr && canApplyGradient && !gradient
2806
+ ? ((state.needsRuntimeColorHelper = true),
2807
+ t.callExpression(state.runtimeColorIds.gradient, [
2808
+ dynamicClassExpr,
2809
+ ]))
2810
+ : null;
2811
+
2812
+ if (dynamicGradientExpr) {
2813
+ const jsxElement = openingEl?.parentPath;
2814
+ if (jsxElement && t.isJSXElement(jsxElement.node)) {
2815
+ const gradId = path.scope.generateUidIdentifier("forgeGradient");
2816
+ const gradientNode = t.jsxElement(
2817
+ t.jsxOpeningElement(
2818
+ t.jsxIdentifier("LinearGradient"),
2819
+ [
2820
+ t.jsxAttribute(
2821
+ t.jsxIdentifier("colors"),
2822
+ t.jsxExpressionContainer(
2823
+ t.memberExpression(gradId, t.identifier("colors")),
2824
+ ),
2825
+ ),
2826
+ t.jsxAttribute(
2827
+ t.jsxIdentifier("start"),
2828
+ t.jsxExpressionContainer(
2829
+ t.memberExpression(gradId, t.identifier("start")),
2830
+ ),
2831
+ ),
2832
+ t.jsxAttribute(
2833
+ t.jsxIdentifier("end"),
2834
+ t.jsxExpressionContainer(
2835
+ t.memberExpression(gradId, t.identifier("end")),
2836
+ ),
2837
+ ),
2838
+ t.jsxAttribute(
2839
+ t.jsxIdentifier("style"),
2840
+ t.jsxExpressionContainer(
2841
+ t.callExpression(
2842
+ t.memberExpression(
2843
+ t.identifier("Object"),
2844
+ t.identifier("assign"),
2845
+ ),
2846
+ [
2847
+ t.objectExpression([
2848
+ t.objectProperty(
2849
+ t.identifier("position"),
2850
+ t.stringLiteral("absolute"),
2851
+ ),
2852
+ t.objectProperty(
2853
+ t.identifier("top"),
2854
+ t.numericLiteral(0),
2855
+ ),
2856
+ t.objectProperty(
2857
+ t.identifier("right"),
2858
+ t.numericLiteral(0),
2859
+ ),
2860
+ t.objectProperty(
2861
+ t.identifier("bottom"),
2862
+ t.numericLiteral(0),
2863
+ ),
2864
+ t.objectProperty(
2865
+ t.identifier("left"),
2866
+ t.numericLiteral(0),
2867
+ ),
2868
+ t.objectProperty(
2869
+ t.identifier("pointerEvents"),
2870
+ t.stringLiteral("none"),
2871
+ ),
2872
+ ...gradientRadiusProps,
2873
+ ]),
2874
+ t.memberExpression(gradId, t.identifier("radii")),
2875
+ ],
2876
+ ),
2877
+ ),
2878
+ ),
2879
+ ],
2880
+ true,
2881
+ ),
2882
+ null,
2883
+ [],
2884
+ true,
2885
+ );
2886
+
2887
+ const gradientExpr = t.callExpression(
2888
+ t.arrowFunctionExpression(
2889
+ [],
2890
+ t.blockStatement([
2891
+ t.variableDeclaration("const", [
2892
+ t.variableDeclarator(gradId, dynamicGradientExpr),
2893
+ ]),
2894
+ t.returnStatement(
2895
+ t.conditionalExpression(
2896
+ gradId,
2897
+ gradientNode,
2898
+ t.nullLiteral(),
2899
+ ),
2900
+ ),
2901
+ ]),
2902
+ ),
2903
+ [],
2904
+ );
2905
+
2906
+ jsxElement.node.children.unshift(
2907
+ t.jsxExpressionContainer(gradientExpr),
2908
+ );
2909
+ state.needsGradientImport = true;
2910
+ if (style.position == null) style.position = "relative";
2911
+ if (style.overflow == null) style.overflow = "hidden";
2912
+ }
2913
+ }
2914
+
2915
+ if (
2916
+ hasStaticStyle ||
2917
+ dynamicStyleExpr ||
2918
+ hasDarkStyle ||
2919
+ _hasApplicableStateStyle ||
2920
+ _hasCompoundStyle
2921
+ ) {
2922
+ const styleAst = styleToAst(t, style);
2923
+ if (
2924
+ _supportsStateStyleFn &&
2925
+ (_hasApplicableStateStyle || dynamicStyleExpr)
2926
+ ) {
2927
+ const stateId = t.identifier("_forgeState");
2928
+ const existingExpr = styleAttrPath
2929
+ ? t.isJSXExpressionContainer(styleAttrPath.node.value)
2930
+ ? styleAttrPath.node.value.expression
2931
+ : t.nullLiteral()
2932
+ : t.nullLiteral();
2933
+
2934
+ const existingResolved = t.conditionalExpression(
2935
+ t.binaryExpression(
2936
+ "===",
2937
+ t.unaryExpression("typeof", existingExpr),
2938
+ t.stringLiteral("function"),
2939
+ ),
2940
+ t.callExpression(existingExpr, [t.cloneNode(stateId)]),
2941
+ existingExpr,
2942
+ );
2943
+
2944
+ const staticStateExprs = Object.entries(_stateStyles)
2945
+ .filter(([, obj]) => Object.keys(obj).length > 0)
2946
+ .map(([k, obj]) =>
2947
+ t.logicalExpression(
2948
+ "&&",
2949
+ t.memberExpression(
2950
+ t.cloneNode(stateId),
2951
+ t.identifier(_statePropMap[k]),
2952
+ ),
2953
+ styleToAst(t, obj),
2954
+ ),
2955
+ );
2956
+
2957
+ const compoundStateExprs = compoundExprsWithState;
2958
+
2959
+ const dynamicWithState = dynamicClassExpr
2960
+ ? ((state.needsRuntimeColorHelper = true),
2961
+ t.callExpression(state.runtimeColorIds.styles, [
2962
+ dynamicClassExpr,
2963
+ t.cloneNode(stateId),
2964
+ ]))
2965
+ : null;
2966
+
2967
+ const arrElements = [];
2968
+ if (hasStaticStyle) arrElements.push(styleAst);
2969
+ if (hasDarkStyle) arrElements.push(darkStyleExpr);
2970
+ arrElements.push(...responsiveExprs);
2971
+ arrElements.push(...staticStateExprs);
2972
+ arrElements.push(...compoundStateExprs);
2973
+ if (dynamicWithState) {
2974
+ arrElements.push(t.spreadElement(dynamicWithState));
2975
+ }
2976
+ arrElements.push(
2977
+ t.spreadElement(runtimeArrayExpr(existingResolved)),
2978
+ );
2979
+
2980
+ const fnStyleExpr = t.arrowFunctionExpression(
2981
+ [t.cloneNode(stateId)],
2982
+ t.arrayExpression(arrElements),
2983
+ );
2984
+
2985
+ if (styleAttrPath) styleAttrPath.remove();
2986
+ path.replaceWith(
2987
+ t.jsxAttribute(
2988
+ t.jsxIdentifier("style"),
2989
+ t.jsxExpressionContainer(fnStyleExpr),
2990
+ ),
2991
+ );
2992
+ } else if (styleAttrPath) {
2993
+ const existing = styleAttrPath.node.value;
2994
+ const existingExpr = t.isJSXExpressionContainer(existing)
2995
+ ? existing.expression
2996
+ : t.nullLiteral();
2997
+ const mergedElements = [];
2998
+ if (hasStaticStyle) mergedElements.push(styleAst);
2999
+ if (hasDarkStyle) mergedElements.push(darkStyleExpr);
3000
+ mergedElements.push(...responsiveExprs);
3001
+ mergedElements.push(...compoundExprsNoState);
3002
+ if (dynamicStyleExpr) {
3003
+ mergedElements.push(t.spreadElement(dynamicStyleExpr));
3004
+ }
3005
+ mergedElements.push(
3006
+ t.spreadElement(runtimeArrayExpr(existingExpr)),
3007
+ );
3008
+ const merged = t.arrayExpression(mergedElements);
3009
+ styleAttrPath.remove();
3010
+ path.replaceWith(
3011
+ t.jsxAttribute(
3012
+ t.jsxIdentifier("style"),
3013
+ t.jsxExpressionContainer(merged),
3014
+ ),
3015
+ );
3016
+ } else {
3017
+ const needsArray =
3018
+ dynamicStyleExpr ||
3019
+ hasDarkStyle ||
3020
+ responsiveExprs.length > 0 ||
3021
+ compoundExprsNoState.length > 0 ||
3022
+ !hasStaticStyle;
3023
+ const nextStyleExpr = needsArray
3024
+ ? t.arrayExpression([
3025
+ ...(hasStaticStyle ? [styleAst] : []),
3026
+ ...(hasDarkStyle ? [darkStyleExpr] : []),
3027
+ ...responsiveExprs,
3028
+ ...compoundExprsNoState,
3029
+ ...(dynamicStyleExpr
3030
+ ? [t.spreadElement(dynamicStyleExpr)]
3031
+ : []),
3032
+ ])
3033
+ : styleAst;
3034
+ path.replaceWith(
3035
+ t.jsxAttribute(
3036
+ t.jsxIdentifier("style"),
3037
+ t.jsxExpressionContainer(nextStyleExpr),
3038
+ ),
3039
+ );
3040
+ }
3041
+ } else {
3042
+ path.remove();
3043
+ }
3044
+ },
3045
+
3046
+ // Inject `const _forgeScheme = require('react-native').useColorScheme() ?? 'light'`
3047
+ // at the top of every function component that contains dark: class styles.
3048
+ // This makes those components automatically subscribe to system theme changes.
3049
+ "FunctionDeclaration|FunctionExpression|ArrowFunctionExpression": {
3050
+ exit(fnPath, state) {
3051
+ const fnNode = fnPath.node;
3052
+ const needsScheme =
3053
+ state.functionsNeedingScheme &&
3054
+ state.functionsNeedingScheme.has(fnNode);
3055
+ const needsDimensions =
3056
+ state.functionsNeedingDimensions &&
3057
+ state.functionsNeedingDimensions.has(fnNode);
3058
+
3059
+ if (!needsScheme && !needsDimensions) return;
3060
+
3061
+ // Support concise arrow components: `() => <View />` → `() => { return <View />; }`
3062
+ if (
3063
+ fnPath.isArrowFunctionExpression() &&
3064
+ !t.isBlockStatement(fnNode.body)
3065
+ ) {
3066
+ fnNode.body = t.blockStatement([t.returnStatement(fnNode.body)]);
3067
+ }
3068
+ const body = fnPath.get("body");
3069
+ if (!body.isBlockStatement()) return;
3070
+
3071
+ // Inject useTheme() for dark: styles to subscribe to theme context
3072
+ if (needsScheme) {
3073
+ const schemeId = state.schemeIds && state.schemeIds.get(fnNode);
3074
+ if (schemeId) {
3075
+ body.unshiftContainer(
3076
+ "body",
3077
+ t.variableDeclaration("const", [
3078
+ t.variableDeclarator(
3079
+ t.objectPattern([
3080
+ t.objectProperty(
3081
+ t.identifier("theme"),
3082
+ t.cloneNode(schemeId),
3083
+ false,
3084
+ false,
3085
+ ),
3086
+ ]),
3087
+ t.callExpression(
3088
+ t.memberExpression(
3089
+ t.callExpression(t.identifier("require"), [
3090
+ t.stringLiteral("@expo-forge/forge-ui"),
3091
+ ]),
3092
+ t.identifier("useTheme"),
3093
+ ),
3094
+ [],
3095
+ ),
3096
+ ),
3097
+ ]),
3098
+ );
3099
+ }
3100
+ }
3101
+
3102
+ // Inject useWindowDimensions() for responsive breakpoint styles
3103
+ if (needsDimensions) {
3104
+ const widthId =
3105
+ state.dimensionIds && state.dimensionIds.get(fnNode);
3106
+ if (widthId) {
3107
+ body.unshiftContainer(
3108
+ "body",
3109
+ t.variableDeclaration("const", [
3110
+ t.variableDeclarator(
3111
+ t.objectPattern([
3112
+ t.objectProperty(
3113
+ t.identifier("width"),
3114
+ t.cloneNode(widthId),
3115
+ false,
3116
+ false,
3117
+ ),
3118
+ ]),
3119
+ t.callExpression(
3120
+ t.memberExpression(
3121
+ t.callExpression(t.identifier("require"), [
3122
+ t.stringLiteral("react-native"),
3123
+ ]),
3124
+ t.identifier("useWindowDimensions"),
3125
+ ),
3126
+ [],
3127
+ ),
3128
+ ),
3129
+ ]),
3130
+ );
3131
+ }
3132
+ }
3133
+ },
3134
+ },
3135
+ },
3136
+ };
3137
+ };