gradient-forge 1.0.0

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 (56) hide show
  1. package/.eslintrc.json +3 -0
  2. package/.github/FUNDING.yml +2 -0
  3. package/README.md +140 -0
  4. package/app/docs/page.tsx +417 -0
  5. package/app/gallery/page.tsx +398 -0
  6. package/app/globals.css +1155 -0
  7. package/app/layout.tsx +36 -0
  8. package/app/page.tsx +600 -0
  9. package/app/showcase/page.tsx +730 -0
  10. package/app/studio/page.tsx +1310 -0
  11. package/cli/index.mjs +1141 -0
  12. package/cli/templates/theme-context.tsx +120 -0
  13. package/cli/templates/theme-engine.ts +237 -0
  14. package/cli/templates/themes.css +512 -0
  15. package/components/site/component-showcase.tsx +623 -0
  16. package/components/site/site-data.ts +103 -0
  17. package/components/site/site-header.tsx +270 -0
  18. package/components/templates/blog.tsx +198 -0
  19. package/components/templates/components-showcase.tsx +298 -0
  20. package/components/templates/dashboard.tsx +246 -0
  21. package/components/templates/ecommerce.tsx +199 -0
  22. package/components/templates/mail.tsx +275 -0
  23. package/components/templates/saas-landing.tsx +169 -0
  24. package/components/theme/studio-code-panel.tsx +485 -0
  25. package/components/theme/theme-context.tsx +120 -0
  26. package/components/theme/theme-engine.ts +237 -0
  27. package/components/theme/theme-exporter.tsx +369 -0
  28. package/components/theme/theme-panel.tsx +268 -0
  29. package/components/theme/token-export-utils.ts +1211 -0
  30. package/components/ui/animated.tsx +55 -0
  31. package/components/ui/avatar.tsx +38 -0
  32. package/components/ui/badge.tsx +32 -0
  33. package/components/ui/button.tsx +65 -0
  34. package/components/ui/card.tsx +56 -0
  35. package/components/ui/checkbox.tsx +19 -0
  36. package/components/ui/command-palette.tsx +245 -0
  37. package/components/ui/gsap-animated.tsx +436 -0
  38. package/components/ui/input.tsx +17 -0
  39. package/components/ui/select.tsx +176 -0
  40. package/components/ui/skeleton.tsx +102 -0
  41. package/components/ui/switch.tsx +43 -0
  42. package/components/ui/tabs.tsx +115 -0
  43. package/components/ui/toast.tsx +119 -0
  44. package/gradient-forge/theme-context.tsx +119 -0
  45. package/gradient-forge/theme-engine.ts +236 -0
  46. package/gradient-forge/themes.css +556 -0
  47. package/lib/animations.ts +50 -0
  48. package/lib/gsap.ts +426 -0
  49. package/lib/utils.ts +6 -0
  50. package/next-env.d.ts +6 -0
  51. package/next.config.mjs +6 -0
  52. package/package.json +53 -0
  53. package/postcss.config.mjs +5 -0
  54. package/tailwind.config.ts +15 -0
  55. package/tsconfig.json +43 -0
  56. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,1310 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect, useRef } from "react";
