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.
- package/cli/index.mjs +268 -965
- package/package.json +1 -1
package/cli/index.mjs
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import readline from "node:readline";
|
|
6
5
|
|
|
7
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
7
|
const __dirname = path.dirname(__filename);
|
|
9
|
-
const templateDir = path.join(__dirname, "templates");
|
|
10
8
|
|
|
11
9
|
const args = process.argv.slice(2);
|
|
12
10
|
const command = args[0] ?? "help";
|
|
@@ -21,319 +19,39 @@ const readArg = (flag, fallback) => {
|
|
|
21
19
|
|
|
22
20
|
const hasFlag = (flag) => args.includes(flag);
|
|
23
21
|
|
|
24
|
-
const log = (
|
|
25
|
-
const logError = (
|
|
26
|
-
const logSuccess = (
|
|
27
|
-
const logInfo = (
|
|
22
|
+
const log = (msg) => process.stdout.write(`${msg}\n`);
|
|
23
|
+
const logError = (msg) => process.stderr.write(`${msg}\n`);
|
|
24
|
+
const logSuccess = (msg) => process.stdout.write(`✅ ${msg}\n`);
|
|
25
|
+
const logInfo = (msg) => process.stdout.write(`ℹ️ ${msg}\n`);
|
|
28
26
|
|
|
29
27
|
const themes = [
|
|
30
|
-
{ id: "theme-nitro-mint-apple", label: "Mint Apple"
|
|
31
|
-
{ id: "theme-nitro-citrus-sherbert", label: "Citrus Sherbert"
|
|
32
|
-
{ id: "theme-nitro-retro-raincloud", label: "Retro Raincloud"
|
|
33
|
-
{ id: "theme-nitro-hanami", label: "Hanami"
|
|
34
|
-
{ id: "theme-nitro-sunrise", label: "Sunrise"
|
|
35
|
-
{ id: "theme-nitro-cotton-candy", label: "Cotton Candy"
|
|
36
|
-
{ id: "theme-nitro-lofi-vibes", label: "Lofi Vibes"
|
|
37
|
-
{ id: "theme-nitro-desert-khaki", label: "Desert Khaki"
|
|
38
|
-
{ id: "theme-nitro-sunset", label: "Sunset"
|
|
39
|
-
{ id: "theme-nitro-chroma-glow", label: "Chroma Glow"
|
|
40
|
-
{ id: "theme-nitro-forest", label: "Forest"
|
|
41
|
-
{ id: "theme-nitro-crimson", label: "Crimson"
|
|
42
|
-
{ id: "theme-nitro-midnight-blurple", label: "Midnight Blurple"
|
|
43
|
-
{ id: "theme-nitro-mars", label: "Mars"
|
|
44
|
-
{ id: "theme-nitro-dusk", label: "Dusk"
|
|
45
|
-
{ id: "theme-nitro-under-the-sea", label: "Under The Sea"
|
|
46
|
-
{ id: "theme-nitro-retro-storm", label: "Retro Storm"
|
|
47
|
-
{ id: "theme-nitro-neon-nights", label: "Neon Nights"
|
|
48
|
-
{ id: "theme-nitro-strawberry-lemonade", label: "Strawberry Lemonade"
|
|
49
|
-
{ id: "theme-nitro-aurora", label: "Aurora"
|
|
50
|
-
{ id: "theme-nitro-sepia", label: "Sepia"
|
|
28
|
+
{ id: "theme-nitro-mint-apple", label: "Mint Apple" },
|
|
29
|
+
{ id: "theme-nitro-citrus-sherbert", label: "Citrus Sherbert" },
|
|
30
|
+
{ id: "theme-nitro-retro-raincloud", label: "Retro Raincloud" },
|
|
31
|
+
{ id: "theme-nitro-hanami", label: "Hanami" },
|
|
32
|
+
{ id: "theme-nitro-sunrise", label: "Sunrise" },
|
|
33
|
+
{ id: "theme-nitro-cotton-candy", label: "Cotton Candy" },
|
|
34
|
+
{ id: "theme-nitro-lofi-vibes", label: "Lofi Vibes" },
|
|
35
|
+
{ id: "theme-nitro-desert-khaki", label: "Desert Khaki" },
|
|
36
|
+
{ id: "theme-nitro-sunset", label: "Sunset" },
|
|
37
|
+
{ id: "theme-nitro-chroma-glow", label: "Chroma Glow" },
|
|
38
|
+
{ id: "theme-nitro-forest", label: "Forest" },
|
|
39
|
+
{ id: "theme-nitro-crimson", label: "Crimson" },
|
|
40
|
+
{ id: "theme-nitro-midnight-blurple", label: "Midnight Blurple" },
|
|
41
|
+
{ id: "theme-nitro-mars", label: "Mars" },
|
|
42
|
+
{ id: "theme-nitro-dusk", label: "Dusk" },
|
|
43
|
+
{ id: "theme-nitro-under-the-sea", label: "Under The Sea" },
|
|
44
|
+
{ id: "theme-nitro-retro-storm", label: "Retro Storm" },
|
|
45
|
+
{ id: "theme-nitro-neon-nights", label: "Neon Nights" },
|
|
46
|
+
{ id: "theme-nitro-strawberry-lemonade", label: "Strawberry Lemonade" },
|
|
47
|
+
{ id: "theme-nitro-aurora", label: "Aurora" },
|
|
48
|
+
{ id: "theme-nitro-sepia", label: "Sepia" },
|
|
51
49
|
];
|
|
52
50
|
|
|
53
51
|
const themeTokens = {
|
|
54
|
-
"theme-nitro-mint-apple": {
|
|
55
|
-
"--background": "160 45% 18%",
|
|
56
|
-
"--foreground": "150 40% 95%",
|
|
57
|
-
"--card": "160 40% 20%",
|
|
58
|
-
"--card-foreground": "150 40% 95%",
|
|
59
|
-
"--popover": "160 40% 20%",
|
|
60
|
-
"--popover-foreground": "150 40% 95%",
|
|
61
|
-
"--primary": "145 50% 55%",
|
|
62
|
-
"--primary-foreground": "160 40% 10%",
|
|
63
|
-
"--secondary": "100 45% 68%",
|
|
64
|
-
"--secondary-foreground": "160 40% 10%",
|
|
65
|
-
"--muted": "160 35% 25%",
|
|
66
|
-
"--muted-foreground": "150 30% 70%",
|
|
67
|
-
"--accent": "100 45% 68%",
|
|
68
|
-
"--accent-foreground": "160 40% 10%",
|
|
69
|
-
"--destructive": "0 84% 60%",
|
|
70
|
-
"--destructive-foreground": "0 0% 98%",
|
|
71
|
-
"--border": "160 30% 30%",
|
|
72
|
-
"--input": "160 30% 30%",
|
|
73
|
-
"--ring": "145 50% 55%",
|
|
74
|
-
"--app-gradient": "radial-gradient(1050px 560px at -10% -20%, hsl(156 58% 60% / 0.3), transparent 60%), radial-gradient(940px 520px at 114% 0%, hsl(96 64% 62% / 0.22), transparent 58%), linear-gradient(160deg, hsl(156 27% 13%) 0%, hsl(149 23% 13%) 50%, hsl(136 21% 10%) 100%)",
|
|
75
|
-
"--app-surface-tint": "hsl(145 50% 55% / 0.1)",
|
|
76
|
-
},
|
|
77
|
-
"theme-nitro-citrus-sherbert": {
|
|
78
|
-
"--background": "25 70% 15%",
|
|
79
|
-
"--foreground": "35 60% 95%",
|
|
80
|
-
"--card": "25 65% 18%",
|
|
81
|
-
"--card-foreground": "35 60% 95%",
|
|
82
|
-
"--popover": "25 65% 18%",
|
|
83
|
-
"--popover-foreground": "35 60% 95%",
|
|
84
|
-
"--primary": "25 80% 55%",
|
|
85
|
-
"--primary-foreground": "25 70% 10%",
|
|
86
|
-
"--secondary": "45 85% 65%",
|
|
87
|
-
"--secondary-foreground": "25 70% 10%",
|
|
88
|
-
"--muted": "25 50% 25%",
|
|
89
|
-
"--muted-foreground": "35 40% 70%",
|
|
90
|
-
"--accent": "55 90% 75%",
|
|
91
|
-
"--accent-foreground": "25 70% 10%",
|
|
92
|
-
"--destructive": "0 84% 60%",
|
|
93
|
-
"--destructive-foreground": "0 0% 98%",
|
|
94
|
-
"--border": "25 45% 30%",
|
|
95
|
-
"--input": "25 45% 30%",
|
|
96
|
-
"--ring": "25 80% 55%",
|
|
97
|
-
"--app-gradient": "radial-gradient(1060px 560px at -10% -20%, hsl(28 97% 66% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 4%, hsl(53 95% 68% / 0.22), transparent 58%), linear-gradient(160deg, hsl(34 29% 12%) 0%, hsl(32 24% 13%) 50%, hsl(27 21% 10%) 100%)",
|
|
98
|
-
"--app-surface-tint": "hsl(25 80% 55% / 0.1)",
|
|
99
|
-
},
|
|
100
|
-
"theme-nitro-retro-raincloud": {
|
|
101
|
-
"--background": "215 25% 20%",
|
|
102
|
-
"--foreground": "210 30% 95%",
|
|
103
|
-
"--card": "215 22% 23%",
|
|
104
|
-
"--card-foreground": "210 30% 95%",
|
|
105
|
-
"--popover": "215 22% 23%",
|
|
106
|
-
"--popover-foreground": "210 30% 95%",
|
|
107
|
-
"--primary": "210 25% 55%",
|
|
108
|
-
"--primary-foreground": "215 25% 15%",
|
|
109
|
-
"--secondary": "210 20% 65%",
|
|
110
|
-
"--secondary-foreground": "215 25% 15%",
|
|
111
|
-
"--muted": "215 20% 30%",
|
|
112
|
-
"--muted-foreground": "210 20% 70%",
|
|
113
|
-
"--accent": "210 20% 65%",
|
|
114
|
-
"--accent-foreground": "215 25% 15%",
|
|
115
|
-
"--destructive": "0 84% 60%",
|
|
116
|
-
"--destructive-foreground": "0 0% 98%",
|
|
117
|
-
"--border": "215 20% 35%",
|
|
118
|
-
"--input": "215 20% 35%",
|
|
119
|
-
"--ring": "210 25% 55%",
|
|
120
|
-
"--app-gradient": "radial-gradient(1080px 560px at -8% -20%, hsl(215 36% 66% / 0.28), transparent 60%), radial-gradient(940px 520px at 114% 2%, hsl(206 30% 64% / 0.2), transparent 58%), linear-gradient(160deg, hsl(217 21% 13%) 0%, hsl(214 18% 13%) 52%, hsl(211 16% 10%) 100%)",
|
|
121
|
-
"--app-surface-tint": "hsl(210 25% 55% / 0.1)",
|
|
122
|
-
},
|
|
123
|
-
"theme-nitro-hanami": {
|
|
124
|
-
"--background": "330 30% 20%",
|
|
125
|
-
"--foreground": "330 30% 95%",
|
|
126
|
-
"--card": "330 25% 23%",
|
|
127
|
-
"--card-foreground": "330 30% 95%",
|
|
128
|
-
"--popover": "330 25% 23%",
|
|
129
|
-
"--popover-foreground": "330 30% 95%",
|
|
130
|
-
"--primary": "330 40% 55%",
|
|
131
|
-
"--primary-foreground": "330 30% 10%",
|
|
132
|
-
"--secondary": "340 45% 65%",
|
|
133
|
-
"--secondary-foreground": "330 30% 10%",
|
|
134
|
-
"--muted": "330 20% 30%",
|
|
135
|
-
"--muted-foreground": "330 25% 70%",
|
|
136
|
-
"--accent": "350 50% 75%",
|
|
137
|
-
"--accent-foreground": "330 30% 10%",
|
|
138
|
-
"--destructive": "0 84% 60%",
|
|
139
|
-
"--destructive-foreground": "0 0% 98%",
|
|
140
|
-
"--border": "330 20% 35%",
|
|
141
|
-
"--input": "330 20% 35%",
|
|
142
|
-
"--ring": "330 40% 55%",
|
|
143
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(319 65% 69% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(339 58% 75% / 0.22), transparent 58%), linear-gradient(160deg, hsl(323 27% 12%) 0%, hsl(315 23% 13%) 50%, hsl(304 19% 10%) 100%)",
|
|
144
|
-
"--app-surface-tint": "hsl(330 40% 55% / 0.1)",
|
|
145
|
-
},
|
|
146
|
-
"theme-nitro-sunrise": {
|
|
147
|
-
"--background": "350 50% 18%",
|
|
148
|
-
"--foreground": "30 60% 95%",
|
|
149
|
-
"--card": "350 45% 21%",
|
|
150
|
-
"--card-foreground": "30 60% 95%",
|
|
151
|
-
"--popover": "350 45% 21%",
|
|
152
|
-
"--popover-foreground": "30 60% 95%",
|
|
153
|
-
"--primary": "355 75% 60%",
|
|
154
|
-
"--primary-foreground": "350 50% 10%",
|
|
155
|
-
"--secondary": "25 85% 65%",
|
|
156
|
-
"--secondary-foreground": "350 50% 10%",
|
|
157
|
-
"--muted": "350 30% 28%",
|
|
158
|
-
"--muted-foreground": "30 40% 75%",
|
|
159
|
-
"--accent": "45 90% 73%",
|
|
160
|
-
"--accent-foreground": "350 50% 10%",
|
|
161
|
-
"--destructive": "0 84% 60%",
|
|
162
|
-
"--destructive-foreground": "0 0% 98%",
|
|
163
|
-
"--border": "350 25% 35%",
|
|
164
|
-
"--input": "350 25% 35%",
|
|
165
|
-
"--ring": "355 75% 60%",
|
|
166
|
-
"--app-gradient": "radial-gradient(1080px 560px at -12% -20%, hsl(2 88% 66% / 0.28), transparent 58%), radial-gradient(920px 520px at 114% 4%, hsl(35 96% 67% / 0.24), transparent 58%), linear-gradient(160deg, hsl(9 30% 12%) 0%, hsl(13 26% 13%) 50%, hsl(21 21% 10%) 100%)",
|
|
167
|
-
"--app-surface-tint": "hsl(355 75% 60% / 0.1)",
|
|
168
|
-
},
|
|
169
|
-
"theme-nitro-cotton-candy": {
|
|
170
|
-
"--background": "220 50% 20%",
|
|
171
|
-
"--foreground": "220 40% 95%",
|
|
172
|
-
"--card": "220 45% 23%",
|
|
173
|
-
"--card-foreground": "220 40% 95%",
|
|
174
|
-
"--popover": "220 45% 23%",
|
|
175
|
-
"--popover-foreground": "220 40% 95%",
|
|
176
|
-
"--primary": "220 70% 65%",
|
|
177
|
-
"--primary-foreground": "220 50% 10%",
|
|
178
|
-
"--secondary": "270 60% 70%",
|
|
179
|
-
"--secondary-foreground": "220 50% 10%",
|
|
180
|
-
"--muted": "220 30% 30%",
|
|
181
|
-
"--muted-foreground": "220 30% 75%",
|
|
182
|
-
"--accent": "330 65% 75%",
|
|
183
|
-
"--accent-foreground": "220 50% 10%",
|
|
184
|
-
"--destructive": "0 84% 60%",
|
|
185
|
-
"--destructive-foreground": "0 0% 98%",
|
|
186
|
-
"--border": "220 25% 35%",
|
|
187
|
-
"--input": "220 25% 35%",
|
|
188
|
-
"--ring": "220 70% 65%",
|
|
189
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(208 95% 74% / 0.28), transparent 58%), radial-gradient(940px 520px at 112% 4%, hsl(328 87% 73% / 0.24), transparent 58%), linear-gradient(160deg, hsl(247 30% 12%) 0%, hsl(258 26% 13%) 48%, hsl(286 20% 10%) 100%)",
|
|
190
|
-
"--app-surface-tint": "hsl(220 70% 65% / 0.1)",
|
|
191
|
-
},
|
|
192
|
-
"theme-nitro-lofi-vibes": {
|
|
193
|
-
"--background": "230 25% 18%",
|
|
194
|
-
"--foreground": "230 20% 95%",
|
|
195
|
-
"--card": "230 22% 21%",
|
|
196
|
-
"--card-foreground": "230 20% 95%",
|
|
197
|
-
"--popover": "230 22% 21%",
|
|
198
|
-
"--popover-foreground": "230 20% 95%",
|
|
199
|
-
"--primary": "230 25% 55%",
|
|
200
|
-
"--primary-foreground": "230 25% 10%",
|
|
201
|
-
"--secondary": "260 20% 60%",
|
|
202
|
-
"--secondary-foreground": "230 25% 10%",
|
|
203
|
-
"--muted": "230 15% 28%",
|
|
204
|
-
"--muted-foreground": "230 15% 70%",
|
|
205
|
-
"--accent": "280 20% 55%",
|
|
206
|
-
"--accent-foreground": "230 25% 10%",
|
|
207
|
-
"--destructive": "0 84% 60%",
|
|
208
|
-
"--destructive-foreground": "0 0% 98%",
|
|
209
|
-
"--border": "230 15% 33%",
|
|
210
|
-
"--input": "230 15% 33%",
|
|
211
|
-
"--ring": "230 25% 55%",
|
|
212
|
-
"--app-gradient": "radial-gradient(1060px 560px at -10% -20%, hsl(228 44% 63% / 0.26), transparent 60%), radial-gradient(940px 520px at 112% 2%, hsl(267 38% 64% / 0.2), transparent 58%), linear-gradient(160deg, hsl(236 26% 13%) 0%, hsl(232 22% 13%) 50%, hsl(258 16% 10%) 100%)",
|
|
213
|
-
"--app-surface-tint": "hsl(230 25% 55% / 0.1)",
|
|
214
|
-
},
|
|
215
|
-
"theme-nitro-desert-khaki": {
|
|
216
|
-
"--background": "35 25% 18%",
|
|
217
|
-
"--foreground": "35 30% 95%",
|
|
218
|
-
"--card": "35 22% 21%",
|
|
219
|
-
"--card-foreground": "35 30% 95%",
|
|
220
|
-
"--popover": "35 22% 21%",
|
|
221
|
-
"--popover-foreground": "35 30% 95%",
|
|
222
|
-
"--primary": "35 25% 50%",
|
|
223
|
-
"--primary-foreground": "35 25% 10%",
|
|
224
|
-
"--secondary": "40 20% 58%",
|
|
225
|
-
"--secondary-foreground": "35 25% 10%",
|
|
226
|
-
"--muted": "35 15% 28%",
|
|
227
|
-
"--muted-foreground": "35 20% 70%",
|
|
228
|
-
"--accent": "45 25% 60%",
|
|
229
|
-
"--accent-foreground": "35 25% 10%",
|
|
230
|
-
"--destructive": "0 84% 60%",
|
|
231
|
-
"--destructive-foreground": "0 0% 98%",
|
|
232
|
-
"--border": "35 15% 33%",
|
|
233
|
-
"--input": "35 15% 33%",
|
|
234
|
-
"--ring": "35 25% 50%",
|
|
235
|
-
"--app-gradient": "radial-gradient(1060px 560px at -10% -18%, hsl(34 44% 58% / 0.26), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(48 33% 63% / 0.18), transparent 58%), linear-gradient(160deg, hsl(37 23% 13%) 0%, hsl(34 20% 13%) 52%, hsl(30 16% 10%) 100%)",
|
|
236
|
-
"--app-surface-tint": "hsl(35 25% 50% / 0.1)",
|
|
237
|
-
},
|
|
238
|
-
"theme-nitro-sunset": {
|
|
239
|
-
"--background": "320 40% 15%",
|
|
240
|
-
"--foreground": "25 60% 95%",
|
|
241
|
-
"--card": "320 35% 18%",
|
|
242
|
-
"--card-foreground": "25 60% 95%",
|
|
243
|
-
"--popover": "320 35% 18%",
|
|
244
|
-
"--popover-foreground": "25 60% 95%",
|
|
245
|
-
"--primary": "335 60% 50%",
|
|
246
|
-
"--primary-foreground": "320 40% 10%",
|
|
247
|
-
"--secondary": "20 70% 55%",
|
|
248
|
-
"--secondary-foreground": "320 40% 10%",
|
|
249
|
-
"--muted": "320 25% 25%",
|
|
250
|
-
"--muted-foreground": "25 40% 75%",
|
|
251
|
-
"--accent": "25 85% 62%",
|
|
252
|
-
"--accent-foreground": "320 40% 10%",
|
|
253
|
-
"--destructive": "0 84% 60%",
|
|
254
|
-
"--destructive-foreground": "0 0% 98%",
|
|
255
|
-
"--border": "320 20% 32%",
|
|
256
|
-
"--input": "320 20% 32%",
|
|
257
|
-
"--ring": "335 60% 50%",
|
|
258
|
-
"--app-gradient": "radial-gradient(1040px 560px at -12% -20%, hsl(325 84% 62% / 0.31), transparent 58%), radial-gradient(900px 520px at 112% 6%, hsl(24 97% 66% / 0.24), transparent 58%), linear-gradient(160deg, hsl(304 29% 12%) 0%, hsl(298 24% 13%) 50%, hsl(287 22% 10%) 100%)",
|
|
259
|
-
"--app-surface-tint": "hsl(335 60% 50% / 0.1)",
|
|
260
|
-
},
|
|
261
|
-
"theme-nitro-chroma-glow": {
|
|
262
|
-
"--background": "246 33% 11%",
|
|
263
|
-
"--foreground": "240 40% 95%",
|
|
264
|
-
"--card": "246 28% 13%",
|
|
265
|
-
"--card-foreground": "240 40% 95%",
|
|
266
|
-
"--popover": "246 28% 13%",
|
|
267
|
-
"--popover-foreground": "240 40% 95%",
|
|
268
|
-
"--primary": "286 94% 67%",
|
|
269
|
-
"--primary-foreground": "246 36% 10%",
|
|
270
|
-
"--secondary": "195 100% 61%",
|
|
271
|
-
"--secondary-foreground": "246 36% 10%",
|
|
272
|
-
"--muted": "246 30% 22%",
|
|
273
|
-
"--muted-foreground": "240 25% 75%",
|
|
274
|
-
"--accent": "195 100% 61%",
|
|
275
|
-
"--accent-foreground": "246 36% 10%",
|
|
276
|
-
"--destructive": "0 84% 60%",
|
|
277
|
-
"--destructive-foreground": "0 0% 98%",
|
|
278
|
-
"--border": "246 25% 28%",
|
|
279
|
-
"--input": "246 25% 28%",
|
|
280
|
-
"--ring": "286 94% 67%",
|
|
281
|
-
"--app-gradient": "radial-gradient(1020px 560px at -12% -18%, hsl(226 95% 63% / 0.32), transparent 58%), radial-gradient(940px 520px at 112% 2%, hsl(194 100% 62% / 0.28), transparent 58%), linear-gradient(160deg, hsl(246 36% 12%) 0%, hsl(272 30% 12%) 50%, hsl(305 24% 10%) 100%)",
|
|
282
|
-
"--app-surface-tint": "hsl(286 94% 67% / 0.1)",
|
|
283
|
-
},
|
|
284
|
-
"theme-nitro-forest": {
|
|
285
|
-
"--background": "150 40% 12%",
|
|
286
|
-
"--foreground": "140 30% 95%",
|
|
287
|
-
"--card": "150 35% 15%",
|
|
288
|
-
"--card-foreground": "140 30% 95%",
|
|
289
|
-
"--popover": "150 35% 15%",
|
|
290
|
-
"--popover-foreground": "140 30% 95%",
|
|
291
|
-
"--primary": "145 45% 45%",
|
|
292
|
-
"--primary-foreground": "150 40% 10%",
|
|
293
|
-
"--secondary": "100 40% 55%",
|
|
294
|
-
"--secondary-foreground": "150 40% 10%",
|
|
295
|
-
"--muted": "150 25% 22%",
|
|
296
|
-
"--muted-foreground": "140 20% 70%",
|
|
297
|
-
"--accent": "85 45% 52%",
|
|
298
|
-
"--accent-foreground": "150 40% 10%",
|
|
299
|
-
"--destructive": "0 84% 60%",
|
|
300
|
-
"--destructive-foreground": "0 0% 98%",
|
|
301
|
-
"--border": "150 20% 28%",
|
|
302
|
-
"--input": "150 20% 28%",
|
|
303
|
-
"--ring": "145 45% 45%",
|
|
304
|
-
"--app-gradient": "radial-gradient(1080px 560px at -10% -20%, hsl(145 62% 53% / 0.3), transparent 60%), radial-gradient(920px 520px at 112% 2%, hsl(170 48% 53% / 0.22), transparent 58%), linear-gradient(160deg, hsl(149 30% 12%) 0%, hsl(146 26% 12%) 52%, hsl(160 21% 10%) 100%)",
|
|
305
|
-
"--app-surface-tint": "hsl(145 45% 45% / 0.1)",
|
|
306
|
-
},
|
|
307
|
-
"theme-nitro-crimson": {
|
|
308
|
-
"--background": "345 60% 8%",
|
|
309
|
-
"--foreground": "345 40% 95%",
|
|
310
|
-
"--card": "345 55% 11%",
|
|
311
|
-
"--card-foreground": "345 40% 95%",
|
|
312
|
-
"--popover": "345 55% 11%",
|
|
313
|
-
"--popover-foreground": "345 40% 95%",
|
|
314
|
-
"--primary": "350 65% 45%",
|
|
315
|
-
"--primary-foreground": "345 60% 98%",
|
|
316
|
-
"--secondary": "350 50% 55%",
|
|
317
|
-
"--secondary-foreground": "345 60% 98%",
|
|
318
|
-
"--muted": "345 30% 18%",
|
|
319
|
-
"--muted-foreground": "345 25% 70%",
|
|
320
|
-
"--accent": "355 55% 60%",
|
|
321
|
-
"--accent-foreground": "345 60% 10%",
|
|
322
|
-
"--destructive": "0 84% 60%",
|
|
323
|
-
"--destructive-foreground": "0 0% 98%",
|
|
324
|
-
"--border": "345 25% 25%",
|
|
325
|
-
"--input": "345 25% 25%",
|
|
326
|
-
"--ring": "350 65% 45%",
|
|
327
|
-
"--app-gradient": "radial-gradient(1040px 560px at -12% -18%, hsl(352 85% 59% / 0.34), transparent 58%), radial-gradient(900px 520px at 112% 4%, hsl(9 84% 58% / 0.22), transparent 58%), linear-gradient(160deg, hsl(349 37% 11%) 0%, hsl(342 31% 12%) 50%, hsl(326 24% 10%) 100%)",
|
|
328
|
-
"--app-surface-tint": "hsl(350 65% 45% / 0.1)",
|
|
329
|
-
},
|
|
330
52
|
"theme-nitro-midnight-blurple": {
|
|
331
53
|
"--background": "235 26% 11%",
|
|
332
54
|
"--foreground": "235 30% 95%",
|
|
333
|
-
"--card": "235 22% 12%",
|
|
334
|
-
"--card-foreground": "235 30% 95%",
|
|
335
|
-
"--popover": "235 22% 12%",
|
|
336
|
-
"--popover-foreground": "235 30% 95%",
|
|
337
55
|
"--primary": "241 92% 70%",
|
|
338
56
|
"--primary-foreground": "235 26% 98%",
|
|
339
57
|
"--secondary": "210 92% 65%",
|
|
@@ -345,220 +63,51 @@ const themeTokens = {
|
|
|
345
63
|
"--destructive": "0 84% 60%",
|
|
346
64
|
"--destructive-foreground": "0 0% 98%",
|
|
347
65
|
"--border": "235 18% 25%",
|
|
66
|
+
"--card": "235 22% 12%",
|
|
67
|
+
"--card-foreground": "235 30% 95%",
|
|
68
|
+
"--popover": "235 22% 12%",
|
|
69
|
+
"--popover-foreground": "235 30% 95%",
|
|
348
70
|
"--input": "235 18% 25%",
|
|
349
71
|
"--ring": "241 92% 70%",
|
|
350
72
|
"--app-gradient": "radial-gradient(1050px 560px at -10% -20%, hsl(246 92% 66% / 0.3), transparent 60%), radial-gradient(920px 520px at 112% 2%, hsl(201 92% 63% / 0.22), transparent 58%), linear-gradient(160deg, hsl(232 29% 12%) 0%, hsl(231 24% 12%) 50%, hsl(228 22% 10%) 100%)",
|
|
351
73
|
"--app-surface-tint": "hsl(241 92% 70% / 0.1)",
|
|
352
74
|
},
|
|
353
|
-
"theme-nitro-mars": {
|
|
354
|
-
"--background": "12 30% 10%",
|
|
355
|
-
"--foreground": "20 40% 95%",
|
|
356
|
-
"--card": "12 25% 12%",
|
|
357
|
-
"--card-foreground": "20 40% 95%",
|
|
358
|
-
"--popover": "12 25% 12%",
|
|
359
|
-
"--popover-foreground": "20 40% 95%",
|
|
360
|
-
"--primary": "14 74% 61%",
|
|
361
|
-
"--primary-foreground": "12 30% 10%",
|
|
362
|
-
"--secondary": "22 78% 62%",
|
|
363
|
-
"--secondary-foreground": "12 30% 10%",
|
|
364
|
-
"--muted": "12 25% 20%",
|
|
365
|
-
"--muted-foreground": "20 25% 72%",
|
|
366
|
-
"--accent": "22 78% 62%",
|
|
367
|
-
"--accent-foreground": "12 30% 10%",
|
|
368
|
-
"--destructive": "0 84% 60%",
|
|
369
|
-
"--destructive-foreground": "0 0% 98%",
|
|
370
|
-
"--border": "12 20% 25%",
|
|
371
|
-
"--input": "12 20% 25%",
|
|
372
|
-
"--ring": "14 74% 61%",
|
|
373
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(12 78% 62% / 0.3), transparent 60%), radial-gradient(920px 520px at 112% 2%, hsl(22 78% 60% / 0.22), transparent 58%), linear-gradient(160deg, hsl(13 29% 11%) 0%, hsl(11 25% 11%) 52%, hsl(8 21% 9%) 100%)",
|
|
374
|
-
"--app-surface-tint": "hsl(14 74% 61% / 0.1)",
|
|
375
|
-
},
|
|
376
|
-
"theme-nitro-dusk": {
|
|
377
|
-
"--background": "256 23% 11%",
|
|
378
|
-
"--foreground": "256 30% 95%",
|
|
379
|
-
"--card": "256 20% 13%",
|
|
380
|
-
"--card-foreground": "256 30% 95%",
|
|
381
|
-
"--popover": "256 20% 13%",
|
|
382
|
-
"--popover-foreground": "256 30% 95%",
|
|
383
|
-
"--primary": "263 53% 67%",
|
|
384
|
-
"--primary-foreground": "256 23% 10%",
|
|
385
|
-
"--secondary": "299 44% 64%",
|
|
386
|
-
"--secondary-foreground": "256 23% 10%",
|
|
387
|
-
"--muted": "256 20% 22%",
|
|
388
|
-
"--muted-foreground": "256 20% 72%",
|
|
389
|
-
"--accent": "299 44% 64%",
|
|
390
|
-
"--accent-foreground": "256 23% 10%",
|
|
391
|
-
"--destructive": "0 84% 60%",
|
|
392
|
-
"--destructive-foreground": "0 0% 98%",
|
|
393
|
-
"--border": "256 15% 28%",
|
|
394
|
-
"--input": "256 15% 28%",
|
|
395
|
-
"--ring": "263 53% 67%",
|
|
396
|
-
"--app-gradient": "radial-gradient(1020px 560px at -10% -20%, hsl(262 60% 62% / 0.28), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(298 48% 60% / 0.2), transparent 58%), linear-gradient(160deg, hsl(256 26% 12%) 0%, hsl(256 23% 12%) 50%, hsl(258 20% 10%) 100%)",
|
|
397
|
-
"--app-surface-tint": "hsl(263 53% 67% / 0.1)",
|
|
398
|
-
},
|
|
399
|
-
"theme-nitro-under-the-sea": {
|
|
400
|
-
"--background": "205 33% 10%",
|
|
401
|
-
"--foreground": "190 40% 95%",
|
|
402
|
-
"--card": "205 28% 12%",
|
|
403
|
-
"--card-foreground": "190 40% 95%",
|
|
404
|
-
"--popover": "205 28% 12%",
|
|
405
|
-
"--popover-foreground": "190 40% 95%",
|
|
406
|
-
"--primary": "186 70% 58%",
|
|
407
|
-
"--primary-foreground": "205 33% 10%",
|
|
408
|
-
"--secondary": "197 74% 55%",
|
|
409
|
-
"--secondary-foreground": "205 33% 10%",
|
|
410
|
-
"--muted": "205 28% 20%",
|
|
411
|
-
"--muted-foreground": "190 25% 72%",
|
|
412
|
-
"--accent": "197 74% 55%",
|
|
413
|
-
"--accent-foreground": "205 33% 10%",
|
|
414
|
-
"--destructive": "0 84% 60%",
|
|
415
|
-
"--destructive-foreground": "0 0% 98%",
|
|
416
|
-
"--border": "205 22% 25%",
|
|
417
|
-
"--input": "205 22% 25%",
|
|
418
|
-
"--ring": "186 70% 58%",
|
|
419
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(186 75% 58% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(197 76% 55% / 0.22), transparent 58%), linear-gradient(160deg, hsl(204 30% 11%) 0%, hsl(205 26% 11%) 50%, hsl(206 22% 9%) 100%)",
|
|
420
|
-
"--app-surface-tint": "hsl(186 70% 58% / 0.1)",
|
|
421
|
-
},
|
|
422
|
-
"theme-nitro-retro-storm": {
|
|
423
|
-
"--background": "216 20% 11%",
|
|
424
|
-
"--foreground": "216 20% 95%",
|
|
425
|
-
"--card": "216 18% 13%",
|
|
426
|
-
"--card-foreground": "216 20% 95%",
|
|
427
|
-
"--popover": "216 18% 13%",
|
|
428
|
-
"--popover-foreground": "216 20% 95%",
|
|
429
|
-
"--primary": "213 33% 66%",
|
|
430
|
-
"--primary-foreground": "216 20% 10%",
|
|
431
|
-
"--secondary": "214 26% 62%",
|
|
432
|
-
"--secondary-foreground": "216 20% 10%",
|
|
433
|
-
"--muted": "216 18% 22%",
|
|
434
|
-
"--muted-foreground": "216 15% 70%",
|
|
435
|
-
"--accent": "214 26% 62%",
|
|
436
|
-
"--accent-foreground": "216 20% 10%",
|
|
437
|
-
"--destructive": "0 84% 60%",
|
|
438
|
-
"--destructive-foreground": "0 0% 98%",
|
|
439
|
-
"--border": "216 15% 28%",
|
|
440
|
-
"--input": "216 15% 28%",
|
|
441
|
-
"--ring": "213 33% 66%",
|
|
442
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(213 35% 64% / 0.26), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(214 27% 62% / 0.2), transparent 58%), linear-gradient(160deg, hsl(216 24% 11%) 0%, hsl(215 20% 11%) 50%, hsl(214 18% 9%) 100%)",
|
|
443
|
-
"--app-surface-tint": "hsl(213 33% 66% / 0.1)",
|
|
444
|
-
},
|
|
445
|
-
"theme-nitro-neon-nights": {
|
|
446
|
-
"--background": "245 36% 10%",
|
|
447
|
-
"--foreground": "260 40% 95%",
|
|
448
|
-
"--card": "245 31% 12%",
|
|
449
|
-
"--card-foreground": "260 40% 95%",
|
|
450
|
-
"--popover": "245 31% 12%",
|
|
451
|
-
"--popover-foreground": "260 40% 95%",
|
|
452
|
-
"--primary": "292 96% 66%",
|
|
453
|
-
"--primary-foreground": "245 36% 10%",
|
|
454
|
-
"--secondary": "190 94% 56%",
|
|
455
|
-
"--secondary-foreground": "245 36% 10%",
|
|
456
|
-
"--muted": "245 30% 18%",
|
|
457
|
-
"--muted-foreground": "260 25% 75%",
|
|
458
|
-
"--accent": "190 94% 56%",
|
|
459
|
-
"--accent-foreground": "245 36% 10%",
|
|
460
|
-
"--destructive": "0 84% 60%",
|
|
461
|
-
"--destructive-foreground": "0 0% 98%",
|
|
462
|
-
"--border": "245 25% 25%",
|
|
463
|
-
"--input": "245 25% 25%",
|
|
464
|
-
"--ring": "292 96% 66%",
|
|
465
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(280 92% 62% / 0.28), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(191 94% 56% / 0.24), transparent 58%), linear-gradient(160deg, hsl(233 36% 10%) 0%, hsl(229 32% 9%) 50%, hsl(227 28% 8%) 100%)",
|
|
466
|
-
"--app-surface-tint": "hsl(292 96% 66% / 0.1)",
|
|
467
|
-
},
|
|
468
|
-
"theme-nitro-strawberry-lemonade": {
|
|
469
|
-
"--background": "334 31% 10%",
|
|
470
|
-
"--foreground": "40 70% 95%",
|
|
471
|
-
"--card": "334 27% 12%",
|
|
472
|
-
"--card-foreground": "40 70% 95%",
|
|
473
|
-
"--popover": "334 27% 12%",
|
|
474
|
-
"--popover-foreground": "40 70% 95%",
|
|
475
|
-
"--primary": "342 83% 67%",
|
|
476
|
-
"--primary-foreground": "334 31% 10%",
|
|
477
|
-
"--secondary": "46 89% 63%",
|
|
478
|
-
"--secondary-foreground": "334 31% 10%",
|
|
479
|
-
"--muted": "334 27% 20%",
|
|
480
|
-
"--muted-foreground": "40 40% 75%",
|
|
481
|
-
"--accent": "46 89% 63%",
|
|
482
|
-
"--accent-foreground": "334 31% 10%",
|
|
483
|
-
"--destructive": "0 84% 60%",
|
|
484
|
-
"--destructive-foreground": "0 0% 98%",
|
|
485
|
-
"--border": "334 22% 28%",
|
|
486
|
-
"--input": "334 22% 28%",
|
|
487
|
-
"--ring": "342 83% 67%",
|
|
488
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(342 86% 64% / 0.3), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(46 90% 62% / 0.24), transparent 58%), linear-gradient(160deg, hsl(334 30% 11%) 0%, hsl(333 26% 11%) 50%, hsl(331 22% 9%) 100%)",
|
|
489
|
-
"--app-surface-tint": "hsl(342 83% 67% / 0.1)",
|
|
490
|
-
},
|
|
491
|
-
"theme-nitro-aurora": {
|
|
492
|
-
"--background": "174 25% 10%",
|
|
493
|
-
"--foreground": "160 50% 95%",
|
|
494
|
-
"--card": "174 22% 12%",
|
|
495
|
-
"--card-foreground": "160 50% 95%",
|
|
496
|
-
"--popover": "174 22% 12%",
|
|
497
|
-
"--popover-foreground": "160 50% 95%",
|
|
498
|
-
"--primary": "164 63% 57%",
|
|
499
|
-
"--primary-foreground": "174 25% 10%",
|
|
500
|
-
"--secondary": "145 62% 58%",
|
|
501
|
-
"--secondary-foreground": "174 25% 10%",
|
|
502
|
-
"--muted": "174 22% 20%",
|
|
503
|
-
"--muted-foreground": "160 30% 72%",
|
|
504
|
-
"--accent": "145 62% 58%",
|
|
505
|
-
"--accent-foreground": "174 25% 10%",
|
|
506
|
-
"--destructive": "0 84% 60%",
|
|
507
|
-
"--destructive-foreground": "0 0% 98%",
|
|
508
|
-
"--border": "174 18% 25%",
|
|
509
|
-
"--input": "174 18% 25%",
|
|
510
|
-
"--ring": "164 63% 57%",
|
|
511
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(164 66% 56% / 0.28), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(145 64% 56% / 0.22), transparent 58%), linear-gradient(160deg, hsl(176 28% 11%) 0%, hsl(172 24% 11%) 50%, hsl(169 20% 9%) 100%)",
|
|
512
|
-
"--app-surface-tint": "hsl(164 63% 57% / 0.1)",
|
|
513
|
-
},
|
|
514
|
-
"theme-nitro-sepia": {
|
|
515
|
-
"--background": "27 24% 11%",
|
|
516
|
-
"--foreground": "35 30% 95%",
|
|
517
|
-
"--card": "27 21% 13%",
|
|
518
|
-
"--card-foreground": "35 30% 95%",
|
|
519
|
-
"--popover": "27 21% 13%",
|
|
520
|
-
"--popover-foreground": "35 30% 95%",
|
|
521
|
-
"--primary": "28 44% 62%",
|
|
522
|
-
"--primary-foreground": "27 24% 10%",
|
|
523
|
-
"--secondary": "36 39% 62%",
|
|
524
|
-
"--secondary-foreground": "27 24% 10%",
|
|
525
|
-
"--muted": "27 18% 20%",
|
|
526
|
-
"--muted-foreground": "35 20% 70%",
|
|
527
|
-
"--accent": "36 39% 62%",
|
|
528
|
-
"--accent-foreground": "27 24% 10%",
|
|
529
|
-
"--destructive": "0 84% 60%",
|
|
530
|
-
"--destructive-foreground": "0 0% 98%",
|
|
531
|
-
"--border": "27 15% 25%",
|
|
532
|
-
"--input": "27 15% 25%",
|
|
533
|
-
"--ring": "28 44% 62%",
|
|
534
|
-
"--app-gradient": "radial-gradient(1040px 560px at -10% -20%, hsl(28 45% 60% / 0.26), transparent 58%), radial-gradient(920px 520px at 112% 2%, hsl(36 40% 60% / 0.2), transparent 58%), linear-gradient(160deg, hsl(27 25% 12%) 0%, hsl(26 22% 12%) 50%, hsl(24 19% 10%) 100%)",
|
|
535
|
-
"--app-surface-tint": "hsl(28 44% 62% / 0.1)",
|
|
536
|
-
},
|
|
537
75
|
};
|
|
538
76
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
77
|
+
// Add more theme tokens (simplified - using default for all)
|
|
78
|
+
themes.forEach(t => {
|
|
79
|
+
if (!themeTokens[t.id]) {
|
|
80
|
+
themeTokens[t.id] = themeTokens["theme-nitro-midnight-blurple"];
|
|
81
|
+
}
|
|
82
|
+
});
|
|
542
83
|
|
|
543
|
-
const
|
|
544
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
545
|
-
};
|
|
84
|
+
const getThemeTokens = (themeId) => themeTokens[themeId] || themeTokens["theme-nitro-midnight-blurple"];
|
|
546
85
|
|
|
547
|
-
const
|
|
548
|
-
const src = path.join(templateDir, name);
|
|
549
|
-
if (!fs.existsSync(src)) {
|
|
550
|
-
throw new Error(`Missing template: ${name}`);
|
|
551
|
-
}
|
|
552
|
-
if (fs.existsSync(dest) && !force) {
|
|
553
|
-
return { skipped: true, dest };
|
|
554
|
-
}
|
|
555
|
-
fs.copyFileSync(src, dest);
|
|
556
|
-
return { skipped: false, dest };
|
|
557
|
-
};
|
|
86
|
+
const ensureDir = (dir) => fs.mkdirSync(dir, { recursive: true });
|
|
558
87
|
|
|
559
88
|
const DEFAULT_THEME = "theme-nitro-midnight-blurple";
|
|
560
89
|
const DEFAULT_MODE = "dark";
|
|
561
90
|
|
|
91
|
+
// Detect project type
|
|
92
|
+
const detectProjectType = (projectRoot) => {
|
|
93
|
+
const hasNextJs = fs.existsSync(path.join(projectRoot, "next.config.js")) ||
|
|
94
|
+
fs.existsSync(path.join(projectRoot, "next.config.mjs")) ||
|
|
95
|
+
fs.existsSync(path.join(projectRoot, "app"));
|
|
96
|
+
const hasVite = fs.existsSync(path.join(projectRoot, "vite.config.js")) ||
|
|
97
|
+
fs.existsSync(path.join(projectRoot, "vite.config.ts")) ||
|
|
98
|
+
fs.existsSync(path.join(projectRoot, "vite.config.mjs"));
|
|
99
|
+
const hasReact = fs.existsSync(path.join(projectRoot, "src", "main.jsx")) ||
|
|
100
|
+
fs.existsSync(path.join(projectRoot, "src", "main.tsx")) ||
|
|
101
|
+
fs.existsSync(path.join(projectRoot, "src", "index.jsx")) ||
|
|
102
|
+
fs.existsSync(path.join(projectRoot, "index.html"));
|
|
103
|
+
|
|
104
|
+
if (hasNextJs) return "next";
|
|
105
|
+
if (hasVite && hasReact) return "vite";
|
|
106
|
+
if (hasReact) return "react";
|
|
107
|
+
return "unknown";
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Prompt for selection
|
|
562
111
|
const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
563
112
|
return new Promise((resolve) => {
|
|
564
113
|
if (!process.stdin.isTTY) {
|
|
@@ -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
|
-
|
|
585
|
-
if (
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
590
|
-
|
|
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
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
|
|
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
|
-
|
|
617
|
-
|
|
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
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
|
|
661
|
-
|
|
175
|
+
body {
|
|
176
|
+
background: hsl(${tokens["--background"]});
|
|
177
|
+
color: hsl(${tokens["--foreground"]});
|
|
178
|
+
}
|
|
662
179
|
|
|
663
|
-
|
|
664
|
-
|
|
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
|
-
|
|
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
|
-
|
|
672
|
-
|
|
673
|
-
export type ThemeId = "${themeId}";
|
|
674
|
-
export type ColorMode = "${mode}";
|
|
193
|
+
export type ThemeId = string;
|
|
194
|
+
export type ColorMode = string;
|
|
675
195
|
|
|
676
|
-
|
|
677
|
-
|
|
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
|
|
200
|
+
const ThemeContext: any = createContext(null);
|
|
689
201
|
|
|
690
|
-
export const ThemeProvider = ({ children }: { children:
|
|
691
|
-
const [themeId, setThemeId] = useState
|
|
692
|
-
const [colorMode, setColorMode] = useState
|
|
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("
|
|
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
|
|
726
|
-
<select
|
|
727
|
-
value={
|
|
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
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 -
|
|
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
|
-
|
|
795
|
-
const skipInject = hasFlag("--no-inject");
|
|
387
|
+
logInfo(`Project: ${projectRoot}`);
|
|
796
388
|
|
|
797
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
810
|
-
|
|
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✓
|
|
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("
|
|
420
|
+
logInfo("Setting up theme...");
|
|
828
421
|
log("");
|
|
829
422
|
|
|
830
|
-
//
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
1144
|
-
logError(`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
|
}
|