gradient-forge 1.0.2 → 1.0.5

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.
Files changed (2) hide show
  1. package/cli/index.mjs +245 -982
  2. 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 = (message) => process.stdout.write(`${message}\n`);
25
- const logError = (message) => process.stderr.write(`${message}\n`);
26
- const logSuccess = (message) => process.stdout.write(`✅ ${message}\n`);
27
- const logInfo = (message) => process.stdout.write(`ℹ️ ${message}\n`);
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", preview: "linear-gradient(145deg, #2d8e74, #70c76a, #d5ef91)" },
31
- { id: "theme-nitro-citrus-sherbert", label: "Citrus Sherbert", preview: "linear-gradient(145deg, #e67d35, #f7bb54, #fff0a3)" },
32
- { id: "theme-nitro-retro-raincloud", label: "Retro Raincloud", preview: "linear-gradient(145deg, #4e6077, #6f86a0, #98aec3)" },
33
- { id: "theme-nitro-hanami", label: "Hanami", preview: "linear-gradient(145deg, #995382, #c77ca9, #f0b7ce)" },
34
- { id: "theme-nitro-sunrise", label: "Sunrise", preview: "linear-gradient(145deg, #e25263, #ef8a57, #ffd07a)" },
35
- { id: "theme-nitro-cotton-candy", label: "Cotton Candy", preview: "linear-gradient(145deg, #5aa2ff, #9b78f0, #f39bca)" },
36
- { id: "theme-nitro-lofi-vibes", label: "Lofi Vibes", preview: "linear-gradient(145deg, #3f476c, #59608f, #7a6f9f)" },
37
- { id: "theme-nitro-desert-khaki", label: "Desert Khaki", preview: "linear-gradient(145deg, #6d5c49, #8f7a5d, #b49f76)" },
38
- { id: "theme-nitro-sunset", label: "Sunset", preview: "linear-gradient(145deg, #3f1b4d, #8c335f, #f4874f)" },
39
- { id: "theme-nitro-chroma-glow", label: "Chroma Glow", preview: "linear-gradient(145deg, #2d3eff, #a726fa, #00c7ff)" },
40
- { id: "theme-nitro-forest", label: "Forest", preview: "linear-gradient(145deg, #163f2e, #246b49, #59a86c)" },
41
- { id: "theme-nitro-crimson", label: "Crimson", preview: "linear-gradient(145deg, #2d050f, #681126, #a82435)" },
42
- { id: "theme-nitro-midnight-blurple", label: "Midnight Blurple", preview: "linear-gradient(145deg, #0f1232, #25366f, #5757dc)" },
43
- { id: "theme-nitro-mars", label: "Mars", preview: "linear-gradient(145deg, #2e140f, #5e261d, #9c422f)" },
44
- { id: "theme-nitro-dusk", label: "Dusk", preview: "linear-gradient(145deg, #1b1632, #3b2d5b, #745495)" },
45
- { id: "theme-nitro-under-the-sea", label: "Under The Sea", preview: "linear-gradient(145deg, #0b2242, #0d4f69, #2b848e)" },
46
- { id: "theme-nitro-retro-storm", label: "Retro Storm", preview: "linear-gradient(145deg, #1d2b3a, #354657, #55667a)" },
47
- { id: "theme-nitro-neon-nights", label: "Neon Nights", preview: "linear-gradient(145deg, #05061a, #180f52, #00bde6)" },
48
- { id: "theme-nitro-strawberry-lemonade", label: "Strawberry Lemonade", preview: "linear-gradient(145deg, #8f1847, #cc3f5e, #efc141)" },
49
- { id: "theme-nitro-aurora", label: "Aurora", preview: "linear-gradient(145deg, #083142, #1b7e74, #5fbf75)" },
50
- { id: "theme-nitro-sepia", label: "Sepia", preview: "linear-gradient(145deg, #35261d, #5d4636, #927454)" },
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
- const getThemeTokens = (themeId) => {
540
- return themeTokens[themeId] || themeTokens["theme-nitro-midnight-blurple"];
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 ensureDir = (dir) => {
544
- fs.mkdirSync(dir, { recursive: true });
545
- };
84
+ const getThemeTokens = (themeId) => themeTokens[themeId] || themeTokens["theme-nitro-midnight-blurple"];
546
85
 
547
- const copyTemplate = (name, dest, force) => {
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,62 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
622
164
  });
623
165
  };