4
+ import gsap from "gsap";
5
+ import { ScrollTrigger } from "gsap/ScrollTrigger";
6
+ import { useThemeContext } from "@/components/theme/theme-context";
7
+ import { NITRO_ALL_THEMES, type ThemeId } from "@/components/theme/theme-engine";
8
+ import { getThemeTokens } from "@/components/theme/token-export-utils";
9
+ import { SiteHeader } from "@/components/site/site-header";
10
+ import { Button } from "@/components/ui/button";
11
+ import { Badge } from "@/components/ui/badge";
12
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
13
+ import { Input } from "@/components/ui/input";
14
+ import { Switch } from "@/components/ui/switch";
15
+ import { Checkbox } from "@/components/ui/checkbox";
16
+ import { Avatar, AvatarFallback } from "@/components/ui/avatar";
17
+ import {
18
+ Tabs,
19
+ TabsContent,
20
+ TabsList,
21
+ TabsTrigger
22
+ } from "@/components/ui/tabs";
23
+ import { useToast } from "@/components/ui/toast";
24
+ import {
25
+ AnimatedSection,
26
+ StaggerContainer,
27
+ MagneticButton,
28
+ FloatingElement
29
+ } from "@/components/ui/gsap-animated";
30
+ import {
31
+ Palette,
32
+ Layers,
33
+ Code2,
34
+ Download,
35
+ Zap,
36
+ Check,
37
+ Copy,
38
+ Smartphone,
39
+ Tablet,
40
+ Monitor,
41
+ Sparkles,
42
+ Eye,
43
+ Settings,
44
+ Sun,
45
+ Moon,
46
+ ChevronRight,
47
+ Search,
48
+ Bell,
49
+ User,
50
+ Mail,
51
+ Loader2,
52
+ AlertCircle,
53
+ Info,
54
+ CheckCircle2,
55
+ X,
56
+ Trash2,
57
+ Plus,
58
+ MoreHorizontal,
59
+ Layout,
60
+ Type,
61
+ Image as ImageIcon,
62
+ Component,
63
+ Palette as PaletteIcon,
64
+ Wand2,
65
+ FileCode,
66
+ Download as DownloadIcon,
67
+ Share2,
68
+ Moon as MoonIcon,
69
+ Sun as SunIcon
70
+ } from "lucide-react";
71
+ import { cn } from "@/lib/utils";
72
+
73
+ if (typeof window !== "undefined") {
74
+ gsap.registerPlugin(ScrollTrigger);
75
+ }
76
+
77
+ // Export formats
78
+ const exportFormats = [
79
+ { id: "css", label: "CSS", icon: FileCode },
80
+ { id: "scss", label: "SCSS", icon: FileCode },
81
+ { id: "json", label: "JSON", icon: FileCode },
82
+ { id: "tailwind", label: "Tailwind", icon: FileCode },
83
+ { id: "w3c", label: "W3C Tokens", icon: FileCode },
84
+ { id: "figma", label: "Figma", icon: FileCode },
85
+ ];
86
+
87
+ // Component preview sections
88
+ const previewSections = [
89
+ { id: "overview", label: "Overview", icon: Layout },
90
+ { id: "buttons", label: "Buttons", icon: Component },
91
+ { id: "inputs", label: "Inputs", icon: Type },
92
+ { id: "cards", label: "Cards", icon: ImageIcon },
93
+ { id: "feedback", label: "Feedback", icon: PaletteIcon },
94
+ ];
95
+
96
+ // Core theme files with their content
97
+ const coreFilesData: Record<string, { filename: string; description: string; extension: string; content: string }> = {
98
+ "theme-engine": {
99
+ filename: "components/theme/theme-engine.ts",
100
+ description: "Core theme logic & definitions",
101
+ extension: "ts",
102
+ content: `export const NITRO_PUBLIC_THEMES = [
103
+ { id: "theme-nitro-mint-apple", label: "Mint Apple", preview: "radial-gradient(circle at 12% 10%, #b2ffe1 0%, transparent 42%), linear-gradient(145deg, #2d8e74 0%, #70c76a 46%, #d5ef91 100%)" },
104
+ { id: "theme-nitro-citrus-sherbert", label: "Citrus Sherbert", preview: "radial-gradient(circle at 10% 10%, #ffd37d 0%, transparent 42%), linear-gradient(145deg, #e67d35 0%, #f7bb54 46%, #fff0a3 100%)" },
105
+ { id: "theme-nitro-retro-raincloud", label: "Retro Raincloud", preview: "radial-gradient(circle at 12% 10%, #b3c6dc 0%, transparent 42%), linear-gradient(145deg, #4e6077 0%, #6f86a0 46%, #98aec3 100%)" },
106
+ { id: "theme-nitro-hanami", label: "Hanami", preview: "radial-gradient(circle at 12% 10%, #ffd5e7 0%, transparent 42%), linear-gradient(145deg, #995382 0%, #c77ca9 46%, #f0b7ce 100%)" },
107
+ { id: "theme-nitro-sunrise", label: "Sunrise", preview: "radial-gradient(circle at 12% 10%, #ffb596 0%, transparent 42%), linear-gradient(145deg, #e25263 0%, #ef8a57 46%, #ffd07a 100%)" },
108
+ { id: "theme-nitro-cotton-candy", label: "Cotton Candy", preview: "radial-gradient(circle at 12% 10%, #c8e9ff 0%, transparent 42%), linear-gradient(145deg, #5aa2ff 0%, #9b78f0 46%, #f39bca 100%)" },
109
+ { id: "theme-nitro-lofi-vibes", label: "Lofi Vibes", preview: "radial-gradient(circle at 10% 10%, #8e97c6 0%, transparent 42%), linear-gradient(145deg, #3f476c 0%, #59608f 46%, #7a6f9f 100%)" },
110
+ { id: "theme-nitro-desert-khaki", label: "Desert Khaki", preview: "radial-gradient(circle at 10% 10%, #d5bf92 0%, transparent 42%), linear-gradient(145deg, #6d5c49 0%, #8f7a5d 46%, #b49f76 100%)" },
111
+ { id: "theme-nitro-sunset", label: "Sunset", preview: "radial-gradient(circle at 10% 10%, #d66a82 0%, transparent 42%), linear-gradient(145deg, #3f1b4d 0%, #8c335f 46%, #f4874f 100%)" },
112
+ { id: "theme-nitro-chroma-glow", label: "Chroma Glow", preview: "radial-gradient(circle at 10% 10%, #7f8dff 0%, transparent 42%), linear-gradient(145deg, #2d3eff 0%, #a726fa 46%, #00c7ff 100%)" },
113
+ { id: "theme-nitro-forest", label: "Forest", preview: "radial-gradient(circle at 10% 10%, #56be87 0%, transparent 42%), linear-gradient(145deg, #163f2e 0%, #246b49 46%, #59a86c 100%)" },
114
+ { id: "theme-nitro-crimson", label: "Crimson", preview: "radial-gradient(circle at 10% 10%, #c23956 0%, transparent 42%), linear-gradient(145deg, #2d050f 0%, #681126 46%, #a82435 100%)" },
115
+ { id: "theme-nitro-midnight-blurple", label: "Midnight Blurple", preview: "radial-gradient(circle at 10% 10%, #6d6eff 0%, transparent 42%), linear-gradient(145deg, #0f1232 0%, #25366f 46%, #5757dc 100%)" },
116
+ { id: "theme-nitro-mars", label: "Mars", preview: "radial-gradient(circle at 10% 10%, #cc654c 0%, transparent 42%), linear-gradient(145deg, #2e140f 0%, #5e261d 46%, #9c422f 100%)" },
117
+ { id: "theme-nitro-dusk", label: "Dusk", preview: "radial-gradient(circle at 10% 10%, #9475c1 0%, transparent 42%), linear-gradient(145deg, #1b1632 0%, #3b2d5b 46%, #745495 100%)" },
118
+ { id: "theme-nitro-under-the-sea", label: "Under The Sea", preview: "radial-gradient(circle at 10% 10%, #3ea6b7 0%, transparent 42%), linear-gradient(145deg, #0b2242 0%, #0d4f69 46%, #2b848e 100%)" },
119
+ { id: "theme-nitro-retro-storm", label: "Retro Storm", preview: "radial-gradient(circle at 10% 10%, #7e95ab 0%, transparent 42%), linear-gradient(145deg, #1d2b3a 0%, #354657 46%, #55667a 100%)" },
120
+ { id: "theme-nitro-neon-nights", label: "Neon Nights", preview: "radial-gradient(circle at 10% 10%, #d13eff 0%, transparent 42%), linear-gradient(145deg, #05061a 0%, #180f52 46%, #00bde6 100%)" },
121
+ { id: "theme-nitro-strawberry-lemonade", label: "Strawberry Lemonade", preview: "radial-gradient(circle at 10% 10%, #ff8aa8 0%, transparent 42%), linear-gradient(145deg, #8f1847 0%, #cc3f5e 46%, #efc141 100%)" },
122
+ { id: "theme-nitro-aurora", label: "Aurora", preview: "radial-gradient(circle at 10% 10%, #41cbb1 0%, transparent 42%), linear-gradient(145deg, #083142 0%, #1b7e74 46%, #5fbf75 100%)" },
123
+ { id: "theme-nitro-sepia", label: "Sepia", preview: "radial-gradient(circle at 10% 10%, #b99672 0%, transparent 42%), linear-gradient(145deg, #35261d 0%, #5d4636 46%, #927454 100%)" },
124
+ ] as const;
125
+
126
+ export const MEMORY_LANE_THEME = {
127
+ id: "theme-nitro-memory-lane",
128
+ label: "Memory Lane",
129
+ preview: "radial-gradient(circle at 10% 10%, #ba8fd4 0%, transparent 42%), linear-gradient(145deg, #462d42 0%, #684b75 35%, #4a7199 68%, #77ad9f 100%)",
130
+ } as const;
131
+
132
+ export const NITRO_ALL_THEMES = [...NITRO_PUBLIC_THEMES, MEMORY_LANE_THEME] as const;
133
+ export type ThemeId = (typeof NITRO_ALL_THEMES)[number]["id"];
134
+ export type ColorMode = "dark" | "light";
135
+
136
+ const DEFAULT_THEME: ThemeId = "theme-nitro-midnight-blurple";
137
+ const DEFAULT_COLOR_MODE: ColorMode = "dark";
138
+ const THEME_STORAGE_KEY = "shadcn-gradient.theme";
139
+ const COLOR_MODE_STORAGE_KEY = "shadcn-gradient.color-mode";
140
+
141
+ export const normalizeTheme = (theme?: string | null): ThemeId => {
142
+ if (!theme) return DEFAULT_THEME;
143
+ return theme as ThemeId;
144
+ };
145
+
146
+ export const normalizeColorMode = (mode?: string | null): ColorMode => {
147
+ if (mode === "light") return "light";
148
+ return DEFAULT_COLOR_MODE;
149
+ };
150
+
151
+ export const getStoredTheme = (): ThemeId => {
152
+ if (typeof window === "undefined") return DEFAULT_THEME;
153
+ return normalizeTheme(localStorage.getItem(THEME_STORAGE_KEY));
154
+ };
155
+
156
+ export const getStoredColorMode = (): ColorMode => {
157
+ if (typeof window === "undefined") return DEFAULT_COLOR_MODE;
158
+ return normalizeColorMode(localStorage.getItem(COLOR_MODE_STORAGE_KEY));
159
+ };
160
+
161
+ export const applyTheme = (theme?: string | null, mode?: ColorMode) => {
162
+ if (typeof window === "undefined") return;
163
+ const normalizedTheme = normalizeTheme(theme);
164
+ const normalizedMode = normalizeColorMode(mode ?? getStoredColorMode());
165
+ const root = document.documentElement;
166
+
167
+ Array.from(root.classList).forEach((className) => {
168
+ if (className === "dark" || className === "light" || className.startsWith("theme-nitro-")) {
169
+ root.classList.remove(className);
170
+ }
171
+ });
172
+
173
+ root.classList.add(normalizedMode, normalizedTheme);
174
+ root.setAttribute("data-theme", normalizedTheme);
175
+ root.setAttribute("data-color-mode", normalizedMode);
176
+ localStorage.setItem(THEME_STORAGE_KEY, normalizedTheme);
177
+ localStorage.setItem(COLOR_MODE_STORAGE_KEY, normalizedMode);
178
+ };
179
+
180
+ export const defaultTheme = DEFAULT_THEME;
181
+ export const defaultColorMode = DEFAULT_COLOR_MODE;`
182
+ },
183
+ "theme-context": {
184
+ filename: "components/theme/theme-context.tsx",
185
+ description: "React context provider",
186
+ extension: "tsx",
187
+ content: `"use client";
188
+
189
+ import {
190
+ type ColorMode,
191
+ type ThemeId,
192
+ NITRO_ALL_THEMES,
193
+ applyTheme,
194
+ defaultColorMode,
195
+ defaultTheme,
196
+ getStoredColorMode,
197
+ getStoredTheme,
198
+ } from "@/components/theme/theme-engine";
199
+ import { createContext, useContext, useEffect, useState } from "react";
200
+
201
+ type ThemeContextValue = {
202
+ themeId: ThemeId;
203
+ colorMode: ColorMode;
204
+ setThemeId: (themeId: ThemeId) => void;
205
+ setColorMode: (mode: ColorMode) => void;
206
+ };
207
+
208
+ const ThemeContext = createContext<ThemeContextValue | null>(null);
209
+
210
+ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
211
+ const [themeId, setThemeIdState] = useState<ThemeId>(defaultTheme);
212
+ const [colorMode, setColorModeState] = useState<ColorMode>(defaultColorMode);
213
+
214
+ useEffect(() => {
215
+ const storedTheme = getStoredTheme();
216
+ const storedMode = getStoredColorMode();
217
+ setThemeIdState(storedTheme);
218
+ setColorModeState(storedMode);
219
+ applyTheme(storedTheme, storedMode);
220
+ }, []);
221
+
222
+ useEffect(() => {
223
+ applyTheme(themeId, colorMode);
224
+ }, [themeId, colorMode]);
225
+
226
+ const setThemeId = (nextTheme: ThemeId) => {
227
+ setThemeIdState(nextTheme);
228
+ };
229
+
230
+ const setColorMode = (mode: ColorMode) => {
231
+ setColorModeState(mode);
232
+ };
233
+
234
+ return (
235
+ <ThemeContext.Provider value={{ themeId, colorMode, setThemeId, setColorMode }}>
236
+ {children}
237
+ </ThemeContext.Provider>
238
+ );
239
+ };
240
+
241
+ export const useThemeContext = () => {
242
+ const ctx = useContext(ThemeContext);
243
+ if (!ctx) throw new Error("useThemeContext must be used within ThemeProvider");
244
+ return ctx;
245
+ };`
246
+ },
247
+ "token-export-utils": {
248
+ filename: "components/theme/token-export-utils.ts",
249
+ description: "Export utilities for tokens",
250
+ extension: "ts",
251
+ content: `import { NITRO_ALL_THEMES, type ThemeId } from "./theme-engine";
252
+
253
+ const themeTokens: Record<string, Record<string, string>> = {
254
+ "theme-nitro-midnight-blurple": {
255
+ "--background": "235 26% 11%",
256
+ "--foreground": "235 30% 95%",
257
+ "--card": "235 22% 12%",
258
+ "--primary": "241 92% 70%",
259
+ "--accent": "210 92% 65%",
260
+ "--ring": "241 92% 70%",
261
+ "--app-surface-tint": "hsl(241 92% 70% / 0.1)",
262
+ },
263
+ };
264
+
265
+ export type ExportFormat = "css" | "json" | "tailwind";
266
+
267
+ export interface ExportOptions {
268
+ format: ExportFormat;
269
+ themeId: ThemeId;
270
+ colorMode?: "dark" | "light";
271
+ }
272
+
273
+ export interface ExportResult {
274
+ content: string;
275
+ filename: string;
276
+ mimeType: string;
277
+ }
278
+
279
+ export const getThemeTokens = (themeId: ThemeId): Record<string, string> => {
280
+ return themeTokens[themeId] ?? themeTokens["theme-nitro-midnight-blurple"];
281
+ };
282
+
283
+ export const generateCSSExport = (options: ExportOptions): ExportResult => {
284
+ const { themeId } = options;
285
+ const tokens = getThemeTokens(themeId);
286
+ const cssVariables = Object.entries(tokens)
287
+ .map(([key, value]) => \` \${key}: \${value};\`)
288
+ .join("\\n");
289
+
290
+ const content = \`/* Gradient Forge Theme */
291
+ .\${themeId} {
292
+ \${cssVariables}
293
+ }\`;
294
+
295
+ return { content, filename: \`\${themeId}.css\`, mimeType: "text/css" };
296
+ };
297
+
298
+ export const exportTokens = (options: ExportOptions): ExportResult => {
299
+ return generateCSSExport(options);
300
+ };
301
+
302
+ export const downloadFile = (result: ExportResult): void => {
303
+ const blob = new Blob([result.content], { type: result.mimeType });
304
+ const url = URL.createObjectURL(blob);
305
+ const link = document.createElement("a");
306
+ link.href = url;
307
+ link.download = result.filename;
308
+ document.body.appendChild(link);
309
+ link.click();
310
+ document.body.removeChild(link);
311
+ URL.revokeObjectURL(url);
312
+ };`
313
+ },
314
+ "globals-css": {
315
+ filename: "app/globals.css",
316
+ description: "CSS variables & theme classes",
317
+ extension: "css",
318
+ content: `@import "tailwindcss";
319
+
320
+ @custom-variant dark (&:is(.dark *));
321
+
322
+ @theme {
323
+ --color-background: hsl(var(--background));
324
+ --color-foreground: hsl(var(--foreground));
325
+ --color-card: hsl(var(--card));
326
+ --color-primary: hsl(var(--primary));
327
+ --color-accent: hsl(var(--accent));
328
+ --radius-lg: var(--radius);
329
+ }
330
+
331
+ @layer base {
332
+ body {
333
+ background-color: hsl(var(--background) / 0.9);
334
+ background-image: var(--app-gradient);
335
+ color: hsl(var(--foreground));
336
+ }
337
+ }
338
+
339
+ @layer base {
340
+ :root {
341
+ --background: 0 0% 100%;
342
+ --foreground: 0 0% 3.9%;
343
+ --card: 0 0% 100%;
344
+ --primary: 0 0% 9%;
345
+ --radius: 0.9rem;
346
+ }
347
+
348
+ .dark {
349
+ --background: 0 0% 3.9%;
350
+ --foreground: 0 0% 98%;
351
+ --app-gradient: radial-gradient(1100px 560px at -10% -20%, hsl(250 90% 66% / 0.28), transparent 60%);
352
+ --app-surface-tint: hsl(250 85% 65% / 0.08);
353
+ }
354
+
355
+ .theme-nitro-midnight-blurple {
356
+ --background: 235 26% 11%;
357
+ --primary: 241 92% 70%;
358
+ --app-gradient: radial-gradient(1050px 560px at -10% -20%, hsl(246 92% 66% / 0.3), transparent 60%);
359
+ --app-surface-tint: hsl(241 92% 70% / 0.1);
360
+ }
361
+ }`
362
+ }
363
+ };
364
+
365
+ export default function StudioPage() {
366
+ const [activeSection, setActiveSection] = useState("overview");
367
+ const [deviceView, setDeviceView] = useState<"mobile" | "tablet" | "desktop">("desktop");
368
+ const [selectedFormat, setSelectedFormat] = useState("css");
369
+ const [searchQuery, setSearchQuery] = useState("");
370
+ const [showCssVariables, setShowCssVariables] = useState(true);
371
+ const [previewColorMode, setPreviewColorMode] = useState<"light" | "dark">("light");
372
+ const [selectedCoreFile, setSelectedCoreFile] = useState<string | null>(null);
373
+ const { showToast } = useToast();
374
+ const { themeId, setThemeId, colorMode, setColorMode } = useThemeContext();
375
+ const previewRef = useRef<HTMLDivElement>(null);
376
+ const codeRef = useRef<HTMLPreElement>(null);
377
+
378
+ const currentTheme = NITRO_ALL_THEMES.find((t: { id: string }) => t.id === themeId) || NITRO_ALL_THEMES[0];
379
+ const flatTokens = getThemeTokens(themeId);
380
+
381
+ // Transform flat tokens into the expected structure for export functions
382
+ const tokens = {
383
+ colors: Object.fromEntries(
384
+ Object.entries(flatTokens).map(([key, value]) => [
385
+ key.replace(/^--/, ''), // Remove leading --
386
+ value
387
+ ])
388
+ )
389
+ };
390
+
391
+ // Filter themes based on search
392
+ const filteredThemes = NITRO_ALL_THEMES.filter((theme: { label: string }) =>
393
+ theme.label.toLowerCase().includes(searchQuery.toLowerCase())
394
+ );
395
+
396
+ // Animate preview cards on section change
397
+ useEffect(() => {
398
+ const preview = previewRef.current;
399
+ if (!preview) return;
400
+
401
+ const cards = preview.querySelectorAll(".preview-item");
402
+ gsap.fromTo(
403
+ cards,
404
+ { opacity: 0, y: 20, scale: 0.95 },
405
+ {
406
+ opacity: 1,
407
+ y: 0,
408
+ scale: 1,
409
+ duration: 0.4,
410
+ stagger: 0.05,
411
+ ease: "back.out(1.5)",
412
+ }
413
+ );
414
+
415
+ return () => {
416
+ ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
417
+ };
418
+ }, [activeSection, deviceView, themeId]);
419
+
420
+ const handleCopyCode = () => {
421
+ const code = generateExportCode(selectedFormat, tokens, themeId);
422
+ navigator.clipboard.writeText(code);
423
+ showToast("Code copied to clipboard!", "success");
424
+ };
425
+
426
+ const handleDownload = () => {
427
+ const code = generateExportCode(selectedFormat, tokens, themeId);
428
+ const blob = new Blob([code], { type: "text/plain" });
429
+ const url = URL.createObjectURL(blob);
430
+ const a = document.createElement("a");
431
+ a.href = url;
432
+ a.download = `${themeId}-tokens.${getFileExtension(selectedFormat)}`;
433
+ document.body.appendChild(a);
434
+ a.click();
435
+ document.body.removeChild(a);
436
+ URL.revokeObjectURL(url);
437
+ showToast("File downloaded!", "success");
438
+ };
439
+
440
+ const handleCopyAll = () => {
441
+ const allTokens = NITRO_ALL_THEMES.map((theme: { id: ThemeId; label: string }) => ({
442
+ theme: theme.label,
443
+ tokens: getThemeTokens(theme.id)
444
+ }));
445
+ navigator.clipboard.writeText(JSON.stringify(allTokens, null, 2));
446
+ showToast("All themes copied to clipboard!", "success");
447
+ };
448
+
449
+ const getDeviceClasses = () => {
450
+ switch (deviceView) {
451
+ case "mobile":
452
+ return "max-w-[375px]";
453
+ case "tablet":
454
+ return "max-w-[768px]";
455
+ default:
456
+ return "";
457
+ }
458
+ };
459
+
460
+ return (
461
+ <main className="min-h-screen bg-background">
462
+ <SiteHeader />
463
+
464
+ {/* Main Studio Layout */}
465
+ <div className="flex h-[calc(100vh-64px)] overflow-hidden">
466
+
467
+ {/* Left Sidebar - Theme List */}
468
+ <aside className="w-72 border-r border-border/50 bg-background/95 backdrop-blur-xl flex flex-col hidden lg:flex">
469
+ <div className="p-4 border-b border-border/50">
470
+ <div className="flex items-center justify-between mb-3">
471
+ <h2 className="font-semibold text-sm flex items-center gap-2">
472
+ <Palette className="h-4 w-4 text-primary" />
473
+ Themes
474
+ </h2>
475
+ <Badge variant="glass" className="text-xs">{NITRO_ALL_THEMES.length}</Badge>
476
+ </div>
477
+ <div className="relative">
478
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
479
+ <Input
480
+ placeholder="Search themes..."
481
+ value={searchQuery}
482
+ onChange={(e) => setSearchQuery(e.target.value)}
483
+ className="pl-9 h-9 text-sm"
484
+ />
485
+ </div>
486
+ </div>
487
+
488
+ <div className="flex-1 overflow-y-auto p-2 space-y-1">
489
+ <div className="space-y-1">
490
+ {filteredThemes.map((theme) => (
491
+ <button
492
+ key={theme.id}
493
+ onClick={() => setThemeId(theme.id)}
494
+ className={cn(
495
+ "w-full flex items-center gap-3 p-3 rounded-xl text-left transition-all duration-200 group",
496
+ themeId === theme.id
497
+ ? "bg-primary/10 border border-primary/30 shadow-sm"
498
+ : "hover:bg-muted/50 border border-transparent"
499
+ )}
500
+ >
501
+ <div
502
+ className="w-10 h-10 rounded-lg shadow-sm shrink-0"
503
+ style={{ background: theme.preview }}
504
+ />
505
+ <div className="flex-1 min-w-0">
506
+ <p className={cn(
507
+ "font-medium text-sm truncate",
508
+ themeId === theme.id && "text-primary"
509
+ )}>
510
+ {theme.label}
511
+ </p>
512
+ <p className="text-xs text-muted-foreground truncate">
513
+ Gradient theme
514
+ </p>
515
+ </div>
516
+ {themeId === theme.id && (
517
+ <Check className="h-4 w-4 text-primary shrink-0" />
518
+ )}
519
+ </button>
520
+ ))}
521
+ </div>
522
+ </div>
523
+
524
+ <div className="p-4 border-t border-border/50 space-y-3">
525
+ <div className="flex items-center justify-between">
526
+ <span className="text-xs font-medium text-muted-foreground">Preview Mode</span>
527
+ <div className="flex items-center gap-1 bg-muted/50 rounded-full p-1">
528
+ <button
529
+ onClick={() => setPreviewColorMode("light")}
530
+ className={cn(
531
+ "p-1.5 rounded-full transition-all",
532
+ previewColorMode === "light" ? "bg-background shadow-sm" : "text-muted-foreground"
533
+ )}
534
+ >
535
+ <SunIcon className="h-3.5 w-3.5" />
536
+ </button>
537
+ <button
538
+ onClick={() => setPreviewColorMode("dark")}
539
+ className={cn(
540
+ "p-1.5 rounded-full transition-all",
541
+ previewColorMode === "dark" ? "bg-background shadow-sm" : "text-muted-foreground"
542
+ )}
543
+ >
544
+ <MoonIcon className="h-3.5 w-3.5" />
545
+ </button>
546
+ </div>
547
+ </div>
548
+ </div>
549
+ </aside>
550
+
551
+ {/* Center - Live Preview */}
552
+ <section className="flex-1 flex flex-col overflow-hidden">
553
+ {/* Toolbar */}
554
+ <div className="h-14 border-b border-border/50 flex items-center justify-between px-4 bg-background/95 backdrop-blur-xl shrink-0">
555
+ <div className="flex items-center gap-2">
556
+ <h1 className="font-semibold text-sm hidden sm:block">Component Preview</h1>
557
+ <div className="h-4 w-px bg-border hidden sm:block" />
558
+ <div className="flex items-center gap-1 bg-muted/50 rounded-full p-1">
559
+ {[
560
+ { id: "mobile", icon: Smartphone, label: "Mobile" },
561
+ { id: "tablet", icon: Tablet, label: "Tablet" },
562
+ { id: "desktop", icon: Monitor, label: "Desktop" },
563
+ ].map(({ id, icon: Icon, label }) => (
564
+ <button
565
+ key={id}
566
+ onClick={() => setDeviceView(id as typeof deviceView)}
567
+ className={cn(
568
+ "p-1.5 rounded-full transition-all",
569
+ deviceView === id
570
+ ? "bg-background shadow-sm text-foreground"
571
+ : "text-muted-foreground hover:text-foreground"
572
+ )}
573
+ title={label}
574
+ >
575
+ <Icon className="h-4 w-4" />
576
+ </button>
577
+ ))}
578
+ </div>
579
+ </div>
580
+
581
+ <div className="flex items-center gap-2">
582
+ <Badge variant="glass" className="gap-1.5 text-xs">
583
+ <Sparkles className="h-3 w-3" />
584
+ {currentTheme.label}
585
+ </Badge>
586
+ </div>
587
+ </div>
588
+
589
+ {/* Preview Content */}
590
+ <div className="flex-1 overflow-y-auto p-4 sm:p-6">
591
+ <div className={cn("mx-auto transition-all duration-300", getDeviceClasses())}>
592
+ {/* Section Tabs */}
593
+ <div className="flex items-center gap-1 mb-6 overflow-x-auto pb-2">
594
+ {previewSections.map(({ id, label, icon: Icon }) => (
595
+ <button
596
+ key={id}
597
+ onClick={() => setActiveSection(id)}
598
+ className={cn(
599
+ "flex items-center gap-2 px-3 py-2 rounded-full text-xs font-medium transition-all whitespace-nowrap",
600
+ activeSection === id
601
+ ? "bg-primary text-primary-foreground shadow-lg shadow-primary/20"
602
+ : "bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground"
603
+ )}
604
+ >
605
+ <Icon className="h-3.5 w-3.5" />
606
+ {label}
607
+ </button>
608
+ ))}
609
+ </div>
610
+
611
+ {/* Preview Area */}
612
+ <div
613
+ ref={previewRef}
614
+ className="bg-card/50 rounded-2xl border border-border/50 p-4 sm:p-6 min-h-[500px]"
615
+ style={{ backgroundColor: previewColorMode === "dark" ? "#0a0a0a" : undefined }}
616
+ >
617
+ {activeSection === "overview" && <OverviewPreview />}
618
+ {activeSection === "buttons" && <ButtonsPreview />}
619
+ {activeSection === "inputs" && <InputsPreview />}
620
+ {activeSection === "cards" && <CardsPreview />}
621
+ {activeSection === "feedback" && <FeedbackPreview />}
622
+ </div>
623
+ </div>
624
+ </div>
625
+ </section>
626
+
627
+ {/* Right Sidebar - Export Panel */}
628
+ <aside className="w-80 border-l border-border/50 bg-background/95 backdrop-blur-xl flex flex-col hidden xl:flex">
629
+ <div className="p-4 border-b border-border/50">
630
+ <h2 className="font-semibold text-sm flex items-center gap-2 mb-3">
631
+ <Code2 className="h-4 w-4 text-primary" />
632
+ Export Tokens
633
+ </h2>
634
+ <div className="flex items-center gap-1 bg-muted/50 rounded-lg p-1">
635
+ {exportFormats.map(({ id, label, icon: Icon }) => (
636
+ <button
637
+ key={id}
638
+ onClick={() => setSelectedFormat(id)}
639
+ className={cn(
640
+ "flex-1 flex items-center justify-center gap-1.5 py-1.5 px-2 rounded-md text-xs font-medium transition-all",
641
+ selectedFormat === id
642
+ ? "bg-background shadow-sm text-foreground"
643
+ : "text-muted-foreground hover:text-foreground"
644
+ )}
645
+ title={label}
646
+ >
647
+ <Icon className="h-3.5 w-3.5" />
648
+ <span className="hidden 2xl:inline">{label}</span>
649
+ </button>
650
+ ))}
651
+ </div>
652
+ </div>
653
+
654
+ <div className="flex-1 overflow-hidden flex flex-col">
655
+ {/* Core Files Documentation */}
656
+ <div className="px-4 py-3 border-b border-border/50 bg-muted/20">
657
+ <p className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider mb-2">Core Theme Files - Click to View & Copy</p>
658
+ <div className="space-y-1.5">
659
+ {Object.entries(coreFilesData).map(([key, file]) => (
660
+ <button
661
+ key={key}
662
+ onClick={() => setSelectedCoreFile(selectedCoreFile === key ? null : key)}
663
+ className={cn(
664
+ "w-full flex items-center gap-2 text-xs p-1.5 rounded border transition-all text-left",
665
+ selectedCoreFile === key
666
+ ? "bg-primary/10 border-primary/30"
667
+ : "bg-background/50 border-border/30 hover:bg-background/80"
668
+ )}
669
+ >
670
+ <FileCode className={cn("h-3 w-3 shrink-0", selectedCoreFile === key ? "text-primary" : "text-muted-foreground")} />
671
+ <div className="flex-1 min-w-0">
672
+ <p className={cn("font-medium truncate", selectedCoreFile === key && "text-primary")}>{file.filename}</p>
673
+ <p className="text-[10px] text-muted-foreground truncate">{file.description}</p>
674
+ </div>
675
+ {selectedCoreFile === key && <Check className="h-3 w-3 text-primary shrink-0" />}
676
+ </button>
677
+ ))}
678
+ </div>
679
+ {selectedCoreFile && (
680
+ <div className="mt-3 p-2 bg-black/90 rounded border border-primary/20">
681
+ <div className="flex items-center justify-between mb-2">
682
+ <span className="text-[10px] text-primary font-medium">{coreFilesData[selectedCoreFile].filename}</span>
683
+ <button
684
+ onClick={() => {
685
+ navigator.clipboard.writeText(coreFilesData[selectedCoreFile].content);
686
+ showToast("File copied to clipboard!", "success");
687
+ }}
688
+ className="text-[10px] px-2 py-1 bg-primary/20 hover:bg-primary/30 text-primary rounded transition-colors"
689
+ >
690
+ Copy File
691
+ </button>
692
+ </div>
693
+ <pre className="text-[10px] text-green-400 overflow-auto max-h-[200px] whitespace-pre-wrap break-all">
694
+ {coreFilesData[selectedCoreFile].content}
695
+ </pre>
696
+ </div>
697
+ )}
698
+ </div>
699
+
700
+ <div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
701
+ <span className="text-xs font-medium text-muted-foreground">
702
+ {selectedCoreFile ? `Viewing: ${coreFilesData[selectedCoreFile].filename}` : "Generated Code"}
703
+ </span>
704
+ <div className="flex items-center gap-1">
705
+ <button
706
+ onClick={() => {
707
+ const content = selectedCoreFile
708
+ ? coreFilesData[selectedCoreFile].content
709
+ : generateExportCode(selectedFormat, tokens, themeId);
710
+ navigator.clipboard.writeText(content);
711
+ showToast(selectedCoreFile ? "File copied!" : "Code copied!", "success");
712
+ }}
713
+ className="p-1.5 rounded-md hover:bg-muted transition-colors"
714
+ title="Copy code"
715
+ >
716
+ <Copy className="h-3.5 w-3.5 text-muted-foreground" />
717
+ </button>
718
+ {!selectedCoreFile && (
719
+ <button
720
+ onClick={handleDownload}
721
+ className="p-1.5 rounded-md hover:bg-muted transition-colors"
722
+ title="Download file"
723
+ >
724
+ <DownloadIcon className="h-3.5 w-3.5 text-muted-foreground" />
725
+ </button>
726
+ )}
727
+ </div>
728
+ </div>
729
+ <div className="flex-1 overflow-auto p-4">
730
+ <pre
731
+ ref={codeRef}
732
+ className={cn(
733
+ "text-xs whitespace-pre-wrap break-all",
734
+ selectedCoreFile ? "text-green-400 font-mono" : "font-mono text-muted-foreground"
735
+ )}
736
+ >
737
+ {selectedCoreFile
738
+ ? coreFilesData[selectedCoreFile].content
739
+ : generateExportCode(selectedFormat, tokens, themeId)}
740
+ </pre>
741
+ </div>
742
+ </div>
743
+
744
+ <div className="p-4 border-t border-border/50 space-y-3">
745
+ <div className="flex items-center justify-between">
746
+ <span className="text-xs text-muted-foreground">Show CSS Variables</span>
747
+ <Switch
748
+ checked={showCssVariables}
749
+ onCheckedChange={setShowCssVariables}
750
+ />
751
+ </div>
752
+ <Button
753
+ variant="outline"
754
+ className="w-full gap-2 text-xs border-primary/30 hover:bg-primary/10"
755
+ onClick={() => {
756
+ const allFilesContent = Object.entries(coreFilesData)
757
+ .map(([key, file]) => `// ===== ${file.filename} =====\\n\\n${file.content}`)
758
+ .join("\\n\\n\\n");
759
+ navigator.clipboard.writeText(allFilesContent);
760
+ showToast("All core files copied! Paste them into your project.", "success");
761
+ }}
762
+ >
763
+ <FileCode className="h-3.5 w-3.5" />
764
+ Copy All Core Files
765
+ </Button>
766
+ <Button
767
+ variant="outline"
768
+ className="w-full gap-2 text-xs"
769
+ onClick={handleCopyAll}
770
+ >
771
+ <Copy className="h-3.5 w-3.5" />
772
+ Copy All Themes
773
+ </Button>
774
+ <Button
775
+ className="w-full gap-2 text-xs"
776
+ onClick={handleDownload}
777
+ >
778
+ <DownloadIcon className="h-3.5 w-3.5" />
779
+ Download Tokens
780
+ </Button>
781
+ </div>
782
+ </aside>
783
+ </div>
784
+ </main>
785
+ );
786
+ }
787
+
788
+ // Preview Components
789
+ function OverviewPreview() {
790
+ return (
791
+ <div className="space-y-6 preview-item">
792
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
793
+ <Card className="preview-item border-border/40 bg-card/70">
794
+ <CardHeader className="pb-3">
795
+ <div className="flex items-center gap-3">
796
+ <div className="h-10 w-10 rounded-xl bg-primary/20 flex items-center justify-center">
797
+ <Zap className="h-5 w-5 text-primary" />
798
+ </div>
799
+ <div>
800
+ <CardTitle className="text-sm">Quick Actions</CardTitle>
801
+ <CardDescription className="text-xs">Common tasks</CardDescription>
802
+ </div>
803
+ </div>
804
+ </CardHeader>
805
+ <CardContent className="space-y-2">
806
+ <Button size="sm" className="w-full text-xs gap-2">
807
+ <Plus className="h-3.5 w-3.5" />
808
+ Create New
809
+ </Button>
810
+ <Button variant="outline" size="sm" className="w-full text-xs gap-2">
811
+ <Layout className="h-3.5 w-3.5" />
812
+ View Dashboard
813
+ </Button>
814
+ </CardContent>
815
+ </Card>
816
+
817
+ <Card className="preview-item border-border/40 bg-card/70">
818
+ <CardHeader className="pb-3">
819
+ <div className="flex items-center gap-3">
820
+ <div className="h-10 w-10 rounded-xl bg-secondary/20 flex items-center justify-center">
821
+ <User className="h-5 w-5 text-secondary" />
822
+ </div>
823
+ <div>
824
+ <CardTitle className="text-sm">Profile</CardTitle>
825
+ <CardDescription className="text-xs">User settings</CardDescription>
826
+ </div>
827
+ </div>
828
+ </CardHeader>
829
+ <CardContent className="space-y-3">
830
+ <div className="flex items-center gap-3">
831
+ <Avatar className="h-10 w-10">
832
+ <AvatarFallback className="bg-primary/20 text-primary text-sm">JD</AvatarFallback>
833
+ </Avatar>
834
+ <div className="flex-1">
835
+ <p className="text-sm font-medium">John Doe</p>
836
+ <p className="text-xs text-muted-foreground">john@example.com</p>
837
+ </div>
838
+ </div>
839
+ <Button variant="outline" size="sm" className="w-full text-xs">
840
+ Edit Profile
841
+ </Button>
842
+ </CardContent>
843
+ </Card>
844
+
845
+ <Card className="preview-item border-border/40 bg-card/70">
846
+ <CardHeader className="pb-3">
847
+ <div className="flex items-center gap-3">
848
+ <div className="h-10 w-10 rounded-xl bg-accent/20 flex items-center justify-center">
849
+ <Bell className="h-5 w-5 text-accent" />
850
+ </div>
851
+ <div>
852
+ <CardTitle className="text-sm">Notifications</CardTitle>
853
+ <CardDescription className="text-xs">3 unread</CardDescription>
854
+ </div>
855
+ </div>
856
+ </CardHeader>
857
+ <CardContent className="space-y-2">
858
+ <div className="flex items-center gap-2 text-xs">
859
+ <div className="h-2 w-2 rounded-full bg-primary animate-pulse" />
860
+ <span className="flex-1">New theme available</span>
861
+ <span className="text-muted-foreground">2m</span>
862
+ </div>
863
+ <div className="flex items-center gap-2 text-xs">
864
+ <div className="h-2 w-2 rounded-full bg-secondary" />
865
+ <span className="flex-1">Export completed</span>
866
+ <span className="text-muted-foreground">1h</span>
867
+ </div>
868
+ <Button variant="ghost" size="sm" className="w-full text-xs">
869
+ View All
870
+ </Button>
871
+ </CardContent>
872
+ </Card>
873
+ </div>
874
+
875
+ <Card className="preview-item border-border/40 bg-card/70">
876
+ <CardHeader>
877
+ <CardTitle className="text-sm">Theme Preview</CardTitle>
878
+ <CardDescription className="text-xs">
879
+ See how your theme looks across different components
880
+ </CardDescription>
881
+ </CardHeader>
882
+ <CardContent>
883
+ <div className="flex flex-wrap gap-2">
884
+ <Badge>Default</Badge>
885
+ <Badge variant="outline">Outline</Badge>
886
+ <Badge variant="glass">Glass</Badge>
887
+ <Badge className="gap-1"><Check className="h-3 w-3" /> Success</Badge>
888
+ </div>
889
+ <div className="flex flex-wrap gap-2 mt-4">
890
+ <Button size="sm">Primary</Button>
891
+ <Button size="sm" variant="secondary">Secondary</Button>
892
+ <Button size="sm" variant="outline">Outline</Button>
893
+ <Button size="sm" variant="ghost">Ghost</Button>
894
+ <Button size="sm" variant="glow">Glow</Button>
895
+ </div>
896
+ </CardContent>
897
+ </Card>
898
+ </div>
899
+ );
900
+ }
901
+
902
+ function ButtonsPreview() {
903
+ return (
904
+ <div className="space-y-6">
905
+ <div className="preview-item">
906
+ <h3 className="text-sm font-medium mb-3">Button Variants</h3>
907
+ <div className="flex flex-wrap gap-2">
908
+ <Button size="sm">Default</Button>
909
+ <Button size="sm" variant="secondary">Secondary</Button>
910
+ <Button size="sm" variant="outline">Outline</Button>
911
+ <Button size="sm" variant="ghost">Ghost</Button>
912
+ <Button size="sm" variant="glow">Glow</Button>
913
+ </div>
914
+ </div>
915
+
916
+ <div className="preview-item">
917
+ <h3 className="text-sm font-medium mb-3">Button Sizes</h3>
918
+ <div className="flex flex-wrap items-center gap-2">
919
+ <Button size="sm" variant="glow">Small</Button>
920
+ <Button size="md" variant="glow">Medium</Button>
921
+ <Button size="lg" variant="glow">Large</Button>
922
+ </div>
923
+ </div>
924
+
925
+ <div className="preview-item">
926
+ <h3 className="text-sm font-medium mb-3">Button States</h3>
927
+ <div className="flex flex-wrap gap-2">
928
+ <Button size="sm" variant="glow">
929
+ <Check className="h-4 w-4 mr-1.5" />
930
+ With Icon
931
+ </Button>
932
+ <Button size="sm" variant="glow" disabled>
933
+ <Loader2 className="h-4 w-4 mr-1.5 animate-spin" />
934
+ Loading
935
+ </Button>
936
+ <Button size="sm" variant="outline" disabled>Disabled</Button>
937
+ </div>
938
+ </div>
939
+
940
+ <div className="preview-item">
941
+ <h3 className="text-sm font-medium mb-3">Icon Buttons</h3>
942
+ <div className="flex flex-wrap gap-2">
943
+ <Button size="sm" variant="outline" className="px-2.5">
944
+ <Settings className="h-4 w-4" />
945
+ </Button>
946
+ <Button size="sm" variant="glow" className="px-2.5">
947
+ <Bell className="h-4 w-4" />
948
+ </Button>
949
+ <Button size="sm" variant="secondary" className="px-2.5">
950
+ <User className="h-4 w-4" />
951
+ </Button>
952
+ </div>
953
+ </div>
954
+ </div>
955
+ );
956
+ }
957
+
958
+ function InputsPreview() {
959
+ return (
960
+ <div className="space-y-6">
961
+ <div className="preview-item">
962
+ <h3 className="text-sm font-medium mb-3">Text Inputs</h3>
963
+ <div className="grid sm:grid-cols-2 gap-4 max-w-md">
964
+ <div className="space-y-2">
965
+ <label className="text-xs font-medium">Default</label>
966
+ <Input placeholder="Enter text..." />
967
+ </div>
968
+ <div className="space-y-2">
969
+ <label className="text-xs font-medium">With Icon</label>
970
+ <div className="relative">
971
+ <Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
972
+ <Input placeholder="Email" className="pl-10" />
973
+ </div>
974
+ </div>
975
+ <div className="space-y-2">
976
+ <label className="text-xs font-medium">Disabled</label>
977
+ <Input placeholder="Disabled" disabled />
978
+ </div>
979
+ <div className="space-y-2">
980
+ <label className="text-xs font-medium">With Button</label>
981
+ <div className="flex gap-2">
982
+ <Input placeholder="Search..." className="flex-1" />
983
+ <Button size="sm" variant="glow">
984
+ <Search className="h-4 w-4" />
985
+ </Button>
986
+ </div>
987
+ </div>
988
+ </div>
989
+ </div>
990
+
991
+ <div className="preview-item">
992
+ <h3 className="text-sm font-medium mb-3">Switches</h3>
993
+ <Card className="max-w-sm">
994
+ <CardContent className="space-y-4 pt-6">
995
+ <div className="flex items-center justify-between">
996
+ <div className="flex items-center gap-3">
997
+ <Sun className="h-4 w-4" />
998
+ <span className="text-sm">Light Mode</span>
999
+ </div>
1000
+ <Switch defaultChecked />
1001
+ </div>
1002
+ <div className="flex items-center justify-between">
1003
+ <div className="flex items-center gap-3">
1004
+ <Bell className="h-4 w-4" />
1005
+ <span className="text-sm">Notifications</span>
1006
+ </div>
1007
+ <Switch defaultChecked />
1008
+ </div>
1009
+ <div className="flex items-center justify-between">
1010
+ <div className="flex items-center gap-3">
1011
+ <Mail className="h-4 w-4" />
1012
+ <span className="text-sm">Email Updates</span>
1013
+ </div>
1014
+ <Switch />
1015
+ </div>
1016
+ </CardContent>
1017
+ </Card>
1018
+ </div>
1019
+
1020
+ <div className="preview-item">
1021
+ <h3 className="text-sm font-medium mb-3">Checkboxes</h3>
1022
+ <div className="space-y-3">
1023
+ <label className="flex items-center gap-3">
1024
+ <Checkbox defaultChecked />
1025
+ <span className="text-sm">Enable notifications</span>
1026
+ </label>
1027
+ <label className="flex items-center gap-3">
1028
+ <Checkbox />
1029
+ <span className="text-sm">Auto-save changes</span>
1030
+ </label>
1031
+ <label className="flex items-center gap-3">
1032
+ <Checkbox defaultChecked />
1033
+ <span className="text-sm">Show previews</span>
1034
+ </label>
1035
+ </div>
1036
+ </div>
1037
+ </div>
1038
+ );
1039
+ }
1040
+
1041
+ function CardsPreview() {
1042
+ return (
1043
+ <div className="space-y-6">
1044
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
1045
+ <Card className="preview-item border-border/40 bg-card/70">
1046
+ <CardHeader>
1047
+ <CardTitle className="text-sm">Simple Card</CardTitle>
1048
+ <CardDescription className="text-xs">Basic card component</CardDescription>
1049
+ </CardHeader>
1050
+ <CardContent>
1051
+ <p className="text-xs text-muted-foreground">
1052
+ Cards provide a flexible container for displaying content.
1053
+ </p>
1054
+ </CardContent>
1055
+ </Card>
1056
+
1057
+ <Card className="preview-item border-primary/30 bg-primary/5">
1058
+ <CardHeader>
1059
+ <CardTitle className="text-sm flex items-center gap-2">
1060
+ <CheckCircle2 className="h-4 w-4 text-primary" />
1061
+ Featured Card
1062
+ </CardTitle>
1063
+ <CardDescription className="text-xs">Highlighted styling</CardDescription>
1064
+ </CardHeader>
1065
+ <CardContent>
1066
+ <p className="text-xs text-muted-foreground">
1067
+ Cards can have custom borders and backgrounds.
1068
+ </p>
1069
+ </CardContent>
1070
+ </Card>
1071
+
1072
+ <Card className="preview-item border-border/40 bg-card/70">
1073
+ <CardHeader className="pb-3">
1074
+ <CardTitle className="text-sm">Interactive Card</CardTitle>
1075
+ </CardHeader>
1076
+ <CardContent className="space-y-3">
1077
+ <p className="text-xs text-muted-foreground">
1078
+ Cards can contain buttons and other interactive elements.
1079
+ </p>
1080
+ <div className="flex gap-2">
1081
+ <Button size="sm" variant="outline" className="text-xs flex-1">Cancel</Button>
1082
+ <Button size="sm" variant="glow" className="text-xs flex-1">Confirm</Button>
1083
+ </div>
1084
+ </CardContent>
1085
+ </Card>
1086
+ </div>
1087
+
1088
+ <Card className="preview-item border-border/40 bg-card/70">
1089
+ <CardHeader className="flex flex-row items-center justify-between">
1090
+ <div>
1091
+ <CardTitle className="text-sm">Card with Actions</CardTitle>
1092
+ <CardDescription className="text-xs">Header actions example</CardDescription>
1093
+ </div>
1094
+ <Button variant="ghost" size="sm" className="px-2">
1095
+ <MoreHorizontal className="h-4 w-4" />
1096
+ </Button>
1097
+ </CardHeader>
1098
+ <CardContent className="space-y-4">
1099
+ <div className="flex items-center gap-4 p-3 rounded-xl bg-muted/50">
1100
+ <Avatar className="h-10 w-10">
1101
+ <AvatarFallback className="bg-primary/20 text-primary">JD</AvatarFallback>
1102
+ </Avatar>
1103
+ <div className="flex-1">
1104
+ <p className="text-sm font-medium">Project Update</p>
1105
+ <p className="text-xs text-muted-foreground">Updated 2 hours ago</p>
1106
+ </div>
1107
+ <Badge variant="glass">New</Badge>
1108
+ </div>
1109
+ <div className="flex gap-2">
1110
+ <Button variant="outline" size="sm" className="text-xs gap-1.5">
1111
+ <Trash2 className="h-3.5 w-3.5" />
1112
+ Delete
1113
+ </Button>
1114
+ <Button size="sm" className="text-xs gap-1.5">
1115
+ <Check className="h-3.5 w-3.5" />
1116
+ Approve
1117
+ </Button>
1118
+ </div>
1119
+ </CardContent>
1120
+ </Card>
1121
+ </div>
1122
+ );
1123
+ }
1124
+
1125
+ function FeedbackPreview() {
1126
+ return (
1127
+ <div className="space-y-6">
1128
+ <div className="preview-item">
1129
+ <h3 className="text-sm font-medium mb-3">Badges</h3>
1130
+ <div className="flex flex-wrap gap-2">
1131
+ <Badge>Default</Badge>
1132
+ <Badge variant="outline">Outline</Badge>
1133
+ <Badge variant="glass">Glass</Badge>
1134
+ <Badge className="gap-1"><Check className="h-3 w-3" /> Success</Badge>
1135
+ <Badge variant="outline" className="gap-1"><Bell className="h-3 w-3" /> Alert</Badge>
1136
+ </div>
1137
+ </div>
1138
+
1139
+ <div className="preview-item">
1140
+ <h3 className="text-sm font-medium mb-3">Loading States</h3>
1141
+ <Card>
1142
+ <CardContent className="p-6 space-y-4">
1143
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
1144
+ <Loader2 className="h-4 w-4 animate-spin" />
1145
+ Loading content...
1146
+ </div>
1147
+ <div className="h-2 bg-muted rounded-full overflow-hidden">
1148
+ <div className="h-full w-2/3 bg-primary rounded-full animate-pulse" />
1149
+ </div>
1150
+ <div className="flex gap-2">
1151
+ <div className="h-8 w-8 rounded-full bg-muted animate-pulse" />
1152
+ <div className="flex-1 space-y-2">
1153
+ <div className="h-3 bg-muted rounded animate-pulse w-1/3" />
1154
+ <div className="h-2 bg-muted rounded animate-pulse w-1/2" />
1155
+ </div>
1156
+ </div>
1157
+ </CardContent>
1158
+ </Card>
1159
+ </div>
1160
+
1161
+ <div className="preview-item">
1162
+ <h3 className="text-sm font-medium mb-3">Alert States</h3>
1163
+ <div className="space-y-3 max-w-md">
1164
+ <div className="flex items-start gap-3 p-3 rounded-xl bg-primary/10 border border-primary/20">
1165
+ <Info className="h-4 w-4 text-primary shrink-0 mt-0.5" />
1166
+ <div className="flex-1">
1167
+ <p className="text-sm font-medium">Information</p>
1168
+ <p className="text-xs text-muted-foreground">This is an informational message.</p>
1169
+ </div>
1170
+ </div>
1171
+ <div className="flex items-start gap-3 p-3 rounded-xl bg-secondary/10 border border-secondary/20">
1172
+ <CheckCircle2 className="h-4 w-4 text-secondary shrink-0 mt-0.5" />
1173
+ <div className="flex-1">
1174
+ <p className="text-sm font-medium">Success</p>
1175
+ <p className="text-xs text-muted-foreground">Your action was completed successfully.</p>
1176
+ </div>
1177
+ </div>
1178
+ <div className="flex items-start gap-3 p-3 rounded-xl bg-destructive/10 border border-destructive/20">
1179
+ <AlertCircle className="h-4 w-4 text-destructive shrink-0 mt-0.5" />
1180
+ <div className="flex-1">
1181
+ <p className="text-sm font-medium">Error</p>
1182
+ <p className="text-xs text-muted-foreground">Something went wrong. Please try again.</p>
1183
+ </div>
1184
+ </div>
1185
+ </div>
1186
+ </div>
1187
+
1188
+ <div className="preview-item">
1189
+ <h3 className="text-sm font-medium mb-3">Empty State</h3>
1190
+ <Card>
1191
+ <CardContent className="text-center py-12">
1192
+ <div className="h-16 w-16 rounded-full bg-muted flex items-center justify-center mx-auto mb-4">
1193
+ <Mail className="h-8 w-8 text-muted-foreground" />
1194
+ </div>
1195
+ <h3 className="font-medium mb-1">No messages yet</h3>
1196
+ <p className="text-sm text-muted-foreground mb-4">
1197
+ Start a conversation to see messages here.
1198
+ </p>
1199
+ <Button size="sm" variant="outline">
1200
+ Start Conversation
1201
+ </Button>
1202
+ </CardContent>
1203
+ </Card>
1204
+ </div>
1205
+ </div>
1206
+ );
1207
+ }
1208
+
1209
+ // Helper functions
1210
+ function generateExportCode(format: string, tokens: any, themeId: string): string {
1211
+ switch (format) {
1212
+ case "css":
1213
+ return generateCSS(tokens, themeId);
1214
+ case "scss":
1215
+ return generateSCSS(tokens, themeId);
1216
+ case "json":
1217
+ return JSON.stringify(tokens, null, 2);
1218
+ case "tailwind":
1219
+ return generateTailwind(tokens, themeId);
1220
+ case "w3c":
1221
+ return generateW3C(tokens, themeId);
1222
+ case "figma":
1223
+ return generateFigma(tokens, themeId);
1224
+ default:
1225
+ return generateCSS(tokens, themeId);
1226
+ }
1227
+ }
1228
+
1229
+ function generateCSS(tokens: any, themeId: string): string {
1230
+ let css = `/* ${themeId} Theme Tokens */\n:root {\n`;
1231
+
1232
+ Object.entries(tokens.colors).forEach(([key, value]) => {
1233
+ css += ` --${key}: ${value};\n`;
1234
+ });
1235
+
1236
+ if (tokens.gradients) {
1237
+ css += `\n /* Gradients */\n`;
1238
+ Object.entries(tokens.gradients).forEach(([key, value]) => {
1239
+ css += ` --gradient-${key}: ${value};\n`;
1240
+ });
1241
+ }
1242
+
1243
+ css += `}\n`;
1244
+ return css;
1245
+ }
1246
+
1247
+ function generateSCSS(tokens: any, themeId: string): string {
1248
+ let scss = `// ${themeId} Theme Tokens\n$tokens: (\n`;
1249
+
1250
+ Object.entries(tokens.colors).forEach(([key, value]) => {
1251
+ scss += ` "${key}": ${value},\n`;
1252
+ });
1253
+
1254
+ scss += `);\n`;
1255
+ return scss;
1256
+ }
1257
+
1258
+ function generateTailwind(tokens: any, themeId: string): string {
1259
+ return `// tailwind.config.js
1260
+ module.exports = {
1261
+ theme: {
1262
+ extend: {
1263
+ colors: ${JSON.stringify(tokens.colors, null, 6).replace(/"/g, "'")},
1264
+ },
1265
+ },
1266
+ }`;
1267
+ }
1268
+
1269
+ function generateW3C(tokens: any, themeId: string): string {
1270
+ return JSON.stringify({
1271
+ $schema: "https://design-tokens.github.io/format/",
1272
+ $themes: [{
1273
+ id: themeId,
1274
+ name: themeId,
1275
+ selectedTokenSets: { [themeId]: "enabled" }
1276
+ }],
1277
+ [themeId]: {
1278
+ color: tokens.colors
1279
+ }
1280
+ }, null, 2);
1281
+ }
1282
+
1283
+ function generateFigma(tokens: any, themeId: string): string {
1284
+ const figmaTokens: any = {};
1285
+
1286
+ Object.entries(tokens.colors).forEach(([key, value]) => {
1287
+ figmaTokens[key] = {
1288
+ value: value,
1289
+ type: "color"
1290
+ };
1291
+ });
1292
+
1293
+ return JSON.stringify({
1294
+ [themeId]: {
1295
+ color: figmaTokens
1296
+ }
1297
+ }, null, 2);
1298
+ }
1299
+
1300
+ function getFileExtension(format: string): string {
1301
+ switch (format) {
1302
+ case "css": return "css";
1303
+ case "scss": return "scss";
1304
+ case "json":
1305
+ case "w3c":
1306
+ case "figma": return "json";
1307
+ case "tailwind": return "js";
1308
+ default: return "txt";
1309
+ }
1310
+ }