Shadcraft (#203)
* chore: allow overriding ControlSection classNames * feat: Support Google Fonts API and load fonts dynamically * feat: Enforce System Prompt to use Google Fonts API if needed * styles: Improve GoogleFontPicker styles * feat: Pull Fonts from fileSystem instead of hitting the Google Fonts API every time * feat: Improve System Prompt to reference Google Fonts * refactor: Use Next.js Cache instead of manually fetching and reading from the filesystem * feat: Only enable Font picker fetch when it's open * chore: Remove JSON for Google Fonts catalogue * feat: Scroll to selected font if it's already fetched and add clean search button * chore: update imports * fix: dashboard preview * feat: Add shadcraft dialog --------- Co-authored-by: Luis Llanes <luisllaboj@gmail.com>
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.55005 8.37721C8.55005 7.08821 9.56905 6.04421 10.825 6.04421C12.082 6.04421 13.1 7.08721 13.1 8.37721C13.1 9.66621 12.082 10.7102 10.825 10.7102C9.57005 10.7102 8.55005 9.66721 8.55005 8.37721Z" fill="#1ABCFE"/>
|
||||
<path d="M4 13.0442C4 11.7552 5.019 10.7102 6.275 10.7102H8.55V13.0442C8.55 14.3322 7.531 15.3772 6.275 15.3772C5.019 15.3772 4 14.3322 4 13.0442Z" fill="#0ACF83"/>
|
||||
<path d="M8.55005 1.37721V6.04421H10.825C12.082 6.04421 13.1 4.99921 13.1 3.71021C13.1 2.42221 12.082 1.37721 10.825 1.37721H8.55005Z" fill="#FF7262"/>
|
||||
<path d="M4 3.71021C4 4.99921 5.019 6.04421 6.275 6.04421H8.55V1.37721H6.275C5.019 1.37721 4 2.42221 4 3.71021Z" fill="#F24E1E"/>
|
||||
<path d="M4 8.37721C4 9.66621 5.019 10.7102 6.275 10.7102H8.55V6.04421H6.275C5.019 6.04421 4 7.08721 4 8.37721Z" fill="#A259FF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 904 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="26" height="25" viewBox="0 0 26 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.775879 12.5256C0.775879 5.71806 6.29445 0.199493 13.102 0.199493C19.9095 0.199493 25.428 5.71806 25.428 12.5256C25.428 19.3331 19.9095 24.8516 13.102 24.8516C6.29445 24.8516 0.775879 19.3331 0.775879 12.5256Z" fill="#171717"/>
|
||||
<path d="M14.8004 14.1782C15.1555 13.8203 15.7334 13.8173 16.0914 14.1723C16.4494 14.5274 16.4514 15.1063 16.0963 15.4643L14.3072 17.268L14.2379 17.3315C13.8805 17.6252 13.3518 17.6057 13.0162 17.2729C12.6807 16.9401 12.6572 16.412 12.9479 16.0522L13.0104 15.9819L14.8004 14.1782ZM10.2809 12.4418C10.6374 12.0857 11.2154 12.0856 11.5719 12.4418C11.9284 12.7984 11.9284 13.3773 11.5719 13.7338L8.03477 17.2709L7.96543 17.3334C7.60692 17.6256 7.07796 17.6049 6.74375 17.2709C6.40957 16.9367 6.38902 16.4079 6.68125 16.0493L6.74375 15.9789L10.2809 12.4418ZM16.8961 11.9799C17.2527 11.6234 17.8315 11.6234 18.1881 11.9799C18.5443 12.3364 18.5443 12.9145 18.1881 13.2709L18.1529 13.3051L18.0836 13.3686C17.725 13.6607 17.1961 13.6393 16.8619 13.3051C16.5279 12.971 16.5074 12.443 16.7994 12.0844L16.8619 12.0141L16.8961 11.9799ZM13.8922 8.95257C14.2527 8.60029 14.8307 8.6069 15.1832 8.96722C15.5356 9.32768 15.5289 9.90568 15.1686 10.2582L13.8219 11.5766L13.7516 11.6381C13.3898 11.9264 12.8614 11.8996 12.5309 11.5619C12.2004 11.2242 12.185 10.6957 12.4811 10.3403L12.5445 10.2709L13.8922 8.95257ZM15.9967 6.7182C16.3533 6.36175 16.9312 6.36168 17.2877 6.7182C17.6442 7.07473 17.6442 7.65264 17.2877 8.00921L17.2516 8.04535L17.1822 8.10785C16.8236 8.40017 16.2948 8.37957 15.9606 8.04535C15.6263 7.71113 15.6057 7.18228 15.8981 6.82367L15.9606 6.75433L15.9967 6.7182Z" fill="#FAFAFA"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,197 @@
|
||||
"use client";
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import Logo from "@/assets/logo.svg";
|
||||
import Shadcraft from "@/assets/shadcraft.svg";
|
||||
import FigmaIcon from "@/assets/figma.svg";
|
||||
import { Check, X, ArrowUpRight, Figma, Cable, Paintbrush } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
interface FigmaExportDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function FigmaExportDialog({ open, onOpenChange }: FigmaExportDialogProps) {
|
||||
const previewUrl =
|
||||
"https://www.figma.com/design/J1e0cfCkDffMx6I0D0g5Nd/PREVIEW-%E2%80%A2-Shadcraft-%E2%80%A2-Beta-0.1.0?node-id=7050-2702&p=f&m=dev";
|
||||
const designers = [
|
||||
{ name: "Designer 1", avatar: "/figma-onboarding/avatar-1.png", fallback: "D1" },
|
||||
{ name: "Designer 2", avatar: "/figma-onboarding/avatar-2.png", fallback: "D2" },
|
||||
{ name: "Designer 3", avatar: "/figma-onboarding/avatar-3.png", fallback: "D3" },
|
||||
{ name: "Designer 4", avatar: "/figma-onboarding/avatar-4.png", fallback: "D4" },
|
||||
{ name: "Designer 5", avatar: "/figma-onboarding/avatar-5.png", fallback: "D5" },
|
||||
{ name: "Designer 6", avatar: "/figma-onboarding/avatar-6.png", fallback: "D6" },
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{
|
||||
step: "Step 1",
|
||||
title: "Download the kit",
|
||||
icon: <Figma className="h-6 w-6" />,
|
||||
},
|
||||
{
|
||||
step: "Step 2",
|
||||
title: "Open the plugin",
|
||||
icon: <Cable className="h-6 w-6" />,
|
||||
},
|
||||
{
|
||||
step: "Step 3",
|
||||
title: "Apply your theme",
|
||||
icon: <Paintbrush className="h-6 w-6" />,
|
||||
},
|
||||
];
|
||||
|
||||
const features = ["51 components", "44 blocks", "Dark mode", "1500+ icons"];
|
||||
|
||||
const handlePurchase = () => {
|
||||
const baseUrl = "https://shadcraft.lemonsqueezy.com/buy/ce9ced1d-3119-413d-82b8-a4baef6aab36";
|
||||
// const affiliateCode = process.env.NEXT_PUBLIC_AFFILIATE_CODE || "YOUR_AFFILIATE_CODE";
|
||||
const successRedirectUrl = "/";
|
||||
|
||||
const url = `${baseUrl}?embed=1&redirect_url=${encodeURIComponent(successRedirectUrl)}`;
|
||||
window.open(url, "_blank");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-h-[90vh] max-w-[600px] overflow-y-auto p-0">
|
||||
{/* Header */}
|
||||
<div className="p-8 pb-5">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Logo className="h-6 w-6" />
|
||||
<div className="text-lg font-bold">tweakcn</div>
|
||||
</div>
|
||||
<X className="h-4 w-4" />
|
||||
<div className="flex items-center gap-2">
|
||||
<Shadcraft className="h-6 w-6" />
|
||||
<div className="text-lg font-bold">shadcraft</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-16 px-8 pb-32">
|
||||
{/* Hero Section */}
|
||||
<div className="space-y-6 text-center">
|
||||
<h1 className="text-5xl leading-12 font-semibold tracking-tight">
|
||||
Apply your theme to the ultimate Figma UI kit
|
||||
</h1>
|
||||
|
||||
<div className="flex justify-center gap-3.5">
|
||||
<Button size="lg" className="h-10 px-8" onClick={handlePurchase}>
|
||||
Get started
|
||||
</Button>
|
||||
<Link href={previewUrl} target="_blank">
|
||||
<Button variant="outline" size="lg" className="h-10 gap-2 px-8">
|
||||
<FigmaIcon className="h-4 w-4" />
|
||||
Preview
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5 pt-1">
|
||||
<p className="text-muted-foreground text-sm">Trusted by top designers</p>
|
||||
<div className="flex justify-center -space-x-3">
|
||||
{designers.map((designer, index) => (
|
||||
<Avatar key={index} className="border-background h-8 w-8 border-2">
|
||||
<AvatarImage src={designer.avatar} alt={designer.name} />
|
||||
<AvatarFallback className="text-xs">{designer.fallback}</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* How it works */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-center text-2xl font-semibold">How it works</h2>
|
||||
<div className="border-border rounded-2xl border px-6">
|
||||
<div className="divide-border grid grid-cols-3 divide-x">
|
||||
{steps.map((step, index) => (
|
||||
<div key={index} className="space-y-2 px-6 py-6 text-center first:pl-0 last:pr-0">
|
||||
<div className="text-foreground mb-2 flex justify-center">{step.icon}</div>
|
||||
<p className="text-muted-foreground text-sm">{step.step}</p>
|
||||
<h3 className="font-semibold">{step.title}</h3>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Feature Description */}
|
||||
<div className="space-y-6 text-center">
|
||||
<div className="mx-auto max-w-sm space-y-1.5">
|
||||
<h2 className="text-2xl font-semibold">Top quality Figma UI kit for professionals</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Shadcraft is packed with top quality components, true to the shadcn/ui ethos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Demo UI Preview */}
|
||||
<div className="border-border relative overflow-hidden rounded-2xl border">
|
||||
<img
|
||||
src="/figma-onboarding/shadcraft-preview.jpg"
|
||||
alt="Shadcraft Figma UI Kit Preview"
|
||||
className="h-auto w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button variant="link" className="gap-1 text-sm">
|
||||
More on Shadcraft
|
||||
<ArrowUpRight className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Pricing */}
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-center text-2xl font-semibold">Pricing</h2>
|
||||
|
||||
<Card className="p-6">
|
||||
<div className="grid gap-7 md:grid-cols-2">
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-semibold">What you get with Shadcraft</h3>
|
||||
<div className="space-y-2">
|
||||
{features.map((feature, index) => (
|
||||
<div key={index} className="flex items-center gap-1.5">
|
||||
<Check className="h-4 w-4 text-green-600" />
|
||||
<span className="text-sm">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Badge variant="secondary" className="w-fit">
|
||||
Introductory offer
|
||||
</Badge>
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-end gap-1">
|
||||
<span className="text-5xl font-semibold">$89</span>
|
||||
</div>
|
||||
<div className="text-muted-foreground text-sm line-through">$119</div>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Button className="flex-1" onClick={handlePurchase}>
|
||||
Get started
|
||||
</Button>
|
||||
<Link href={previewUrl} target="_blank">
|
||||
<Button variant="outline" className="gap-2">
|
||||
<FigmaIcon className="h-4 w-4" />
|
||||
Preview
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<p className="text-muted-foreground text-center text-xs">Prices in USD</p>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import DiscordIcon from "@/assets/discord.svg";
|
||||
import FigmaIcon from "@/assets/figma.svg";
|
||||
import GitHubIcon from "@/assets/github.svg";
|
||||
import Logo from "@/assets/logo.svg";
|
||||
import TwitterIcon from "@/assets/twitter.svg";
|
||||
import { FigmaExportDialog } from "@/components/figma-export-dialog";
|
||||
import { SocialLink } from "@/components/social-link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { UserProfileDropdown } from "@/components/user-profile-dropdown";
|
||||
import { useGithubStars } from "@/hooks/use-github-stars";
|
||||
import { formatCompactNumber } from "@/utils/format";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { GetProCTA } from "./get-pro-cta";
|
||||
|
||||
export function Header() {
|
||||
const { stargazersCount } = useGithubStars("jnsahaj", "tweakcn");
|
||||
const [figmaDialogOpen, setFigmaDialogOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<header className="border-b">
|
||||
@@ -46,9 +51,19 @@ export function Header() {
|
||||
</SocialLink>
|
||||
</div>
|
||||
<Separator orientation="vertical" className="h-8" />
|
||||
<Button
|
||||
onClick={() => setFigmaDialogOpen(true)}
|
||||
variant="outline"
|
||||
className="flex h-8 items-center gap-2"
|
||||
>
|
||||
<FigmaIcon className="size-4" />
|
||||
Export to Figma
|
||||
</Button>
|
||||
<UserProfileDropdown />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FigmaExportDialog open={figmaDialogOpen} onOpenChange={setFigmaDialogOpen} />
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ const MarqueeRow = ({
|
||||
|
||||
animationFrame.current = requestAnimationFrame(animate);
|
||||
return () => cancelAnimationFrame(animationFrame.current);
|
||||
}, [containerWidth, reverse, shouldReduceMotion]);
|
||||
}, [containerWidth, reverse, shouldReduceMotion, speed]);
|
||||
|
||||
const pause = () => (isPaused.current = true);
|
||||
const resume = () => {
|
||||
|
||||
|
After Width: | Height: | Size: 571 KiB |
|
After Width: | Height: | Size: 508 KiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 504 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 182 KiB |