gradient-forge 1.0.1 → 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.
Files changed (2) hide show
  1. package/cli/index.mjs +268 -965
  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) {
@@ -567,6 +116,7 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
567
116
  }
568
117
 
569
118
  let index = defaultIndex;
119
+ let buffer = "";
570
120
 
571
121
  const render = () => {
572
122
  process.stdout.write("\x1b[2J\x1b[H");
@@ -581,27 +131,28 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
581
131
  };
582
132
 
583
133
  const onKey = (data) => {
584
- const key = data.toString();
585
- if (key === "\u0003") {
586
- process.stdin.setRawMode(false);
587
- process.stdin.pause();
588
- process.exit(1);
589
- }
590
- if (key === "\r") {
134
+ buffer += data.toString();
135
+ if (buffer.includes("\u001b[A")) {
136
+ index = index > 0 ? index - 1 : items.length - 1;
137
+ buffer = "";
138
+ render();
139
+ } else if (buffer.includes("\u001b[B")) {
140
+ index = index < items.length - 1 ? index + 1 : 0;
141
+ buffer = "";
142
+ render();
143
+ } else if (buffer.includes("\r") || buffer.includes("\n")) {
591
144
  process.stdin.setRawMode(false);
592
145
  process.stdin.pause();
593
146
  process.stdin.removeListener("data", onKey);
594
147
  process.stdout.write("\x1b[?25h");
595
148
  resolve(items[index]);
596
149
  return;
597
- }
598
- if (key === "\u001b[A") {
599
- index = index > 0 ? index - 1 : items.length - 1;
600
- render();
601
- }
602
- if (key === "\u001b[B") {
603
- index = index < items.length - 1 ? index + 1 : 0;
604
- render();
150
+ } else if (buffer.includes("\u0003")) {
151
+ process.stdin.setRawMode(false);
152
+ process.stdin.pause();
153
+ process.exit(1);
154
+ } else {
155
+ buffer = "";
605
156
  }
606
157
  };
607
158
 
@@ -613,95 +164,54 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
613
164
  });
614
165
  };
615
166
 
