gradient-forge 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/index.mjs +240 -985
- package/package.json +1 -1
package/cli/index.mjs
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import readline from "node:readline";
|
|
6
5
|
|
|
7
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
7
|
const __dirname = path.dirname(__filename);
|
|
9
|
-
const templateDir = path.join(__dirname, "templates");
|
|
10
8
|
|
|
11
9
|
const args = process.argv.slice(2);
|
|
12
10
|
const command = args[0] ?? "help";
|
|
@@ -21,319 +19,39 @@ const readArg = (flag, fallback) => {
|
|
|
21
19
|
|
|
22
20
|
const hasFlag = (flag) => args.includes(flag);
|
|
23
21
|
|
|
24
|
-
const log = (
|
|
25
|
-
const logError = (
|
|
26
|
-
const logSuccess = (
|
|
27
|
-
const logInfo = (
|
|
22
|
+
const log = (msg) => process.stdout.write(`${msg}\n`);
|
|
23
|
+
const logError = (msg) => process.stderr.write(`${msg}\n`);
|
|
24
|
+
const logSuccess = (msg) => process.stdout.write(`✅ ${msg}\n`);
|
|
25
|
+
const logInfo = (msg) => process.stdout.write(`ℹ️ ${msg}\n`);
|
|
28
26
|
|
|
29
27
|
const themes = [
|
|
30
|
-
{ id: "theme-nitro-mint-apple", label: "Mint Apple"
|
|
31
|
-
{ id: "theme-nitro-citrus-sherbert", label: "Citrus Sherbert"
|
|
32
|
-
{ id: "theme-nitro-retro-raincloud", label: "Retro Raincloud"
|
|
33
|
-
{ id: "theme-nitro-hanami", label: "Hanami"
|
|
34
|
-
{ id: "theme-nitro-sunrise", label: "Sunrise"
|
|
35
|
-
{ id: "theme-nitro-cotton-candy", label: "Cotton Candy"
|
|
36
|
-
{ id: "theme-nitro-lofi-vibes", label: "Lofi Vibes"
|
|
37
|
-
{ id: "theme-nitro-desert-khaki", label: "Desert Khaki"
|
|
38
|
-
{ id: "theme-nitro-sunset", label: "Sunset"
|
|
39
|
-
{ id: "theme-nitro-chroma-glow", label: "Chroma Glow"
|
|
40
|
-
{ id: "theme-nitro-forest", label: "Forest"
|
|
41
|
-
{ id: "theme-nitro-crimson", label: "Crimson"
|
|
42
|
-
{ id: "theme-nitro-midnight-blurple", label: "Midnight Blurple"
|
|
43
|
-
{ id: "theme-nitro-mars", label: "Mars"
|
|
44
|
-
{ id: "theme-nitro-dusk", label: "Dusk"
|
|
45
|
-
{ id: "theme-nitro-under-the-sea", label: "Under The Sea"
|
|
46
|
-
{ id: "theme-nitro-retro-storm", label: "Retro Storm"
|
|
47
|
-
{ id: "theme-nitro-neon-nights", label: "Neon Nights"
|
|
48
|
-
{ id: "theme-nitro-strawberry-lemonade", label: "Strawberry Lemonade"
|
|
49
|
-
{ id: "theme-nitro-aurora", label: "Aurora"
|
|
50
|
-
{ id: "theme-nitro-sepia", label: "Sepia"
|
|
28
|
+
{ id: "theme-nitro-mint-apple", label: "Mint Apple" },
|
|
29
|
+
{ id: "theme-nitro-citrus-sherbert", label: "Citrus Sherbert" },
|
|
30
|
+
{ id: "theme-nitro-retro-raincloud", label: "Retro Raincloud" },
|
|
31
|
+
{ id: "theme-nitro-hanami", label: "Hanami" },
|
|
32
|
+
{ id: "theme-nitro-sunrise", label: "Sunrise" },
|
|
33
|
+
{ id: "theme-nitro-cotton-candy", label: "Cotton Candy" },
|
|
34
|
+
{ id: "theme-nitro-lofi-vibes", label: "Lofi Vibes" },
|
|
35
|
+
{ id: "theme-nitro-desert-khaki", label: "Desert Khaki" },
|
|
36
|
+
{ id: "theme-nitro-sunset", label: "Sunset" },
|
|
37
|
+
{ id: "theme-nitro-chroma-glow", label: "Chroma Glow" },
|
|
38
|
+
{ id: "theme-nitro-forest", label: "Forest" },
|
|
39
|
+
{ id: "theme-nitro-crimson", label: "Crimson" },
|
|
40
|
+
{ id: "theme-nitro-midnight-blurple", label: "Midnight Blurple" },
|
|
41
|
+
{ id: "theme-nitro-mars", label: "Mars" },
|
|
42
|
+
{ id: "theme-nitro-dusk", label: "Dusk" },
|
|
43
|
+
{ id: "theme-nitro-under-the-sea", label: "Under The Sea" },
|
|
44
|
+
{ id: "theme-nitro-retro-storm", label: "Retro Storm" },
|
|
45
|
+
{ id: "theme-nitro-neon-nights", label: "Neon Nights" },
|
|
46
|
+
{ id: "theme-nitro-strawberry-lemonade", label: "Strawberry Lemonade" },
|
|
47
|
+
{ id: "theme-nitro-aurora", label: "Aurora" },
|
|
48
|
+
{ id: "theme-nitro-sepia", label: "Sepia" },
|
|
51
49
|
];
|
|
52
50
|
|
|
53
51
|
const themeTokens = {
|
|
54
|
-
"theme-nitro-mint-apple": {
|
|
55
|
-
"--background": "160 45% 18%",
|
|
56
|
-
"--foreground": "150 40% 95%",
|
|
57
|
-
"--card": "160 40% 20%",
|
|
58
|
-
"--card-foreground": "150 40% 95%",
|
|
59
|
-
"--popover": "160 40% 20%",
|
|
60
|
-
"--popover-foreground": "150 40% 95%",
|
|
61
|
-
"--primary": "145 50% 55%",
|
|
62
|
-
"--primary-foreground": "160 40% 10%",
|
|
63
|
-
"--secondary": "100 45% 68%",
|
|
64
|
-
"--secondary-foreground": "160 40% 10%",
|
|
65
|
-
"--muted": "160 35% 25%",
|
|
66
|
-
"--muted-foreground": "150 30% 70%",
|
|
67
|
-
"--accent": "100 45% 68%",
|
|
68
|
-
"--accent-foreground": "160 40% 10%",
|
|
69
|
-
"--destructive": "0 84% 60%",
|
|
70
|
-
"--destructive-foreground": "0 0% 98%",
|
|
71
|
-
"--border": "160 30% 30%",
|
|
72
|
-
"--input": "160 30% 30%",
|
|
73
|
-
"--ring": "145 50% 55%",
|
|
74
|
-
"--app-gradient": "radial-gradient(1050px 560px at -10% -20%, hsl(156 58% 60% / 0.3), transparent 60%), radial-gradient(940px 520px at 114% 0%, hsl(96 64% 62% / 0.22), transparent 58%), linear-gradient(160deg, hsl(156 27% 13%) 0%, hsl(149 23% 13%) 50%, hsl(136 21% 10%) 100%)",
|
|
75
|
-
"--app-surface-tint": "hsl(145 50% 55% / 0.1)",
|
|
76
|
-
},
|
|
77
|
-
"theme-nitro-citrus-sherbert": {
|
|
78
|
-
"--background": "25 70% 15%",
|
|
79
|
-
"--foreground": "35 60% 95%",
|
|
80
|
-
"--card": "25 65% 18%",
|
|
81
|
-
"--card-foreground": "35 60% 95%",
|
|
82
|
-
"--popover": "25 65% 18%",
|
|
83
|
-
"--popover-foreground": "35 60% 95%",
|
|
84
|
-
"--primary": "25 80% 55%",
|
|
85
|
-
"--primary-foreground": "25 70% 10%",
|
|
86
|
-
"--secondary": "45 85% 65%",
|
|
87
|
-
"--secondary-foreground": "25 70% 10%",
|
|
88
|
-
"--muted": "25 50% 25%",
|
|
89
|
-
"--muted-foreground": "35 40% 70%",
|
|
90
|
-
"--accent": "55 90% 75%",
|
|
91
|
-
"--accent-foreground": "25 70% 10%",
|
|
92
|
-
"--destructive": "0 84% 60%",
|
|
93
|
-
"--destructive-foreground": "0 0% 98%",
|
|
94
|
-
"--border": "25 45% 30%",
|
|
95
|
-
"--input": "25 45% 30%",
|
|
96
|
-
"--ring": "25 80% 55%",
|
|
97
|
-
"--app-gradient": "radial-gradient(1060px 560px at -10% -20%, hsl(28 97% 66% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 4%, hsl(53 95% 68% / 0.22), transparent 58%), linear-gradient(160deg, hsl(34 29% 12%) 0%, hsl(32 24% 13%) 50%, hsl(27 21% 10%) 100%)",
|
|
98
|
-
"--app-surface-tint": "hsl(25 80% 55% / 0.1)",
|
|
99
|
-
},
|
|
100
|
-
"theme-nitro-retro-raincloud": {
|
|
101
|
-
"--background": "215 25% 20%",
|
|
102
|
-
"--foreground": "210 30% 95%",
|
|
103
|
-
"--card": "215 22% 23%",
|
|
104
|
-
"--card-foreground": "210 30% 95%",
|
|
105
|
-
"--popover": "215 22% 23%",
|
|
106
|
-
"--popover-foreground": "210 30% 95%",
|
|
107
|
-
"--primary": "210 25% 55%",
|
|
108
|
-
"--primary-foreground": "215 25% 15%",
|
|
109
|
-
"--secondary": "210 20% 65%",
|
|
110
|
-
"--secondary-foreground": "215 25% 15%",
|
|
111
|
-
"--muted": "215 20% 30%",
|
|
112
|
-
"--muted-foreground": "210 20% 70%",
|
|
113
|
-
"--accent": "210 20% 65%",
|
|
114
|
-
"--accent-foreground": "215 25% 15%",
|
|
115
|
-
"--destructive": "0 84% 60%",
|
|
116
|
-
"--destructive-foreground": "0 0% 98%",
|
|
117
|
-
"--border": "215 20% 35%",
|
|
118
|
-
"--input": "215 20% 35%",
|
|
119
|
-
"--ring": "210 25% 55%",
|
|
120
|
-
"--app-gradient": "radial-gradient(1080px 560px at -8% -20%, hsl(215 36% 66% / 0.28), transparent 60%), radial-gradient(940px 520px at 114% 2%, hsl(206 30% 64% / 0.2), transparent 58%), linear-gradient(160deg, hsl(217 21% 13%) 0%, hsl(214 18% 13%) 52%, hsl(211 16% 10%) 100%)",
|
|
121
|
-
"--app-surface-tint": "hsl(210 25% 55% / 0.1)",
|
|
122
|
-
},
|
|
123
|
-
"theme-nitro-hanami": {
|
|
124
|
-
"--background": "330 30% 20%",
|
|
125
|
-
"--foreground": "330 30% 95%",
|
|
126
|
-
"--card": "330 25% 23%",
|
|
127
|
-
"--card-foreground": "330 30% 95%",
|
|
128
|
-
"--popover": "330 25% 23%",
|
|
129
|
-
"--popover-foreground": "330 30% 95%",
|
|
130
|
-
"--primary": "330 40% 55%",
|
|
131
|
-
"--primary-foreground": "330 30% 10%",
|
|
132
|
-
"--secondary": "340 45% 65%",
|
|
133
|
-
"--secondary-foreground": "330 30% 10%",
|
|
134
|
-
"--muted": "330 20% 30%",
|
|
135
|
-
"--muted-foreground": "330 25% 70%",
|
|
136
|
-
"--accent": "350 50% 75%",
|
|
137
|
-
"--accent-foreground": "330 30% 10%",
|
|
138
|
-
"--destructive": "0 84% 60%",
|
|
139
|
-
"--destructive-foreground": "0 0% 98%",
|
|
140
|
-
"--border": "330 20% 35%",
|
|
141
|
-
"--input": "330 20% 35%",
|
|
142
|
-
"--ring": "330 40% 55%",
|
|
143
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(319 65% 69% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(339 58% 75% / 0.22), transparent 58%), linear-gradient(160deg, hsl(323 27% 12%) 0%, hsl(315 23% 13%) 50%, hsl(304 19% 10%) 100%)",
|
|
144
|
-
"--app-surface-tint": "hsl(330 40% 55% / 0.1)",
|
|
145
|
-
},
|
|
146
|
-
"theme-nitro-sunrise": {
|
|
147
|
-
"--background": "350 50% 18%",
|
|
148
|
-
"--foreground": "30 60% 95%",
|
|
149
|
-
"--card": "350 45% 21%",
|
|
150
|
-
"--card-foreground": "30 60% 95%",
|
|
151
|
-
"--popover": "350 45% 21%",
|
|
152
|
-
"--popover-foreground": "30 60% 95%",
|
|
153
|
-
"--primary": "355 75% 60%",
|
|
154
|
-
"--primary-foreground": "350 50% 10%",
|
|
155
|
-
"--secondary": "25 85% 65%",
|
|
156
|
-
"--secondary-foreground": "350 50% 10%",
|
|
157
|
-
"--muted": "350 30% 28%",
|
|
158
|
-
"--muted-foreground": "30 40% 75%",
|
|
159
|
-
"--accent": "45 90% 73%",
|
|
160
|
-
"--accent-foreground": "350 50% 10%",
|
|
161
|
-
"--destructive": "0 84% 60%",
|
|
162
|
-
"--destructive-foreground": "0 0% 98%",
|
|
163
|
-
"--border": "350 25% 35%",
|
|
164
|
-
"--input": "350 25% 35%",
|
|
165
|
-
"--ring": "355 75% 60%",
|
|
166
|
-
"--app-gradient": "radial-gradient(1080px 560px at -12% -20%, hsl(2 88% 66% / 0.28), transparent 58%), radial-gradient(920px 520px at 114% 4%, hsl(35 96% 67% / 0.24), transparent 58%), linear-gradient(160deg, hsl(9 30% 12%) 0%, hsl(13 26% 13%) 50%, hsl(21 21% 10%) 100%)",
|
|
167
|
-
"--app-surface-tint": "hsl(355 75% 60% / 0.1)",
|
|
168
|
-
},
|
|
169
|
-
"theme-nitro-cotton-candy": {
|
|
170
|
-
"--background": "220 50% 20%",
|
|
171
|
-
"--foreground": "220 40% 95%",
|
|
172
|
-
"--card": "220 45% 23%",
|
|
173
|
-
"--card-foreground": "220 40% 95%",
|
|
174
|
-
"--popover": "220 45% 23%",
|
|
175
|
-
"--popover-foreground": "220 40% 95%",
|
|
176
|
-
"--primary": "220 70% 65%",
|
|
177
|
-
"--primary-foreground": "220 50% 10%",
|
|
178
|
-
"--secondary": "270 60% 70%",
|
|
179
|
-
"--secondary-foreground": "220 50% 10%",
|
|
180
|
-
"--muted": "220 30% 30%",
|
|
181
|
-
"--muted-foreground": "220 30% 75%",
|
|
182
|
-
"--accent": "330 65% 75%",
|
|
183
|
-
"--accent-foreground": "220 50% 10%",
|
|
184
|
-
"--destructive": "0 84% 60%",
|
|
185
|
-
"--destructive-foreground": "0 0% 98%",
|
|
186
|
-
"--border": "220 25% 35%",
|
|
187
|
-
"--input": "220 25% 35%",
|
|
188
|
-
"--ring": "220 70% 65%",
|
|
189
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(208 95% 74% / 0.28), transparent 58%), radial-gradient(940px 520px at 112% 4%, hsl(328 87% 73% / 0.24), transparent 58%), linear-gradient(160deg, hsl(247 30% 12%) 0%, hsl(258 26% 13%) 48%, hsl(286 20% 10%) 100%)",
|
|
190
|
-
"--app-surface-tint": "hsl(220 70% 65% / 0.1)",
|
|
191
|
-
},
|
|
192
|
-
"theme-nitro-lofi-vibes": {
|
|
193
|
-
"--background": "230 25% 18%",
|
|
194
|
-
"--foreground": "230 20% 95%",
|
|
195
|
-
"--card": "230 22% 21%",
|
|
196
|
-
"--card-foreground": "230 20% 95%",
|
|
197
|
-
"--popover": "230 22% 21%",
|
|
198
|
-
"--popover-foreground": "230 20% 95%",
|
|
199
|
-
"--primary": "230 25% 55%",
|
|
200
|
-
"--primary-foreground": "230 25% 10%",
|
|
201
|
-
"--secondary": "260 20% 60%",
|
|
202
|
-
"--secondary-foreground": "230 25% 10%",
|
|
203
|
-
"--muted": "230 15% 28%",
|
|
204
|
-
"--muted-foreground": "230 15% 70%",
|
|
205
|
-
"--accent": "280 20% 55%",
|
|
206
|
-
"--accent-foreground": "230 25% 10%",
|
|
207
|
-
"--destructive": "0 84% 60%",
|
|
208
|
-
"--destructive-foreground": "0 0% 98%",
|
|
209
|
-
"--border": "230 15% 33%",
|
|
210
|
-
"--input": "230 15% 33%",
|
|
211
|
-
"--ring": "230 25% 55%",
|
|
212
|
-
"--app-gradient": "radial-gradient(1060px 560px at -10% -20%, hsl(228 44% 63% / 0.26), transparent 60%), radial-gradient(940px 520px at 112% 2%, hsl(267 38% 64% / 0.2), transparent 58%), linear-gradient(160deg, hsl(236 26% 13%) 0%, hsl(232 22% 13%) 50%, hsl(258 16% 10%) 100%)",
|
|
213
|
-
"--app-surface-tint": "hsl(230 25% 55% / 0.1)",
|
|
214
|
-
},
|
|
215
|
-
"theme-nitro-desert-khaki": {
|
|
216
|
-
"--background": "35 25% 18%",
|
|
217
|
-
"--foreground": "35 30% 95%",
|
|
218
|
-
"--card": "35 22% 21%",
|
|
219
|
-
"--card-foreground": "35 30% 95%",
|
|
220
|
-
"--popover": "35 22% 21%",
|
|
221
|
-
"--popover-foreground": "35 30% 95%",
|
|
222
|
-
"--primary": "35 25% 50%",
|
|
223
|
-
"--primary-foreground": "35 25% 10%",
|
|
224
|
-
"--secondary": "40 20% 58%",
|
|
225
|
-
"--secondary-foreground": "35 25% 10%",
|
|
226
|
-
"--muted": "35 15% 28%",
|
|
227
|
-
"--muted-foreground": "35 20% 70%",
|
|
228
|
-
"--accent": "45 25% 60%",
|
|
229
|
-
"--accent-foreground": "35 25% 10%",
|
|
230
|
-
"--destructive": "0 84% 60%",
|
|
231
|
-
"--destructive-foreground": "0 0% 98%",
|
|
232
|
-
"--border": "35 15% 33%",
|
|
233
|
-
"--input": "35 15% 33%",
|
|
234
|
-
"--ring": "35 25% 50%",
|
|
235
|
-
"--app-gradient": "radial-gradient(1060px 560px at -10% -18%, hsl(34 44% 58% / 0.26), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(48 33% 63% / 0.18), transparent 58%), linear-gradient(160deg, hsl(37 23% 13%) 0%, hsl(34 20% 13%) 52%, hsl(30 16% 10%) 100%)",
|
|
236
|
-
"--app-surface-tint": "hsl(35 25% 50% / 0.1)",
|
|
237
|
-
},
|
|
238
|
-
"theme-nitro-sunset": {
|
|
239
|
-
"--background": "320 40% 15%",
|
|
240
|
-
"--foreground": "25 60% 95%",
|
|
241
|
-
"--card": "320 35% 18%",
|
|
242
|
-
"--card-foreground": "25 60% 95%",
|
|
243
|
-
"--popover": "320 35% 18%",
|
|
244
|
-
"--popover-foreground": "25 60% 95%",
|
|
245
|
-
"--primary": "335 60% 50%",
|
|
246
|
-
"--primary-foreground": "320 40% 10%",
|
|
247
|
-
"--secondary": "20 70% 55%",
|
|
248
|
-
"--secondary-foreground": "320 40% 10%",
|
|
249
|
-
"--muted": "320 25% 25%",
|
|
250
|
-
"--muted-foreground": "25 40% 75%",
|
|
251
|
-
"--accent": "25 85% 62%",
|
|
252
|
-
"--accent-foreground": "320 40% 10%",
|
|
253
|
-
"--destructive": "0 84% 60%",
|
|
254
|
-
"--destructive-foreground": "0 0% 98%",
|
|
255
|
-
"--border": "320 20% 32%",
|
|
256
|
-
"--input": "320 20% 32%",
|
|
257
|
-
"--ring": "335 60% 50%",
|
|
258
|
-
"--app-gradient": "radial-gradient(1040px 560px at -12% -20%, hsl(325 84% 62% / 0.31), transparent 58%), radial-gradient(900px 520px at 112% 6%, hsl(24 97% 66% / 0.24), transparent 58%), linear-gradient(160deg, hsl(304 29% 12%) 0%, hsl(298 24% 13%) 50%, hsl(287 22% 10%) 100%)",
|
|
259
|
-
"--app-surface-tint": "hsl(335 60% 50% / 0.1)",
|
|
260
|
-
},
|
|
261
|
-
"theme-nitro-chroma-glow": {
|
|
262
|
-
"--background": "246 33% 11%",
|
|
263
|
-
"--foreground": "240 40% 95%",
|
|
264
|
-
"--card": "246 28% 13%",
|
|
265
|
-
"--card-foreground": "240 40% 95%",
|
|
266
|
-
"--popover": "246 28% 13%",
|
|
267
|
-
"--popover-foreground": "240 40% 95%",
|
|
268
|
-
"--primary": "286 94% 67%",
|
|
269
|
-
"--primary-foreground": "246 36% 10%",
|
|
270
|
-
"--secondary": "195 100% 61%",
|
|
271
|
-
"--secondary-foreground": "246 36% 10%",
|
|
272
|
-
"--muted": "246 30% 22%",
|
|
273
|
-
"--muted-foreground": "240 25% 75%",
|
|
274
|
-
"--accent": "195 100% 61%",
|
|
275
|
-
"--accent-foreground": "246 36% 10%",
|
|
276
|
-
"--destructive": "0 84% 60%",
|
|
277
|
-
"--destructive-foreground": "0 0% 98%",
|
|
278
|
-
"--border": "246 25% 28%",
|
|
279
|
-
"--input": "246 25% 28%",
|
|
280
|
-
"--ring": "286 94% 67%",
|
|
281
|
-
"--app-gradient": "radial-gradient(1020px 560px at -12% -18%, hsl(226 95% 63% / 0.32), transparent 58%), radial-gradient(940px 520px at 112% 2%, hsl(194 100% 62% / 0.28), transparent 58%), linear-gradient(160deg, hsl(246 36% 12%) 0%, hsl(272 30% 12%) 50%, hsl(305 24% 10%) 100%)",
|
|
282
|
-
"--app-surface-tint": "hsl(286 94% 67% / 0.1)",
|
|
283
|
-
},
|
|
284
|
-
"theme-nitro-forest": {
|
|
285
|
-
"--background": "150 40% 12%",
|
|
286
|
-
"--foreground": "140 30% 95%",
|
|
287
|
-
"--card": "150 35% 15%",
|
|
288
|
-
"--card-foreground": "140 30% 95%",
|
|
289
|
-
"--popover": "150 35% 15%",
|
|
290
|
-
"--popover-foreground": "140 30% 95%",
|
|
291
|
-
"--primary": "145 45% 45%",
|
|
292
|
-
"--primary-foreground": "150 40% 10%",
|
|
293
|
-
"--secondary": "100 40% 55%",
|
|
294
|
-
"--secondary-foreground": "150 40% 10%",
|
|
295
|
-
"--muted": "150 25% 22%",
|
|
296
|
-
"--muted-foreground": "140 20% 70%",
|
|
297
|
-
"--accent": "85 45% 52%",
|
|
298
|
-
"--accent-foreground": "150 40% 10%",
|
|
299
|
-
"--destructive": "0 84% 60%",
|
|
300
|
-
"--destructive-foreground": "0 0% 98%",
|
|
301
|
-
"--border": "150 20% 28%",
|
|
302
|
-
"--input": "150 20% 28%",
|
|
303
|
-
"--ring": "145 45% 45%",
|
|
304
|
-
"--app-gradient": "radial-gradient(1080px 560px at -10% -20%, hsl(145 62% 53% / 0.3), transparent 60%), radial-gradient(920px 520px at 112% 2%, hsl(170 48% 53% / 0.22), transparent 58%), linear-gradient(160deg, hsl(149 30% 12%) 0%, hsl(146 26% 12%) 52%, hsl(160 21% 10%) 100%)",
|
|
305
|
-
"--app-surface-tint": "hsl(145 45% 45% / 0.1)",
|
|
306
|
-
},
|
|
307
|
-
"theme-nitro-crimson": {
|
|
308
|
-
"--background": "345 60% 8%",
|
|
309
|
-
"--foreground": "345 40% 95%",
|
|
310
|
-
"--card": "345 55% 11%",
|
|
311
|
-
"--card-foreground": "345 40% 95%",
|
|
312
|
-
"--popover": "345 55% 11%",
|
|
313
|
-
"--popover-foreground": "345 40% 95%",
|
|
314
|
-
"--primary": "350 65% 45%",
|
|
315
|
-
"--primary-foreground": "345 60% 98%",
|
|
316
|
-
"--secondary": "350 50% 55%",
|
|
317
|
-
"--secondary-foreground": "345 60% 98%",
|
|
318
|
-
"--muted": "345 30% 18%",
|
|
319
|
-
"--muted-foreground": "345 25% 70%",
|
|
320
|
-
"--accent": "355 55% 60%",
|
|
321
|
-
"--accent-foreground": "345 60% 10%",
|
|
322
|
-
"--destructive": "0 84% 60%",
|
|
323
|
-
"--destructive-foreground": "0 0% 98%",
|
|
324
|
-
"--border": "345 25% 25%",
|
|
325
|
-
"--input": "345 25% 25%",
|
|
326
|
-
"--ring": "350 65% 45%",
|
|
327
|
-
"--app-gradient": "radial-gradient(1040px 560px at -12% -18%, hsl(352 85% 59% / 0.34), transparent 58%), radial-gradient(900px 520px at 112% 4%, hsl(9 84% 58% / 0.22), transparent 58%), linear-gradient(160deg, hsl(349 37% 11%) 0%, hsl(342 31% 12%) 50%, hsl(326 24% 10%) 100%)",
|
|
328
|
-
"--app-surface-tint": "hsl(350 65% 45% / 0.1)",
|
|
329
|
-
},
|
|
330
52
|
"theme-nitro-midnight-blurple": {
|
|
331
53
|
"--background": "235 26% 11%",
|
|
332
54
|
"--foreground": "235 30% 95%",
|
|
333
|
-
"--card": "235 22% 12%",
|
|
334
|
-
"--card-foreground": "235 30% 95%",
|
|
335
|
-
"--popover": "235 22% 12%",
|
|
336
|
-
"--popover-foreground": "235 30% 95%",
|
|
337
55
|
"--primary": "241 92% 70%",
|
|
338
56
|
"--primary-foreground": "235 26% 98%",
|
|
339
57
|
"--secondary": "210 92% 65%",
|
|
@@ -345,220 +63,51 @@ const themeTokens = {
|
|
|
345
63
|
"--destructive": "0 84% 60%",
|
|
346
64
|
"--destructive-foreground": "0 0% 98%",
|
|
347
65
|
"--border": "235 18% 25%",
|
|
66
|
+
"--card": "235 22% 12%",
|
|
67
|
+
"--card-foreground": "235 30% 95%",
|
|
68
|
+
"--popover": "235 22% 12%",
|
|
69
|
+
"--popover-foreground": "235 30% 95%",
|
|
348
70
|
"--input": "235 18% 25%",
|
|
349
71
|
"--ring": "241 92% 70%",
|
|
350
72
|
"--app-gradient": "radial-gradient(1050px 560px at -10% -20%, hsl(246 92% 66% / 0.3), transparent 60%), radial-gradient(920px 520px at 112% 2%, hsl(201 92% 63% / 0.22), transparent 58%), linear-gradient(160deg, hsl(232 29% 12%) 0%, hsl(231 24% 12%) 50%, hsl(228 22% 10%) 100%)",
|
|
351
73
|
"--app-surface-tint": "hsl(241 92% 70% / 0.1)",
|
|
352
74
|
},
|
|
353
|
-
"theme-nitro-mars": {
|
|
354
|
-
"--background": "12 30% 10%",
|
|
355
|
-
"--foreground": "20 40% 95%",
|
|
356
|
-
"--card": "12 25% 12%",
|
|
357
|
-
"--card-foreground": "20 40% 95%",
|
|
358
|
-
"--popover": "12 25% 12%",
|
|
359
|
-
"--popover-foreground": "20 40% 95%",
|
|
360
|
-
"--primary": "14 74% 61%",
|
|
361
|
-
"--primary-foreground": "12 30% 10%",
|
|
362
|
-
"--secondary": "22 78% 62%",
|
|
363
|
-
"--secondary-foreground": "12 30% 10%",
|
|
364
|
-
"--muted": "12 25% 20%",
|
|
365
|
-
"--muted-foreground": "20 25% 72%",
|
|
366
|
-
"--accent": "22 78% 62%",
|
|
367
|
-
"--accent-foreground": "12 30% 10%",
|
|
368
|
-
"--destructive": "0 84% 60%",
|
|
369
|
-
"--destructive-foreground": "0 0% 98%",
|
|
370
|
-
"--border": "12 20% 25%",
|
|
371
|
-
"--input": "12 20% 25%",
|
|
372
|
-
"--ring": "14 74% 61%",
|
|
373
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(12 78% 62% / 0.3), transparent 60%), radial-gradient(920px 520px at 112% 2%, hsl(22 78% 60% / 0.22), transparent 58%), linear-gradient(160deg, hsl(13 29% 11%) 0%, hsl(11 25% 11%) 52%, hsl(8 21% 9%) 100%)",
|
|
374
|
-
"--app-surface-tint": "hsl(14 74% 61% / 0.1)",
|
|
375
|
-
},
|
|
376
|
-
"theme-nitro-dusk": {
|
|
377
|
-
"--background": "256 23% 11%",
|
|
378
|
-
"--foreground": "256 30% 95%",
|
|
379
|
-
"--card": "256 20% 13%",
|
|
380
|
-
"--card-foreground": "256 30% 95%",
|
|
381
|
-
"--popover": "256 20% 13%",
|
|
382
|
-
"--popover-foreground": "256 30% 95%",
|
|
383
|
-
"--primary": "263 53% 67%",
|
|
384
|
-
"--primary-foreground": "256 23% 10%",
|
|
385
|
-
"--secondary": "299 44% 64%",
|
|
386
|
-
"--secondary-foreground": "256 23% 10%",
|
|
387
|
-
"--muted": "256 20% 22%",
|
|
388
|
-
"--muted-foreground": "256 20% 72%",
|
|
389
|
-
"--accent": "299 44% 64%",
|
|
390
|
-
"--accent-foreground": "256 23% 10%",
|
|
391
|
-
"--destructive": "0 84% 60%",
|
|
392
|
-
"--destructive-foreground": "0 0% 98%",
|
|
393
|
-
"--border": "256 15% 28%",
|
|
394
|
-
"--input": "256 15% 28%",
|
|
395
|
-
"--ring": "263 53% 67%",
|
|
396
|
-
"--app-gradient": "radial-gradient(1020px 560px at -10% -20%, hsl(262 60% 62% / 0.28), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(298 48% 60% / 0.2), transparent 58%), linear-gradient(160deg, hsl(256 26% 12%) 0%, hsl(256 23% 12%) 50%, hsl(258 20% 10%) 100%)",
|
|
397
|
-
"--app-surface-tint": "hsl(263 53% 67% / 0.1)",
|
|
398
|
-
},
|
|
399
|
-
"theme-nitro-under-the-sea": {
|
|
400
|
-
"--background": "205 33% 10%",
|
|
401
|
-
"--foreground": "190 40% 95%",
|
|
402
|
-
"--card": "205 28% 12%",
|
|
403
|
-
"--card-foreground": "190 40% 95%",
|
|
404
|
-
"--popover": "205 28% 12%",
|
|
405
|
-
"--popover-foreground": "190 40% 95%",
|
|
406
|
-
"--primary": "186 70% 58%",
|
|
407
|
-
"--primary-foreground": "205 33% 10%",
|
|
408
|
-
"--secondary": "197 74% 55%",
|
|
409
|
-
"--secondary-foreground": "205 33% 10%",
|
|
410
|
-
"--muted": "205 28% 20%",
|
|
411
|
-
"--muted-foreground": "190 25% 72%",
|
|
412
|
-
"--accent": "197 74% 55%",
|
|
413
|
-
"--accent-foreground": "205 33% 10%",
|
|
414
|
-
"--destructive": "0 84% 60%",
|
|
415
|
-
"--destructive-foreground": "0 0% 98%",
|
|
416
|
-
"--border": "205 22% 25%",
|
|
417
|
-
"--input": "205 22% 25%",
|
|
418
|
-
"--ring": "186 70% 58%",
|
|
419
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(186 75% 58% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(197 76% 55% / 0.22), transparent 58%), linear-gradient(160deg, hsl(204 30% 11%) 0%, hsl(205 26% 11%) 50%, hsl(206 22% 9%) 100%)",
|
|
420
|
-
"--app-surface-tint": "hsl(186 70% 58% / 0.1)",
|
|
421
|
-
},
|
|
422
|
-
"theme-nitro-retro-storm": {
|
|
423
|
-
"--background": "216 20% 11%",
|
|
424
|
-
"--foreground": "216 20% 95%",
|
|
425
|
-
"--card": "216 18% 13%",
|
|
426
|
-
"--card-foreground": "216 20% 95%",
|
|
427
|
-
"--popover": "216 18% 13%",
|
|
428
|
-
"--popover-foreground": "216 20% 95%",
|
|
429
|
-
"--primary": "213 33% 66%",
|
|
430
|
-
"--primary-foreground": "216 20% 10%",
|
|
431
|
-
"--secondary": "214 26% 62%",
|
|
432
|
-
"--secondary-foreground": "216 20% 10%",
|
|
433
|
-
"--muted": "216 18% 22%",
|
|
434
|
-
"--muted-foreground": "216 15% 70%",
|
|
435
|
-
"--accent": "214 26% 62%",
|
|
436
|
-
"--accent-foreground": "216 20% 10%",
|
|
437
|
-
"--destructive": "0 84% 60%",
|
|
438
|
-
"--destructive-foreground": "0 0% 98%",
|
|
439
|
-
"--border": "216 15% 28%",
|
|
440
|
-
"--input": "216 15% 28%",
|
|
441
|
-
"--ring": "213 33% 66%",
|
|
442
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(213 35% 64% / 0.26), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(214 27% 62% / 0.2), transparent 58%), linear-gradient(160deg, hsl(216 24% 11%) 0%, hsl(215 20% 11%) 50%, hsl(214 18% 9%) 100%)",
|
|
443
|
-
"--app-surface-tint": "hsl(213 33% 66% / 0.1)",
|
|
444
|
-
},
|
|
445
|
-
"theme-nitro-neon-nights": {
|
|
446
|
-
"--background": "245 36% 10%",
|
|
447
|
-
"--foreground": "260 40% 95%",
|
|
448
|
-
"--card": "245 31% 12%",
|
|
449
|
-
"--card-foreground": "260 40% 95%",
|
|
450
|
-
"--popover": "245 31% 12%",
|
|
451
|
-
"--popover-foreground": "260 40% 95%",
|
|
452
|
-
"--primary": "292 96% 66%",
|
|
453
|
-
"--primary-foreground": "245 36% 10%",
|
|
454
|
-
"--secondary": "190 94% 56%",
|
|
455
|
-
"--secondary-foreground": "245 36% 10%",
|
|
456
|
-
"--muted": "245 30% 18%",
|
|
457
|
-
"--muted-foreground": "260 25% 75%",
|
|
458
|
-
"--accent": "190 94% 56%",
|
|
459
|
-
"--accent-foreground": "245 36% 10%",
|
|
460
|
-
"--destructive": "0 84% 60%",
|
|
461
|
-
"--destructive-foreground": "0 0% 98%",
|
|
462
|
-
"--border": "245 25% 25%",
|
|
463
|
-
"--input": "245 25% 25%",
|
|
464
|
-
"--ring": "292 96% 66%",
|
|
465
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(280 92% 62% / 0.28), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(191 94% 56% / 0.24), transparent 58%), linear-gradient(160deg, hsl(233 36% 10%) 0%, hsl(229 32% 9%) 50%, hsl(227 28% 8%) 100%)",
|
|
466
|
-
"--app-surface-tint": "hsl(292 96% 66% / 0.1)",
|
|
467
|
-
},
|
|
468
|
-
"theme-nitro-strawberry-lemonade": {
|
|
469
|
-
"--background": "334 31% 10%",
|
|
470
|
-
"--foreground": "40 70% 95%",
|
|
471
|
-
"--card": "334 27% 12%",
|
|
472
|
-
"--card-foreground": "40 70% 95%",
|
|
473
|
-
"--popover": "334 27% 12%",
|
|
474
|
-
"--popover-foreground": "40 70% 95%",
|
|
475
|
-
"--primary": "342 83% 67%",
|
|
476
|
-
"--primary-foreground": "334 31% 10%",
|
|
477
|
-
"--secondary": "46 89% 63%",
|
|
478
|
-
"--secondary-foreground": "334 31% 10%",
|
|
479
|
-
"--muted": "334 27% 20%",
|
|
480
|
-
"--muted-foreground": "40 40% 75%",
|
|
481
|
-
"--accent": "46 89% 63%",
|
|
482
|
-
"--accent-foreground": "334 31% 10%",
|
|
483
|
-
"--destructive": "0 84% 60%",
|
|
484
|
-
"--destructive-foreground": "0 0% 98%",
|
|
485
|
-
"--border": "334 22% 28%",
|
|
486
|
-
"--input": "334 22% 28%",
|
|
487
|
-
"--ring": "342 83% 67%",
|
|
488
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(342 86% 64% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(46 90% 62% / 0.24), transparent 58%), linear-gradient(160deg, hsl(334 30% 11%) 0%, hsl(333 26% 11%) 50%, hsl(331 22% 9%) 100%)",
|
|
489
|
-
"--app-surface-tint": "hsl(342 83% 67% / 0.1)",
|
|
490
|
-
},
|
|
491
|
-
"theme-nitro-aurora": {
|
|
492
|
-
"--background": "174 25% 10%",
|
|
493
|
-
"--foreground": "160 50% 95%",
|
|
494
|
-
"--card": "174 22% 12%",
|
|
495
|
-
"--card-foreground": "160 50% 95%",
|
|
496
|
-
"--popover": "174 22% 12%",
|
|
497
|
-
"--popover-foreground": "160 50% 95%",
|
|
498
|
-
"--primary": "164 63% 57%",
|
|
499
|
-
"--primary-foreground": "174 25% 10%",
|
|
500
|
-
"--secondary": "145 62% 58%",
|
|
501
|
-
"--secondary-foreground": "174 25% 10%",
|
|
502
|
-
"--muted": "174 22% 20%",
|
|
503
|
-
"--muted-foreground": "160 30% 72%",
|
|
504
|
-
"--accent": "145 62% 58%",
|
|
505
|
-
"--accent-foreground": "174 25% 10%",
|
|
506
|
-
"--destructive": "0 84% 60%",
|
|
507
|
-
"--destructive-foreground": "0 0% 98%",
|
|
508
|
-
"--border": "174 18% 25%",
|
|
509
|
-
"--input": "174 18% 25%",
|
|
510
|
-
"--ring": "164 63% 57%",
|
|
511
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(164 66% 56% / 0.28), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(145 64% 56% / 0.22), transparent 58%), linear-gradient(160deg, hsl(176 28% 11%) 0%, hsl(172 24% 11%) 50%, hsl(169 20% 9%) 100%)",
|
|
512
|
-
"--app-surface-tint": "hsl(164 63% 57% / 0.1)",
|
|
513
|
-
},
|
|
514
|
-
"theme-nitro-sepia": {
|
|
515
|
-
"--background": "27 24% 11%",
|
|
516
|
-
"--foreground": "35 30% 95%",
|
|
517
|
-
"--card": "27 21% 13%",
|
|
518
|
-
"--card-foreground": "35 30% 95%",
|
|
519
|
-
"--popover": "27 21% 13%",
|
|
520
|
-
"--popover-foreground": "35 30% 95%",
|
|
521
|
-
"--primary": "28 44% 62%",
|
|
522
|
-
"--primary-foreground": "27 24% 10%",
|
|
523
|
-
"--secondary": "36 39% 62%",
|
|
524
|
-
"--secondary-foreground": "27 24% 10%",
|
|
525
|
-
"--muted": "27 18% 20%",
|
|
526
|
-
"--muted-foreground": "35 20% 70%",
|
|
527
|
-
"--accent": "36 39% 62%",
|
|
528
|
-
"--accent-foreground": "27 24% 10%",
|
|
529
|
-
"--destructive": "0 84% 60%",
|
|
530
|
-
"--destructive-foreground": "0 0% 98%",
|
|
531
|
-
"--border": "27 15% 25%",
|
|
532
|
-
"--input": "27 15% 25%",
|
|
533
|
-
"--ring": "28 44% 62%",
|
|
534
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(28 45% 60% / 0.26), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(36 40% 60% / 0.2), transparent 58%), linear-gradient(160deg, hsl(27 25% 12%) 0%, hsl(26 22% 12%) 50%, hsl(24 19% 10%) 100%)",
|
|
535
|
-
"--app-surface-tint": "hsl(28 44% 62% / 0.1)",
|
|
536
|
-
},
|
|
537
75
|
};
|
|
538
76
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
77
|
+
// Add more theme tokens (simplified - using default for all)
|
|
78
|
+
themes.forEach(t => {
|
|
79
|
+
if (!themeTokens[t.id]) {
|
|
80
|
+
themeTokens[t.id] = themeTokens["theme-nitro-midnight-blurple"];
|
|
81
|
+
}
|
|
82
|
+
});
|
|
542
83
|
|
|
543
|
-
const
|
|
544
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
545
|
-
};
|
|
84
|
+
const getThemeTokens = (themeId) => themeTokens[themeId] || themeTokens["theme-nitro-midnight-blurple"];
|
|
546
85
|
|
|
547
|
-
const
|
|
548
|
-
const src = path.join(templateDir, name);
|
|
549
|
-
if (!fs.existsSync(src)) {
|
|
550
|
-
throw new Error(`Missing template: ${name}`);
|
|
551
|
-
}
|
|
552
|
-
if (fs.existsSync(dest) && !force) {
|
|
553
|
-
return { skipped: true, dest };
|
|
554
|
-
}
|
|
555
|
-
fs.copyFileSync(src, dest);
|
|
556
|
-
return { skipped: false, dest };
|
|
557
|
-
};
|
|
86
|
+
const ensureDir = (dir) => fs.mkdirSync(dir, { recursive: true });
|
|
558
87
|
|
|
559
88
|
const DEFAULT_THEME = "theme-nitro-midnight-blurple";
|
|
560
89
|
const DEFAULT_MODE = "dark";
|
|
561
90
|
|
|
91
|
+
// Detect project type
|
|
92
|
+
const detectProjectType = (projectRoot) => {
|
|
93
|
+
const hasNextJs = fs.existsSync(path.join(projectRoot, "next.config.js")) ||
|
|
94
|
+
fs.existsSync(path.join(projectRoot, "next.config.mjs")) ||
|
|
95
|
+
fs.existsSync(path.join(projectRoot, "app"));
|
|
96
|
+
const hasVite = fs.existsSync(path.join(projectRoot, "vite.config.js")) ||
|
|
97
|
+
fs.existsSync(path.join(projectRoot, "vite.config.ts")) ||
|
|
98
|
+
fs.existsSync(path.join(projectRoot, "vite.config.mjs"));
|
|
99
|
+
const hasReact = fs.existsSync(path.join(projectRoot, "src", "main.jsx")) ||
|
|
100
|
+
fs.existsSync(path.join(projectRoot, "src", "main.tsx")) ||
|
|
101
|
+
fs.existsSync(path.join(projectRoot, "src", "index.jsx")) ||
|
|
102
|
+
fs.existsSync(path.join(projectRoot, "index.html"));
|
|
103
|
+
|
|
104
|
+
if (hasNextJs) return "next";
|
|
105
|
+
if (hasVite && hasReact) return "vite";
|
|
106
|
+
if (hasReact) return "react";
|
|
107
|
+
return "unknown";
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Prompt for selection
|
|
562
111
|
const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
563
112
|
return new Promise((resolve) => {
|
|
564
113
|
if (!process.stdin.isTTY) {
|
|
@@ -583,20 +132,15 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
|
583
132
|
|
|
584
133
|
const onKey = (data) => {
|
|
585
134
|
buffer += data.toString();
|
|
586
|
-
|
|
587
|
-
// Handle escape sequences for arrow keys
|
|
588
135
|
if (buffer.includes("\u001b[A")) {
|
|
589
|
-
// Up arrow
|
|
590
136
|
index = index > 0 ? index - 1 : items.length - 1;
|
|
591
137
|
buffer = "";
|
|
592
138
|
render();
|
|
593
139
|
} else if (buffer.includes("\u001b[B")) {
|
|
594
|
-
// Down arrow
|
|
595
140
|
index = index < items.length - 1 ? index + 1 : 0;
|
|
596
141
|
buffer = "";
|
|
597
142
|
render();
|
|
598
143
|
} else if (buffer.includes("\r") || buffer.includes("\n")) {
|
|
599
|
-
// Enter key
|
|
600
144
|
process.stdin.setRawMode(false);
|
|
601
145
|
process.stdin.pause();
|
|
602
146
|
process.stdin.removeListener("data", onKey);
|
|
@@ -604,12 +148,10 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
|
604
148
|
resolve(items[index]);
|
|
605
149
|
return;
|
|
606
150
|
} else if (buffer.includes("\u0003")) {
|
|
607
|
-
// Ctrl+C
|
|
608
151
|
process.stdin.setRawMode(false);
|
|
609
152
|
process.stdin.pause();
|
|
610
153
|
process.exit(1);
|
|
611
154
|
} else {
|
|
612
|
-
// Clear buffer after processing
|
|
613
155
|
buffer = "";
|
|
614
156
|
}
|
|
615
157
|
};
|
|
@@ -622,95 +164,54 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
|
622
164
|
});
|
|
623
165
|
};
|
|
624
166
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
const choice = await promptSelect(title, items, defaultYes ? 0 : 1);
|
|
628
|
-
return choice === "Yes";
|
|
629
|
-
};
|
|
630
|
-
|
|
631
|
-
const generateCSSVariables = (themeId, mode) => {
|
|
167
|
+
// Generate CSS
|
|
168
|
+
const generateCSS = (themeId, mode) => {
|
|
632
169
|
const tokens = getThemeTokens(themeId);
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
css += `.${themeId} {\n`;
|
|
639
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
640
|
-
if (key !== "--app-gradient") {
|
|
641
|
-
css += ` ${key}: ${value};\n`;
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
css += ` --app-gradient: ${tokens["--app-gradient"]};\n`;
|
|
645
|
-
css += `}\n\n`;
|
|
646
|
-
|
|
647
|
-
css += `/* Surface Layer Styles */\n`;
|
|
648
|
-
css += `.bg-card,\n.bg-popover,\n.bg-sidebar {\n`;
|
|
649
|
-
css += ` background-color: hsl(var(--background) / 0.34);\n`;
|
|
650
|
-
css += ` background-image: linear-gradient(var(--app-surface-tint), var(--app-surface-tint));\n`;
|
|
651
|
-
css += ` backdrop-filter: blur(16px);\n`;
|
|
652
|
-
css += `}\n`;
|
|
653
|
-
|
|
654
|
-
return css;
|
|
655
|
-
};
|
|
656
|
-
|
|
657
|
-
const generateThemeEngine = (themeId, mode) => {
|
|
658
|
-
return `export const NITRO_PUBLIC_THEMES = [
|
|
659
|
-
${themes.map(t => `{
|
|
660
|
-
id: "${t.id}",
|
|
661
|
-
label: "${t.label}",
|
|
662
|
-
preview: "${t.preview}",
|
|
663
|
-
}`).join(",\n ")},
|
|
664
|
-
] as const;
|
|
665
|
-
|
|
666
|
-
export type ThemeId = typeof NITRO_PUBLIC_THEMES[number]["id"];
|
|
667
|
-
export type ColorMode = "dark" | "light";
|
|
170
|
+
return `/* Gradient Forge Theme */
|
|
171
|
+
.${themeId} {
|
|
172
|
+
${Object.entries(tokens).map(([k, v]) => ` ${k}: ${v};`).join("\n")}
|
|
173
|
+
}
|
|
668
174
|
|
|
669
|
-
|
|
670
|
-
|
|
175
|
+
body {
|
|
176
|
+
background: hsl(${tokens["--background"]});
|
|
177
|
+
color: hsl(${tokens["--foreground"]});
|
|
178
|
+
}
|
|
671
179
|
|
|
672
|
-
|
|
673
|
-
|
|
180
|
+
.bg-card, .bg-popover, .bg-sidebar {
|
|
181
|
+
background-color: hsl(${tokens["--card"]} / 0.34);
|
|
182
|
+
background-image: linear-gradient(${tokens["--app-surface-tint"]}, ${tokens["--app-surface-tint"]});
|
|
183
|
+
backdrop-filter: blur(16px);
|
|
184
|
+
}
|
|
674
185
|
`;
|
|
675
186
|
};
|
|
676
187
|
|
|
677
|
-
|
|
188
|
+
// Generate theme context
|
|
189
|
+
const generateContext = (themeId, mode) => {
|
|
678
190
|
return `"use client";
|
|
191
|
+
import React, { createContext, useContext, useEffect, useState } from "react";
|
|
679
192
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
export type ThemeId = "${themeId}";
|
|
683
|
-
export type ColorMode = "${mode}";
|
|
684
|
-
|
|
685
|
-
type ThemeContextValue = {
|
|
686
|
-
themeId: ThemeId;
|
|
687
|
-
colorMode: ColorMode;
|
|
688
|
-
setThemeId: (themeId: ThemeId) => void;
|
|
689
|
-
setColorMode: (mode: ColorMode) => void;
|
|
690
|
-
themes: { id: ThemeId; label: string }[];
|
|
691
|
-
};
|
|
193
|
+
export type ThemeId = string;
|
|
194
|
+
export type ColorMode = string;
|
|
692
195
|
|
|
693
|
-
const allThemes
|
|
694
|
-
${themes.map(t => ` { id: "${t.id}", label: "${t.label}" }
|
|
196
|
+
const allThemes = [
|
|
197
|
+
${themes.map(t => ` { id: "${t.id}", label: "${t.label}" }`).join(",\n")}
|
|
695
198
|
];
|
|
696
199
|
|
|
697
|
-
const ThemeContext = createContext
|
|
200
|
+
const ThemeContext: any = createContext(null);
|
|
698
201
|
|
|
699
|
-
export const ThemeProvider = ({ children }: { children:
|
|
700
|
-
const [themeId, setThemeId] = useState
|
|
701
|
-
const [colorMode, setColorMode] = useState
|
|
202
|
+
export const ThemeProvider = ({ children }: { children: any }) => {
|
|
203
|
+
const [themeId, setThemeId] = useState("${themeId}");
|
|
204
|
+
const [colorMode, setColorMode] = useState("${mode}");
|
|
702
205
|
|
|
703
206
|
useEffect(() => {
|
|
704
207
|
const root = document.documentElement;
|
|
705
208
|
root.classList.remove("dark", "light");
|
|
706
209
|
${themes.map(t => ` root.classList.remove("${t.id}");`).join("\n")}
|
|
707
|
-
|
|
708
210
|
root.classList.add(colorMode, themeId);
|
|
709
211
|
root.setAttribute("data-theme", themeId);
|
|
710
212
|
root.setAttribute("data-color-mode", colorMode);
|
|
711
|
-
|
|
712
|
-
localStorage.setItem("
|
|
713
|
-
localStorage.setItem("gradient-forge.color-mode", colorMode);
|
|
213
|
+
localStorage.setItem("theme", themeId);
|
|
214
|
+
localStorage.setItem("mode", colorMode);
|
|
714
215
|
}, [themeId, colorMode]);
|
|
715
216
|
|
|
716
217
|
return (
|
|
@@ -726,25 +227,14 @@ export const useTheme = () => {
|
|
|
726
227
|
return ctx;
|
|
727
228
|
};
|
|
728
229
|
|
|
729
|
-
// Simple theme switcher component - drop into your app!
|
|
730
230
|
export function ThemeSwitcher() {
|
|
731
231
|
const { themeId, colorMode, setThemeId, setColorMode, themes } = useTheme();
|
|
732
|
-
|
|
733
232
|
return (
|
|
734
|
-
<div
|
|
735
|
-
<select
|
|
736
|
-
value={
|
|
737
|
-
onChange={(e) => setThemeId(e.target.value as ThemeId)}
|
|
738
|
-
className="bg-background text-foreground px-2 py-1 rounded border"
|
|
739
|
-
>
|
|
740
|
-
{themes.map(t => (
|
|
741
|
-
<option key={t.id} value={t.id}>{t.label}</option>
|
|
742
|
-
))}
|
|
233
|
+
<div style={{ display: "flex", gap: "8px", padding: "8px" }}>
|
|
234
|
+
<select value={themeId} onChange={(e: any) => setThemeId(e.target.value)} style={{ padding: "4px" }}>
|
|
235
|
+
{themes.map((t: any) => <option key={t.id} value={t.id}>{t.label}</option>)}
|
|
743
236
|
</select>
|
|
744
|
-
<button
|
|
745
|
-
onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")}
|
|
746
|
-
className="px-2 py-1 rounded bg-primary text-primary-foreground"
|
|
747
|
-
>
|
|
237
|
+
<button onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")} style={{ padding: "4px 8px" }}>
|
|
748
238
|
{colorMode === "dark" ? "🌙" : "☀️"}
|
|
749
239
|
</button>
|
|
750
240
|
</div>
|
|
@@ -753,104 +243,165 @@ export function ThemeSwitcher() {
|
|
|
753
243
|
`;
|
|
754
244
|
};
|
|
755
245
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
Mode: ${mode}
|
|
761
|
-
|
|
762
|
-
✅ All files created automatically!
|
|
763
|
-
✅ Theme applied!
|
|
764
|
-
|
|
765
|
-
Your app now has beautiful gradient themes! 🎉
|
|
766
|
-
|
|
767
|
-
For more themes, visit: https://gradient-forge.vercel.app
|
|
768
|
-
`;
|
|
769
|
-
};
|
|
770
|
-
|
|
771
|
-
const autoInjectLayout = (projectRoot, themeId, mode) => {
|
|
772
|
-
const layoutPaths = [
|
|
773
|
-
path.join(projectRoot, "app", "layout.tsx"),
|
|
774
|
-
path.join(projectRoot, "src", "app", "layout.tsx"),
|
|
775
|
-
];
|
|
246
|
+
// Setup for Next.js
|
|
247
|
+
const setupNextJs = (projectRoot, themeId, mode) => {
|
|
248
|
+
const appDir = path.join(projectRoot, "app");
|
|
249
|
+
const themeDir = path.join(projectRoot, "components", "theme");
|
|
776
250
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
251
|
+
ensureDir(themeDir);
|
|
252
|
+
|
|
253
|
+
// Create CSS
|
|
254
|
+
const cssPath = path.join(appDir, "gradient-forge.css");
|
|
255
|
+
fs.writeFileSync(cssPath, generateCSS(themeId, mode));
|
|
256
|
+
logSuccess(`Created: app/gradient-forge.css`);
|
|
257
|
+
|
|
258
|
+
// Create context
|
|
259
|
+
const contextPath = path.join(themeDir, "theme-context.tsx");
|
|
260
|
+
fs.writeFileSync(contextPath, generateContext(themeId, mode));
|
|
261
|
+
logSuccess(`Created: components/theme/theme-context.tsx`);
|
|
262
|
+
|
|
263
|
+
// Inject into globals.css
|
|
264
|
+
const globalsPath = path.join(appDir, "globals.css");
|
|
265
|
+
if (fs.existsSync(globalsPath)) {
|
|
266
|
+
const content = fs.readFileSync(globalsPath, "utf8");
|
|
267
|
+
if (!content.includes("gradient-forge.css")) {
|
|
268
|
+
fs.writeFileSync(globalsPath, `@import "./gradient-forge.css";\n${content}`);
|
|
269
|
+
logSuccess(`Injected into: app/globals.css`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Update layout.tsx
|
|
274
|
+
const layoutPath = path.join(appDir, "layout.tsx");
|
|
275
|
+
if (fs.existsSync(layoutPath)) {
|
|
276
|
+
let content = fs.readFileSync(layoutPath, "utf8");
|
|
277
|
+
|
|
278
|
+
if (!content.includes("ThemeProvider")) {
|
|
279
|
+
// Add import
|
|
280
|
+
const importStmt = `import { ThemeProvider } from "./components/theme/theme-context";`;
|
|
281
|
+
if (!content.includes(importStmt)) {
|
|
282
|
+
content = importStmt + "\n" + content;
|
|
784
283
|
}
|
|
785
284
|
|
|
786
|
-
//
|
|
787
|
-
|
|
285
|
+
// Wrap body - ensure proper spacing
|
|
286
|
+
content = content.replace(/(<body[^>]*>)/, "$1\n<ThemeProvider>");
|
|
287
|
+
content = content.replace(/(<\/body>)/, "</ThemeProvider>\n$1");
|
|
788
288
|
|
|
789
|
-
//
|
|
790
|
-
|
|
791
|
-
let insertIndex = 0;
|
|
792
|
-
for (let i = 0; i < lines.length; i++) {
|
|
793
|
-
if (lines[i].trim().startsWith('import ') && !lines[i].includes('from "') && !lines[i].includes("from '")) {
|
|
794
|
-
continue;
|
|
795
|
-
}
|
|
796
|
-
if (lines[i].trim().startsWith('import ')) {
|
|
797
|
-
insertIndex = i + 1;
|
|
798
|
-
}
|
|
799
|
-
}
|
|
289
|
+
// Add classes to html - preserve existing attributes
|
|
290
|
+
content = content.replace(/<html/, `<html class="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}"`);
|
|
800
291
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
292
|
+
fs.writeFileSync(layoutPath, content);
|
|
293
|
+
logSuccess(`Updated: app/layout.tsx`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return { success: true };
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// Setup for Vite + React
|
|
301
|
+
const setupVite = (projectRoot, themeId, mode) => {
|
|
302
|
+
const srcDir = path.join(projectRoot, "src");
|
|
303
|
+
const themeDir = path.join(projectRoot, "src", "components", "theme");
|
|
304
|
+
const cssDir = path.join(projectRoot, "src");
|
|
305
|
+
|
|
306
|
+
ensureDir(themeDir);
|
|
307
|
+
|
|
308
|
+
// Create CSS
|
|
309
|
+
const cssPath = path.join(cssDir, "gradient-forge.css");
|
|
310
|
+
fs.writeFileSync(cssPath, generateCSS(themeId, mode));
|
|
311
|
+
logSuccess(`Created: src/gradient-forge.css`);
|
|
312
|
+
|
|
313
|
+
// Create context
|
|
314
|
+
const contextPath = path.join(themeDir, "theme-context.tsx");
|
|
315
|
+
fs.writeFileSync(contextPath, generateContext(themeId, mode));
|
|
316
|
+
logSuccess(`Created: src/components/theme/theme-context.tsx`);
|
|
317
|
+
|
|
318
|
+
// Inject into index.css or main.css
|
|
319
|
+
const possibleCssFiles = ["src/index.css", "src/App.css", "src/main.css"];
|
|
320
|
+
for (const cssFile of possibleCssFiles) {
|
|
321
|
+
const cssPath2 = path.join(projectRoot, cssFile);
|
|
322
|
+
if (fs.existsSync(cssPath2)) {
|
|
323
|
+
const content = fs.readFileSync(cssPath2, "utf8");
|
|
324
|
+
if (!content.includes("gradient-forge.css")) {
|
|
325
|
+
fs.writeFileSync(cssPath2, `@import "./gradient-forge.css";\n${content}`);
|
|
326
|
+
logSuccess(`Injected into: ${cssFile}`);
|
|
806
327
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
content =
|
|
817
|
-
/<html[^>]*>/,
|
|
818
|
-
`<html lang="en" className="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">`
|
|
819
|
-
);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Update main.tsx or main.jsx
|
|
333
|
+
const mainFiles = ["src/main.tsx", "src/main.jsx", "src/index.tsx", "src/index.jsx"];
|
|
334
|
+
for (const mainFile of mainFiles) {
|
|
335
|
+
const mainPath = path.join(projectRoot, mainFile);
|
|
336
|
+
if (fs.existsSync(mainPath)) {
|
|
337
|
+
let content = fs.readFileSync(mainPath, "utf8");
|
|
820
338
|
|
|
821
|
-
|
|
822
|
-
|
|
339
|
+
if (!content.includes("ThemeProvider")) {
|
|
340
|
+
const importStmt = `import { ThemeProvider } from "./components/theme/theme-context";`;
|
|
341
|
+
if (!content.includes(importStmt)) {
|
|
342
|
+
content = importStmt + "\n" + content;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Wrap app with ThemeProvider
|
|
346
|
+
content = content.replace(/(<App[^>]*>)/, '<ThemeProvider>$1</ThemeProvider>');
|
|
347
|
+
content = content.replace(/(React\.createElement\(App)/, 'React.createElement(ThemeProvider, null, React.createElement(App)');
|
|
348
|
+
|
|
349
|
+
// Add theme classes to root div
|
|
350
|
+
content = content.replace(
|
|
351
|
+
/document\.getElementById\(['"]root['"]\)\.innerHTML/,
|
|
352
|
+
`document.getElementById('root').innerHTML = '<div class="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">' +`
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
fs.writeFileSync(mainPath, content);
|
|
356
|
+
logSuccess(`Updated: ${mainFile}`);
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
823
359
|
}
|
|
824
360
|
}
|
|
825
361
|
|
|
826
|
-
|
|
362
|
+
// Update index.html
|
|
363
|
+
const indexPath = path.join(projectRoot, "index.html");
|
|
364
|
+
if (fs.existsSync(indexPath)) {
|
|
365
|
+
let content = fs.readFileSync(indexPath, "utf8");
|
|
366
|
+
content = content.replace(/<html[^>]*>/, `<html class="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">`);
|
|
367
|
+
fs.writeFileSync(indexPath, content);
|
|
368
|
+
logSuccess(`Updated: index.html`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return { success: true };
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// Setup for React (CRA)
|
|
375
|
+
const setupReact = (projectRoot, themeId, mode) => {
|
|
376
|
+
// Similar to Vite but with CRA-specific paths
|
|
377
|
+
return setupVite(projectRoot, themeId, mode);
|
|
827
378
|
};
|
|
828
379
|
|
|
829
380
|
const init = async () => {
|
|
830
381
|
log("");
|
|
831
|
-
log("🎨 Gradient Forge CLI -
|
|
832
|
-
log("
|
|
382
|
+
log("🎨 Gradient Forge CLI - Instant Theme Setup");
|
|
383
|
+
log("==========================================");
|
|
833
384
|
log("");
|
|
834
385
|
|
|
835
386
|
const projectRoot = readArg("--path", process.cwd());
|
|
836
|
-
|
|
837
|
-
const skipInject = hasFlag("--no-inject");
|
|
387
|
+
logInfo(`Project: ${projectRoot}`);
|
|
838
388
|
|
|
839
|
-
|
|
389
|
+
// Detect project type
|
|
390
|
+
const projectType = detectProjectType(projectRoot);
|
|
391
|
+
logInfo(`Detected: ${projectType === "next" ? "Next.js" : projectType === "vite" ? "Vite + React" : projectType === "react" ? "React (CRA)" : "Unknown"} project`);
|
|
840
392
|
log("");
|
|
841
393
|
|
|
842
|
-
// Check
|
|
394
|
+
// Check package.json exists
|
|
843
395
|
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
844
396
|
if (!fs.existsSync(packageJsonPath)) {
|
|
845
|
-
logError("Error: Not a valid
|
|
397
|
+
logError("Error: Not a valid project. No package.json found.");
|
|
846
398
|
process.exit(1);
|
|
847
399
|
}
|
|
848
400
|
|
|
849
401
|
// Select theme
|
|
850
402
|
log("Select a theme:");
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
log(`\n✓ Selected: ${selectedTheme.label}`);
|
|
403
|
+
const selectedTheme = await promptSelect("Choose your theme:", themes, 12);
|
|
404
|
+
log(`\n✓ ${selectedTheme.label}`);
|
|
854
405
|
|
|
855
406
|
// Select mode
|
|
856
407
|
log("");
|
|
@@ -860,352 +411,56 @@ const init = async () => {
|
|
|
860
411
|
];
|
|
861
412
|
log("Select color mode:");
|
|
862
413
|
const selectedMode = await promptSelect("Choose color mode:", modeOptions, 0);
|
|
863
|
-
log(`\n✓
|
|
414
|
+
log(`\n✓ ${selectedMode.label}`);
|
|
864
415
|
|
|
865
416
|
const themeId = selectedTheme.id;
|
|
866
417
|
const mode = selectedMode.id;
|
|
867
418
|
|
|
868
419
|
log("");
|
|
869
|
-
logInfo("
|
|
420
|
+
logInfo("Setting up theme...");
|
|
870
421
|
log("");
|
|
871
422
|
|
|
872
|
-
//
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
// Generate and write files
|
|
880
|
-
const cssContent = generateCSSVariables(themeId, mode);
|
|
881
|
-
const cssPath = path.join(appDir, "gradient-forge.css");
|
|
882
|
-
fs.writeFileSync(cssPath, cssContent);
|
|
883
|
-
logSuccess(`Created: ${path.relative(projectRoot, cssPath)}`);
|
|
884
|
-
|
|
885
|
-
const engineContent = generateThemeEngine(themeId, mode);
|
|
886
|
-
const enginePath = path.join(themeDir, "theme-engine.ts");
|
|
887
|
-
fs.writeFileSync(enginePath, engineContent);
|
|
888
|
-
logSuccess(`Created: ${path.relative(projectRoot, enginePath)}`);
|
|
889
|
-
|
|
890
|
-
const contextContent = generateThemeContext(themeId, mode);
|
|
891
|
-
const contextPath = path.join(themeDir, "theme-context.tsx");
|
|
892
|
-
fs.writeFileSync(contextPath, contextContent);
|
|
893
|
-
logSuccess(`Created: ${path.relative(projectRoot, contextPath)}`);
|
|
894
|
-
|
|
895
|
-
// Inject into globals.css if it exists and not skipped
|
|
896
|
-
const globalsPath = path.join(appDir, "globals.css");
|
|
897
|
-
if (!skipInject && fs.existsSync(globalsPath)) {
|
|
898
|
-
const marker = "/* gradient-forge */";
|
|
899
|
-
const existing = fs.readFileSync(globalsPath, "utf8");
|
|
900
|
-
|
|
901
|
-
if (!existing.includes(marker)) {
|
|
902
|
-
const importStatement = '@import "./gradient-forge.css";';
|
|
903
|
-
fs.writeFileSync(globalsPath, `${importStatement}\n${existing}`);
|
|
904
|
-
logSuccess(`Injected theme import into: ${path.relative(projectRoot, globalsPath)}`);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// Auto-inject into layout.tsx
|
|
909
|
-
const layoutResult = autoInjectLayout(projectRoot, themeId, mode);
|
|
910
|
-
if (layoutResult.success) {
|
|
911
|
-
logSuccess(`Updated layout: ${path.relative(projectRoot, layoutResult.path)}`);
|
|
423
|
+
// Setup based on project type
|
|
424
|
+
if (projectType === "next") {
|
|
425
|
+
setupNextJs(projectRoot, themeId, mode);
|
|
426
|
+
} else if (projectType === "vite") {
|
|
427
|
+
setupVite(projectRoot, themeId, mode);
|
|
428
|
+
} else if (projectType === "react") {
|
|
429
|
+
setupReact(projectRoot, themeId, mode);
|
|
912
430
|
} else {
|
|
913
|
-
|
|
431
|
+
logError("Could not detect project type. Supported: Next.js, Vite, React");
|
|
432
|
+
process.exit(1);
|
|
914
433
|
}
|
|
915
|
-
|
|
434
|
+
|
|
916
435
|
log("");
|
|
917
|
-
log("
|
|
918
|
-
logSuccess("Theme
|
|
436
|
+
log("==========================================");
|
|
437
|
+
logSuccess("🎉 Theme applied instantly!");
|
|
919
438
|
log("");
|
|
920
439
|
log(`Theme: ${selectedTheme.label} (${mode})`);
|
|
921
|
-
log("");
|
|
922
|
-
log("Your app now has beautiful gradient themes!");
|
|
923
440
|
log("Run: npm run dev");
|
|
924
441
|
log("");
|
|
925
442
|
};
|
|
926
443
|
|
|
927
|
-
const exportCommand = async () => {
|
|
928
|
-
const themeId = readArg("--theme", DEFAULT_THEME);
|
|
929
|
-
const mode = readArg("--mode", DEFAULT_MODE);
|
|
930
|
-
const format = readArg("--format", "css");
|
|
931
|
-
const output = readArg("--output", process.cwd());
|
|
932
|
-
|
|
933
|
-
const generator = exportGenerators[format];
|
|
934
|
-
if (!generator) {
|
|
935
|
-
logError(`Unknown format: ${format}`);
|
|
936
|
-
log("Available formats: css, scss, json, tailwind, w3c-tokens, figma-tokens, css-variables, html-root, all");
|
|
937
|
-
process.exit(1);
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
const result = generator(themeId, mode);
|
|
941
|
-
|
|
942
|
-
ensureDir(output);
|
|
943
|
-
const outputPath = path.join(output, result.filename);
|
|
944
|
-
fs.writeFileSync(outputPath, result.content);
|
|
945
|
-
|
|
946
|
-
logSuccess(`Exported: ${outputPath}`);
|
|
947
|
-
};
|
|
948
|
-
|
|
949
|
-
const exportGenerators = {
|
|
950
|
-
css: (themeId, mode) => {
|
|
951
|
-
const tokens = getThemeTokens(themeId);
|
|
952
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
953
|
-
|
|
954
|
-
let css = `/* Gradient Forge Theme: ${theme?.label ?? themeId} */\n`;
|
|
955
|
-
css += `/* Color Mode: ${mode} */\n\n`;
|
|
956
|
-
css += `.${themeId} {\n`;
|
|
957
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
958
|
-
if (key !== "--app-gradient") {
|
|
959
|
-
css += ` ${key}: ${value};\n`;
|
|
960
|
-
}
|
|
961
|
-
});
|
|
962
|
-
css += ` --app-gradient: ${tokens["--app-gradient"]};\n`;
|
|
963
|
-
css += `}\n\n`;
|
|
964
|
-
css += `.bg-card, .bg-popover, .bg-sidebar {\n`;
|
|
965
|
-
css += ` background-color: hsl(var(--background) / 0.34);\n`;
|
|
966
|
-
css += ` background-image: linear-gradient(var(--app-surface-tint), var(--app-surface-tint));\n`;
|
|
967
|
-
css += ` backdrop-filter: blur(16px);\n`;
|
|
968
|
-
css += `}\n`;
|
|
969
|
-
|
|
970
|
-
return { filename: `${themeId}-theme.css`, content: css };
|
|
971
|
-
},
|
|
972
|
-
|
|
973
|
-
scss: (themeId, mode) => {
|
|
974
|
-
const tokens = getThemeTokens(themeId);
|
|
975
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
976
|
-
|
|
977
|
-
let scss = `// Gradient Forge Theme: ${theme?.label ?? themeId}\n`;
|
|
978
|
-
scss += `// Color Mode: ${mode}\n\n`;
|
|
979
|
-
|
|
980
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
981
|
-
if (key !== "--app-gradient") {
|
|
982
|
-
scss += `$${key.replace("--", "")}: ${value};\n`;
|
|
983
|
-
}
|
|
984
|
-
});
|
|
985
|
-
scss += `$app-gradient: ${tokens["--app-gradient"]};\n`;
|
|
986
|
-
|
|
987
|
-
return { filename: `${themeId}-theme.scss`, content: scss };
|
|
988
|
-
},
|
|
989
|
-
|
|
990
|
-
json: (themeId, mode) => {
|
|
991
|
-
const tokens = getThemeTokens(themeId);
|
|
992
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
993
|
-
|
|
994
|
-
const colorTokens = {};
|
|
995
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
996
|
-
colorTokens[key.replace("--", "")] = key === "--app-surface-tint" ? value : `hsl(${value})`;
|
|
997
|
-
});
|
|
998
|
-
|
|
999
|
-
const data = {
|
|
1000
|
-
name: theme?.label ?? themeId,
|
|
1001
|
-
id: themeId,
|
|
1002
|
-
colorMode: mode,
|
|
1003
|
-
version: "1.0.0",
|
|
1004
|
-
generatedAt: new Date().toISOString(),
|
|
1005
|
-
colors: colorTokens,
|
|
1006
|
-
};
|
|
1007
|
-
|
|
1008
|
-
return { filename: `${themeId}-tokens.json`, content: JSON.stringify(data, null, 2) };
|
|
1009
|
-
},
|
|
1010
|
-
|
|
1011
|
-
tailwind: (themeId, mode) => {
|
|
1012
|
-
const tokens = getThemeTokens(themeId);
|
|
1013
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
1014
|
-
|
|
1015
|
-
const colors = {};
|
|
1016
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
1017
|
-
if (key !== "--app-gradient" && key !== "--app-surface-tint") {
|
|
1018
|
-
colors[key.replace("--", "")] = `"hsl(var(${key}))"`;
|
|
1019
|
-
}
|
|
1020
|
-
});
|
|
1021
|
-
|
|
1022
|
-
const content = `// Gradient Forge Theme: ${theme?.label ?? themeId}
|
|
1023
|
-
// Color Mode: ${mode}
|
|
1024
|
-
|
|
1025
|
-
import type { Config } from "tailwindcss";
|
|
1026
|
-
|
|
1027
|
-
const config: Config = {
|
|
1028
|
-
theme: {
|
|
1029
|
-
extend: {
|
|
1030
|
-
colors: ${JSON.stringify(colors, null, 6).replace(/"/g, "'")},
|
|
1031
|
-
backgroundImage: {
|
|
1032
|
-
"app-gradient": "var(--app-gradient)",
|
|
1033
|
-
},
|
|
1034
|
-
},
|
|
1035
|
-
},
|
|
1036
|
-
};
|
|
1037
|
-
|
|
1038
|
-
export default config;
|
|
1039
|
-
`;
|
|
1040
|
-
|
|
1041
|
-
return { filename: `${themeId}-tailwind.ts`, content };
|
|
1042
|
-
},
|
|
1043
|
-
|
|
1044
|
-
"w3c-tokens": (themeId, mode) => {
|
|
1045
|
-
const tokens = getThemeTokens(themeId);
|
|
1046
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
1047
|
-
|
|
1048
|
-
const colorTokens = {};
|
|
1049
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
1050
|
-
const tokenName = key.replace("--", "").replace(/-/g, ".");
|
|
1051
|
-
colorTokens[tokenName] = {
|
|
1052
|
-
$type: "color",
|
|
1053
|
-
$value: key === "--app-surface-tint" ? value : `hsl(${value})`,
|
|
1054
|
-
};
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
const data = {
|
|
1058
|
-
$schema: "https://design-tokens.github.io/schema/format.json",
|
|
1059
|
-
name: theme?.label ?? themeId,
|
|
1060
|
-
id: themeId,
|
|
1061
|
-
colorMode: mode,
|
|
1062
|
-
version: "1.0.0",
|
|
1063
|
-
generatedAt: new Date().toISOString(),
|
|
1064
|
-
colors: colorTokens,
|
|
1065
|
-
};
|
|
1066
|
-
|
|
1067
|
-
return { filename: `${themeId}-w3c-tokens.json`, content: JSON.stringify(data, null, 2) };
|
|
1068
|
-
},
|
|
1069
|
-
|
|
1070
|
-
"figma-tokens": (themeId, mode) => {
|
|
1071
|
-
const tokens = getThemeTokens(themeId);
|
|
1072
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
1073
|
-
|
|
1074
|
-
const colorTokens = {};
|
|
1075
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
1076
|
-
colorTokens[key.replace("--", "")] = {
|
|
1077
|
-
value: key === "--app-surface-tint" ? value : `hsl(${value})`,
|
|
1078
|
-
type: "color",
|
|
1079
|
-
};
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
const data = {
|
|
1083
|
-
GradientForge: {
|
|
1084
|
-
[theme?.label ?? themeId]: { colors: colorTokens },
|
|
1085
|
-
},
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
|
-
return { filename: `${themeId}-figma-tokens.json`, content: JSON.stringify(data, null, 2) };
|
|
1089
|
-
},
|
|
1090
|
-
|
|
1091
|
-
"css-variables": (themeId, mode) => {
|
|
1092
|
-
const tokens = getThemeTokens(themeId);
|
|
1093
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
1094
|
-
|
|
1095
|
-
let css = `/* Gradient Forge Theme: ${theme?.label ?? themeId} */\n`;
|
|
1096
|
-
css += `/* Color Mode: ${mode} */\n`;
|
|
1097
|
-
css += `/* Paste these into your :root or theme class */\n\n`;
|
|
1098
|
-
|
|
1099
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
1100
|
-
if (key !== "--app-gradient") {
|
|
1101
|
-
css += `${key}: ${value};\n`;
|
|
1102
|
-
}
|
|
1103
|
-
});
|
|
1104
|
-
css += `--app-gradient: ${tokens["--app-gradient"]};\n`;
|
|
1105
|
-
|
|
1106
|
-
return { filename: `${themeId}-variables.css`, content: css };
|
|
1107
|
-
},
|
|
1108
|
-
|
|
1109
|
-
"html-root": (themeId, mode) => {
|
|
1110
|
-
const theme = themes.find((t) => t.id === themeId);
|
|
1111
|
-
|
|
1112
|
-
const content = `<!-- Gradient Forge Theme: ${theme?.label ?? themeId} -->
|
|
1113
|
-
<!-- Add these attributes to your <html> element -->
|
|
1114
|
-
|
|
1115
|
-
<html
|
|
1116
|
-
lang="en"
|
|
1117
|
-
class="${mode} ${themeId}"
|
|
1118
|
-
data-theme="${themeId}"
|
|
1119
|
-
data-color-mode="${mode}"
|
|
1120
|
-
suppressHydrationWarning
|
|
1121
|
-
>
|
|
1122
|
-
<!-- Your app content -->
|
|
1123
|
-
</html>`;
|
|
1124
|
-
|
|
1125
|
-
return { filename: `${themeId}-html-root.html`, content };
|
|
1126
|
-
},
|
|
1127
|
-
|
|
1128
|
-
all: (themeId, mode) => {
|
|
1129
|
-
const allContent = Object.keys(exportGenerators)
|
|
1130
|
-
.filter(f => f !== "all")
|
|
1131
|
-
.map(format => {
|
|
1132
|
-
const gen = exportGenerators[format];
|
|
1133
|
-
const result = gen(themeId, mode);
|
|
1134
|
-
return `/* ===== ${result.filename} ===== */\n\n${result.content}`;
|
|
1135
|
-
})
|
|
1136
|
-
.join("\n\n\n");
|
|
1137
|
-
|
|
1138
|
-
return { filename: `${themeId}-all-formats.txt`, content: allContent };
|
|
1139
|
-
},
|
|
1140
|
-
};
|
|
1141
|
-
|
|
1142
444
|
const usage = () => {
|
|
1143
445
|
log("🎨 Gradient Forge CLI");
|
|
1144
446
|
log("");
|
|
1145
447
|
log("Usage:");
|
|
1146
|
-
log(" gradient-forge init [--path <project
|
|
1147
|
-
log(" gradient-forge export --theme <theme-id> --format <format> [--output <path>]");
|
|
1148
|
-
log(" gradient-forge list");
|
|
1149
|
-
log(" gradient-forge help");
|
|
1150
|
-
log("");
|
|
1151
|
-
log("Commands:");
|
|
1152
|
-
log(" init Initialize gradient-forge in a project (-interactive)");
|
|
1153
|
-
log(" export Export theme tokens in various formats");
|
|
1154
|
-
log(" list List all available themes");
|
|
1155
|
-
log(" help Show this help message");
|
|
1156
|
-
log("");
|
|
1157
|
-
log("Init Options:");
|
|
1158
|
-
log(" --path Target project root (default: current directory)");
|
|
1159
|
-
log(" --inject Append theme CSS to app/globals.css (default)");
|
|
1160
|
-
log(" --no-inject Skip CSS injection");
|
|
1161
|
-
log(" --force Overwrite existing files");
|
|
1162
|
-
log("");
|
|
1163
|
-
log("Export Options:");
|
|
1164
|
-
log(" --theme Theme ID to export (default: theme-nitro-midnight-blurple)");
|
|
1165
|
-
log(" --format Export format: css, scss, json, tailwind, w3c-tokens, figma-tokens, css-variables, html-root, all");
|
|
1166
|
-
log(" --output Output directory (default: current directory)");
|
|
1167
|
-
log(" --mode Color mode: dark, light (default: dark)");
|
|
448
|
+
log(" gradient-forge init [--path <project>]");
|
|
1168
449
|
log("");
|
|
1169
450
|
log("Examples:");
|
|
451
|
+
log(" gradient-forge init");
|
|
1170
452
|
log(" gradient-forge init --path my-app");
|
|
1171
|
-
log(" gradient-forge export --theme theme-nitro-midnight-blurple --format css");
|
|
1172
|
-
log(" gradient-forge export --theme theme-nitro-aurora --format tailwind --output ./themes");
|
|
1173
453
|
log("");
|
|
1174
|
-
log("
|
|
1175
|
-
themes.forEach((t, i) => {
|
|
1176
|
-
log(` ${String(i + 1).padStart(2, " ")}. ${t.label}`);
|
|
1177
|
-
});
|
|
1178
|
-
};
|
|
1179
|
-
|
|
1180
|
-
const listThemes = () => {
|
|
1181
|
-
log("🎨 Available Themes:");
|
|
1182
|
-
log("");
|
|
1183
|
-
themes.forEach((t, i) => {
|
|
1184
|
-
log(` ${String(i + 1).padStart(2, " ")}. ${t.id}`);
|
|
1185
|
-
log(` ${t.label}`);
|
|
1186
|
-
});
|
|
454
|
+
log("Supported: Next.js, Vite + React, React (CRA)");
|
|
1187
455
|
};
|
|
1188
456
|
|
|
1189
457
|
switch (command) {
|
|
1190
458
|
case "init":
|
|
1191
|
-
init().catch(
|
|
1192
|
-
logError(`Error: ${
|
|
459
|
+
init().catch(err => {
|
|
460
|
+
logError(`Error: ${err.message}`);
|
|
1193
461
|
process.exit(1);
|
|
1194
462
|
});
|
|
1195
463
|
break;
|
|
1196
|
-
case "export":
|
|
1197
|
-
exportCommand().catch((error) => {
|
|
1198
|
-
logError(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
1199
|
-
process.exit(1);
|
|
1200
|
-
});
|
|
1201
|
-
break;
|
|
1202
|
-
case "list":
|
|
1203
|
-
listThemes();
|
|
1204
|
-
break;
|
|
1205
|
-
case "help":
|
|
1206
|
-
case "--help":
|
|
1207
|
-
case "-h":
|
|
1208
464
|
default:
|
|
1209
465
|
usage();
|
|
1210
|
-
break;
|
|
1211
466
|
}
|