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.
- package/.eslintrc.json +3 -0
- package/.github/FUNDING.yml +2 -0
- package/README.md +140 -0
- package/app/docs/page.tsx +417 -0
- package/app/gallery/page.tsx +398 -0
- package/app/globals.css +1155 -0
- package/app/layout.tsx +36 -0
- package/app/page.tsx +600 -0
- package/app/showcase/page.tsx +730 -0
- package/app/studio/page.tsx +1310 -0
- package/cli/index.mjs +1141 -0
- package/cli/templates/theme-context.tsx +120 -0
- package/cli/templates/theme-engine.ts +237 -0
- package/cli/templates/themes.css +512 -0
- package/components/site/component-showcase.tsx +623 -0
- package/components/site/site-data.ts +103 -0
- package/components/site/site-header.tsx +270 -0
- package/components/templates/blog.tsx +198 -0
- package/components/templates/components-showcase.tsx +298 -0
- package/components/templates/dashboard.tsx +246 -0
- package/components/templates/ecommerce.tsx +199 -0
- package/components/templates/mail.tsx +275 -0
- package/components/templates/saas-landing.tsx +169 -0
- package/components/theme/studio-code-panel.tsx +485 -0
- package/components/theme/theme-context.tsx +120 -0
- package/components/theme/theme-engine.ts +237 -0
- package/components/theme/theme-exporter.tsx +369 -0
- package/components/theme/theme-panel.tsx +268 -0
- package/components/theme/token-export-utils.ts +1211 -0
- package/components/ui/animated.tsx +55 -0
- package/components/ui/avatar.tsx +38 -0
- package/components/ui/badge.tsx +32 -0
- package/components/ui/button.tsx +65 -0
- package/components/ui/card.tsx +56 -0
- package/components/ui/checkbox.tsx +19 -0
- package/components/ui/command-palette.tsx +245 -0
- package/components/ui/gsap-animated.tsx +436 -0
- package/components/ui/input.tsx +17 -0
- package/components/ui/select.tsx +176 -0
- package/components/ui/skeleton.tsx +102 -0
- package/components/ui/switch.tsx +43 -0
- package/components/ui/tabs.tsx +115 -0
- package/components/ui/toast.tsx +119 -0
- package/gradient-forge/theme-context.tsx +119 -0
- package/gradient-forge/theme-engine.ts +236 -0
- package/gradient-forge/themes.css +556 -0
- package/lib/animations.ts +50 -0
- package/lib/gsap.ts +426 -0
- package/lib/utils.ts +6 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +6 -0
- package/package.json +53 -0
- package/postcss.config.mjs +5 -0
- package/tailwind.config.ts +15 -0
- package/tsconfig.json +43 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import gsap from "gsap";
|
|
5
|
+
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
6
|
+
import { SiteHeader } from "@/components/site/site-header";
|
|
7
|
+
import { Badge } from "@/components/ui/badge";
|
|
8
|
+
import { Button } from "@/components/ui/button";
|
|
9
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
10
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
11
|
+
import {
|
|
12
|
+
LayoutTemplate,
|
|
13
|
+
Monitor,
|
|
14
|
+
Smartphone,
|
|
15
|
+
Tablet,
|
|
16
|
+
Code2,
|
|
17
|
+
Copy,
|
|
18
|
+
Check,
|
|
19
|
+
Download,
|
|
20
|
+
Eye,
|
|
21
|
+
X,
|
|
22
|
+
ChevronRight,
|
|
23
|
+
Palette,
|
|
24
|
+
Sparkles,
|
|
25
|
+
Zap,
|
|
26
|
+
RefreshCw,
|
|
27
|
+
Maximize2
|
|
28
|
+
} from "lucide-react";
|
|
29
|
+
import { useThemeContext } from "@/components/theme/theme-context";
|
|
30
|
+
import { NITRO_ALL_THEMES, type ThemeId } from "@/components/theme/theme-engine";
|
|
31
|
+
import { useToast } from "@/components/ui/toast";
|
|
32
|
+
import { cn } from "@/lib/utils";
|
|
33
|
+
import { MagneticButton } from "@/components/ui/gsap-animated";
|
|
34
|
+
|
|
35
|
+
// Template imports
|
|
36
|
+
import { SaaSLandingTemplate } from "@/components/templates/saas-landing";
|
|
37
|
+
import { DashboardTemplate } from "@/components/templates/dashboard";
|
|
38
|
+
import { MailTemplate } from "@/components/templates/mail";
|
|
39
|
+
import { EcommerceTemplate } from "@/components/templates/ecommerce";
|
|
40
|
+
import { BlogTemplate } from "@/components/templates/blog";
|
|
41
|
+
import { ComponentsTemplate } from "@/components/templates/components-showcase";
|
|
42
|
+
|
|
43
|
+
if (typeof window !== "undefined") {
|
|
44
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const templates = [
|
|
48
|
+
{
|
|
49
|
+
id: "saas-landing",
|
|
50
|
+
title: "SaaS Landing Page",
|
|
51
|
+
description: "Modern landing page with hero section, features, pricing, and CTA. Perfect for startups.",
|
|
52
|
+
icon: LayoutTemplate,
|
|
53
|
+
color: "from-blue-500/20 to-purple-500/20",
|
|
54
|
+
borderColor: "border-blue-500/30",
|
|
55
|
+
preview: "saas",
|
|
56
|
+
tags: ["Marketing", "Startup", "B2B"],
|
|
57
|
+
popular: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "dashboard",
|
|
61
|
+
title: "Analytics Dashboard",
|
|
62
|
+
description: "Full-featured dashboard with sidebar navigation, charts, stats cards, and data tables.",
|
|
63
|
+
icon: Monitor,
|
|
64
|
+
color: "from-emerald-500/20 to-teal-500/20",
|
|
65
|
+
borderColor: "border-emerald-500/30",
|
|
66
|
+
preview: "dashboard",
|
|
67
|
+
tags: ["Admin", "Analytics", "SaaS"],
|
|
68
|
+
popular: true,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "mail",
|
|
72
|
+
title: "Mail Application",
|
|
73
|
+
description: "Email client interface with inbox, compose, sidebar, and message threading.",
|
|
74
|
+
icon: LayoutTemplate,
|
|
75
|
+
color: "from-orange-500/20 to-red-500/20",
|
|
76
|
+
borderColor: "border-orange-500/30",
|
|
77
|
+
preview: "mail",
|
|
78
|
+
tags: ["Productivity", "Communication"],
|
|
79
|
+
popular: false,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "ecommerce",
|
|
83
|
+
title: "E-commerce Store",
|
|
84
|
+
description: "Product listings, shopping cart, checkout flow, and order management.",
|
|
85
|
+
icon: LayoutTemplate,
|
|
86
|
+
color: "from-pink-500/20 to-rose-500/20",
|
|
87
|
+
borderColor: "border-pink-500/30",
|
|
88
|
+
preview: "ecommerce",
|
|
89
|
+
tags: ["Shop", "Retail", "B2C"],
|
|
90
|
+
popular: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "blog",
|
|
94
|
+
title: "Blog Platform",
|
|
95
|
+
description: "Content-focused blog with article cards, reading view, and author profiles.",
|
|
96
|
+
icon: LayoutTemplate,
|
|
97
|
+
color: "from-violet-500/20 to-indigo-500/20",
|
|
98
|
+
borderColor: "border-violet-500/30",
|
|
99
|
+
preview: "blog",
|
|
100
|
+
tags: ["Content", "Publishing"],
|
|
101
|
+
popular: false,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "components",
|
|
105
|
+
title: "Component Library",
|
|
106
|
+
description: "Complete component showcase with all shadcn/ui elements styled with your theme.",
|
|
107
|
+
icon: LayoutTemplate,
|
|
108
|
+
color: "from-cyan-500/20 to-blue-500/20",
|
|
109
|
+
borderColor: "border-cyan-500/30",
|
|
110
|
+
preview: "components",
|
|
111
|
+
tags: ["UI Kit", "Design System"],
|
|
112
|
+
popular: false,
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const templateComponents: Record<string, React.FC<{ preview?: boolean }>> = {
|
|
117
|
+
"saas-landing": SaaSLandingTemplate,
|
|
118
|
+
"dashboard": DashboardTemplate,
|
|
119
|
+
"mail": MailTemplate,
|
|
120
|
+
"ecommerce": EcommerceTemplate,
|
|
121
|
+
"blog": BlogTemplate,
|
|
122
|
+
"components": ComponentsTemplate,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default function ShowcasePage() {
|
|
126
|
+
const [selectedTemplate, setSelectedTemplate] = useState<string>("saas-landing");
|
|
127
|
+
const [previewMode, setPreviewMode] = useState<"desktop" | "tablet" | "mobile">("desktop");
|
|
128
|
+
const [showCode, setShowCode] = useState(false);
|
|
129
|
+
const [activeTab, setActiveTab] = useState<string>("preview");
|
|
130
|
+
const [copied, setCopied] = useState(false);
|
|
131
|
+
const { themeId, setThemeId, colorMode } = useThemeContext();
|
|
132
|
+
const { showToast } = useToast();
|
|
133
|
+
const previewRef = useRef<HTMLDivElement>(null);
|
|
134
|
+
const sidebarRef = useRef<HTMLDivElement>(null);
|
|
135
|
+
|
|
136
|
+
const selectedTemplateData = templates.find(t => t.id === selectedTemplate);
|
|
137
|
+
const TemplateComponent = templateComponents[selectedTemplate];
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
// Animate template cards
|
|
141
|
+
const cards = document.querySelectorAll(".template-card");
|
|
142
|
+
gsap.fromTo(
|
|
143
|
+
cards,
|
|
144
|
+
{ opacity: 0, y: 30 },
|
|
145
|
+
{
|
|
146
|
+
opacity: 1,
|
|
147
|
+
y: 0,
|
|
148
|
+
duration: 0.5,
|
|
149
|
+
stagger: 0.08,
|
|
150
|
+
ease: "power3.out",
|
|
151
|
+
scrollTrigger: {
|
|
152
|
+
trigger: ".template-grid",
|
|
153
|
+
start: "top 85%",
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
return () => {
|
|
159
|
+
ScrollTrigger.getAll().forEach(trigger => trigger.kill());
|
|
160
|
+
};
|
|
161
|
+
}, []);
|
|
162
|
+
|
|
163
|
+
const handleCopyCode = () => {
|
|
164
|
+
const code = generateTemplateCode(selectedTemplate);
|
|
165
|
+
navigator.clipboard.writeText(code);
|
|
166
|
+
setCopied(true);
|
|
167
|
+
showToast("Template code copied!", "success");
|
|
168
|
+
setTimeout(() => setCopied(false), 2000);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const handleExport = () => {
|
|
172
|
+
showToast("Template exported!", "success");
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const handleThemeChange = (newThemeId: ThemeId) => {
|
|
176
|
+
setThemeId(newThemeId);
|
|
177
|
+
showToast(`Applied ${NITRO_ALL_THEMES.find(t => t.id === newThemeId)?.label} theme`, "success");
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const getPreviewWidth = () => {
|
|
181
|
+
switch (previewMode) {
|
|
182
|
+
case "mobile": return "375px";
|
|
183
|
+
case "tablet": return "768px";
|
|
184
|
+
default: return "100%";
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div className="min-h-screen flex flex-col">
|
|
190
|
+
<SiteHeader />
|
|
191
|
+
|
|
192
|
+
<main className="flex-1 flex">
|
|
193
|
+
{/* Left Sidebar - Template List */}
|
|
194
|
+
<aside className="w-72 border-r border-border/50 bg-background/50 backdrop-blur-xl flex flex-col">
|
|
195
|
+
<div className="p-4 border-b border-border/50">
|
|
196
|
+
<h2 className="font-semibold flex items-center gap-2">
|
|
197
|
+
<LayoutTemplate className="h-4 w-4 text-primary" />
|
|
198
|
+
Templates
|
|
199
|
+
</h2>
|
|
200
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
201
|
+
Select a template to preview
|
|
202
|
+
</p>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<div className="flex-1 overflow-y-auto p-3 space-y-2 template-grid">
|
|
206
|
+
{templates.map((template) => (
|
|
207
|
+
<div
|
|
208
|
+
key={template.id}
|
|
209
|
+
className={cn(
|
|
210
|
+
"template-card cursor-pointer rounded-xl border p-3 transition-all duration-200",
|
|
211
|
+
selectedTemplate === template.id
|
|
212
|
+
? cn("bg-primary/5 border-primary/50 shadow-md", template.borderColor)
|
|
213
|
+
: "border-border/50 hover:border-primary/30 hover:bg-muted/30"
|
|
214
|
+
)}
|
|
215
|
+
onClick={() => {
|
|
216
|
+
setSelectedTemplate(template.id);
|
|
217
|
+
gsap.fromTo(
|
|
218
|
+
previewRef.current,
|
|
219
|
+
{ opacity: 0, y: 10 },
|
|
220
|
+
{ opacity: 1, y: 0, duration: 0.3 }
|
|
221
|
+
);
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<div className="flex items-start gap-3">
|
|
225
|
+
<div className={cn(
|
|
226
|
+
"h-10 w-10 rounded-lg flex items-center justify-center shrink-0",
|
|
227
|
+
"bg-gradient-to-br",
|
|
228
|
+
template.color
|
|
229
|
+
)}>
|
|
230
|
+
<template.icon className="h-5 w-5 text-foreground/80" />
|
|
231
|
+
</div>
|
|
232
|
+
<div className="flex-1 min-w-0">
|
|
233
|
+
<div className="flex items-center gap-2">
|
|
234
|
+
<h3 className="font-medium text-sm truncate">{template.title}</h3>
|
|
235
|
+
{template.popular && (
|
|
236
|
+
<Badge variant="default" className="text-[10px] px-1.5 py-0 h-4">
|
|
237
|
+
<Sparkles className="h-2.5 w-2.5 mr-0.5" />
|
|
238
|
+
Popular
|
|
239
|
+
</Badge>
|
|
240
|
+
)}
|
|
241
|
+
</div>
|
|
242
|
+
<p className="text-xs text-muted-foreground line-clamp-2 mt-0.5">
|
|
243
|
+
{template.description}
|
|
244
|
+
</p>
|
|
245
|
+
<div className="flex flex-wrap gap-1 mt-2">
|
|
246
|
+
{template.tags.map(tag => (
|
|
247
|
+
<span key={tag} className="text-[10px] px-1.5 py-0.5 rounded bg-muted text-muted-foreground">
|
|
248
|
+
{tag}
|
|
249
|
+
</span>
|
|
250
|
+
))}
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
))}
|
|
256
|
+
</div>
|
|
257
|
+
</aside>
|
|
258
|
+
|
|
259
|
+
{/* Main Content Area */}
|
|
260
|
+
<div className="flex-1 flex flex-col min-w-0">
|
|
261
|
+
{/* Toolbar */}
|
|
262
|
+
<div className="h-14 border-b border-border/50 bg-background/50 backdrop-blur-xl flex items-center justify-between px-4">
|
|
263
|
+
<div className="flex items-center gap-4">
|
|
264
|
+
<h1 className="font-semibold">{selectedTemplateData?.title}</h1>
|
|
265
|
+
<div className="h-4 w-px bg-border" />
|
|
266
|
+
<div className="flex items-center gap-1 bg-muted rounded-lg p-1">
|
|
267
|
+
<Button
|
|
268
|
+
variant={previewMode === "desktop" ? "secondary" : "ghost"}
|
|
269
|
+
size="sm"
|
|
270
|
+
className="h-7 w-7 p-0"
|
|
271
|
+
onClick={() => setPreviewMode("desktop")}
|
|
272
|
+
>
|
|
273
|
+
<Monitor className="h-4 w-4" />
|
|
274
|
+
</Button>
|
|
275
|
+
<Button
|
|
276
|
+
variant={previewMode === "tablet" ? "secondary" : "ghost"}
|
|
277
|
+
size="sm"
|
|
278
|
+
className="h-7 w-7 p-0"
|
|
279
|
+
onClick={() => setPreviewMode("tablet")}
|
|
280
|
+
>
|
|
281
|
+
<Tablet className="h-4 w-4" />
|
|
282
|
+
</Button>
|
|
283
|
+
<Button
|
|
284
|
+
variant={previewMode === "mobile" ? "secondary" : "ghost"}
|
|
285
|
+
size="sm"
|
|
286
|
+
className="h-7 w-7 p-0"
|
|
287
|
+
onClick={() => setPreviewMode("mobile")}
|
|
288
|
+
>
|
|
289
|
+
<Smartphone className="h-4 w-4" />
|
|
290
|
+
</Button>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
|
|
294
|
+
<div className="flex items-center gap-2">
|
|
295
|
+
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-auto">
|
|
296
|
+
<TabsList className="h-8">
|
|
297
|
+
<TabsTrigger value="preview" className="text-xs gap-1.5">
|
|
298
|
+
<Eye className="h-3.5 w-3.5" />
|
|
299
|
+
Preview
|
|
300
|
+
</TabsTrigger>
|
|
301
|
+
<TabsTrigger value="code" className="text-xs gap-1.5">
|
|
302
|
+
<Code2 className="h-3.5 w-3.5" />
|
|
303
|
+
Code
|
|
304
|
+
</TabsTrigger>
|
|
305
|
+
</TabsList>
|
|
306
|
+
</Tabs>
|
|
307
|
+
|
|
308
|
+
<div className="h-4 w-px bg-border mx-1" />
|
|
309
|
+
|
|
310
|
+
<MagneticButton strength={0.1}>
|
|
311
|
+
<Button
|
|
312
|
+
variant="outline"
|
|
313
|
+
size="sm"
|
|
314
|
+
className="gap-1.5 text-xs"
|
|
315
|
+
onClick={handleCopyCode}
|
|
316
|
+
>
|
|
317
|
+
{copied ? (
|
|
318
|
+
<Check className="h-3.5 w-3.5 text-emerald-500" />
|
|
319
|
+
) : (
|
|
320
|
+
<Copy className="h-3.5 w-3.5" />
|
|
321
|
+
)}
|
|
322
|
+
Copy Code
|
|
323
|
+
</Button>
|
|
324
|
+
</MagneticButton>
|
|
325
|
+
|
|
326
|
+
<MagneticButton strength={0.1}>
|
|
327
|
+
<Button
|
|
328
|
+
variant="glow"
|
|
329
|
+
size="sm"
|
|
330
|
+
className="gap-1.5 text-xs"
|
|
331
|
+
onClick={handleExport}
|
|
332
|
+
>
|
|
333
|
+
<Download className="h-3.5 w-3.5" />
|
|
334
|
+
Export
|
|
335
|
+
</Button>
|
|
336
|
+
</MagneticButton>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
{/* Preview/Code Area */}
|
|
341
|
+
<div className="flex-1 overflow-hidden bg-muted/30 relative">
|
|
342
|
+
{activeTab === "preview" ? (
|
|
343
|
+
<div className="h-full overflow-auto p-8 flex justify-center">
|
|
344
|
+
<div
|
|
345
|
+
ref={previewRef}
|
|
346
|
+
className="bg-background rounded-xl shadow-2xl overflow-hidden transition-all duration-300"
|
|
347
|
+
style={{
|
|
348
|
+
width: getPreviewWidth(),
|
|
349
|
+
minHeight: "600px",
|
|
350
|
+
maxWidth: "100%"
|
|
351
|
+
}}
|
|
352
|
+
>
|
|
353
|
+
<TemplateComponent preview={true} />
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
) : (
|
|
357
|
+
<div className="h-full overflow-auto">
|
|
358
|
+
<div className="max-w-4xl mx-auto p-6">
|
|
359
|
+
<div className="rounded-xl bg-black/90 border border-white/10 overflow-hidden">
|
|
360
|
+
<div className="flex items-center justify-between px-4 py-2 bg-white/5 border-b border-white/10">
|
|
361
|
+
<span className="text-xs text-white/60">Template Code</span>
|
|
362
|
+
<Button
|
|
363
|
+
variant="ghost"
|
|
364
|
+
size="sm"
|
|
365
|
+
className="h-7 text-xs text-white/60 hover:text-white"
|
|
366
|
+
onClick={handleCopyCode}
|
|
367
|
+
>
|
|
368
|
+
{copied ? <Check className="h-3.5 w-3.5 text-emerald-500" /> : <Copy className="h-3.5 w-3.5" />}
|
|
369
|
+
{copied ? "Copied!" : "Copy"}
|
|
370
|
+
</Button>
|
|
371
|
+
</div>
|
|
372
|
+
<pre className="p-4 text-sm text-white/90 overflow-x-auto">
|
|
373
|
+
<code>{generateTemplateCode(selectedTemplate)}</code>
|
|
374
|
+
</pre>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
{/* Right Sidebar - Theme Switcher */}
|
|
383
|
+
<aside
|
|
384
|
+
ref={sidebarRef}
|
|
385
|
+
className="w-72 border-l border-border/50 bg-background/50 backdrop-blur-xl flex flex-col"
|
|
386
|
+
>
|
|
387
|
+
<div className="p-4 border-b border-border/50">
|
|
388
|
+
<h2 className="font-semibold flex items-center gap-2">
|
|
389
|
+
<Palette className="h-4 w-4 text-primary" />
|
|
390
|
+
Theme Preview
|
|
391
|
+
</h2>
|
|
392
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
393
|
+
See how this template looks with different themes
|
|
394
|
+
</p>
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
{/* Current Theme Display */}
|
|
398
|
+
<div className="p-4 border-b border-border/50">
|
|
399
|
+
<div className="text-xs text-muted-foreground mb-2">Current Theme</div>
|
|
400
|
+
<div
|
|
401
|
+
className="rounded-xl p-4 border transition-all duration-300"
|
|
402
|
+
style={{
|
|
403
|
+
borderColor: "hsl(var(--primary) / 0.3)",
|
|
404
|
+
background: "linear-gradient(135deg, hsl(var(--primary) / 0.1), hsl(var(--accent) / 0.1))"
|
|
405
|
+
}}
|
|
406
|
+
>
|
|
407
|
+
<div className="flex items-center gap-3">
|
|
408
|
+
<div
|
|
409
|
+
className="h-12 w-12 rounded-xl shadow-lg"
|
|
410
|
+
style={{
|
|
411
|
+
background: NITRO_ALL_THEMES.find(t => t.id === themeId)?.preview
|
|
412
|
+
}}
|
|
413
|
+
/>
|
|
414
|
+
<div>
|
|
415
|
+
<div className="font-medium text-sm">
|
|
416
|
+
{NITRO_ALL_THEMES.find(t => t.id === themeId)?.label}
|
|
417
|
+
</div>
|
|
418
|
+
<div className="text-xs text-muted-foreground capitalize">
|
|
419
|
+
{colorMode} Mode
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
{/* Theme List */}
|
|
427
|
+
<div className="flex-1 overflow-y-auto p-3 space-y-2">
|
|
428
|
+
<div className="text-xs text-muted-foreground px-1 mb-2">All Themes ({NITRO_ALL_THEMES.length})</div>
|
|
429
|
+
{NITRO_ALL_THEMES.map((theme) => (
|
|
430
|
+
<button
|
|
431
|
+
key={theme.id}
|
|
432
|
+
onClick={() => handleThemeChange(theme.id)}
|
|
433
|
+
className={cn(
|
|
434
|
+
"w-full flex items-center gap-3 p-2.5 rounded-xl border transition-all duration-200 text-left",
|
|
435
|
+
themeId === theme.id
|
|
436
|
+
? "border-primary/50 bg-primary/5 shadow-sm"
|
|
437
|
+
: "border-border/50 hover:border-primary/30 hover:bg-muted/30"
|
|
438
|
+
)}
|
|
439
|
+
>
|
|
440
|
+
<div
|
|
441
|
+
className="h-10 w-10 rounded-lg shadow-sm shrink-0"
|
|
442
|
+
style={{ background: theme.preview }}
|
|
443
|
+
/>
|
|
444
|
+
<div className="flex-1 min-w-0">
|
|
445
|
+
<div className="font-medium text-sm truncate">{theme.label}</div>
|
|
446
|
+
<div className="text-[10px] text-muted-foreground truncate">
|
|
447
|
+
{theme.id.replace("theme-nitro-", "").replace(/-/g, " ")}
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
{themeId === theme.id && (
|
|
451
|
+
<Check className="h-4 w-4 text-primary shrink-0" />
|
|
452
|
+
)}
|
|
453
|
+
</button>
|
|
454
|
+
))}
|
|
455
|
+
</div>
|
|
456
|
+
|
|
457
|
+
{/* Quick Actions */}
|
|
458
|
+
<div className="p-4 border-t border-border/50 space-y-2">
|
|
459
|
+
<Button
|
|
460
|
+
variant="outline"
|
|
461
|
+
size="sm"
|
|
462
|
+
className="w-full gap-2 text-xs"
|
|
463
|
+
onClick={() => setThemeId("theme-nitro-midnight-blurple")}
|
|
464
|
+
>
|
|
465
|
+
<RefreshCw className="h-3.5 w-3.5" />
|
|
466
|
+
Reset to Default
|
|
467
|
+
</Button>
|
|
468
|
+
<Button
|
|
469
|
+
variant="secondary"
|
|
470
|
+
size="sm"
|
|
471
|
+
className="w-full gap-2 text-xs"
|
|
472
|
+
asChild
|
|
473
|
+
>
|
|
474
|
+
<a href="/gallery">
|
|
475
|
+
Browse All Themes
|
|
476
|
+
<ChevronRight className="h-3.5 w-3.5" />
|
|
477
|
+
</a>
|
|
478
|
+
</Button>
|
|
479
|
+
</div>
|
|
480
|
+
</aside>
|
|
481
|
+
</main>
|
|
482
|
+
</div>
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Generate template code
|
|
487
|
+
function generateTemplateCode(templateId: string): string {
|
|
488
|
+
const codes: Record<string, string> = {
|
|
489
|
+
"saas-landing": `import { Button } from "@/components/ui/button";
|
|
490
|
+
import { Badge } from "@/components/ui/badge";
|
|
491
|
+
import { Card } from "@/components/ui/card";
|
|
492
|
+
import { ArrowRight, Check, Sparkles } from "lucide-react";
|
|
493
|
+
|
|
494
|
+
export default function LandingPage() {
|
|
495
|
+
return (
|
|
496
|
+
<div className="min-h-screen">
|
|
497
|
+
{/* Navbar */}
|
|
498
|
+
<nav className="border-b border-border/50 bg-background/80 backdrop-blur-xl">
|
|
499
|
+
<div className="max-w-7xl mx-auto px-4 h-16 flex items-center justify-between">
|
|
500
|
+
<div className="flex items-center gap-2">
|
|
501
|
+
<Sparkles className="h-5 w-5 text-primary" />
|
|
502
|
+
<span className="font-semibold">Your SaaS</span>
|
|
503
|
+
</div>
|
|
504
|
+
<div className="flex items-center gap-4">
|
|
505
|
+
<Button variant="ghost" size="sm">Features</Button>
|
|
506
|
+
<Button variant="ghost" size="sm">Pricing</Button>
|
|
507
|
+
<Button variant="glow" size="sm">Get Started</Button>
|
|
508
|
+
</div>
|
|
509
|
+
</div>
|
|
510
|
+
</nav>
|
|
511
|
+
|
|
512
|
+
{/* Hero */}
|
|
513
|
+
<section className="py-20 px-4 text-center">
|
|
514
|
+
<Badge variant="glass" className="mb-4">Now in Beta</Badge>
|
|
515
|
+
<h1 className="text-4xl md:text-6xl font-bold mb-6">
|
|
516
|
+
Build faster with{" "}
|
|
517
|
+
<span className="bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent">
|
|
518
|
+
Gradient Forge
|
|
519
|
+
</span>
|
|
520
|
+
</h1>
|
|
521
|
+
<p className="text-xl text-muted-foreground max-w-2xl mx-auto mb-8">
|
|
522
|
+
Production-ready theming system with beautiful gradients and surface-aware overlays.
|
|
523
|
+
</p>
|
|
524
|
+
<div className="flex gap-4 justify-center">
|
|
525
|
+
<Button size="lg" variant="glow">
|
|
526
|
+
Start Building <ArrowRight className="ml-2 h-4 w-4" />
|
|
527
|
+
</Button>
|
|
528
|
+
<Button size="lg" variant="outline">
|
|
529
|
+
View Documentation
|
|
530
|
+
</Button>
|
|
531
|
+
</div>
|
|
532
|
+
</section>
|
|
533
|
+
</div>
|
|
534
|
+
);
|
|
535
|
+
}`,
|
|
536
|
+
"dashboard": `import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
537
|
+
import { Button } from "@/components/ui/button";
|
|
538
|
+
import { Badge } from "@/components/ui/badge";
|
|
539
|
+
import { BarChart3, Users, DollarSign, TrendingUp } from "lucide-react";
|
|
540
|
+
|
|
541
|
+
export default function Dashboard() {
|
|
542
|
+
return (
|
|
543
|
+
<div className="min-h-screen bg-background">
|
|
544
|
+
{/* Header */}
|
|
545
|
+
<header className="border-b border-border/50 bg-background/80 backdrop-blur-xl">
|
|
546
|
+
<div className="flex h-16 items-center px-6 gap-4">
|
|
547
|
+
<h1 className="font-semibold">Analytics Dashboard</h1>
|
|
548
|
+
<div className="flex-1" />
|
|
549
|
+
<Button variant="outline" size="sm">Export</Button>
|
|
550
|
+
</div>
|
|
551
|
+
</header>
|
|
552
|
+
|
|
553
|
+
<main className="p-6">
|
|
554
|
+
{/* Stats Grid */}
|
|
555
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 mb-6">
|
|
556
|
+
<Card className="border-border/50 bg-background/60">
|
|
557
|
+
<CardHeader className="pb-2">
|
|
558
|
+
<CardTitle className="text-sm font-medium text-muted-foreground">
|
|
559
|
+
Total Revenue
|
|
560
|
+
</CardTitle>
|
|
561
|
+
</CardHeader>
|
|
562
|
+
<CardContent>
|
|
563
|
+
<div className="text-2xl font-bold">$45,231.89</div>
|
|
564
|
+
<p className="text-xs text-emerald-500">+20.1% from last month</p>
|
|
565
|
+
</CardContent>
|
|
566
|
+
</Card>
|
|
567
|
+
{/* More stat cards... */}
|
|
568
|
+
</div>
|
|
569
|
+
</main>
|
|
570
|
+
</div>
|
|
571
|
+
);
|
|
572
|
+
}`,
|
|
573
|
+
"mail": `import { Button } from "@/components/ui/button";
|
|
574
|
+
import { Card } from "@/components/ui/card";
|
|
575
|
+
import { Badge } from "@/components/ui/badge";
|
|
576
|
+
import { Inbox, Send, Archive, Trash } from "lucide-react";
|
|
577
|
+
|
|
578
|
+
export default function MailApp() {
|
|
579
|
+
return (
|
|
580
|
+
<div className="h-screen flex">
|
|
581
|
+
{/* Sidebar */}
|
|
582
|
+
<aside className="w-64 border-r border-border/50 bg-muted/30 p-4">
|
|
583
|
+
<Button className="w-full mb-4" variant="glow">
|
|
584
|
+
Compose
|
|
585
|
+
</Button>
|
|
586
|
+
<nav className="space-y-1">
|
|
587
|
+
<Button variant="ghost" className="w-full justify-start">
|
|
588
|
+
<Inbox className="mr-2 h-4 w-4" /> Inbox
|
|
589
|
+
</Button>
|
|
590
|
+
<Button variant="ghost" className="w-full justify-start">
|
|
591
|
+
<Send className="mr-2 h-4 w-4" /> Sent
|
|
592
|
+
</Button>
|
|
593
|
+
</nav>
|
|
594
|
+
</aside>
|
|
595
|
+
|
|
596
|
+
{/* Email List */}
|
|
597
|
+
<div className="flex-1 flex">
|
|
598
|
+
<div className="w-80 border-r border-border/50">
|
|
599
|
+
{/* Email items */}
|
|
600
|
+
</div>
|
|
601
|
+
|
|
602
|
+
{/* Email Content */}
|
|
603
|
+
<main className="flex-1 p-6">
|
|
604
|
+
{/* Email view */}
|
|
605
|
+
</main>
|
|
606
|
+
</div>
|
|
607
|
+
</div>
|
|
608
|
+
);
|
|
609
|
+
}`,
|
|
610
|
+
"ecommerce": `import { Button } from "@/components/ui/button";
|
|
611
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
612
|
+
import { Badge } from "@/components/ui/badge";
|
|
613
|
+
import { ShoppingCart, Star } from "lucide-react";
|
|
614
|
+
|
|
615
|
+
export default function Ecommerce() {
|
|
616
|
+
return (
|
|
617
|
+
<div className="min-h-screen">
|
|
618
|
+
{/* Header */}
|
|
619
|
+
<header className="border-b border-border/50 bg-background/80 backdrop-blur-xl">
|
|
620
|
+
<div className="flex h-16 items-center justify-between px-6">
|
|
621
|
+
<h1 className="font-semibold text-xl">Shop</h1>
|
|
622
|
+
<Button variant="outline" size="sm">
|
|
623
|
+
<ShoppingCart className="mr-2 h-4 w-4" />
|
|
624
|
+
Cart (0)
|
|
625
|
+
</Button>
|
|
626
|
+
</div>
|
|
627
|
+
</header>
|
|
628
|
+
|
|
629
|
+
{/* Products Grid */}
|
|
630
|
+
<main className="p-6">
|
|
631
|
+
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
|
632
|
+
{[1, 2, 3, 4].map((product) => (
|
|
633
|
+
<Card key={product} className="border-border/50">
|
|
634
|
+
<CardContent className="p-4">
|
|
635
|
+
<div className="aspect-square bg-muted rounded-lg mb-4" />
|
|
636
|
+
<h3 className="font-semibold">Product Name</h3>
|
|
637
|
+
<p className="text-sm text-muted-foreground">$99.00</p>
|
|
638
|
+
<Button className="w-full mt-4" variant="glow">
|
|
639
|
+
Add to Cart
|
|
640
|
+
</Button>
|
|
641
|
+
</CardContent>
|
|
642
|
+
</Card>
|
|
643
|
+
))}
|
|
644
|
+
</div>
|
|
645
|
+
</main>
|
|
646
|
+
</div>
|
|
647
|
+
);
|
|
648
|
+
}`,
|
|
649
|
+
"blog": `import { Card, CardContent } from "@/components/ui/card";
|
|
650
|
+
import { Badge } from "@/components/ui/badge";
|
|
651
|
+
import { Button } from "@/components/ui/button";
|
|
652
|
+
import { Calendar, Clock } from "lucide-react";
|
|
653
|
+
|
|
654
|
+
export default function Blog() {
|
|
655
|
+
return (
|
|
656
|
+
<div className="min-h-screen">
|
|
657
|
+
{/* Header */}
|
|
658
|
+
<header className="border-b border-border/50 bg-background/80 backdrop-blur-xl">
|
|
659
|
+
<div className="max-w-4xl mx-auto h-16 flex items-center justify-center">
|
|
660
|
+
<h1 className="font-semibold text-xl">My Blog</h1>
|
|
661
|
+
</div>
|
|
662
|
+
</header>
|
|
663
|
+
|
|
664
|
+
{/* Articles */}
|
|
665
|
+
<main className="max-w-4xl mx-auto p-6">
|
|
666
|
+
<article className="mb-12">
|
|
667
|
+
<div className="h-64 bg-muted rounded-2xl mb-6" />
|
|
668
|
+
<Badge variant="glass">Technology</Badge>
|
|
669
|
+
<h2 className="text-3xl font-bold mt-4 mb-4">
|
|
670
|
+
Building Beautiful UIs with Gradient Forge
|
|
671
|
+
</h2>
|
|
672
|
+
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-6">
|
|
673
|
+
<span className="flex items-center gap-1">
|
|
674
|
+
<Calendar className="h-4 w-4" /> Jan 15, 2024
|
|
675
|
+
</span>
|
|
676
|
+
<span className="flex items-center gap-1">
|
|
677
|
+
<Clock className="h-4 w-4" /> 5 min read
|
|
678
|
+
</span>
|
|
679
|
+
</div>
|
|
680
|
+
<p className="text-muted-foreground">
|
|
681
|
+
Learn how to create stunning gradient themes...
|
|
682
|
+
</p>
|
|
683
|
+
</article>
|
|
684
|
+
</main>
|
|
685
|
+
</div>
|
|
686
|
+
);
|
|
687
|
+
}`,
|
|
688
|
+
"components": `import { Button } from "@/components/ui/button";
|
|
689
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
690
|
+
import { Badge } from "@/components/ui/badge";
|
|
691
|
+
import { Input } from "@/components/ui/input";
|
|
692
|
+
|
|
693
|
+
export default function Components() {
|
|
694
|
+
return (
|
|
695
|
+
<div className="min-h-screen p-8">
|
|
696
|
+
<h1 className="text-3xl font-bold mb-8">Component Showcase</h1>
|
|
697
|
+
|
|
698
|
+
{/* Buttons */}
|
|
699
|
+
<Card className="mb-6">
|
|
700
|
+
<CardHeader>
|
|
701
|
+
<CardTitle>Buttons</CardTitle>
|
|
702
|
+
</CardHeader>
|
|
703
|
+
<CardContent className="flex flex-wrap gap-4">
|
|
704
|
+
<Button variant="default">Default</Button>
|
|
705
|
+
<Button variant="secondary">Secondary</Button>
|
|
706
|
+
<Button variant="outline">Outline</Button>
|
|
707
|
+
<Button variant="ghost">Ghost</Button>
|
|
708
|
+
<Button variant="glow">Glow</Button>
|
|
709
|
+
</CardContent>
|
|
710
|
+
</Card>
|
|
711
|
+
|
|
712
|
+
{/* Badges */}
|
|
713
|
+
<Card className="mb-6">
|
|
714
|
+
<CardHeader>
|
|
715
|
+
<CardTitle>Badges</CardTitle>
|
|
716
|
+
</CardHeader>
|
|
717
|
+
<CardContent className="flex flex-wrap gap-4">
|
|
718
|
+
<Badge>Default</Badge>
|
|
719
|
+
<Badge variant="secondary">Secondary</Badge>
|
|
720
|
+
<Badge variant="outline">Outline</Badge>
|
|
721
|
+
<Badge variant="glass">Glass</Badge>
|
|
722
|
+
</CardContent>
|
|
723
|
+
</Card>
|
|
724
|
+
</div>
|
|
725
|
+
);
|
|
726
|
+
}`,
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
return codes[templateId] || "// Template code not available";
|
|
730
|
+
}
|