616
- const promptYesNo = async (title, defaultYes = true) => {
617
- const items = defaultYes ? ["Yes", "No"] : ["No", "Yes"];
618
- const choice = await promptSelect(title, items, defaultYes ? 0 : 1);
619
- return choice === "Yes";
620
- };
621
-
622
- const generateCSSVariables = (themeId, mode) => {
167
+ // Generate CSS
168
+ const generateCSS = (themeId, mode) => {
623
169
  const tokens = getThemeTokens(themeId);
624
- const theme = themes.find((t) => t.id === themeId);
625
-
626
- let css = `/* Gradient Forge Theme: ${theme?.label ?? themeId} */\n`;
627
- css += `/* Color Mode: ${mode} */\n\n`;
628
-
629
- css += `.${themeId} {\n`;
630
- Object.entries(tokens).forEach(([key, value]) => {
631
- if (key !== "--app-gradient") {
632
- css += ` ${key}: ${value};\n`;
633
- }
634
- });
635
- css += ` --app-gradient: ${tokens["--app-gradient"]};\n`;
636
- css += `}\n\n`;
637
-
638
- css += `/* Surface Layer Styles */\n`;
639
- css += `.bg-card,\n.bg-popover,\n.bg-sidebar {\n`;
640
- css += ` background-color: hsl(var(--background) / 0.34);\n`;
641
- css += ` background-image: linear-gradient(var(--app-surface-tint), var(--app-surface-tint));\n`;
642
- css += ` backdrop-filter: blur(16px);\n`;
643
- css += `}\n`;
644
-
645
- return css;
646
- };
647
-
648
- const generateThemeEngine = (themeId, mode) => {
649
- return `export const NITRO_PUBLIC_THEMES = [
650
- ${themes.map(t => `{
651
- id: "${t.id}",
652
- label: "${t.label}",
653
- preview: "${t.preview}",
654
- }`).join(",\n ")},
655
- ] as const;
656
-
657
- export type ThemeId = typeof NITRO_PUBLIC_THEMES[number]["id"];
658
- export type ColorMode = "dark" | "light";
170
+ return `/* Gradient Forge Theme */
171
+ .${themeId} {
172
+ ${Object.entries(tokens).map(([k, v]) => ` ${k}: ${v};`).join("\n")}
173
+ }
659
174
 
660
- const DEFAULT_THEME: ThemeId = "${themeId}";
661
- const DEFAULT_COLOR_MODE: ColorMode = "${mode}";
175
+ body {
176
+ background: hsl(${tokens["--background"]});
177
+ color: hsl(${tokens["--foreground"]});
178
+ }
662
179
 
663
- export const defaultTheme = DEFAULT_THEME;
664
- 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
+ }
665
185
  `;
666
186
  };
667
187
 
668
- const generateThemeContext = (themeId, mode) => {
188
+ // Generate theme context
189
+ const generateContext = (themeId, mode) => {
669
190
  return `"use client";
191
+ import React, { createContext, useContext, useEffect, useState } from "react";
670
192
 
671
- import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
672
-
673
- export type ThemeId = "${themeId}";
674
- export type ColorMode = "${mode}";
193
+ export type ThemeId = string;
194
+ export type ColorMode = string;
675
195
 
676
- type ThemeContextValue = {
677
- themeId: ThemeId;
678
- colorMode: ColorMode;
679
- setThemeId: (themeId: ThemeId) => void;
680
- setColorMode: (mode: ColorMode) => void;
681
- themes: { id: ThemeId; label: string }[];
682
- };
683
-
684
- const allThemes: { id: ThemeId; label: string }[] = [
685
- ${themes.map(t => ` { id: "${t.id}", label: "${t.label}" },`).join("\n")}
196
+ const allThemes = [
197
+ ${themes.map(t => ` { id: "${t.id}", label: "${t.label}" }`).join(",\n")}
686
198
  ];
687
199
 
688
- const ThemeContext = createContext<ThemeContextValue | null>(null);
200
+ const ThemeContext: any = createContext(null);
689
201
 
690
- export const ThemeProvider = ({ children }: { children: ReactNode }) => {
691
- const [themeId, setThemeId] = useState<ThemeId>("${themeId}");
692
- const [colorMode, setColorMode] = useState<ColorMode>("${mode}");
202
+ export const ThemeProvider = ({ children }: { children: any }) => {
203
+ const [themeId, setThemeId] = useState("${themeId}");
204
+ const [colorMode, setColorMode] = useState("${mode}");
693
205
 
694
206
  useEffect(() => {
695
207
  const root = document.documentElement;
696
208
  root.classList.remove("dark", "light");
697
209
  ${themes.map(t => ` root.classList.remove("${t.id}");`).join("\n")}
698
-
699
210
  root.classList.add(colorMode, themeId);
700
211
  root.setAttribute("data-theme", themeId);
701
212
  root.setAttribute("data-color-mode", colorMode);
702
-
703
- localStorage.setItem("gradient-forge.theme", themeId);
704
- localStorage.setItem("gradient-forge.color-mode", colorMode);
213
+ localStorage.setItem("theme", themeId);
214
+ localStorage.setItem("mode", colorMode);
705
215
  }, [themeId, colorMode]);
706
216
 
707
217
  return (
@@ -717,25 +227,14 @@ export const useTheme = () => {
717
227
  return ctx;
718
228
  };
719
229
 
720
- // Simple theme switcher component - drop into your app!
721
230
  export function ThemeSwitcher() {
722
231
  const { themeId, colorMode, setThemeId, setColorMode, themes } = useTheme();
723
-
724
232
  return (
725
- <div className="flex items-center gap-2 p-2 rounded-lg bg-card border">
726
- <select
727
- value={themeId}
728
- onChange={(e) => setThemeId(e.target.value as ThemeId)}
729
- className="bg-background text-foreground px-2 py-1 rounded border"
730
- >
731
- {themes.map(t => (
732
- <option key={t.id} value={t.id}>{t.label}</option>
733
- ))}
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>)}
734
236
  </select>
735
- <button
736
- onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")}
737
- className="px-2 py-1 rounded bg-primary text-primary-foreground"
738
- >
237
+ <button onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")} style={{ padding: "4px 8px" }}>
739
238
  {colorMode === "dark" ? "🌙" : "☀️"}
740
239
  </button>
741
240
  </div>
@@ -744,71 +243,165 @@ export function ThemeSwitcher() {
744
243
  `;
745
244
  };
746
245
 
747
- const generateSetupInstructions = (projectRoot, themeId, mode, themeLabel) => {
748
- return `# Gradient Forge Setup Complete! 🎨
749
-
750
- Theme: ${themeLabel}
751
- Mode: ${mode}
752
-
753
- ## Quick Setup (Done!)
754
-
755
- Already configured:
756
- - app/gradient-forge.css - Theme CSS
757
- - components/theme/theme-context.tsx - Theme provider + ThemeSwitcher component
758
- - components/theme/theme-engine.ts - Theme definitions
759
- - app/globals.css - Theme imported
760
-
761
- ## Add to your app:
762
-
763
- 1. Wrap your app with ThemeProvider in layout.tsx:
764
- import { ThemeProvider } from "./components/theme/theme-context";
765
-
766
- export default function RootLayout({ children }) {
767
- return (
768
- <html lang="en" className="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">
769
- <body>
770
- <ThemeProvider>{children}</ThemeProvider>
771
- </body>
772
- </html>
773
- );
774
- }
775
-
776
- 2. Add the theme switcher anywhere in your app:
777
- import { ThemeSwitcher } from "./components/theme/theme-context";
778
-
779
- // Add <ThemeSwitcher /> anywhere to let users change themes!
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");
250
+
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;
283
+ }
284
+
285
+ // Wrap body - ensure proper spacing
286
+ content = content.replace(/(<body[^>]*>)/, "$1\n<ThemeProvider>");
287
+ content = content.replace(/(<\/body>)/, "</ThemeProvider>\n$1");
288
+
289
+ // Add classes to html - preserve existing attributes
290
+ content = content.replace(/<html/, `<html class="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}"`);
291
+
292
+ fs.writeFileSync(layoutPath, content);
293
+ logSuccess(`Updated: app/layout.tsx`);
294
+ }
295
+ }
296
+
297
+ return { success: true };
298
+ };
780
299
 
781
- ## Done! 🎉 Your app now has gradient themes!
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}`);
327
+ }
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");
338
+
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;
359
+ }
360
+ }
361
+
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
+ };
782
373
 
783
- For more themes, visit: https://gradient-forge.vercel.app
784
- `;
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);
785
378
  };
