gradient-forge 1.0.4 → 1.0.7
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 +81 -23
- package/package.json +1 -1
package/cli/index.mjs
CHANGED
|
@@ -164,23 +164,61 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
|
164
164
|
});
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
-
// Generate CSS
|
|
167
|
+
// Generate CSS - with !important to override tailwind defaults
|
|
168
168
|
const generateCSS = (themeId, mode) => {
|
|
169
169
|
const tokens = getThemeTokens(themeId);
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
const isDark = mode === "dark";
|
|
171
|
+
|
|
172
|
+
return `/* Gradient Forge Theme - ${mode} */
|
|
173
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
174
|
+
|
|
175
|
+
:root {
|
|
176
|
+
--font-sans: 'Inter', sans-serif;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Theme CSS Variables */
|
|
180
|
+
.${themeId},
|
|
181
|
+
.${themeId} body {
|
|
172
182
|
${Object.entries(tokens).map(([k, v]) => ` ${k}: ${v};`).join("\n")}
|
|
173
183
|
}
|
|
174
184
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
/* Force theme override with !important */
|
|
186
|
+
.${themeId} {
|
|
187
|
+
--background: ${tokens["--background"]} !important;
|
|
188
|
+
--foreground: ${tokens["--foreground"]} !important;
|
|
189
|
+
--primary: ${tokens["--primary"]} !important;
|
|
190
|
+
--primary-foreground: ${tokens["--primary-foreground"]} !important;
|
|
191
|
+
--secondary: ${tokens["--secondary"]} !important;
|
|
192
|
+
--secondary-foreground: ${tokens["--secondary-foreground"]} !important;
|
|
193
|
+
--muted: ${tokens["--muted"]} !important;
|
|
194
|
+
--muted-foreground: ${tokens["--muted-foreground"]} !important;
|
|
195
|
+
--accent: ${tokens["--accent"]} !important;
|
|
196
|
+
--accent-foreground: ${tokens["--accent-foreground"]} !important;
|
|
197
|
+
--destructive: ${tokens["--destructive"]} !important;
|
|
198
|
+
--destructive-foreground: ${tokens["--destructive-foreground"]} !important;
|
|
199
|
+
--border: ${tokens["--border"]} !important;
|
|
200
|
+
--card: ${tokens["--card"]} !important;
|
|
201
|
+
--card-foreground: ${tokens["--card-foreground"]} !important;
|
|
202
|
+
--popover: ${tokens["--popover"]} !important;
|
|
203
|
+
--popover-foreground: ${tokens["--popover-foreground"]} !important;
|
|
204
|
+
--input: ${tokens["--input"]} !important;
|
|
205
|
+
--ring: ${tokens["--ring"]} !important;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* Apply gradient background */
|
|
209
|
+
.${themeId} body {
|
|
210
|
+
background: linear-gradient(160deg, hsl(${tokens["--background"]}) 0%, hsl(${tokens["--background"]}) 50%, hsl(${tokens["--background"]}) 100%);
|
|
211
|
+
background-attachment: fixed;
|
|
178
212
|
}
|
|
179
213
|
|
|
180
|
-
|
|
181
|
-
|
|
214
|
+
/* Surface layers with glassmorphism */
|
|
215
|
+
.${themeId} .bg-card,
|
|
216
|
+
.${themeId} .bg-popover,
|
|
217
|
+
.${themeId} .bg-sidebar {
|
|
218
|
+
background-color: hsl(${tokens["--card"]} / 0.4) !important;
|
|
182
219
|
background-image: linear-gradient(${tokens["--app-surface-tint"]}, ${tokens["--app-surface-tint"]});
|
|
183
220
|
backdrop-filter: blur(16px);
|
|
221
|
+
border: 1px solid hsl(${tokens["--border"]} / 0.3);
|
|
184
222
|
}
|
|
185
223
|
`;
|
|
186
224
|
};
|
|
@@ -188,18 +226,26 @@ body {
|
|
|
188
226
|
// Generate theme context
|
|
189
227
|
const generateContext = (themeId, mode) => {
|
|
190
228
|
return `"use client";
|
|
191
|
-
import
|
|
229
|
+
import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
|
|
192
230
|
|
|
193
231
|
export type ThemeId = string;
|
|
194
232
|
export type ColorMode = string;
|
|
195
233
|
|
|
234
|
+
interface ThemeContextType {
|
|
235
|
+
themeId: string;
|
|
236
|
+
colorMode: string;
|
|
237
|
+
setThemeId: (id: string) => void;
|
|
238
|
+
setColorMode: (mode: string) => void;
|
|
239
|
+
themes: { id: string; label: string }[];
|
|
240
|
+
}
|
|
241
|
+
|
|
196
242
|
const allThemes = [
|
|
197
243
|
${themes.map(t => ` { id: "${t.id}", label: "${t.label}" }`).join(",\n")}
|
|
198
244
|
];
|
|
199
245
|
|
|
200
|
-
const ThemeContext
|
|
246
|
+
const ThemeContext = createContext<ThemeContextType | null>(null);
|
|
201
247
|
|
|
202
|
-
export const ThemeProvider = ({ children }: { children:
|
|
248
|
+
export const ThemeProvider = ({ children }: { children: ReactNode }) => {
|
|
203
249
|
const [themeId, setThemeId] = useState("${themeId}");
|
|
204
250
|
const [colorMode, setColorMode] = useState("${mode}");
|
|
205
251
|
|
|
@@ -231,8 +277,8 @@ export function ThemeSwitcher() {
|
|
|
231
277
|
const { themeId, colorMode, setThemeId, setColorMode, themes } = useTheme();
|
|
232
278
|
return (
|
|
233
279
|
<div style={{ display: "flex", gap: "8px", padding: "8px" }}>
|
|
234
|
-
<select value={themeId} onChange={(e
|
|
235
|
-
{themes.map((t
|
|
280
|
+
<select value={themeId} onChange={(e) => setThemeId(e.target.value)} style={{ padding: "4px" }}>
|
|
281
|
+
{themes.map((t) => <option key={t.id} value={t.id}>{t.label}</option>)}
|
|
236
282
|
</select>
|
|
237
283
|
<button onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")} style={{ padding: "4px 8px" }}>
|
|
238
284
|
{colorMode === "dark" ? "🌙" : "☀️"}
|
|
@@ -260,12 +306,14 @@ const setupNextJs = (projectRoot, themeId, mode) => {
|
|
|
260
306
|
fs.writeFileSync(contextPath, generateContext(themeId, mode));
|
|
261
307
|
logSuccess(`Created: components/theme/theme-context.tsx`);
|
|
262
308
|
|
|
263
|
-
// Inject into globals.css
|
|
309
|
+
// Inject into globals.css - add AFTER tailwind imports so theme overrides tailwind
|
|
264
310
|
const globalsPath = path.join(appDir, "globals.css");
|
|
265
311
|
if (fs.existsSync(globalsPath)) {
|
|
266
|
-
|
|
312
|
+
let content = fs.readFileSync(globalsPath, "utf8");
|
|
267
313
|
if (!content.includes("gradient-forge.css")) {
|
|
268
|
-
|
|
314
|
+
// Add at the end after all tailwind imports
|
|
315
|
+
content = content + '\n@import "./gradient-forge.css";';
|
|
316
|
+
fs.writeFileSync(globalsPath, content);
|
|
269
317
|
logSuccess(`Injected into: app/globals.css`);
|
|
270
318
|
}
|
|
271
319
|
}
|
|
@@ -276,18 +324,28 @@ const setupNextJs = (projectRoot, themeId, mode) => {
|
|
|
276
324
|
let content = fs.readFileSync(layoutPath, "utf8");
|
|
277
325
|
|
|
278
326
|
if (!content.includes("ThemeProvider")) {
|
|
279
|
-
//
|
|
280
|
-
const importStmt = `import { ThemeProvider } from "
|
|
327
|
+
// Use @/ import path for Next.js
|
|
328
|
+
const importStmt = `import { ThemeProvider } from "@/components/theme/theme-context";`;
|
|
281
329
|
if (!content.includes(importStmt)) {
|
|
282
330
|
content = importStmt + "\n" + content;
|
|
283
331
|
}
|
|
284
332
|
|
|
285
|
-
// Wrap body -
|
|
286
|
-
content = content.replace(/(<body[^>]*>)
|
|
287
|
-
|
|
333
|
+
// Wrap body - keep existing className and add ThemeProvider around children
|
|
334
|
+
content = content.replace(/(<body[^>]*>)([\s\S]*?)(<\/body>)/,
|
|
335
|
+
'$1<ThemeProvider>$2</ThemeProvider>$3');
|
|
288
336
|
|
|
289
|
-
// Add classes to html - preserve existing
|
|
290
|
-
|
|
337
|
+
// Add theme classes to html - preserve existing className and add theme classes
|
|
338
|
+
if (content.includes('className=')) {
|
|
339
|
+
// For JSX className={expression}, add theme classes to the expression
|
|
340
|
+
content = content.replace(
|
|
341
|
+
/(className=\{)([^}]+)(\})/,
|
|
342
|
+
`$1$2 + " ${mode} ${themeId}"$3`
|
|
343
|
+
);
|
|
344
|
+
} else {
|
|
345
|
+
// For plain html or no className, add className attribute
|
|
346
|
+
content = content.replace(/<html/,
|
|
347
|
+
`<html className="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}"`);
|
|
348
|
+
}
|
|
291
349
|
|
|
292
350
|
fs.writeFileSync(layoutPath, content);
|
|
293
351
|
logSuccess(`Updated: app/layout.tsx`);
|