624
166
 
625
- const promptYesNo = async (title, defaultYes = true) => {
626
- const items = defaultYes ? ["Yes", "No"] : ["No", "Yes"];
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
- const theme = themes.find((t) => t.id === themeId);
634
-
635
- let css = `/* Gradient Forge Theme: ${theme?.label ?? themeId} */\n`;
636
- css += `/* Color Mode: ${mode} */\n\n`;
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
- const DEFAULT_THEME: ThemeId = "${themeId}";
670
- const DEFAULT_COLOR_MODE: ColorMode = "${mode}";
175
+ body {
176
+ background: hsl(${tokens["--background"]});
177
+ color: hsl(${tokens["--foreground"]});
178
+ }
671
179
 
672
- export const defaultTheme = DEFAULT_THEME;
673
- export const defaultColorMode = DEFAULT_COLOR_MODE;
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
- const generateThemeContext = (themeId, mode) => {
188
+ // Generate theme context
189
+ const generateContext = (themeId, mode) => {
678
190
  return `"use client";
679
-
680
191
  import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
681
192
 
682
- export type ThemeId = "${themeId}";
683
- export type ColorMode = "${mode}";
193
+ export type ThemeId = string;
194
+ export type ColorMode = string;
684
195
 
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
- };
196
+ interface ThemeContextType {
197
+ themeId: string;
198
+ colorMode: string;
199
+ setThemeId: (id: string) => void;
200
+ setColorMode: (mode: string) => void;
201
+ themes: { id: string; label: string }[];
202
+ }
692
203
 
693
- const allThemes: { id: ThemeId; label: string }[] = [
694
- ${themes.map(t => ` { id: "${t.id}", label: "${t.label}" },`).join("\n")}
204
+ const allThemes = [
205
+ ${themes.map(t => ` { id: "${t.id}", label: "${t.label}" }`).join(",\n")}
695
206
  ];
696
207
 
697
- const ThemeContext = createContext<ThemeContextValue | null>(null);
208
+ const ThemeContext = createContext<ThemeContextType | null>(null);
698
209
 
699
210
  export const ThemeProvider = ({ children }: { children: ReactNode }) => {
700
- const [themeId, setThemeId] = useState<ThemeId>("${themeId}");
701
- const [colorMode, setColorMode] = useState<ColorMode>("${mode}");
211
+ const [themeId, setThemeId] = useState("${themeId}");
212
+ const [colorMode, setColorMode] = useState("${mode}");
702
213
 
703
214
  useEffect(() => {
704
215
  const root = document.documentElement;
705
216
  root.classList.remove("dark", "light");
706
217
  ${themes.map(t => ` root.classList.remove("${t.id}");`).join("\n")}
707
-
708
218
  root.classList.add(colorMode, themeId);
709
219
  root.setAttribute("data-theme", themeId);
710
220
  root.setAttribute("data-color-mode", colorMode);
711
-
712
- localStorage.setItem("gradient-forge.theme", themeId);
713
- localStorage.setItem("gradient-forge.color-mode", colorMode);
221
+ localStorage.setItem("theme", themeId);
222
+ localStorage.setItem("mode", colorMode);
714
223
  }, [themeId, colorMode]);
715
224
 
716
225
  return (
@@ -726,25 +235,14 @@ export const useTheme = () => {
726
235
  return ctx;
727
236
  };
728
237
 
729
- // Simple theme switcher component - drop into your app!
730
238
  export function ThemeSwitcher() {
731
239
  const { themeId, colorMode, setThemeId, setColorMode, themes } = useTheme();
732
-
733
240
  return (
734
- <div className="flex items-center gap-2 p-2 rounded-lg bg-card border">
735
- <select
736
- value={themeId}
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
- ))}
241
+ <div style={{ display: "flex", gap: "8px", padding: "8px" }}>
242
+ <select value={themeId} onChange={(e) => setThemeId(e.target.value)} style={{ padding: "4px" }}>
243
+ {themes.map((t) => <option key={t.id} value={t.id}>{t.label}</option>)}
743
244
  </select>
744
- <button
745
- onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")}
746
- className="px-2 py-1 rounded bg-primary text-primary-foreground"
747
- >
245
+ <button onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")} style={{ padding: "4px 8px" }}>
748
246
  {colorMode === "dark" ? "🌙" : "☀️"}
749
247
  </button>
750
248
  </div>
@@ -753,104 +251,165 @@ export function ThemeSwitcher() {
753
251
  `;
754
252
  };
755
253
 
756
- const generateSetupInstructions = (projectRoot, themeId, mode, themeLabel) => {
757
- return `# Gradient Forge Setup Complete! 🎨
758
-
759
- Theme: ${themeLabel}
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
- ];
254
+ // Setup for Next.js
255
+ const setupNextJs = (projectRoot, themeId, mode) => {
256
+ const appDir = path.join(projectRoot, "app");
257
+ const themeDir = path.join(projectRoot, "components", "theme");
776
258
 
777
- for (const layoutPath of layoutPaths) {
778
- if (fs.existsSync(layoutPath)) {
779
- let content = fs.readFileSync(layoutPath, "utf8");
780
-
781
- // Check if already has ThemeProvider
782
- if (content.includes("ThemeProvider")) {
783
- return { success: true, path: layoutPath, message: "Already configured" };
259
+ ensureDir(themeDir);
260
+
261
+ // Create CSS
262
+ const cssPath = path.join(appDir, "gradient-forge.css");
263
+ fs.writeFileSync(cssPath, generateCSS(themeId, mode));
264
+ logSuccess(`Created: app/gradient-forge.css`);
265
+
266
+ // Create context
267
+ const contextPath = path.join(themeDir, "theme-context.tsx");
268
+ fs.writeFileSync(contextPath, generateContext(themeId, mode));
269
+ logSuccess(`Created: components/theme/theme-context.tsx`);
270
+
271
+ // Inject into globals.css
272
+ const globalsPath = path.join(appDir, "globals.css");
273
+ if (fs.existsSync(globalsPath)) {
274
+ const content = fs.readFileSync(globalsPath, "utf8");
275
+ if (!content.includes("gradient-forge.css")) {
276
+ fs.writeFileSync(globalsPath, `@import "./gradient-forge.css";\n${content}`);
277
+ logSuccess(`Injected into: app/globals.css`);
278
+ }
279
+ }
280
+
281
+ // Update layout.tsx
282
+ const layoutPath = path.join(appDir, "layout.tsx");
283
+ if (fs.existsSync(layoutPath)) {
284
+ let content = fs.readFileSync(layoutPath, "utf8");
285
+
286
+ if (!content.includes("ThemeProvider")) {
287
+ // Use @/ import path for Next.js
288
+ const importStmt = `import { ThemeProvider } from "@/components/theme/theme-context";`;
289
+ if (!content.includes(importStmt)) {
290
+ content = importStmt + "\n" + content;
784
291
  }
785
292
 
786
- // Add import - insert after the first import or at the start
787
- const importStatement = `import { ThemeProvider } from "./components/theme/theme-context";`;
293
+ // Wrap body - ensure proper spacing
294
+ content = content.replace(/(<body[^>]*>)/, "$1\n<ThemeProvider>");
295
+ content = content.replace(/(<\/body>)/, "</ThemeProvider>\n$1");
788
296
 
789
- // Find position after the last import line
790
- const lines = content.split('\n');
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
- }
297
+ // Add classes to html - preserve existing attributes
298
+ content = content.replace(/<html/, `<html class="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}"`);
800
299
 
801
- // Insert the import
802
- if (insertIndex > 0 && lines[insertIndex - 1].trim() !== '') {
803
- lines.splice(insertIndex, 0, importStatement);
804
- } else {
805
- lines.splice(insertIndex, 0, importStatement);
300
+ fs.writeFileSync(layoutPath, content);
301
+ logSuccess(`Updated: app/layout.tsx`);
302
+ }
303
+ }
304
+
305
+ return { success: true };
306
+ };
307
+
308
+ // Setup for Vite + React
309
+ const setupVite = (projectRoot, themeId, mode) => {
310
+ const srcDir = path.join(projectRoot, "src");
311
+ const themeDir = path.join(projectRoot, "src", "components", "theme");
312
+ const cssDir = path.join(projectRoot, "src");
313
+
314
+ ensureDir(themeDir);
315
+
316
+ // Create CSS
317
+ const cssPath = path.join(cssDir, "gradient-forge.css");
318
+ fs.writeFileSync(cssPath, generateCSS(themeId, mode));
319
+ logSuccess(`Created: src/gradient-forge.css`);
320
+
321
+ // Create context
322
+ const contextPath = path.join(themeDir, "theme-context.tsx");
323
+ fs.writeFileSync(contextPath, generateContext(themeId, mode));
324
+ logSuccess(`Created: src/components/theme/theme-context.tsx`);
325
+
326
+ // Inject into index.css or main.css
327
+ const possibleCssFiles = ["src/index.css", "src/App.css", "src/main.css"];
328
+ for (const cssFile of possibleCssFiles) {
329
+ const cssPath2 = path.join(projectRoot, cssFile);
330
+ if (fs.existsSync(cssPath2)) {
331
+ const content = fs.readFileSync(cssPath2, "utf8");
332
+ if (!content.includes("gradient-forge.css")) {
333
+ fs.writeFileSync(cssPath2, `@import "./gradient-forge.css";\n${content}`);
334
+ logSuccess(`Injected into: ${cssFile}`);
806
335
  }
807
- content = lines.join('\n');
808
-
809
- // Wrap body content with ThemeProvider
810
- content = content.replace(
811
- /(<body[^>]*>)([\s\S]*)(<\/body>)/,
812
- '$1<ThemeProvider>$2</ThemeProvider>$3'
813
- );
814
-
815
- // Add theme classes to html tag - use className for JSX
816
- content = content.replace(
817
- /<html[^>]*>/,
818
- `<html lang="en" className="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">`
819
- );
336
+ break;
337
+ }
338
+ }
339
+
340
+ // Update main.tsx or main.jsx
341
+ const mainFiles = ["src/main.tsx", "src/main.jsx", "src/index.tsx", "src/index.jsx"];
342
+ for (const mainFile of mainFiles) {
343
+ const mainPath = path.join(projectRoot, mainFile);
344
+ if (fs.existsSync(mainPath)) {
345
+ let content = fs.readFileSync(mainPath, "utf8");
820
346
 
821
- fs.writeFileSync(layoutPath, content);
822
- return { success: true, path: layoutPath };
347
+ if (!content.includes("ThemeProvider")) {
348
+ const importStmt = `import { ThemeProvider } from "./components/theme/theme-context";`;
349
+ if (!content.includes(importStmt)) {
350
+ content = importStmt + "\n" + content;
351
+ }
352
+
353
+ // Wrap app with ThemeProvider
354
+ content = content.replace(/(<App[^>]*>)/, '<ThemeProvider>$1</ThemeProvider>');
355
+ content = content.replace(/(React\.createElement\(App)/, 'React.createElement(ThemeProvider, null, React.createElement(App)');
356
+
357
+ // Add theme classes to root div
358
+ content = content.replace(
359
+ /document\.getElementById\(['"]root['"]\)\.innerHTML/,
360
+ `document.getElementById('root').innerHTML = '<div class="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">' +`
361
+ );
362
+
363
+ fs.writeFileSync(mainPath, content);
364
+ logSuccess(`Updated: ${mainFile}`);
365
+ }
366
+ break;
823
367
  }
824
368
  }
825
369
 
826
- return { success: false, message: "No layout.tsx found" };
370
+ // Update index.html
371
+ const indexPath = path.join(projectRoot, "index.html");
372
+ if (fs.existsSync(indexPath)) {
373
+ let content = fs.readFileSync(indexPath, "utf8");
374
+ content = content.replace(/<html[^>]*>/, `<html class="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">`);
375
+ fs.writeFileSync(indexPath, content);
376
+ logSuccess(`Updated: index.html`);
377
+ }
378
+
379
+ return { success: true };
380
+ };
381
+
382
+ // Setup for React (CRA)
383
+ const setupReact = (projectRoot, themeId, mode) => {
384
+ // Similar to Vite but with CRA-specific paths
385
+ return setupVite(projectRoot, themeId, mode);
827
386
  };
828
387
 
829
388
  const init = async () => {
830
389
  log("");
831
- log("🎨 Gradient Forge CLI - Initialize Theme");
832
- log("======================================");
390
+ log("🎨 Gradient Forge CLI - Instant Theme Setup");
391
+ log("==========================================");
833
392
  log("");
834
393
 
835
394
  const projectRoot = readArg("--path", process.cwd());
836
- const force = hasFlag("--force");
837
- const skipInject = hasFlag("--no-inject");
395
+ logInfo(`Project: ${projectRoot}`);
838
396
 
839
- logInfo(`Project root: ${projectRoot}`);
397
+ // Detect project type
398
+ const projectType = detectProjectType(projectRoot);
399
+ logInfo(`Detected: ${projectType === "next" ? "Next.js" : projectType === "vite" ? "Vite + React" : projectType === "react" ? "React (CRA)" : "Unknown"} project`);
840
400
  log("");
841
401
 
842
- // Check if it's a Next.js project
402
+ // Check package.json exists
843
403
  const packageJsonPath = path.join(projectRoot, "package.json");
844
404
  if (!fs.existsSync(packageJsonPath)) {
845
- logError("Error: Not a valid Node.js project. No package.json found.");
405
+ logError("Error: Not a valid project. No package.json found.");
846
406
  process.exit(1);
847
407
  }
848
408
 
849
409
  // Select theme
850
410
  log("Select a theme:");
851
- log("");
852
- const selectedTheme = await promptSelect("Choose your theme:", themes, 12); // Default: Midnight Blurple
853
- log(`\n✓ Selected: ${selectedTheme.label}`);
411
+ const selectedTheme = await promptSelect("Choose your theme:", themes, 12);
412
+ log(`\n✓ ${selectedTheme.label}`);
854
413
 
855
414
  // Select mode
856
415
  log("");
@@ -860,352 +419,56 @@ const init = async () => {
860
419
  ];
861
420
  log("Select color mode:");
862
421
  const selectedMode = await promptSelect("Choose color mode:", modeOptions, 0);
863
- log(`\n✓ Selected: ${selectedMode.label}`);
422
+ log(`\n✓ ${selectedMode.label}`);
864
423
 
865
424
  const themeId = selectedTheme.id;
866
425
  const mode = selectedMode.id;
867
426
 
868
427
  log("");
869
- logInfo("Generating theme files...");
428
+ logInfo("Setting up theme...");
870
429
  log("");
871
430
 
872
- // Create directories
873
- const themeDir = path.join(projectRoot, "components", "theme");
874
- const appDir = path.join(projectRoot, "app");
875
-
876
- ensureDir(themeDir);
877
- ensureDir(appDir);
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)}`);
431
+ // Setup based on project type
432
+ if (projectType === "next") {
433
+ setupNextJs(projectRoot, themeId, mode);
434
+ } else if (projectType === "vite") {
435
+ setupVite(projectRoot, themeId, mode);
436
+ } else if (projectType === "react") {
437
+ setupReact(projectRoot, themeId, mode);
912
438
  } else {
913
- logInfo("Note: Could not auto-detect layout.tsx");
439
+ logError("Could not detect project type. Supported: Next.js, Vite, React");
440
+ process.exit(1);
914
441
  }
915
-
442
+
916
443
  log("");
917
- log("======================================");
918
- logSuccess("Theme setup complete! 🎉");
444
+ log("==========================================");
445
+ logSuccess("🎉 Theme applied instantly!");
919
446
  log("");
920
447
  log(`Theme: ${selectedTheme.label} (${mode})`);
921
- log("");
922
- log("Your app now has beautiful gradient themes!");
923
448
  log("Run: npm run dev");
924
449
  log("");
925
450
  };
926
451
 
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
452
  const usage = () => {
1143
453
  log("🎨 Gradient Forge CLI");
1144
454
  log("");
1145
455
  log("Usage:");
1146
- log(" gradient-forge init [--path <project-root>] [--inject|--no-inject] [--force]");
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)");
456
+ log(" gradient-forge init [--path <project>]");
1168
457
  log("");
1169
458
  log("Examples:");
459
+ log(" gradient-forge init");
1170
460
  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
461
  log("");
1174
- log("Available Themes:");
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
- });
462
+ log("Supported: Next.js, Vite + React, React (CRA)");
1187
463
  };
1188
464
 
1189
465
  switch (command) {
1190
466
  case "init":
1191
- init().catch((error) => {
1192
- logError(`Error: ${error instanceof Error ? error.message : String(error)}`);
467
+ init().catch(err => {
468
+ logError(`Error: ${err.message}`);
1193
469
  process.exit(1);
1194
470
  });
1195
471
  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
472
  default:
1209
473
  usage();
1210
- break;
1211
474
  }