786
379
 
787
380
  const init = async () => {
788
381
  log("");
789
- log("🎨 Gradient Forge CLI - Initialize Theme");
790
- log("======================================");
382
+ log("🎨 Gradient Forge CLI - Instant Theme Setup");
383
+ log("==========================================");
791
384
  log("");
792
385
 
793
386
  const projectRoot = readArg("--path", process.cwd());
794
- const force = hasFlag("--force");
795
- const skipInject = hasFlag("--no-inject");
387
+ logInfo(`Project: ${projectRoot}`);
796
388
 
797
- logInfo(`Project root: ${projectRoot}`);
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`);
798
392
  log("");
799
393
 
800
- // Check if it's a Next.js project
394
+ // Check package.json exists
801
395
  const packageJsonPath = path.join(projectRoot, "package.json");
802
396
  if (!fs.existsSync(packageJsonPath)) {
803
- logError("Error: Not a valid Node.js project. No package.json found.");
397
+ logError("Error: Not a valid project. No package.json found.");
804
398
  process.exit(1);
805
399
  }
806
400
 
807
401
  // Select theme
808
402
  log("Select a theme:");
809
- log("");
810
- const selectedTheme = await promptSelect("Choose your theme:", themes, 12); // Default: Midnight Blurple
811
- log(`\n✓ Selected: ${selectedTheme.label}`);
403
+ const selectedTheme = await promptSelect("Choose your theme:", themes, 12);
404
+ log(`\n✓ ${selectedTheme.label}`);
812
405
 
813
406
  // Select mode
814
407
  log("");
@@ -818,346 +411,56 @@ const init = async () => {
818
411
  ];
819
412
  log("Select color mode:");
820
413
  const selectedMode = await promptSelect("Choose color mode:", modeOptions, 0);
821
- log(`\n✓ Selected: ${selectedMode.label}`);
414
+ log(`\n✓ ${selectedMode.label}`);
822
415
 
823
416
  const themeId = selectedTheme.id;
824
417
  const mode = selectedMode.id;
825
418
 
826
419
  log("");
827
- logInfo("Generating theme files...");
420
+ logInfo("Setting up theme...");
828
421
  log("");
829
422
 
830
- // Create directories
831
- const themeDir = path.join(projectRoot, "components", "theme");
832
- const appDir = path.join(projectRoot, "app");
833
-
834
- ensureDir(themeDir);
835
- ensureDir(appDir);
836
-
837
- // Generate and write files
838
- const cssContent = generateCSSVariables(themeId, mode);
839
- const cssPath = path.join(appDir, "gradient-forge.css");
840
- fs.writeFileSync(cssPath, cssContent);
841
- logSuccess(`Created: ${path.relative(projectRoot, cssPath)}`);
842
-
843
- const engineContent = generateThemeEngine(themeId, mode);
844
- const enginePath = path.join(themeDir, "theme-engine.ts");
845
- fs.writeFileSync(enginePath, engineContent);
846
- logSuccess(`Created: ${path.relative(projectRoot, enginePath)}`);
847
-
848
- const contextContent = generateThemeContext(themeId, mode);
849
- const contextPath = path.join(themeDir, "theme-context.tsx");
850
- fs.writeFileSync(contextPath, contextContent);
851
- logSuccess(`Created: ${path.relative(projectRoot, contextPath)}`);
852
-
853
- // Inject into globals.css if it exists and not skipped
854
- const globalsPath = path.join(appDir, "globals.css");
855
- if (!skipInject && fs.existsSync(globalsPath)) {
856
- const marker = "/* gradient-forge */";
857
- const existing = fs.readFileSync(globalsPath, "utf8");
858
-
859
- if (!existing.includes(marker)) {
860
- const importStatement = '@import "./gradient-forge.css";';
861
- fs.writeFileSync(globalsPath, `${importStatement}\n${existing}`);
862
- logSuccess(`Injected theme import into: ${path.relative(projectRoot, globalsPath)}`);
863
- }
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);
430
+ } else {
431
+ logError("Could not detect project type. Supported: Next.js, Vite, React");
432
+ process.exit(1);
864
433
  }
865
434
 
866
- // Generate setup instructions
867
- const instructions = generateSetupInstructions(projectRoot, themeId, mode, selectedTheme.label);
868
- const readmePath = path.join(projectRoot, "GRADIENT_FORGE_SETUP.md");
869
- fs.writeFileSync(readmePath, instructions);
870
- logSuccess(`Created setup guide: ${path.relative(projectRoot, readmePath)}`);
871
-
872
435
  log("");
873
- log("======================================");
874
- logSuccess("Theme setup complete! 🎉");
436
+ log("==========================================");
437
+ logSuccess("🎉 Theme applied instantly!");
438
+ log("");
439
+ log(`Theme: ${selectedTheme.label} (${mode})`);
440
+ log("Run: npm run dev");
875
441
  log("");
876
- log(instructions);
877
- };
878
-
879
- const exportCommand = async () => {
880
- const themeId = readArg("--theme", DEFAULT_THEME);
881
- const mode = readArg("--mode", DEFAULT_MODE);
882
- const format = readArg("--format", "css");
883
- const output = readArg("--output", process.cwd());
884
-
885
- const generator = exportGenerators[format];
886
- if (!generator) {
887
- logError(`Unknown format: ${format}`);
888
- log("Available formats: css, scss, json, tailwind, w3c-tokens, figma-tokens, css-variables, html-root, all");
889
- process.exit(1);
890
- }
891
-
892
- const result = generator(themeId, mode);
893
-
894
- ensureDir(output);
895
- const outputPath = path.join(output, result.filename);
896
- fs.writeFileSync(outputPath, result.content);
897
-
898
- logSuccess(`Exported: ${outputPath}`);
899
- };
900
-
901
- const exportGenerators = {
902
- css: (themeId, mode) => {
903
- const tokens = getThemeTokens(themeId);
904
- const theme = themes.find((t) => t.id === themeId);
905
-
906
- let css = `/* Gradient Forge Theme: ${theme?.label ?? themeId} */\n`;
907
- css += `/* Color Mode: ${mode} */\n\n`;
908
- css += `.${themeId} {\n`;
909
- Object.entries(tokens).forEach(([key, value]) => {
910
- if (key !== "--app-gradient") {
911
- css += ` ${key}: ${value};\n`;
912
- }
913
- });
914
- css += ` --app-gradient: ${tokens["--app-gradient"]};\n`;
915
- css += `}\n\n`;
916
- css += `.bg-card, .bg-popover, .bg-sidebar {\n`;
917
- css += ` background-color: hsl(var(--background) / 0.34);\n`;
918
- css += ` background-image: linear-gradient(var(--app-surface-tint), var(--app-surface-tint));\n`;
919
- css += ` backdrop-filter: blur(16px);\n`;
920
- css += `}\n`;
921
-
922
- return { filename: `${themeId}-theme.css`, content: css };
923
- },
924
-
925
- scss: (themeId, mode) => {
926
- const tokens = getThemeTokens(themeId);
927
- const theme = themes.find((t) => t.id === themeId);
928
-
929
- let scss = `// Gradient Forge Theme: ${theme?.label ?? themeId}\n`;
930
- scss += `// Color Mode: ${mode}\n\n`;
931
-
932
- Object.entries(tokens).forEach(([key, value]) => {
933
- if (key !== "--app-gradient") {
934
- scss += `$${key.replace("--", "")}: ${value};\n`;
935
- }
936
- });
937
- scss += `$app-gradient: ${tokens["--app-gradient"]};\n`;
938
-
939
- return { filename: `${themeId}-theme.scss`, content: scss };
940
- },
941
-
942
- json: (themeId, mode) => {
943
- const tokens = getThemeTokens(themeId);
944
- const theme = themes.find((t) => t.id === themeId);
945
-
946
- const colorTokens = {};
947
- Object.entries(tokens).forEach(([key, value]) => {
948
- colorTokens[key.replace("--", "")] = key === "--app-surface-tint" ? value : `hsl(${value})`;
949
- });
950
-
951
- const data = {
952
- name: theme?.label ?? themeId,
953
- id: themeId,
954
- colorMode: mode,
955
- version: "1.0.0",
956
- generatedAt: new Date().toISOString(),
957
- colors: colorTokens,
958
- };
959
-
960
- return { filename: `${themeId}-tokens.json`, content: JSON.stringify(data, null, 2) };
961
- },
962
-
963
- tailwind: (themeId, mode) => {
964
- const tokens = getThemeTokens(themeId);
965
- const theme = themes.find((t) => t.id === themeId);
966
-
967
- const colors = {};
968
- Object.entries(tokens).forEach(([key, value]) => {
969
- if (key !== "--app-gradient" && key !== "--app-surface-tint") {
970
- colors[key.replace("--", "")] = `"hsl(var(${key}))"`;
971
- }
972
- });
973
-
974
- const content = `// Gradient Forge Theme: ${theme?.label ?? themeId}
975
- // Color Mode: ${mode}
976
-
977
- import type { Config } from "tailwindcss";
978
-
979
- const config: Config = {
980
- theme: {
981
- extend: {
982
- colors: ${JSON.stringify(colors, null, 6).replace(/"/g, "'")},
983
- backgroundImage: {
984
- "app-gradient": "var(--app-gradient)",
985
- },
986
- },
987
- },
988
- };
989
-
990
- export default config;
991
- `;
992
-
993
- return { filename: `${themeId}-tailwind.ts`, content };
994
- },
995
-
996
- "w3c-tokens": (themeId, mode) => {
997
- const tokens = getThemeTokens(themeId);
998
- const theme = themes.find((t) => t.id === themeId);
999
-
1000
- const colorTokens = {};
1001
- Object.entries(tokens).forEach(([key, value]) => {
1002
- const tokenName = key.replace("--", "").replace(/-/g, ".");
1003
- colorTokens[tokenName] = {
1004
- $type: "color",
1005
- $value: key === "--app-surface-tint" ? value : `hsl(${value})`,
1006
- };
1007
- });
1008
-
1009
- const data = {
1010
- $schema: "https://design-tokens.github.io/schema/format.json",
1011
- name: theme?.label ?? themeId,
1012
- id: themeId,
1013
- colorMode: mode,
1014
- version: "1.0.0",
1015
- generatedAt: new Date().toISOString(),
1016
- colors: colorTokens,
1017
- };
1018
-
1019
- return { filename: `${themeId}-w3c-tokens.json`, content: JSON.stringify(data, null, 2) };
1020
- },
1021
-
1022
- "figma-tokens": (themeId, mode) => {
1023
- const tokens = getThemeTokens(themeId);
1024
- const theme = themes.find((t) => t.id === themeId);
1025
-
1026
- const colorTokens = {};
1027
- Object.entries(tokens).forEach(([key, value]) => {
1028
- colorTokens[key.replace("--", "")] = {
1029
- value: key === "--app-surface-tint" ? value : `hsl(${value})`,
1030
- type: "color",
1031
- };
1032
- });
1033
-
1034
- const data = {
1035
- GradientForge: {
1036
- [theme?.label ?? themeId]: { colors: colorTokens },
1037
- },
1038
- };
1039
-
1040
- return { filename: `${themeId}-figma-tokens.json`, content: JSON.stringify(data, null, 2) };
1041
- },
1042
-
1043
- "css-variables": (themeId, mode) => {
1044
- const tokens = getThemeTokens(themeId);
1045
- const theme = themes.find((t) => t.id === themeId);
1046
-
1047
- let css = `/* Gradient Forge Theme: ${theme?.label ?? themeId} */\n`;
1048
- css += `/* Color Mode: ${mode} */\n`;
1049
- css += `/* Paste these into your :root or theme class */\n\n`;
1050
-
1051
- Object.entries(tokens).forEach(([key, value]) => {
1052
- if (key !== "--app-gradient") {
1053
- css += `${key}: ${value};\n`;
1054
- }
1055
- });
1056
- css += `--app-gradient: ${tokens["--app-gradient"]};\n`;
1057
-
1058
- return { filename: `${themeId}-variables.css`, content: css };
1059
- },
1060
-
1061
- "html-root": (themeId, mode) => {
1062
- const theme = themes.find((t) => t.id === themeId);
1063
-
1064
- const content = `<!-- Gradient Forge Theme: ${theme?.label ?? themeId} -->
1065
- <!-- Add these attributes to your <html> element -->
1066
-
1067
- <html
1068
- lang="en"
1069
- class="${mode} ${themeId}"
1070
- data-theme="${themeId}"
1071
- data-color-mode="${mode}"
1072
- suppressHydrationWarning
1073
- >
1074
- <!-- Your app content -->
1075
- </html>`;
1076
-
1077
- return { filename: `${themeId}-html-root.html`, content };
1078
- },
1079
-
1080
- all: (themeId, mode) => {
1081
- const allContent = Object.keys(exportGenerators)
1082
- .filter(f => f !== "all")
1083
- .map(format => {
1084
- const gen = exportGenerators[format];
1085
- const result = gen(themeId, mode);
1086
- return `/* ===== ${result.filename} ===== */\n\n${result.content}`;
1087
- })
1088
- .join("\n\n\n");
1089
-
1090
- return { filename: `${themeId}-all-formats.txt`, content: allContent };
1091
- },
1092
442
  };
