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,268 @@
1
+ "use client";
2
+
3
+ import { useThemeContext } from "@/components/theme/theme-context";
4
+ import { MEMORY_LANE_THEME, NITRO_PUBLIC_THEMES, NITRO_ALL_THEMES, type ThemeId } from "@/components/theme/theme-engine";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8
+ import { Switch } from "@/components/ui/switch";
9
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
10
+ import { cn } from "@/lib/utils";
11
+ import { Lock, Moon, Sun, Download, Copy, Check, FileCode, Palette } from "lucide-react";
12
+ import { useState } from "react";
13
+ import { exportTokens, downloadFile, copyToClipboard, exportFormats, generateAllThemesCSS, type ExportFormat } from "./token-export-utils";
14
+
15
+ export function ThemePanel() {
16
+ const {
17
+ themeId,
18
+ colorMode,
19
+ availableThemes,
20
+ memoryLaneUnlocked,
21
+ remainingForUnlock,
22
+ setThemeId,
23
+ setColorMode,
24
+ } = useThemeContext();
25
+
26
+ const isLight = colorMode === "light";
27
+ const totalThemes = NITRO_PUBLIC_THEMES.length;
28
+ const seenThemes = totalThemes - remainingForUnlock;
29
+ const progress = Math.round((seenThemes / totalThemes) * 100);
30
+
31
+ const [selectedExportFormat, setSelectedExportFormat] = useState<ExportFormat>("css");
32
+ const [copyStatus, setCopyStatus] = useState<"idle" | "copying" | "copied" | "error">("idle");
33
+
34
+ const handleCopy = async (content: string) => {
35
+ setCopyStatus("copying");
36
+ const success = await copyToClipboard(content);
37
+ setCopyStatus(success ? "copied" : "error");
38
+ setTimeout(() => setCopyStatus("idle"), 1500);
39
+ };
40
+
41
+ const handleDownload = () => {
42
+ const result = exportTokens({
43
+ format: selectedExportFormat,
44
+ themeId,
45
+ colorMode
46
+ });
47
+ downloadFile(result);
48
+ };
49
+
50
+ const handleDownloadAll = () => {
51
+ const result = generateAllThemesCSS();
52
+ downloadFile(result);
53
+ };
54
+
55
+ const currentTheme = NITRO_ALL_THEMES.find(t => t.id === themeId);
56
+
57
+ return (
58
+ <Card className="border-border/50 bg-background/60">
59
+ <CardHeader>
60
+ <div className="flex flex-wrap items-center justify-between gap-3">
61
+ <div>
62
+ <CardTitle>Gradient Studio</CardTitle>
63
+ <CardDescription>
64
+ Preview every theme to unlock the secret Memory Lane palette.
65
+ </CardDescription>
66
+ </div>
67
+ <Badge variant="glass">Shadcn Ready</Badge>
68
+ </div>
69
+ </CardHeader>
70
+ <CardContent className="space-y-6">
71
+ {/* Theme Selection */}
72
+ <div className="flex flex-col gap-3 rounded-2xl border border-border/40 bg-background/50 px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
73
+ <div className="space-y-1 flex-1">
74
+ <p className="text-sm font-semibold flex items-center gap-2">
75
+ <Palette className="h-4 w-4" />
76
+ Select Theme
77
+ </p>
78
+ <p className="text-xs text-muted-foreground">
79
+ Choose a theme to export.
80
+ </p>
81
+ </div>
82
+ <Select value={themeId} onValueChange={(v: string) => setThemeId(v as ThemeId)}>
83
+ <SelectTrigger className="w-full sm:w-[220px]">
84
+ <SelectValue placeholder="Select theme" />
85
+ </SelectTrigger>
86
+ <SelectContent>
87
+ {NITRO_ALL_THEMES.map((theme) => (
88
+ <SelectItem key={theme.id} value={theme.id}>
89
+ <div className="flex items-center gap-2">
90
+ <div
91
+ className="w-4 h-4 rounded-md border border-border/30"
92
+ style={{ background: theme.preview }}
93
+ />
94
+ <span>{theme.label}</span>
95
+ </div>
96
+ </SelectItem>
97
+ ))}
98
+ </SelectContent>
99
+ </Select>
100
+ </div>
101
+
102
+ {/* Color Mode Toggle */}
103
+ <div className="flex flex-col gap-3 rounded-2xl border border-border/40 bg-background/50 px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
104
+ <div className="space-y-1">
105
+ <p className="text-sm font-semibold">Color Mode</p>
106
+ <p className="text-xs text-muted-foreground">
107
+ Switch between light and dark surfaces.
108
+ </p>
109
+ </div>
110
+ <div className="flex items-center gap-3 sm:justify-end">
111
+ <Sun className={cn("h-4 w-4", isLight ? "text-foreground" : "text-muted-foreground")} />
112
+ <Switch
113
+ checked={!isLight}
114
+ onCheckedChange={(checked) => setColorMode(checked ? "dark" : "light")}
115
+ label="Toggle dark mode"
116
+ />
117
+ <Moon className={cn("h-4 w-4", !isLight ? "text-foreground" : "text-muted-foreground")} />
118
+ </div>
119
+ </div>
120
+
121
+ {/* Export Section */}
122
+ <div className="flex flex-col gap-3 rounded-2xl border border-border/40 bg-background/50 px-4 py-3">
123
+ <div className="flex flex-wrap items-center justify-between gap-3">
124
+ <div>
125
+ <p className="text-sm font-semibold flex items-center gap-2">
126
+ <Download className="h-4 w-4" />
127
+ Export Theme
128
+ </p>
129
+ <p className="text-xs text-muted-foreground">
130
+ Download theme files for your project.
131
+ </p>
132
+ </div>
133
+ </div>
134
+
135
+ <div className="flex flex-wrap gap-2">
136
+ <Select value={selectedExportFormat} onValueChange={(v: string) => setSelectedExportFormat(v as ExportFormat)}>
137
+ <SelectTrigger className="w-[140px] h-8">
138
+ <SelectValue placeholder="Format" />
139
+ </SelectTrigger>
140
+ <SelectContent>
141
+ {exportFormats.map((format) => (
142
+ <SelectItem key={format.value} value={format.value}>
143
+ {format.label}
144
+ </SelectItem>
145
+ ))}
146
+ </SelectContent>
147
+ </Select>
148
+
149
+ <Button
150
+ variant="outline"
151
+ size="sm"
152
+ className="h-8 gap-1.5"
153
+ onClick={() => handleCopy(exportTokens({ format: selectedExportFormat, themeId, colorMode }).content)}
154
+ disabled={copyStatus === "copying"}
155
+ >
156
+ {copyStatus === "copied" ? <Check className="h-3.5 w-3.5" /> : <Copy className="h-3.5 w-3.5" />}
157
+ {copyStatus === "copying" ? "Copying..." : copyStatus === "copied" ? "Copied!" : "Copy"}
158
+ </Button>
159
+
160
+ <Button
161
+ size="sm"
162
+ className="h-8 gap-1.5"
163
+ onClick={handleDownload}
164
+ >
165
+ <Download className="h-3.5 w-3.5" />
166
+ Download
167
+ </Button>
168
+ </div>
169
+
170
+ <div className="pt-2 border-t border-border/30">
171
+ <Button
172
+ variant="secondary"
173
+ size="sm"
174
+ className="w-full h-8 gap-1.5"
175
+ onClick={handleDownloadAll}
176
+ >
177
+ <FileCode className="h-3.5 w-3.5" />
178
+ Download All Themes ({NITRO_ALL_THEMES.length} themes)
179
+ </Button>
180
+ </div>
181
+ </div>
182
+
183
+ <div className="space-y-3 rounded-2xl border border-border/40 bg-background/50 px-4 py-3">
184
+ <div className="flex flex-wrap items-center justify-between gap-3">
185
+ <div>
186
+ <p className="text-sm font-semibold">Secret Theme Unlock</p>
187
+ <p className="text-xs text-muted-foreground">
188
+ Preview all {totalThemes} public themes to reveal Memory Lane.
189
+ </p>
190
+ </div>
191
+ <Badge variant={memoryLaneUnlocked ? "default" : "outline"}>
192
+ {memoryLaneUnlocked ? "Unlocked" : `${remainingForUnlock} remaining`}
193
+ </Badge>
194
+ </div>
195
+ <div className="space-y-2">
196
+ <div className="flex items-center justify-between text-xs text-muted-foreground">
197
+ <span>{seenThemes} of {totalThemes} previewed</span>
198
+ <span>{progress}%</span>
199
+ </div>
200
+ <div className="h-2 rounded-full bg-border/60">
201
+ <div
202
+ className="h-full rounded-full bg-primary/80 transition-all"
203
+ style={{ width: `${progress}%` }}
204
+ aria-hidden
205
+ />
206
+ </div>
207
+ </div>
208
+ </div>
209
+
210
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
211
+ {availableThemes.map((theme) => {
212
+ const isActive = themeId === theme.id;
213
+ const isSecret = theme.id === MEMORY_LANE_THEME.id;
214
+ const isLocked = isSecret && !memoryLaneUnlocked;
215
+
216
+ return (
217
+ <div
218
+ key={theme.id}
219
+ role="button"
220
+ tabIndex={isLocked ? -1 : 0}
221
+ onClick={() => !isLocked && setThemeId(theme.id)}
222
+ onKeyDown={(event) => {
223
+ if (isLocked) return;
224
+ if (event.key === "Enter" || event.key === " ") {
225
+ event.preventDefault();
226
+ setThemeId(theme.id);
227
+ }
228
+ }}
229
+ className={cn(
230
+ "group flex flex-col overflow-hidden rounded-2xl border border-border/40 bg-background/40 text-left transition-all",
231
+ isActive && "border-primary/60 shadow-[0_0_25px_hsl(var(--primary)_/_0.35)]",
232
+ isLocked && "cursor-not-allowed opacity-70",
233
+ )}
234
+ >
235
+ <div
236
+ className="relative h-24 w-full"
237
+ style={{ backgroundImage: theme.preview }}
238
+ >
239
+ <div className="absolute inset-0 bg-black/10" />
240
+ {isLocked && (
241
+ <div className="absolute inset-0 flex items-center justify-center bg-black/40 text-white">
242
+ <Lock className="h-5 w-5" />
243
+ </div>
244
+ )}
245
+ </div>
246
+ <div className="flex flex-wrap items-center justify-between gap-2 px-3 py-3">
247
+ <div className="min-w-0">
248
+ <p className="text-sm font-semibold">{theme.label}</p>
249
+ <p className="text-xs text-muted-foreground">
250
+ {isActive ? "Active" : "Preview"}
251
+ </p>
252
+ </div>
253
+ <Button
254
+ variant={isActive ? "glow" : "ghost"}
255
+ size="sm"
256
+ className="h-7 px-3"
257
+ >
258
+ {isActive ? "Live" : "Set"}
259
+ </Button>
260
+ </div>
261
+ </div>
262
+ );
263
+ })}
264
+ </div>
265
+ </CardContent>
266
+ </Card>
267
+ );
268
+ }