1093
443
 
1094
444
  const usage = () => {
1095
445
  log("🎨 Gradient Forge CLI");
1096
446
  log("");
1097
447
  log("Usage:");
1098
- log(" gradient-forge init [--path <project-root>] [--inject|--no-inject] [--force]");
1099
- log(" gradient-forge export --theme <theme-id> --format <format> [--output <path>]");
1100
- log(" gradient-forge list");
1101
- log(" gradient-forge help");
1102
- log("");
1103
- log("Commands:");
1104
- log(" init Initialize gradient-forge in a project (-interactive)");
1105
- log(" export Export theme tokens in various formats");
1106
- log(" list List all available themes");
1107
- log(" help Show this help message");
1108
- log("");
1109
- log("Init Options:");
1110
- log(" --path Target project root (default: current directory)");
1111
- log(" --inject Append theme CSS to app/globals.css (default)");
1112
- log(" --no-inject Skip CSS injection");
1113
- log(" --force Overwrite existing files");
1114
- log("");
1115
- log("Export Options:");
1116
- log(" --theme Theme ID to export (default: theme-nitro-midnight-blurple)");
1117
- log(" --format Export format: css, scss, json, tailwind, w3c-tokens, figma-tokens, css-variables, html-root, all");
1118
- log(" --output Output directory (default: current directory)");
1119
- log(" --mode Color mode: dark, light (default: dark)");
448
+ log(" gradient-forge init [--path <project>]");
1120
449
  log("");
1121
450
  log("Examples:");
451
+ log(" gradient-forge init");
1122
452
  log(" gradient-forge init --path my-app");
1123
- log(" gradient-forge export --theme theme-nitro-midnight-blurple --format css");
1124
- log(" gradient-forge export --theme theme-nitro-aurora --format tailwind --output ./themes");
1125
- log("");
1126
- log("Available Themes:");
1127
- themes.forEach((t, i) => {
1128
- log(` ${String(i + 1).padStart(2, " ")}. ${t.label}`);
1129
- });
1130
- };
1131
-
1132
- const listThemes = () => {
1133
- log("🎨 Available Themes:");
1134
453
  log("");
1135
- themes.forEach((t, i) => {
1136
- log(` ${String(i + 1).padStart(2, " ")}. ${t.id}`);
1137
- log(` ${t.label}`);
1138
- });
454
+ log("Supported: Next.js, Vite + React, React (CRA)");
1139
455
  };
1140
456
 
1141
457
  switch (command) {
1142
458
  case "init":
1143
- init().catch((error) => {
1144
- logError(`Error: ${error instanceof Error ? error.message : String(error)}`);
1145
- process.exit(1);
1146
- });
1147
- break;
1148
- case "export":
1149
- exportCommand().catch((error) => {
1150
- logError(`Error: ${error instanceof Error ? error.message : String(error)}`);
459
+ init().catch(err => {
460
+ logError(`Error: ${err.message}`);
1151
461
  process.exit(1);
1152
462
  });
1153
463
  break;
1154
- case "list":
1155
- listThemes();
1156
- break;
1157
- case "help":
1158
- case "--help":
1159
- case "-h":
1160
464
  default:
1161
465
  usage();
1162
- break;
1163
466
  }